1 package org.codehaus.mojo.javacc;
2
3 /*
4 * Licensed to the Apache Software Foundation (ASF) under one
5 * or more contributor license agreements. See the NOTICE file
6 * distributed with this work for additional information
7 * regarding copyright ownership. The ASF licenses this file
8 * to you under the Apache License, Version 2.0 (the
9 * "License"); you may not use this file except in compliance
10 * with the License. You may obtain a copy of the License at
11 *
12 * http://www.apache.org/licenses/LICENSE-2.0
13 *
14 * Unless required by applicable law or agreed to in writing,
15 * software distributed under the License is distributed on an
16 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17 * KIND, either express or implied. See the License for the
18 * specific language governing permissions and limitations
19 * under the License.
20 */
21
22 import java.io.File;
23
24 import org.apache.maven.plugin.MojoExecutionException;
25 import org.apache.maven.plugin.MojoFailureException;
26
27 /**
28 * Preprocesses ordinary grammar files (<code>*.jtb</code>) with JTB and passes the output to JavaCC in order to
29 * finally generate a parser with parse tree actions.<br/><br/><strong>Note:</strong> <a
30 * href="http://compilers.cs.ucla.edu/jtb/">JTB</a> requires Java 1.5 or higher. This goal will not work with earlier
31 * versions of the JRE.
32 *
33 * @goal jtb-javacc
34 * @phase generate-sources
35 * @since 2.4
36 * @author Benjamin Bentmann
37 * @version $Id: JTBJavaCCMojo.java 8156 2008-11-26 18:20:19Z bentmann $
38 */
39 public class JTBJavaCCMojo
40 extends AbstractJavaCCMojo
41 {
42
43 /**
44 * This option is short for <code>nodePackageName</code> = <code><packageName>.syntaxtree</code> and
45 * <code>visitorPackageName</code> = <code><packageName>.visitor</code>. Note that this option takes
46 * precedence over <code>nodePackageName</code> and <code>visitorPackageName</code> if specified.
47 *
48 * @parameter expression="${package}"
49 */
50 private String packageName;
51
52 /**
53 * This option specifies the package for the generated AST nodes. This value may use a leading asterisk to reference
54 * the package of the corresponding parser. For example, if the parser package is <code>org.apache</code> and this
55 * parameter is set to <code>*.demo</code>, the tree node classes will be located in the package
56 * <code>org.apache.demo</code>. Default value is <code>*.syntaxtree</code>.
57 *
58 * @parameter expression="${nodePackageName}"
59 */
60 private String nodePackageName;
61
62 /**
63 * This option specifies the package for the generated visitors. This value may use a leading asterisk to reference
64 * the package of the corresponding parser. For example, if the parser package is <code>org.apache</code> and this
65 * parameter is set to <code>*.demo</code>, the visitor classes will be located in the package
66 * <code>org.apache.demo</code>. Default value is <code>*.visitor</code>.
67 *
68 * @parameter expression="${visitorPackageName}"
69 */
70 private String visitorPackageName;
71
72 /**
73 * If <code>true</code>, JTB will suppress its semantic error checking. Default value is <code>false</code>.
74 *
75 * @parameter expression="${supressErrorChecking}"
76 */
77 private Boolean supressErrorChecking;
78
79 /**
80 * If <code>true</code>, all generated comments will be wrapped in <code><pre></code> tags so that they
81 * are formatted correctly in API docs. Default value is <code>false</code>.
82 *
83 * @parameter expression="${javadocFriendlyComments}"
84 */
85 private Boolean javadocFriendlyComments;
86
87 /**
88 * Setting this option to <code>true</code> causes JTB to generate field names that reflect the structure of the
89 * tree instead of generic names like <code>f0</code>, <code>f1</code> etc. Default value is <code>false</code>.
90 *
91 * @parameter expression="${descriptiveFieldNames}"
92 */
93 private Boolean descriptiveFieldNames;
94
95 /**
96 * The qualified name of a user-defined class from which all AST nodes will inherit. By default, AST nodes will
97 * inherit from the generated class <code>Node</code>.
98 *
99 * @parameter expression="${nodeParentClass}"
100 */
101 private String nodeParentClass;
102
103 /**
104 * If <code>true</code>, all nodes will contain fields for its parent node. Default value is <code>false</code>.
105 *
106 * @parameter expression="${parentPointers}"
107 */
108 private Boolean parentPointers;
109
110 /**
111 * If <code>true</code>, JTB will include JavaCC "special tokens" in the AST. Default value is <code>false</code>.
112 *
113 * @parameter expression="${specialTokens}"
114 */
115 private Boolean specialTokens;
116
117 /**
118 * If <code>true</code>, JTB will generate the following files to support the Schema programming language:
119 * <ul>
120 * <li>Scheme records representing the grammar.</li>
121 * <li>A Scheme tree building visitor.</li>
122 * </ul>
123 * Default value is <code>false</code>.
124 *
125 * @parameter expression="${scheme}"
126 */
127 private Boolean scheme;
128
129 /**
130 * If <code>true</code>, JTB will generate a syntax tree dumping visitor. Default value is <code>false</code>.
131 *
132 * @parameter expression="${printer}"
133 */
134 private Boolean printer;
135
136 /**
137 * The directory where the JavaCC grammar files (<code>*.jtb</code>) are located. It will be recursively scanned
138 * for input files to pass to JTB. The parameters <code>includes</code> and <code>excludes</code> can be used to
139 * select a subset of files.
140 *
141 * @parameter expression="${sourceDirectory}" default-value="${basedir}/src/main/jtb"
142 */
143 private File sourceDirectory;
144
145 /**
146 * The directory where the visitor and AST node files generated by JTB will be stored. The directory will be
147 * registered as a compile source root of the project such that the generated files will participate in later build
148 * phases like compiling and packaging.
149 *
150 * @parameter expression="${interimDirectory}" default-value="${project.build.directory}/generated-sources/jtb"
151 */
152 private File interimDirectory;
153
154 /**
155 * The directory where the parser files generated by JavaCC will be stored. The directory will be registered as a
156 * compile source root of the project such that the generated files will participate in later build phases like
157 * compiling and packaging.
158 *
159 * @parameter expression="${outputDirectory}" default-value="${project.build.directory}/generated-sources/javacc"
160 */
161 private File outputDirectory;
162
163 /**
164 * A set of Ant-like inclusion patterns used to select files from the source directory for processing. By default,
165 * the patterns <code>**/*.jj</code>, <code>**/*.JJ</code>, <code>**/*.jtb</code> and
166 * <code>**/*.JTB</code> are used to select grammar files.
167 *
168 * @parameter
169 */
170 private String[] includes;
171
172 /**
173 * A set of Ant-like exclusion patterns used to prevent certain files from being processing. By default, this set is
174 * empty such that no files are excluded.
175 *
176 * @parameter
177 */
178 private String[] excludes;
179
180 /**
181 * The granularity in milliseconds of the last modification date for testing whether a grammar file needs
182 * recompilation.
183 *
184 * @parameter expression="${lastModGranularityMs}" default-value="0"
185 */
186 private int staleMillis;
187
188 /**
189 * {@inheritDoc}
190 */
191 protected File getSourceDirectory()
192 {
193 return this.sourceDirectory;
194 }
195
196 /**
197 * {@inheritDoc}
198 */
199 protected String[] getIncludes()
200 {
201 if ( this.includes != null )
202 {
203 return this.includes;
204 }
205 else
206 {
207 return new String[] { "**/*.jj", "**/*.JJ", "**/*.jtb", "**/*.JTB" };
208 }
209 }
210
211 /**
212 * {@inheritDoc}
213 */
214 protected String[] getExcludes()
215 {
216 return this.excludes;
217 }
218
219 /**
220 * {@inheritDoc}
221 */
222 protected File getOutputDirectory()
223 {
224 return this.outputDirectory;
225 }
226
227 /**
228 * {@inheritDoc}
229 */
230 protected int getStaleMillis()
231 {
232 return this.staleMillis;
233 }
234
235 /**
236 * Gets the absolute path to the directory where the interim output from JTB will be stored.
237 *
238 * @return The absolute path to the directory where the interim output from JTB will be stored.
239 */
240 private File getInterimDirectory()
241 {
242 return this.interimDirectory;
243 }
244
245 /**
246 * {@inheritDoc}
247 */
248 protected File[] getCompileSourceRoots()
249 {
250 return new File[] { getOutputDirectory(), getInterimDirectory() };
251 }
252
253 /**
254 * {@inheritDoc}
255 */
256 protected void processGrammar( GrammarInfo grammarInfo )
257 throws MojoExecutionException, MojoFailureException
258 {
259 File jtbFile = grammarInfo.getGrammarFile();
260 File jtbDirectory = jtbFile.getParentFile();
261
262 File tempDirectory = getTempDirectory();
263
264 // setup output directory of grammar file (*.jj) generated by JTB
265 File jjDirectory = tempDirectory;
266
267 // setup output directory of tree node files (*.java) generated by JTB
268 String nodePackage = grammarInfo.resolvePackageName( getNodePackageName() );
269 File nodeDirectory = new File( tempDirectory, "node" );
270
271 // setup output directory of visitor files (*.java) generated by JTB
272 String visitorPackage = grammarInfo.resolvePackageName( getVisitorPackageName() );
273 File visitorDirectory = new File( tempDirectory, "visitor" );
274
275 // setup output directory of parser file (*.java) generated by JavaCC
276 File parserDirectory = new File( tempDirectory, "parser" );
277
278 // generate final grammar file and the node/visitor files
279 JTB jtb = newJTB();
280 jtb.setInputFile( jtbFile );
281 jtb.setOutputDirectory( jjDirectory );
282 jtb.setNodeDirectory( nodeDirectory );
283 jtb.setVisitorDirectory( visitorDirectory );
284 jtb.setNodePackageName( nodePackage );
285 jtb.setVisitorPackageName( visitorPackage );
286 jtb.run();
287
288 // generate parser files
289 JavaCC javacc = newJavaCC();
290 javacc.setInputFile( jtb.getOutputFile() );
291 javacc.setOutputDirectory( parserDirectory );
292 javacc.run();
293
294 // copy tree node files from JTB
295 copyGrammarOutput( getInterimDirectory(), nodePackage, nodeDirectory, "!Node*" );
296
297 // copy visitor files from JTB
298 copyGrammarOutput( getInterimDirectory(), visitorPackage, visitorDirectory, "" );
299
300 // copy parser files from JavaCC
301 copyGrammarOutput( getOutputDirectory(), grammarInfo.getParserPackage(), parserDirectory,
302 grammarInfo.getParserName() + "*" );
303
304 // copy source files which are next to grammar unless the grammar resides in an ordinary source root
305 // (legacy support for custom sources)
306 if ( !isSourceRoot( grammarInfo.getSourceDirectory() ) )
307 {
308 copyGrammarOutput( getOutputDirectory(), grammarInfo.getParserPackage(), jtbDirectory, "*" );
309 }
310
311 deleteTempDirectory( tempDirectory );
312 }
313
314 /**
315 * Gets the effective package name for the AST node files.
316 *
317 * @return The effective package name for the AST node files, never <code>null</code>.
318 */
319 private String getNodePackageName()
320 {
321 if ( this.packageName != null )
322 {
323 return this.packageName + ".syntaxtree";
324 }
325 else if ( this.nodePackageName != null )
326 {
327 return this.nodePackageName;
328 }
329 else
330 {
331 return "*.syntaxtree";
332 }
333 }
334
335 /**
336 * Gets the effective package name for the visitor files.
337 *
338 * @return The effective package name for the visitor files, never <code>null</code>.
339 */
340 private String getVisitorPackageName()
341 {
342 if ( this.packageName != null )
343 {
344 return this.packageName + ".visitor";
345 }
346 else if ( this.visitorPackageName != null )
347 {
348 return this.visitorPackageName;
349 }
350 else
351 {
352 return "*.visitor";
353 }
354 }
355
356 /**
357 * Creates a new facade to invoke JTB. Most options for the invocation are derived from the current values of the
358 * corresponding mojo parameters. The caller is responsible to set the input file, output directories and packages
359 * on the returned facade.
360 *
361 * @return The facade for the tool invocation, never <code>null</code>.
362 */
363 private JTB newJTB()
364 {
365 JTB jtb = new JTB();
366 jtb.setLog( getLog() );
367 jtb.setDescriptiveFieldNames( this.descriptiveFieldNames );
368 jtb.setJavadocFriendlyComments( this.javadocFriendlyComments );
369 jtb.setNodeParentClass( this.nodeParentClass );
370 jtb.setParentPointers( this.parentPointers );
371 jtb.setPrinter( this.printer );
372 jtb.setScheme( this.scheme );
373 jtb.setSpecialTokens( this.specialTokens );
374 jtb.setSupressErrorChecking( this.supressErrorChecking );
375 return jtb;
376 }
377
378 }