001: /*
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: *
017: */
018:
019: package org.apache.tools.ant.taskdefs.optional.javacc;
020:
021: import java.io.File;
022: import java.io.InputStream;
023: import java.util.Enumeration;
024: import java.util.Hashtable;
025:
026: import org.apache.tools.ant.AntClassLoader;
027: import org.apache.tools.ant.BuildException;
028: import org.apache.tools.ant.Project;
029: import org.apache.tools.ant.Task;
030: import org.apache.tools.ant.taskdefs.Execute;
031: import org.apache.tools.ant.types.Commandline;
032: import org.apache.tools.ant.types.CommandlineJava;
033: import org.apache.tools.ant.types.Path;
034: import org.apache.tools.ant.util.JavaEnvUtils;
035:
036: /**
037: * JavaCC compiler compiler task.
038: *
039: */
040: public class JavaCC extends Task {
041:
042: // keys to optional attributes
043: private static final String LOOKAHEAD = "LOOKAHEAD";
044: private static final String CHOICE_AMBIGUITY_CHECK = "CHOICE_AMBIGUITY_CHECK";
045: private static final String OTHER_AMBIGUITY_CHECK = "OTHER_AMBIGUITY_CHECK";
046:
047: private static final String STATIC = "STATIC";
048: private static final String DEBUG_PARSER = "DEBUG_PARSER";
049: private static final String DEBUG_LOOKAHEAD = "DEBUG_LOOKAHEAD";
050: private static final String DEBUG_TOKEN_MANAGER = "DEBUG_TOKEN_MANAGER";
051: private static final String OPTIMIZE_TOKEN_MANAGER = "OPTIMIZE_TOKEN_MANAGER";
052: private static final String ERROR_REPORTING = "ERROR_REPORTING";
053: private static final String JAVA_UNICODE_ESCAPE = "JAVA_UNICODE_ESCAPE";
054: private static final String UNICODE_INPUT = "UNICODE_INPUT";
055: private static final String IGNORE_CASE = "IGNORE_CASE";
056: private static final String COMMON_TOKEN_ACTION = "COMMON_TOKEN_ACTION";
057: private static final String USER_TOKEN_MANAGER = "USER_TOKEN_MANAGER";
058: private static final String USER_CHAR_STREAM = "USER_CHAR_STREAM";
059: private static final String BUILD_PARSER = "BUILD_PARSER";
060: private static final String BUILD_TOKEN_MANAGER = "BUILD_TOKEN_MANAGER";
061: private static final String SANITY_CHECK = "SANITY_CHECK";
062: private static final String FORCE_LA_CHECK = "FORCE_LA_CHECK";
063: private static final String CACHE_TOKENS = "CACHE_TOKENS";
064: private static final String KEEP_LINE_COLUMN = "KEEP_LINE_COLUMN";
065: private static final String JDK_VERSION = "JDK_VERSION";
066:
067: private final Hashtable optionalAttrs = new Hashtable();
068:
069: // required attributes
070: private File outputDirectory = null;
071: private File targetFile = null;
072: private File javaccHome = null;
073:
074: private CommandlineJava cmdl = new CommandlineJava();
075:
076: protected static final int TASKDEF_TYPE_JAVACC = 1;
077: protected static final int TASKDEF_TYPE_JJTREE = 2;
078: protected static final int TASKDEF_TYPE_JJDOC = 3;
079:
080: protected static final String[] ARCHIVE_LOCATIONS = new String[] {
081: "JavaCC.zip", "bin/lib/JavaCC.zip", "bin/lib/javacc.jar",
082: "javacc.jar", // used by jpackage for JavaCC 3.x
083: };
084:
085: protected static final int[] ARCHIVE_LOCATIONS_VS_MAJOR_VERSION = new int[] {
086: 1, 2, 3, 3, };
087:
088: protected static final String COM_PACKAGE = "COM.sun.labs.";
089: protected static final String COM_JAVACC_CLASS = "javacc.Main";
090: protected static final String COM_JJTREE_CLASS = "jjtree.Main";
091: protected static final String COM_JJDOC_CLASS = "jjdoc.JJDocMain";
092:
093: protected static final String ORG_PACKAGE_3_0 = "org.netbeans.javacc.";
094: protected static final String ORG_PACKAGE_3_1 = "org.javacc.";
095: protected static final String ORG_JAVACC_CLASS = "parser.Main";
096: protected static final String ORG_JJTREE_CLASS = COM_JJTREE_CLASS;
097: protected static final String ORG_JJDOC_CLASS = COM_JJDOC_CLASS;
098:
099: /**
100: * Sets the LOOKAHEAD grammar option.
101: * @param lookahead an <code>int</code> value.
102: */
103: public void setLookahead(int lookahead) {
104: optionalAttrs.put(LOOKAHEAD, new Integer(lookahead));
105: }
106:
107: /**
108: * Sets the CHOICE_AMBIGUITY_CHECK grammar option.
109: * @param choiceAmbiguityCheck an <code>int</code> value.
110: */
111: public void setChoiceambiguitycheck(int choiceAmbiguityCheck) {
112: optionalAttrs.put(CHOICE_AMBIGUITY_CHECK, new Integer(
113: choiceAmbiguityCheck));
114: }
115:
116: /**
117: * Sets the OTHER_AMBIGUITY_CHECK grammar option.
118: * @param otherAmbiguityCheck an <code>int</code> value.
119: */
120: public void setOtherambiguityCheck(int otherAmbiguityCheck) {
121: optionalAttrs.put(OTHER_AMBIGUITY_CHECK, new Integer(
122: otherAmbiguityCheck));
123: }
124:
125: /**
126: * Sets the STATIC grammar option.
127: * @param staticParser a <code>boolean</code> value.
128: */
129: public void setStatic(boolean staticParser) {
130: optionalAttrs.put(STATIC, staticParser ? Boolean.TRUE
131: : Boolean.FALSE);
132: }
133:
134: /**
135: * Sets the DEBUG_PARSER grammar option.
136: * @param debugParser a <code>boolean</code> value.
137: */
138: public void setDebugparser(boolean debugParser) {
139: optionalAttrs.put(DEBUG_PARSER, debugParser ? Boolean.TRUE
140: : Boolean.FALSE);
141: }
142:
143: /**
144: * Sets the DEBUG_LOOKAHEAD grammar option.
145: * @param debugLookahead a <code>boolean</code> value.
146: */
147: public void setDebuglookahead(boolean debugLookahead) {
148: optionalAttrs.put(DEBUG_LOOKAHEAD,
149: debugLookahead ? Boolean.TRUE : Boolean.FALSE);
150: }
151:
152: /**
153: * Sets the DEBUG_TOKEN_MANAGER grammar option.
154: * @param debugTokenManager a <code>boolean</code> value.
155: */
156: public void setDebugtokenmanager(boolean debugTokenManager) {
157: optionalAttrs.put(DEBUG_TOKEN_MANAGER,
158: debugTokenManager ? Boolean.TRUE : Boolean.FALSE);
159: }
160:
161: /**
162: * Sets the OPTIMIZE_TOKEN_MANAGER grammar option.
163: * @param optimizeTokenManager a <code>boolean</code> value.
164: */
165: public void setOptimizetokenmanager(boolean optimizeTokenManager) {
166: optionalAttrs.put(OPTIMIZE_TOKEN_MANAGER,
167: optimizeTokenManager ? Boolean.TRUE : Boolean.FALSE);
168: }
169:
170: /**
171: * Sets the ERROR_REPORTING grammar option.
172: * @param errorReporting a <code>boolean</code> value.
173: */
174: public void setErrorreporting(boolean errorReporting) {
175: optionalAttrs.put(ERROR_REPORTING,
176: errorReporting ? Boolean.TRUE : Boolean.FALSE);
177: }
178:
179: /**
180: * Sets the JAVA_UNICODE_ESCAPE grammar option.
181: * @param javaUnicodeEscape a <code>boolean</code> value.
182: */
183: public void setJavaunicodeescape(boolean javaUnicodeEscape) {
184: optionalAttrs.put(JAVA_UNICODE_ESCAPE,
185: javaUnicodeEscape ? Boolean.TRUE : Boolean.FALSE);
186: }
187:
188: /**
189: * Sets the UNICODE_INPUT grammar option.
190: * @param unicodeInput a <code>boolean</code> value.
191: */
192: public void setUnicodeinput(boolean unicodeInput) {
193: optionalAttrs.put(UNICODE_INPUT, unicodeInput ? Boolean.TRUE
194: : Boolean.FALSE);
195: }
196:
197: /**
198: * Sets the IGNORE_CASE grammar option.
199: * @param ignoreCase a <code>boolean</code> value.
200: */
201: public void setIgnorecase(boolean ignoreCase) {
202: optionalAttrs.put(IGNORE_CASE, ignoreCase ? Boolean.TRUE
203: : Boolean.FALSE);
204: }
205:
206: /**
207: * Sets the COMMON_TOKEN_ACTION grammar option.
208: * @param commonTokenAction a <code>boolean</code> value.
209: */
210: public void setCommontokenaction(boolean commonTokenAction) {
211: optionalAttrs.put(COMMON_TOKEN_ACTION,
212: commonTokenAction ? Boolean.TRUE : Boolean.FALSE);
213: }
214:
215: /**
216: * Sets the USER_TOKEN_MANAGER grammar option.
217: * @param userTokenManager a <code>boolean</code> value.
218: */
219: public void setUsertokenmanager(boolean userTokenManager) {
220: optionalAttrs.put(USER_TOKEN_MANAGER,
221: userTokenManager ? Boolean.TRUE : Boolean.FALSE);
222: }
223:
224: /**
225: * Sets the USER_CHAR_STREAM grammar option.
226: * @param userCharStream a <code>boolean</code> value.
227: */
228: public void setUsercharstream(boolean userCharStream) {
229: optionalAttrs.put(USER_CHAR_STREAM,
230: userCharStream ? Boolean.TRUE : Boolean.FALSE);
231: }
232:
233: /**
234: * Sets the BUILD_PARSER grammar option.
235: * @param buildParser a <code>boolean</code> value.
236: */
237: public void setBuildparser(boolean buildParser) {
238: optionalAttrs.put(BUILD_PARSER, buildParser ? Boolean.TRUE
239: : Boolean.FALSE);
240: }
241:
242: /**
243: * Sets the BUILD_TOKEN_MANAGER grammar option.
244: * @param buildTokenManager a <code>boolean</code> value.
245: */
246: public void setBuildtokenmanager(boolean buildTokenManager) {
247: optionalAttrs.put(BUILD_TOKEN_MANAGER,
248: buildTokenManager ? Boolean.TRUE : Boolean.FALSE);
249: }
250:
251: /**
252: * Sets the SANITY_CHECK grammar option.
253: * @param sanityCheck a <code>boolean</code> value.
254: */
255: public void setSanitycheck(boolean sanityCheck) {
256: optionalAttrs.put(SANITY_CHECK, sanityCheck ? Boolean.TRUE
257: : Boolean.FALSE);
258: }
259:
260: /**
261: * Sets the FORCE_LA_CHECK grammar option.
262: * @param forceLACheck a <code>boolean</code> value.
263: */
264: public void setForcelacheck(boolean forceLACheck) {
265: optionalAttrs.put(FORCE_LA_CHECK, forceLACheck ? Boolean.TRUE
266: : Boolean.FALSE);
267: }
268:
269: /**
270: * Sets the CACHE_TOKENS grammar option.
271: * @param cacheTokens a <code>boolean</code> value.
272: */
273: public void setCachetokens(boolean cacheTokens) {
274: optionalAttrs.put(CACHE_TOKENS, cacheTokens ? Boolean.TRUE
275: : Boolean.FALSE);
276: }
277:
278: /**
279: * Sets the KEEP_LINE_COLUMN grammar option.
280: * @param keepLineColumn a <code>boolean</code> value.
281: */
282: public void setKeeplinecolumn(boolean keepLineColumn) {
283: optionalAttrs.put(KEEP_LINE_COLUMN,
284: keepLineColumn ? Boolean.TRUE : Boolean.FALSE);
285: }
286:
287: /**
288: * Sets the JDK_VERSION option.
289: * @param jdkVersion the version to use.
290: * @since Ant1.7
291: */
292: public void setJDKversion(String jdkVersion) {
293: optionalAttrs.put(JDK_VERSION, jdkVersion);
294: }
295:
296: /**
297: * The directory to write the generated files to.
298: * If not set, the files are written to the directory
299: * containing the grammar file.
300: * @param outputDirectory the output directory.
301: */
302: public void setOutputdirectory(File outputDirectory) {
303: this .outputDirectory = outputDirectory;
304: }
305:
306: /**
307: * The grammar file to process.
308: * @param targetFile the grammar file.
309: */
310: public void setTarget(File targetFile) {
311: this .targetFile = targetFile;
312: }
313:
314: /**
315: * The directory containing the JavaCC distribution.
316: * @param javaccHome the directory.
317: */
318: public void setJavacchome(File javaccHome) {
319: this .javaccHome = javaccHome;
320: }
321:
322: /**
323: * Constructor
324: */
325: public JavaCC() {
326: cmdl.setVm(JavaEnvUtils.getJreExecutable("java"));
327: }
328:
329: /**
330: * Run the task.
331: * @throws BuildException on error.
332: */
333: public void execute() throws BuildException {
334:
335: // load command line with optional attributes
336: Enumeration iter = optionalAttrs.keys();
337: while (iter.hasMoreElements()) {
338: String name = (String) iter.nextElement();
339: Object value = optionalAttrs.get(name);
340: cmdl.createArgument().setValue(
341: "-" + name + ":" + value.toString());
342: }
343:
344: // check the target is a file
345: if (targetFile == null || !targetFile.isFile()) {
346: throw new BuildException("Invalid target: " + targetFile);
347: }
348:
349: // use the directory containing the target as the output directory
350: if (outputDirectory == null) {
351: outputDirectory = new File(targetFile.getParent());
352: } else if (!outputDirectory.isDirectory()) {
353: throw new BuildException("Outputdir not a directory.");
354: }
355: cmdl.createArgument().setValue(
356: "-OUTPUT_DIRECTORY:"
357: + outputDirectory.getAbsolutePath());
358:
359: // determine if the generated java file is up-to-date
360: final File javaFile = getOutputJavaFile(outputDirectory,
361: targetFile);
362: if (javaFile.exists()
363: && targetFile.lastModified() < javaFile.lastModified()) {
364: log("Target is already built - skipping (" + targetFile
365: + ")", Project.MSG_VERBOSE);
366: return;
367: }
368: cmdl.createArgument().setValue(targetFile.getAbsolutePath());
369:
370: final Path classpath = cmdl.createClasspath(getProject());
371: final File javaccJar = JavaCC.getArchiveFile(javaccHome);
372: classpath.createPathElement().setPath(
373: javaccJar.getAbsolutePath());
374: classpath.addJavaRuntime();
375:
376: cmdl.setClassname(JavaCC.getMainClass(classpath,
377: JavaCC.TASKDEF_TYPE_JAVACC));
378:
379: final Commandline.Argument arg = cmdl.createVmArgument();
380: arg.setValue("-mx140M");
381: arg.setValue("-Dinstall.root=" + javaccHome.getAbsolutePath());
382:
383: Execute.runCommand(this , cmdl.getCommandline());
384: }
385:
386: /**
387: * Helper method to retrieve the path used to store the JavaCC.zip
388: * or javacc.jar which is different from versions.
389: *
390: * @param home the javacc home path directory.
391: * @throws BuildException thrown if the home directory is invalid
392: * or if the archive could not be found despite attempts to do so.
393: * @return the file object pointing to the JavaCC archive.
394: */
395: protected static File getArchiveFile(File home)
396: throws BuildException {
397: return new File(home,
398: ARCHIVE_LOCATIONS[getArchiveLocationIndex(home)]);
399: }
400:
401: /**
402: * Helper method to retrieve main class which is different from versions.
403: * @param home the javacc home path directory.
404: * @param type the taskdef.
405: * @throws BuildException thrown if the home directory is invalid
406: * or if the archive could not be found despite attempts to do so.
407: * @return the main class for the taskdef.
408: */
409: protected static String getMainClass(File home, int type)
410: throws BuildException {
411:
412: Path p = new Path(null);
413: p.createPathElement().setLocation(getArchiveFile(home));
414: p.addJavaRuntime();
415: return getMainClass(p, type);
416: }
417:
418: /**
419: * Helper method to retrieve main class which is different from versions.
420: * @param path classpath to search in.
421: * @param type the taskdef.
422: * @throws BuildException thrown if the home directory is invalid
423: * or if the archive could not be found despite attempts to do so.
424: * @return the main class for the taskdef.
425: * @since Ant 1.7
426: */
427: protected static String getMainClass(Path path, int type)
428: throws BuildException {
429: String packagePrefix = null;
430: String mainClass = null;
431:
432: AntClassLoader l = new AntClassLoader();
433: l.setClassPath(path.concatSystemClasspath("ignore"));
434: String javaccClass = COM_PACKAGE + COM_JAVACC_CLASS;
435: InputStream is = l.getResourceAsStream(javaccClass.replace('.',
436: '/')
437: + ".class");
438: if (is != null) {
439: packagePrefix = COM_PACKAGE;
440: switch (type) {
441: case TASKDEF_TYPE_JAVACC:
442: mainClass = COM_JAVACC_CLASS;
443:
444: break;
445:
446: case TASKDEF_TYPE_JJTREE:
447: mainClass = COM_JJTREE_CLASS;
448:
449: break;
450:
451: case TASKDEF_TYPE_JJDOC:
452: mainClass = COM_JJDOC_CLASS;
453:
454: break;
455: default:
456: // Fall Through
457: }
458: } else {
459: javaccClass = ORG_PACKAGE_3_1 + ORG_JAVACC_CLASS;
460: is = l.getResourceAsStream(javaccClass.replace('.', '/')
461: + ".class");
462: if (is != null) {
463: packagePrefix = ORG_PACKAGE_3_1;
464: } else {
465: javaccClass = ORG_PACKAGE_3_0 + ORG_JAVACC_CLASS;
466: is = l.getResourceAsStream(javaccClass
467: .replace('.', '/')
468: + ".class");
469: if (is != null) {
470: packagePrefix = ORG_PACKAGE_3_0;
471: }
472: }
473:
474: if (is != null) {
475: switch (type) {
476: case TASKDEF_TYPE_JAVACC:
477: mainClass = ORG_JAVACC_CLASS;
478:
479: break;
480:
481: case TASKDEF_TYPE_JJTREE:
482: mainClass = ORG_JJTREE_CLASS;
483:
484: break;
485:
486: case TASKDEF_TYPE_JJDOC:
487: mainClass = ORG_JJDOC_CLASS;
488:
489: break;
490: default:
491: // Fall Through
492: }
493: }
494: }
495:
496: if (packagePrefix == null) {
497: throw new BuildException("failed to load JavaCC");
498: }
499: if (mainClass == null) {
500: throw new BuildException("unknown task type " + type);
501: }
502: return packagePrefix + mainClass;
503: }
504:
505: /**
506: * Helper method to determine the archive location index.
507: *
508: * @param home the javacc home path directory.
509: * @throws BuildException thrown if the home directory is invalid
510: * or if the archive could not be found despite attempts to do so.
511: * @return the archive location index
512: */
513: private static int getArchiveLocationIndex(File home)
514: throws BuildException {
515:
516: if (home == null || !home.isDirectory()) {
517: throw new BuildException(
518: "JavaCC home must be a valid directory.");
519: }
520:
521: for (int i = 0; i < ARCHIVE_LOCATIONS.length; i++) {
522: File f = new File(home, ARCHIVE_LOCATIONS[i]);
523:
524: if (f.exists()) {
525: return i;
526: }
527: }
528:
529: throw new BuildException("Could not find a path to JavaCC.zip "
530: + "or javacc.jar from '" + home + "'.");
531: }
532:
533: /**
534: * Helper method to determine the major version number of JavaCC.
535: *
536: * @param home the javacc home path directory.
537: * @throws BuildException thrown if the home directory is invalid
538: * or if the archive could not be found despite attempts to do so.
539: * @return a the major version number
540: */
541: protected static int getMajorVersionNumber(File home)
542: throws BuildException {
543:
544: return ARCHIVE_LOCATIONS_VS_MAJOR_VERSION[getArchiveLocationIndex(home)];
545: }
546:
547: /**
548: * Determines the output Java file to be generated by the given grammar
549: * file.
550: *
551: */
552: private File getOutputJavaFile(File outputdir, File srcfile) {
553: String path = srcfile.getPath();
554:
555: // Extract file's base-name
556: int startBasename = path.lastIndexOf(File.separator);
557: if (startBasename != -1) {
558: path = path.substring(startBasename + 1);
559: }
560:
561: // Replace the file's extension with '.java'
562: int startExtn = path.lastIndexOf('.');
563: if (startExtn != -1) {
564: path = path.substring(0, startExtn) + ".java";
565: } else {
566: path += ".java";
567: }
568:
569: // Change the directory
570: if (outputdir != null) {
571: path = outputdir + File.separator + path;
572: }
573:
574: return new File(path);
575: }
576: }
|