View Javadoc

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 decorated grammar files (<code>*.jjt</code>) with JJTree and passes the output to JavaCC in order to
29   * finally generate a parser with parse tree actions.
30   * 
31   * @goal jjtree-javacc
32   * @phase generate-sources
33   * @since 2.4
34   * @author Benjamin Bentmann
35   * @version $Id: JJTreeJavaCCMojo.java 10603 2009-09-06 15:05:08Z bentmann $
36   */
37  public class JJTreeJavaCCMojo
38      extends AbstractJavaCCMojo
39  {
40  
41      /**
42       * A flag whether to generate sample implementations for <code>SimpleNode</code> and any other nodes used in the
43       * grammar. Default value is <code>true</code>.
44       * 
45       * @parameter expression="${buildNodeFiles}"
46       */
47      private Boolean buildNodeFiles;
48  
49      /**
50       * A flag whether to generate a multi mode parse tree or a single mode parse tree. Default value is
51       * <code>false</code>.
52       * 
53       * @parameter expression="${multi}"
54       */
55      private Boolean multi;
56  
57      /**
58       * A flag whether to make each non-decorated production void instead of an indefinite node. Default value is
59       * <code>false</code>.
60       * 
61       * @parameter expression="${nodeDefaultVoid}"
62       */
63      private Boolean nodeDefaultVoid;
64  
65      /**
66       * The name of a custom class that extends <code>SimpleNode</code> and will be used as the super class for the
67       * generated tree node classes. By default, the tree node classes will directly extend the class
68       * <code>SimpleNode</code>.
69       * 
70       * @parameter expression="${nodeClass}"
71       * @since 2.5
72       */
73      private String nodeClass;
74  
75      /**
76       * The name of a custom factory class used to create <code>Node</code> objects. This class must have a method with
77       * the signature <code>public static Node jjtCreate(int id)</code>. By default, the class <code>SimpleNode</code>
78       * will be used as the factory class.
79       * 
80       * @parameter expression="${nodeFactory}"
81       */
82      private String nodeFactory;
83  
84      /**
85       * The package to generate the AST node classes into. This value may use a leading asterisk to reference the package
86       * of the corresponding parser. For example, if the parser package is <code>org.apache</code> and this parameter
87       * is set to <code>*.node</code>, the tree node classes will be located in the package
88       * <code>org.apache.node</code>. By default, the package of the corresponding parser is used.
89       * 
90       * @parameter expression="${nodePackage}"
91       */
92      private String nodePackage;
93  
94      /**
95       * The prefix used to construct node class names from node identifiers in multi mode. Default value is
96       * <code>AST</code>.
97       * 
98       * @parameter expression="${nodePrefix}"
99       */
100     private String nodePrefix;
101 
102     /**
103      * A flag whether user-defined parser methods should be called on entry and exit of every node scope. Default value
104      * is <code>false</code>.
105      * 
106      * @parameter expression="${nodeScopeHook}"
107      */
108     private Boolean nodeScopeHook;
109 
110     /**
111      * A flag whether the node construction routines need an additional method parameter to receive the parser object.
112      * Default value is <code>false</code>.
113      * 
114      * @parameter expression="${nodeUsesParser}"
115      */
116     private Boolean nodeUsesParser;
117 
118     /**
119      * A flag whether to insert the methods <code>jjtGetFirstToken()</code>, <code>jjtSetFirstToken()</code>,
120      * <code>getLastToken()</code> and <code>jjtSetLastToken()</code> into the class <code>SimpleNode</code>. Default
121      * value is <code>false</code>.
122      * 
123      * @parameter expression="${trackTokens}"
124      * @since 2.5
125      */
126     private Boolean trackTokens;
127 
128     /**
129      * A flag whether to insert a <code>jjtAccept()</code> method in the node classes and to generate a visitor
130      * implementation with an entry for every node type used in the grammar. Default value is <code>false</code>.
131      * 
132      * @parameter expression="${visitor}"
133      */
134     private Boolean visitor;
135 
136     /**
137      * The name of a class to use for the data argument of the <code>jjtAccept()</code> and <code>visit()</code>
138      * methods. Default value is <code>java.lang.Object</code>.
139      * 
140      * @parameter expression="${visitorDataType}"
141      * @since 2.5
142      */
143     private String visitorDataType;
144 
145     /**
146      * The name of a class to use as the return type of the <code>jjtAccept()</code> and <code>visit()</code> methods.
147      * Default value is <code>java.lang.Object</code>.
148      * 
149      * @parameter expression="${visitorReturnType}"
150      * @since 2.5
151      */
152     private String visitorReturnType;
153 
154     /**
155      * The name of an exception class to include in the signature of the generated <code>jjtAccept()</code> and
156      * <code>visit()</code> methods. By default, the <code>throws</code> clause of the generated methods is empty such
157      * that only unchecked exceptions can be thrown.
158      * 
159      * @parameter expression="${visitorException}"
160      */
161     private String visitorException;
162 
163     /**
164      * The directory where the decorated JavaCC grammar files (<code>*.jjt</code>) are located. It will be
165      * recursively scanned for input files to pass to JJTree. The parameters <code>includes</code> and
166      * <code>excludes</code> can be used to select a subset of files.
167      * 
168      * @parameter expression="${sourceDirectory}" default-value="${basedir}/src/main/jjtree"
169      */
170     private File sourceDirectory;
171 
172     /**
173      * The directory where the AST node files generated by JJTree will be stored. The directory will be registered as a
174      * compile source root of the project such that the generated files will participate in later build phases like
175      * compiling and packaging.
176      * 
177      * @parameter expression="${interimDirectory}" default-value="${project.build.directory}/generated-sources/jjtree"
178      */
179     private File interimDirectory;
180 
181     /**
182      * The directory where the parser files generated by JavaCC will be stored. The directory will be registered as a
183      * compile source root of the project such that the generated files will participate in later build phases like
184      * compiling and packaging.
185      * 
186      * @parameter expression="${outputDirectory}" default-value="${project.build.directory}/generated-sources/javacc"
187      */
188     private File outputDirectory;
189 
190     /**
191      * A set of Ant-like inclusion patterns used to select files from the source directory for processing. By default,
192      * the patterns <code>**&#47;*.jj</code>, <code>**&#47;*.JJ</code>, <code>**&#47;*.jjt</code> and
193      * <code>**&#47;*.JJT</code> are used to select grammar files.
194      * 
195      * @parameter
196      */
197     private String[] includes;
198 
199     /**
200      * A set of Ant-like exclusion patterns used to prevent certain files from being processed. By default, this set is
201      * empty such that no files are excluded.
202      * 
203      * @parameter
204      */
205     private String[] excludes;
206 
207     /**
208      * The granularity in milliseconds of the last modification date for testing whether a grammar file needs
209      * recompilation.
210      * 
211      * @parameter expression="${lastModGranularityMs}" default-value="0"
212      */
213     private int staleMillis;
214 
215     /**
216      * {@inheritDoc}
217      */
218     protected File getSourceDirectory()
219     {
220         return this.sourceDirectory;
221     }
222 
223     /**
224      * {@inheritDoc}
225      */
226     protected String[] getIncludes()
227     {
228         if ( this.includes != null )
229         {
230             return this.includes;
231         }
232         else
233         {
234             return new String[] { "**/*.jj", "**/*.JJ", "**/*.jjt", "**/*.JJT" };
235         }
236     }
237 
238     /**
239      * {@inheritDoc}
240      */
241     protected String[] getExcludes()
242     {
243         return this.excludes;
244     }
245 
246     /**
247      * {@inheritDoc}
248      */
249     protected File getOutputDirectory()
250     {
251         return this.outputDirectory;
252     }
253 
254     /**
255      * {@inheritDoc}
256      */
257     protected int getStaleMillis()
258     {
259         return this.staleMillis;
260     }
261 
262     /**
263      * Gets the absolute path to the directory where the interim output from JJTree will be stored.
264      * 
265      * @return The absolute path to the directory where the interim output from JJTree will be stored.
266      */
267     private File getInterimDirectory()
268     {
269         return this.interimDirectory;
270     }
271 
272     /**
273      * {@inheritDoc}
274      */
275     protected File[] getCompileSourceRoots()
276     {
277         return new File[] { getOutputDirectory(), getInterimDirectory() };
278     }
279 
280     /**
281      * {@inheritDoc}
282      */
283     protected void processGrammar( GrammarInfo grammarInfo )
284         throws MojoExecutionException, MojoFailureException
285     {
286         File jjtFile = grammarInfo.getGrammarFile();
287         File jjtDirectory = jjtFile.getParentFile();
288 
289         File tempDirectory = getTempDirectory();
290 
291         // setup output directory of grammar file (*.jj) and node files (*.java) generated by JJTree
292         File jjDirectory = new File( tempDirectory, "node" );
293 
294         // setup output directory of parser file (*.java) generated by JavaCC
295         File parserDirectory = new File( tempDirectory, "parser" );
296 
297         // setup output directory of tree node files (*.java) generated by JJTree
298         String nodePackageName = grammarInfo.resolvePackageName( this.nodePackage );
299 
300         // generate final grammar file
301         JJTree jjtree = newJJTree();
302         jjtree.setInputFile( jjtFile );
303         jjtree.setOutputDirectory( jjDirectory );
304         jjtree.setNodePackage( nodePackageName );
305         jjtree.run();
306 
307         // generate parser files
308         JavaCC javacc = newJavaCC();
309         javacc.setInputFile( jjtree.getOutputFile() );
310         javacc.setOutputDirectory( parserDirectory );
311         javacc.run();
312 
313         // copy output from JJTree
314         copyGrammarOutput( getInterimDirectory(), ( nodePackageName != null ) ? nodePackageName
315                         : grammarInfo.getParserPackage(), jjDirectory, grammarInfo.getParserName() + "TreeConstants*" );
316 
317         // copy parser files from JavaCC
318         copyGrammarOutput( getOutputDirectory(), grammarInfo.getParserPackage(), parserDirectory,
319                            grammarInfo.getParserName() + "*" );
320 
321         // copy source files which are next to grammar unless the grammar resides in an ordinary source root
322         // (legacy support for custom sources)
323         if ( !isSourceRoot( grammarInfo.getSourceDirectory() ) )
324         {
325             copyGrammarOutput( getOutputDirectory(), grammarInfo.getParserPackage(), jjtDirectory, "*" );
326         }
327 
328         deleteTempDirectory( tempDirectory );
329     }
330 
331     /**
332      * Creates a new facade to invoke JJTree. Most options for the invocation are derived from the current values of the
333      * corresponding mojo parameters. The caller is responsible to set the input file, output directory and package on
334      * the returned facade.
335      * 
336      * @return The facade for the tool invocation, never <code>null</code>.
337      */
338     protected JJTree newJJTree()
339     {
340         JJTree jjtree = new JJTree();
341         jjtree.setLog( getLog() );
342         jjtree.setGrammarEncoding( getGrammarEncoding() );
343         jjtree.setJdkVersion( getJdkVersion() );
344         jjtree.setStatic( getIsStatic() );
345         jjtree.setBuildNodeFiles( this.buildNodeFiles );
346         jjtree.setMulti( this.multi );
347         jjtree.setNodeDefaultVoid( this.nodeDefaultVoid );
348         jjtree.setNodeClass( this.nodeClass );
349         jjtree.setNodeFactory( this.nodeFactory );
350         jjtree.setNodePrefix( this.nodePrefix );
351         jjtree.setNodeScopeHook( this.nodeScopeHook );
352         jjtree.setNodeUsesParser( this.nodeUsesParser );
353         jjtree.setTrackTokens( this.trackTokens );
354         jjtree.setVisitor( this.visitor );
355         jjtree.setVisitorDataType( this.visitorDataType );
356         jjtree.setVisitorReturnType( this.visitorReturnType );
357         jjtree.setVisitorException( this.visitorException );
358         return jjtree;
359     }
360 
361 }