1. /*
  2. * Copyright 2001-2004 The Apache Software Foundation
  3. *
  4. * Licensed under the Apache License, Version 2.0 (the "License");
  5. * you may not use this file except in compliance with the License.
  6. * You may obtain a copy of the License at
  7. *
  8. * http://www.apache.org/licenses/LICENSE-2.0
  9. *
  10. * Unless required by applicable law or agreed to in writing, software
  11. * distributed under the License is distributed on an "AS IS" BASIS,
  12. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13. * See the License for the specific language governing permissions and
  14. * limitations under the License.
  15. *
  16. */
  17. package org.apache.tools.ant.taskdefs.optional.metamata;
  18. import java.io.File;
  19. import java.io.FileWriter;
  20. import java.io.IOException;
  21. import java.io.PrintWriter;
  22. import java.util.Vector;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.Project;
  25. import org.apache.tools.ant.taskdefs.Execute;
  26. import org.apache.tools.ant.taskdefs.ExecuteStreamHandler;
  27. import org.apache.tools.ant.taskdefs.LogStreamHandler;
  28. import org.apache.tools.ant.types.Commandline;
  29. import org.apache.tools.ant.types.Path;
  30. import org.apache.tools.ant.util.JavaEnvUtils;
  31. /**
  32. * Simple Metamata MParse task.
  33. * Based on the original written by
  34. * <a href="mailto:thomas.haas@softwired-inc.com">Thomas Haas</a>.
  35. *
  36. * This version was written for Metamata 2.0 available at
  37. * <a href="http://www.metamata.com">http://www.metamata.com</a>
  38. *
  39. * @todo make a subclass of AbstractMetaMataTask
  40. */
  41. public class MParse extends AbstractMetamataTask {
  42. private File target = null;
  43. private boolean verbose = false;
  44. private boolean debugparser = false;
  45. private boolean debugscanner = false;
  46. private boolean cleanup = false;
  47. /** The .jj file to process; required. */
  48. public void setTarget(File target) {
  49. this.target = target;
  50. }
  51. /** set verbose mode */
  52. public void setVerbose(boolean flag) {
  53. verbose = flag;
  54. }
  55. /** set scanner debug mode; optional, default false */
  56. public void setDebugscanner(boolean flag) {
  57. debugscanner = flag;
  58. }
  59. /** set parser debug mode; optional, default false */
  60. public void setDebugparser(boolean flag) {
  61. debugparser = flag;
  62. }
  63. /** Remove the intermediate Sun JavaCC file
  64. * ; optional, default false.
  65. */
  66. public void setCleanup(boolean value) {
  67. cleanup = value;
  68. }
  69. public MParse() {
  70. cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
  71. cmdl.setClassname("com.metamata.jj.MParse");
  72. }
  73. /** execute the command line */
  74. public void execute() throws BuildException {
  75. try {
  76. setUp();
  77. ExecuteStreamHandler handler = createStreamHandler();
  78. _execute(handler);
  79. } finally {
  80. cleanUp();
  81. }
  82. }
  83. /** return the default stream handler for this task */
  84. protected ExecuteStreamHandler createStreamHandler() {
  85. return new LogStreamHandler(this, Project.MSG_INFO, Project.MSG_INFO);
  86. }
  87. /**
  88. * check the options and build the command line
  89. */
  90. protected void setUp() throws BuildException {
  91. checkOptions();
  92. // set the classpath as the jar files
  93. File[] jars = getMetamataLibs();
  94. final Path classPath = cmdl.createClasspath(getProject());
  95. for (int i = 0; i < jars.length; i++) {
  96. classPath.createPathElement().setLocation(jars[i]);
  97. }
  98. // set the metamata.home property
  99. final Commandline.Argument vmArgs = cmdl.createVmArgument();
  100. vmArgs.setValue("-Dmetamata.home=" + metamataHome.getAbsolutePath());
  101. // write all the options to a temp file and use it ro run the process
  102. Vector opts = getOptions();
  103. String[] options = new String[ opts.size() ];
  104. opts.copyInto(options);
  105. optionsFile = createTmpFile();
  106. generateOptionsFile(optionsFile, options);
  107. Commandline.Argument args = cmdl.createArgument();
  108. args.setLine("-arguments " + optionsFile.getAbsolutePath());
  109. }
  110. /** execute the process with a specific handler */
  111. protected void _execute(ExecuteStreamHandler handler) throws BuildException {
  112. // target has been checked as a .jj, see if there is a matching
  113. // java file and if it is needed to run to process the grammar
  114. String pathname = target.getAbsolutePath();
  115. int pos = pathname.length() - ".jj".length();
  116. pathname = pathname.substring(0, pos) + ".java";
  117. File javaFile = new File(pathname);
  118. if (javaFile.exists() && target.lastModified() < javaFile.lastModified()) {
  119. getProject().log("Target is already build - skipping (" + target + ")");
  120. return;
  121. }
  122. final Execute process = new Execute(handler);
  123. log(cmdl.describeCommand(), Project.MSG_VERBOSE);
  124. process.setCommandline(cmdl.getCommandline());
  125. try {
  126. if (process.execute() != 0) {
  127. throw new BuildException("Metamata task failed.");
  128. }
  129. } catch (IOException e) {
  130. throw new BuildException("Failed to launch Metamata task: ", e);
  131. }
  132. }
  133. /** clean up all the mess that we did with temporary objects */
  134. protected void cleanUp() {
  135. if (optionsFile != null) {
  136. optionsFile.delete();
  137. optionsFile = null;
  138. }
  139. if (cleanup) {
  140. String name = target.getName();
  141. int pos = name.length() - ".jj".length();
  142. name = "__jj" + name.substring(0, pos) + ".sunjj";
  143. final File sunjj = new File(target.getParent(), name);
  144. if (sunjj.exists()) {
  145. getProject().log("Removing stale file: " + sunjj.getName());
  146. sunjj.delete();
  147. }
  148. }
  149. }
  150. /**
  151. * return an array of files containing the path to the needed
  152. * libraries to run metamata. The file are not checked for
  153. * existence. You should do this yourself if needed or simply let the
  154. * forked process do it for you.
  155. * @return array of jars/zips needed to run metamata.
  156. */
  157. protected File[] getMetamataLibs() {
  158. Vector files = new Vector();
  159. files.addElement(new File(metamataHome, "lib/metamata.jar"));
  160. files.addElement(new File(metamataHome, "bin/lib/JavaCC.zip"));
  161. File[] array = new File[ files.size() ];
  162. files.copyInto(array);
  163. return array;
  164. }
  165. /**
  166. * validate options set and resolve files and paths
  167. * @throws BuildException thrown if an option has an incorrect state.
  168. */
  169. protected void checkOptions() throws BuildException {
  170. // check that the home is ok.
  171. if (metamataHome == null || !metamataHome.exists()) {
  172. throw new BuildException("'metamatahome' must point to Metamata home directory.");
  173. }
  174. metamataHome = getProject().resolveFile(metamataHome.getPath());
  175. // check that the needed jar exists.
  176. File[] jars = getMetamataLibs();
  177. for (int i = 0; i < jars.length; i++) {
  178. if (!jars[i].exists()) {
  179. throw new BuildException(jars[i]
  180. + " does not exist. Check your metamata installation.");
  181. }
  182. }
  183. // check that the target is ok and resolve it.
  184. if (target == null || !target.isFile()
  185. || !target.getName().endsWith(".jj")) {
  186. throw new BuildException("Invalid target: " + target);
  187. }
  188. target = getProject().resolveFile(target.getPath());
  189. }
  190. /**
  191. * return all options of the command line as string elements
  192. * @return an array of options corresponding to the setted options.
  193. */
  194. protected Vector getOptions() {
  195. Vector options = new Vector();
  196. if (verbose) {
  197. options.addElement("-verbose");
  198. }
  199. if (debugscanner) {
  200. options.addElement("-ds");
  201. }
  202. if (debugparser) {
  203. options.addElement("-dp");
  204. }
  205. if (classPath != null) {
  206. options.addElement("-classpath");
  207. options.addElement(classPath.toString());
  208. }
  209. if (sourcePath != null) {
  210. options.addElement("-sourcepath");
  211. options.addElement(sourcePath.toString());
  212. }
  213. options.addElement(target.getAbsolutePath());
  214. return options;
  215. }
  216. /**
  217. * write all options to a file with one option / line
  218. * @param tofile the file to write the options to.
  219. * @param options the array of options element to write to the file.
  220. * @throws BuildException thrown if there is a problem while writing
  221. * to the file.
  222. */
  223. protected void generateOptionsFile(File tofile, String[] options) throws BuildException {
  224. FileWriter fw = null;
  225. try {
  226. fw = new FileWriter(tofile);
  227. PrintWriter pw = new PrintWriter(fw);
  228. for (int i = 0; i < options.length; i++) {
  229. pw.println(options[i]);
  230. }
  231. pw.flush();
  232. } catch (IOException e) {
  233. throw new BuildException("Error while writing options file " + tofile, e);
  234. } finally {
  235. if (fw != null) {
  236. try {
  237. fw.close();
  238. } catch (IOException ignored) {
  239. // ignore
  240. }
  241. }
  242. }
  243. }
  244. }