1. /*
  2. * Copyright 2000-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.javacc;
  18. import java.io.File;
  19. import java.io.IOException;
  20. import java.util.Enumeration;
  21. import java.util.Hashtable;
  22. import java.util.zip.ZipFile;
  23. import org.apache.tools.ant.BuildException;
  24. import org.apache.tools.ant.Project;
  25. import org.apache.tools.ant.Task;
  26. import org.apache.tools.ant.taskdefs.Execute;
  27. import org.apache.tools.ant.types.Commandline;
  28. import org.apache.tools.ant.types.CommandlineJava;
  29. import org.apache.tools.ant.types.Path;
  30. import org.apache.tools.ant.util.JavaEnvUtils;
  31. /**
  32. * JavaCC compiler compiler task.
  33. *
  34. */
  35. public class JavaCC extends Task {
  36. // keys to optional attributes
  37. private static final String LOOKAHEAD = "LOOKAHEAD";
  38. private static final String CHOICE_AMBIGUITY_CHECK = "CHOICE_AMBIGUITY_CHECK";
  39. private static final String OTHER_AMBIGUITY_CHECK = "OTHER_AMBIGUITY_CHECK";
  40. private static final String STATIC = "STATIC";
  41. private static final String DEBUG_PARSER = "DEBUG_PARSER";
  42. private static final String DEBUG_LOOKAHEAD = "DEBUG_LOOKAHEAD";
  43. private static final String DEBUG_TOKEN_MANAGER = "DEBUG_TOKEN_MANAGER";
  44. private static final String OPTIMIZE_TOKEN_MANAGER = "OPTIMIZE_TOKEN_MANAGER";
  45. private static final String ERROR_REPORTING = "ERROR_REPORTING";
  46. private static final String JAVA_UNICODE_ESCAPE = "JAVA_UNICODE_ESCAPE";
  47. private static final String UNICODE_INPUT = "UNICODE_INPUT";
  48. private static final String IGNORE_CASE = "IGNORE_CASE";
  49. private static final String COMMON_TOKEN_ACTION = "COMMON_TOKEN_ACTION";
  50. private static final String USER_TOKEN_MANAGER = "USER_TOKEN_MANAGER";
  51. private static final String USER_CHAR_STREAM = "USER_CHAR_STREAM";
  52. private static final String BUILD_PARSER = "BUILD_PARSER";
  53. private static final String BUILD_TOKEN_MANAGER = "BUILD_TOKEN_MANAGER";
  54. private static final String SANITY_CHECK = "SANITY_CHECK";
  55. private static final String FORCE_LA_CHECK = "FORCE_LA_CHECK";
  56. private static final String CACHE_TOKENS = "CACHE_TOKENS";
  57. private static final String KEEP_LINE_COLUMN = "KEEP_LINE_COLUMN";
  58. private final Hashtable optionalAttrs = new Hashtable();
  59. // required attributes
  60. private File outputDirectory = null;
  61. private File target = null;
  62. private File javaccHome = null;
  63. private CommandlineJava cmdl = new CommandlineJava();
  64. protected static final int TASKDEF_TYPE_JAVACC = 1;
  65. protected static final int TASKDEF_TYPE_JJTREE = 2;
  66. protected static final int TASKDEF_TYPE_JJDOC = 3;
  67. protected static final String[] ARCHIVE_LOCATIONS =
  68. new String[] {
  69. "JavaCC.zip",
  70. "bin/lib/JavaCC.zip",
  71. "bin/lib/javacc.jar",
  72. "javacc.jar", // used by jpackage for JavaCC 3.x
  73. };
  74. protected static final int[] ARCHIVE_LOCATIONS_VS_MAJOR_VERSION =
  75. new int[] {
  76. 1,
  77. 2,
  78. 3,
  79. 3,
  80. };
  81. protected static final String COM_PACKAGE = "COM.sun.labs.";
  82. protected static final String COM_JAVACC_CLASS = "javacc.Main";
  83. protected static final String COM_JJTREE_CLASS = "jjtree.Main";
  84. protected static final String COM_JJDOC_CLASS = "jjdoc.JJDocMain";
  85. protected static final String ORG_PACKAGE_3_0 = "org.netbeans.javacc.";
  86. protected static final String ORG_PACKAGE_3_1 = "org.javacc.";
  87. protected static final String ORG_JAVACC_CLASS = "parser.Main";
  88. protected static final String ORG_JJTREE_CLASS = COM_JJTREE_CLASS;
  89. protected static final String ORG_JJDOC_CLASS = COM_JJDOC_CLASS;
  90. /**
  91. * Sets the LOOKAHEAD grammar option.
  92. */
  93. public void setLookahead(int lookahead) {
  94. optionalAttrs.put(LOOKAHEAD, new Integer(lookahead));
  95. }
  96. /**
  97. * Sets the CHOICE_AMBIGUITY_CHECK grammar option.
  98. */
  99. public void setChoiceambiguitycheck(int choiceAmbiguityCheck) {
  100. optionalAttrs.put(CHOICE_AMBIGUITY_CHECK, new Integer(choiceAmbiguityCheck));
  101. }
  102. /**
  103. * Sets the OTHER_AMBIGUITY_CHECK grammar option.
  104. */
  105. public void setOtherambiguityCheck(int otherAmbiguityCheck) {
  106. optionalAttrs.put(OTHER_AMBIGUITY_CHECK, new Integer(otherAmbiguityCheck));
  107. }
  108. /**
  109. * Sets the STATIC grammar option.
  110. */
  111. public void setStatic(boolean staticParser) {
  112. optionalAttrs.put(STATIC, new Boolean(staticParser));
  113. }
  114. /**
  115. * Sets the DEBUG_PARSER grammar option.
  116. */
  117. public void setDebugparser(boolean debugParser) {
  118. optionalAttrs.put(DEBUG_PARSER, new Boolean(debugParser));
  119. }
  120. /**
  121. * Sets the DEBUG_LOOKAHEAD grammar option.
  122. */
  123. public void setDebuglookahead(boolean debugLookahead) {
  124. optionalAttrs.put(DEBUG_LOOKAHEAD, new Boolean(debugLookahead));
  125. }
  126. /**
  127. * Sets the DEBUG_TOKEN_MANAGER grammar option.
  128. */
  129. public void setDebugtokenmanager(boolean debugTokenManager) {
  130. optionalAttrs.put(DEBUG_TOKEN_MANAGER, new Boolean(debugTokenManager));
  131. }
  132. /**
  133. * Sets the OPTIMIZE_TOKEN_MANAGER grammar option.
  134. */
  135. public void setOptimizetokenmanager(boolean optimizeTokenManager) {
  136. optionalAttrs.put(OPTIMIZE_TOKEN_MANAGER, new Boolean(optimizeTokenManager));
  137. }
  138. /**
  139. * Sets the ERROR_REPORTING grammar option.
  140. */
  141. public void setErrorreporting(boolean errorReporting) {
  142. optionalAttrs.put(ERROR_REPORTING, new Boolean(errorReporting));
  143. }
  144. /**
  145. * Sets the JAVA_UNICODE_ESCAPE grammar option.
  146. */
  147. public void setJavaunicodeescape(boolean javaUnicodeEscape) {
  148. optionalAttrs.put(JAVA_UNICODE_ESCAPE, new Boolean(javaUnicodeEscape));
  149. }
  150. /**
  151. * Sets the UNICODE_INPUT grammar option.
  152. */
  153. public void setUnicodeinput(boolean unicodeInput) {
  154. optionalAttrs.put(UNICODE_INPUT, new Boolean(unicodeInput));
  155. }
  156. /**
  157. * Sets the IGNORE_CASE grammar option.
  158. */
  159. public void setIgnorecase(boolean ignoreCase) {
  160. optionalAttrs.put(IGNORE_CASE, new Boolean(ignoreCase));
  161. }
  162. /**
  163. * Sets the COMMON_TOKEN_ACTION grammar option.
  164. */
  165. public void setCommontokenaction(boolean commonTokenAction) {
  166. optionalAttrs.put(COMMON_TOKEN_ACTION, new Boolean(commonTokenAction));
  167. }
  168. /**
  169. * Sets the USER_TOKEN_MANAGER grammar option.
  170. */
  171. public void setUsertokenmanager(boolean userTokenManager) {
  172. optionalAttrs.put(USER_TOKEN_MANAGER, new Boolean(userTokenManager));
  173. }
  174. /**
  175. * Sets the USER_CHAR_STREAM grammar option.
  176. */
  177. public void setUsercharstream(boolean userCharStream) {
  178. optionalAttrs.put(USER_CHAR_STREAM, new Boolean(userCharStream));
  179. }
  180. /**
  181. * Sets the BUILD_PARSER grammar option.
  182. */
  183. public void setBuildparser(boolean buildParser) {
  184. optionalAttrs.put(BUILD_PARSER, new Boolean(buildParser));
  185. }
  186. /**
  187. * Sets the BUILD_TOKEN_MANAGER grammar option.
  188. */
  189. public void setBuildtokenmanager(boolean buildTokenManager) {
  190. optionalAttrs.put(BUILD_TOKEN_MANAGER, new Boolean(buildTokenManager));
  191. }
  192. /**
  193. * Sets the SANITY_CHECK grammar option.
  194. */
  195. public void setSanitycheck(boolean sanityCheck) {
  196. optionalAttrs.put(SANITY_CHECK, new Boolean(sanityCheck));
  197. }
  198. /**
  199. * Sets the FORCE_LA_CHECK grammar option.
  200. */
  201. public void setForcelacheck(boolean forceLACheck) {
  202. optionalAttrs.put(FORCE_LA_CHECK, new Boolean(forceLACheck));
  203. }
  204. /**
  205. * Sets the CACHE_TOKENS grammar option.
  206. */
  207. public void setCachetokens(boolean cacheTokens) {
  208. optionalAttrs.put(CACHE_TOKENS, new Boolean(cacheTokens));
  209. }
  210. /**
  211. * Sets the KEEP_LINE_COLUMN grammar option.
  212. */
  213. public void setKeeplinecolumn(boolean keepLineColumn) {
  214. optionalAttrs.put(KEEP_LINE_COLUMN, new Boolean(keepLineColumn));
  215. }
  216. /**
  217. * The directory to write the generated files to.
  218. * If not set, the files are written to the directory
  219. * containing the grammar file.
  220. */
  221. public void setOutputdirectory(File outputDirectory) {
  222. this.outputDirectory = outputDirectory;
  223. }
  224. /**
  225. * The grammar file to process.
  226. */
  227. public void setTarget(File target) {
  228. this.target = target;
  229. }
  230. /**
  231. * The directory containing the JavaCC distribution.
  232. */
  233. public void setJavacchome(File javaccHome) {
  234. this.javaccHome = javaccHome;
  235. }
  236. public JavaCC() {
  237. cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
  238. }
  239. public void execute() throws BuildException {
  240. // load command line with optional attributes
  241. Enumeration iter = optionalAttrs.keys();
  242. while (iter.hasMoreElements()) {
  243. String name = (String) iter.nextElement();
  244. Object value = optionalAttrs.get(name);
  245. cmdl.createArgument().setValue("-" + name + ":" + value.toString());
  246. }
  247. // check the target is a file
  248. if (target == null || !target.isFile()) {
  249. throw new BuildException("Invalid target: " + target);
  250. }
  251. // use the directory containing the target as the output directory
  252. if (outputDirectory == null) {
  253. outputDirectory = new File(target.getParent());
  254. } else if (!outputDirectory.isDirectory()) {
  255. throw new BuildException("Outputdir not a directory.");
  256. }
  257. cmdl.createArgument().setValue("-OUTPUT_DIRECTORY:"
  258. + outputDirectory.getAbsolutePath());
  259. // determine if the generated java file is up-to-date
  260. final File javaFile = getOutputJavaFile(outputDirectory, target);
  261. if (javaFile.exists() && target.lastModified() < javaFile.lastModified()) {
  262. log("Target is already built - skipping (" + target + ")", Project.MSG_VERBOSE);
  263. return;
  264. }
  265. cmdl.createArgument().setValue(target.getAbsolutePath());
  266. cmdl.setClassname(JavaCC.getMainClass(javaccHome,
  267. JavaCC.TASKDEF_TYPE_JAVACC));
  268. final Path classpath = cmdl.createClasspath(getProject());
  269. final File javaccJar = JavaCC.getArchiveFile(javaccHome);
  270. classpath.createPathElement().setPath(javaccJar.getAbsolutePath());
  271. classpath.addJavaRuntime();
  272. final Commandline.Argument arg = cmdl.createVmArgument();
  273. arg.setValue("-mx140M");
  274. arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
  275. Execute.runCommand(this, cmdl.getCommandline());
  276. }
  277. /**
  278. * Helper method to retrieve the path used to store the JavaCC.zip
  279. * or javacc.jar which is different from versions.
  280. *
  281. * @param home the javacc home path directory.
  282. * @throws BuildException thrown if the home directory is invalid
  283. * or if the archive could not be found despite attempts to do so.
  284. * @return the file object pointing to the JavaCC archive.
  285. */
  286. protected static File getArchiveFile(File home) throws BuildException {
  287. return new File(home,
  288. ARCHIVE_LOCATIONS[getArchiveLocationIndex(home)]);
  289. }
  290. /**
  291. * Helper method to retrieve main class which is different from versions.
  292. * @param home the javacc home path directory.
  293. * @param type the taskdef.
  294. * @throws BuildException thrown if the home directory is invalid
  295. * or if the archive could not be found despite attempts to do so.
  296. * @return the main class for the taskdef.
  297. */
  298. protected static String getMainClass(File home, int type)
  299. throws BuildException {
  300. int majorVersion = getMajorVersionNumber(home);
  301. String packagePrefix = null;
  302. String mainClass = null;
  303. switch (majorVersion) {
  304. case 1:
  305. case 2:
  306. packagePrefix = COM_PACKAGE;
  307. switch (type) {
  308. case TASKDEF_TYPE_JAVACC:
  309. mainClass = COM_JAVACC_CLASS;
  310. break;
  311. case TASKDEF_TYPE_JJTREE:
  312. mainClass = COM_JJTREE_CLASS;
  313. break;
  314. case TASKDEF_TYPE_JJDOC:
  315. mainClass = COM_JJDOC_CLASS;
  316. break;
  317. }
  318. break;
  319. case 3:
  320. /*
  321. * This is where the fun starts, JavaCC 3.0 uses
  322. * org.netbeans.javacc, 3.1 uses org.javacc - I wonder
  323. * which version is going to use net.java.javacc.
  324. *
  325. * Look into to the archive to pick up the best
  326. * package.
  327. */
  328. ZipFile zf = null;
  329. try {
  330. zf = new ZipFile(getArchiveFile(home));
  331. if (zf.getEntry(ORG_PACKAGE_3_0.replace('.', '/')) != null) {
  332. packagePrefix = ORG_PACKAGE_3_0;
  333. } else {
  334. packagePrefix = ORG_PACKAGE_3_1;
  335. }
  336. } catch (IOException e) {
  337. throw new BuildException("Error reading javacc.jar", e);
  338. } finally {
  339. if (zf != null) {
  340. try {
  341. zf.close();
  342. } catch (IOException e) {
  343. throw new BuildException(e);
  344. }
  345. }
  346. }
  347. switch (type) {
  348. case TASKDEF_TYPE_JAVACC:
  349. mainClass = ORG_JAVACC_CLASS;
  350. break;
  351. case TASKDEF_TYPE_JJTREE:
  352. mainClass = ORG_JJTREE_CLASS;
  353. break;
  354. case TASKDEF_TYPE_JJDOC:
  355. mainClass = ORG_JJDOC_CLASS;
  356. break;
  357. }
  358. break;
  359. }
  360. return packagePrefix + mainClass;
  361. }
  362. /**
  363. * Helper method to determine the archive location index.
  364. *
  365. * @param home the javacc home path directory.
  366. * @throws BuildException thrown if the home directory is invalid
  367. * or if the archive could not be found despite attempts to do so.
  368. * @return the archive location index
  369. */
  370. private static int getArchiveLocationIndex(File home)
  371. throws BuildException {
  372. if (home == null || !home.isDirectory()) {
  373. throw new BuildException("JavaCC home must be a valid directory.");
  374. }
  375. for (int i = 0; i < ARCHIVE_LOCATIONS.length; i++) {
  376. File f = new File(home, ARCHIVE_LOCATIONS[i]);
  377. if (f.exists()) {
  378. return i;
  379. }
  380. }
  381. throw new BuildException("Could not find a path to JavaCC.zip "
  382. + "or javacc.jar from '" + home + "'.");
  383. }
  384. /**
  385. * Helper method to determine the major version number of JavaCC.
  386. *
  387. * @param home the javacc home path directory.
  388. * @throws BuildException thrown if the home directory is invalid
  389. * or if the archive could not be found despite attempts to do so.
  390. * @return a the major version number
  391. */
  392. protected static int getMajorVersionNumber(File home)
  393. throws BuildException {
  394. return
  395. ARCHIVE_LOCATIONS_VS_MAJOR_VERSION[getArchiveLocationIndex(home)];
  396. }
  397. /**
  398. * Determines the output Java file to be generated by the given grammar
  399. * file.
  400. *
  401. */
  402. private File getOutputJavaFile(File outputdir, File srcfile) {
  403. String path = srcfile.getPath();
  404. // Extract file's base-name
  405. int startBasename = path.lastIndexOf(File.separator);
  406. if (startBasename != -1) {
  407. path = path.substring(startBasename + 1);
  408. }
  409. // Replace the file's extension with '.java'
  410. int startExtn = path.lastIndexOf('.');
  411. if (startExtn != -1) {
  412. path = path.substring(0, startExtn) + ".java";
  413. } else {
  414. path += ".java";
  415. }
  416. // Change the directory
  417. if (outputdir != null) {
  418. path = outputdir + File.separator + path;
  419. }
  420. return new File(path);
  421. }
  422. }