org.codehaus.mojo.antlr.AbstractAntlrMojo Maven / Gradle / Ivy
package org.codehaus.mojo.antlr;
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.FilenameFilter;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;
import java.util.StringTokenizer;
import java.util.regex.Pattern;
import java.lang.reflect.InvocationTargetException;
import org.apache.maven.artifact.Artifact;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.project.MavenProject;
import org.apache.maven.project.MavenProjectHelper;
import org.codehaus.mojo.antlr.options.Grammar;
import org.codehaus.plexus.util.StringOutputStream;
import org.codehaus.plexus.util.StringUtils;
/**
* Base class with majority of Antlr functionalities.
*
* @author Vincent Siveton
* @version $Id: AbstractAntlrMojo.java 6341 2008-02-25 08:31:17Z dholroyd $
*/
public abstract class AbstractAntlrMojo
extends AbstractMojo
{
// ----------------------------------------------------------------------
// Mojo parameters
// ----------------------------------------------------------------------
/**
* Specifies the Antlr directory containing grammar files.
*
* @parameter expression="${basedir}/src/main/antlr"
* @required
*/
protected File sourceDirectory;
/**
* The Maven Project Object
*
* @parameter expression="${project}"
* @required
*/
protected MavenProject project;
/**
* The maven project's helper.
*
* @parameter expression="${component.org.apache.maven.project.MavenProjectHelper}"
* @required
* @readonly
*/
private MavenProjectHelper projectHelper;
// ----------------------------------------------------------------------
// Antlr parameters
// See http://www.antlr2.org/doc/options.html#Command%20Line%20Options
// ----------------------------------------------------------------------
/**
* Specifies the destination directory where Antlr should generate files.
*
* See Command Line Options
*
* @parameter expression="${project.build.directory}/generated-sources/antlr"
* @required
*/
protected File outputDirectory;
/**
* Comma separated grammar file names or grammar pattern file names present in the sourceDirectory
* directory.
*
* See Command Line Options
*
* @parameter expression="${grammars}"
*/
protected String grammars;
/**
* Grammar list presents in the sourceDirectory
directory.
*
* See Command Line Options
*
* Example:
*
* <grammarDefs>
* <grammar>
* <name>myGrammar.g</name>
* <glib>mySuperGrammar.g;myOtherSuperGrammar.g</glib>
* </grammar>
* </grammarDefs>
*
*
* @parameter expression="${grammarDefs}"
*/
protected Grammar[] grammarDefs;
/**
* Launch the ParseView debugger upon parser invocation.
*
* See Command Line Options
*
* @parameter expression="${debug}" default-value="false"
*/
private boolean debug;
/**
* Generate a text file from your grammar with a lot of debugging info.
*
* See Command Line Options
*
* @parameter expression="${diagnostic}" default-value="false"
*/
private boolean diagnostic;
/**
* Have all rules call traceIn/traceOut.
*
* See Command Line Options
*
* @parameter expression="${trace}" default-value="false"
*/
private boolean trace;
/**
* Have parser rules call traceIn/traceOut.
*
* See Command Line Options
*
* @parameter expression="${traceParser}" default-value="false"
*/
private boolean traceParser;
/**
* Have lexer rules call traceIn/traceOut.
*
* See Command Line Options
*
* @parameter expression="${traceLexer}" default-value="false"
*/
private boolean traceLexer;
/**
* Have tree rules call traceIn/traceOut.
*
* See Command Line Options
*
* @parameter expression="${traceTreeParser}" default-value="false"
*/
private boolean traceTreeParser;
/**
* @throws MojoExecutionException
*/
protected void executeAntlr()
throws MojoExecutionException
{
validateParameters();
// ----------------------------------------------------------------------
// Find the Antlr dependency to use for generation
// ----------------------------------------------------------------------
Artifact antlrArtifact = null;
if ( project.getCompileArtifacts() != null ) {
Iterator projectArtifacts = project.getCompileArtifacts().iterator();
while ( projectArtifacts.hasNext() ) {
final Artifact artifact = ( Artifact ) projectArtifacts.next();;
if ( "antlr".equals( artifact.getGroupId() ) &&
( "antlr".equals( artifact.getArtifactId() ) || "antlr-all".equals( artifact.getArtifactId() ) ) ) {
antlrArtifact = artifact;
break;
}
}
}
if ( antlrArtifact == null ) {
throw new NoAntlrDependencyDefinedException( "project did not define antlr:antlr depenency" );
}
// TODO : enforce specific version range; e.g. [2.7,3.0) ???
// ----------------------------------------------------------------------
// Call Antlr for each grammar
// ----------------------------------------------------------------------
Grammar[] grammarsUsed = getGrammars();
for ( int i = 0; i < grammarsUsed.length; i++ )
{
String grammarName = grammarsUsed[i].getName();
if ( StringUtils.isEmpty( grammarName ) )
{
getLog().info( "Empty grammar in the configuration. Skipped." );
continue;
}
File grammar = new File( sourceDirectory, grammarName.trim() );
if ( !grammar.exists() )
{
throw new MojoExecutionException( "The grammar '" + grammar.getAbsolutePath() + "' doesnt exist." );
}
getLog().info( "Using Antlr grammar: " + grammar );
File generated = null;
try
{
generated = getGeneratedFile( grammar.getPath(), outputDirectory );
}
catch ( IOException e )
{
throw new MojoExecutionException( "Failed to get generated file: " + e.getMessage(), e );
}
if ( generated.exists() )
{
if ( generated.lastModified() > grammar.lastModified() )
{
// it's more recent, skip.
getLog().info( "The grammar is already generated." );
continue;
}
}
if ( !generated.getParentFile().exists() )
{
generated.getParentFile().mkdirs();
}
// ----------------------------------------------------------------------
// Wrap arguments
// Note: grammar file should be last
// ----------------------------------------------------------------------
List arguments = new LinkedList();
addArgIf( arguments, debug, "-debug" );
addArgIf( arguments, diagnostic, "-diagnostic" );
addArgIf( arguments, trace, "-trace" );
addArgIf( arguments, traceParser, "-traceParser" );
addArgIf( arguments, traceLexer, "-traceLexer" );
addArgIf( arguments, traceTreeParser, "-traceTreeParser" );
addArgs( arguments );
arguments.add( "-o" );
arguments.add( generated.getParentFile().getPath() );
if ( StringUtils.isNotEmpty( grammarsUsed[i].getGlib() ) )
{
StringBuffer glib = new StringBuffer();
StringTokenizer st = new StringTokenizer( grammarsUsed[i].getGlib(), ",; " );
while ( st.hasMoreTokens() )
{
glib.append( new File( sourceDirectory, st.nextToken().trim() ) );
if ( st.hasMoreTokens() )
{
glib.append( ";" );
}
}
arguments.add( "-glib" );
arguments.add( glib.toString() );
}
arguments.add( grammar.getPath() );
String[] args = (String[]) arguments.toArray( new String[0] );
if ( getLog().isDebugEnabled() )
{
getLog().debug( "antlr args=\n" + StringUtils.join( args, "\n" ) );
}
// ----------------------------------------------------------------------
// Call Antlr
// ----------------------------------------------------------------------
boolean failedSetManager = false;
SecurityManager oldSm = null;
try
{
oldSm = System.getSecurityManager();
System.setSecurityManager( NoExitSecurityManager.INSTANCE );
}
catch ( SecurityException ex )
{
//ANTLR-12
oldSm = null;
failedSetManager = true;
//ignore, in embedded environment the security manager can already be set.
// in such a case assume the exit call is handled properly..
getLog().warn(
"Cannot set custom SecurityManager. "
+ "Antlr's call to System.exit() can cause application shutdown "
+ "if not handled by the current SecurityManager." );
}
PrintStream oldErr = System.err;
OutputStream errOS = new StringOutputStream();
PrintStream err = new PrintStream( errOS );
System.setErr( err );
try
{
executeAntlrInIsolatedClassLoader( (String[]) arguments.toArray( new String[0] ), antlrArtifact );
}
catch ( SecurityException e )
{
if ( e.getMessage().equals( "exitVM-0" )
|| e.getClass().getName().equals( "org.netbeans.core.execution.ExitSecurityException" ) ) //netbeans IDE Sec Manager.
{
//ANTLR-12
//now basically every secutiry manager could set different message, how to handle in generic way?
//probably only by external execution
/// in case of NetBeans SecurityManager, it's not possible to distinguish exit codes, rather swallow than fail.
getLog().debug( e );
}
else
{
throw new MojoExecutionException( "Antlr execution failed: " + e.getMessage()
+ "\n Error output:\n" + errOS, e );
}
}
finally
{
if ( !failedSetManager )
{
System.setSecurityManager( oldSm );
}
System.setErr( oldErr );
System.err.println( errOS.toString() );
}
}
if ( project != null )
{
projectHelper.addResource( project, outputDirectory.getAbsolutePath(), Collections
.singletonList( "**/**.txt" ), new ArrayList() );
project.addCompileSourceRoot( outputDirectory.getAbsolutePath() );
}
}
private void executeAntlrInIsolatedClassLoader(String[] args, Artifact antlrArtifact) throws MojoExecutionException {
try {
URLClassLoader classLoader = new URLClassLoader(
new URL[] { antlrArtifact.getFile().toURL() },
ClassLoader.getSystemClassLoader()
);
Class toolClass = classLoader.loadClass( "antlr.Tool" );
toolClass.getMethod( "main", new Class[] { String[].class } ).invoke( null, new Object[] { args } );
}
catch ( MalformedURLException e ) {
throw new MojoExecutionException( "Unable to resolve antlr:antlr artifact url", e );
}
catch ( ClassNotFoundException e ) {
throw new MojoExecutionException( "could not locate antlr.Tool class" );
}
catch ( NoSuchMethodException e ) {
throw new MojoExecutionException( "error locating antlt.Tool#main", e );
}
catch ( InvocationTargetException e ) {
throw new MojoExecutionException( "error perforing antlt.Tool#main", e.getTargetException() );
}
catch ( IllegalAccessException e ) {
throw new MojoExecutionException( "error perforing antlt.Tool#main", e );
}
}
/**
* Add arguments to be included in Antlr call
*
* @param arguments
*/
protected abstract void addArgs( List arguments );
/**
* Convenience method to add an argument
*
* @param arguments
* @param b
* @param value
*/
protected static void addArgIf( List arguments, boolean b, String value )
{
if ( b )
{
arguments.add( value );
}
}
/**
* @param grammar
* @param outputDir
* @return generated file
* @throws IOException
*/
private File getGeneratedFile( String grammar, File outputDir )
throws IOException
{
String generatedFileName = null;
String packageName = "";
BufferedReader in = new BufferedReader( new FileReader( grammar ) );
String line;
while ( ( line = in.readLine() ) != null )
{
line = line.trim();
int extendsIndex = line.indexOf( " extends " );
if ( line.startsWith( "class " ) && extendsIndex > -1 )
{
generatedFileName = line.substring( 6, extendsIndex ).trim();
break;
}
else if ( line.startsWith( "package" ) )
{
packageName = line.substring( 8 ).trim();
}
}
in.close();
if ( generatedFileName == null )
{
throw new IOException( "Unable to generate the output file name: is the grammar '" + grammar + "' valide?" );
}
File genFile = null;
if ( "".equals( packageName ) )
{
genFile = new File( outputDir, generatedFileName + ".java" );
}
else
{
String packagePath = packageName.replace( '.', File.separatorChar );
packagePath = packagePath.replace( ';', File.separatorChar );
genFile = new File( new File( outputDir, packagePath ), generatedFileName + ".java" );
}
return genFile;
}
/**
* grammars or grammarDefs parameters is required
*
* @throws MojoExecutionException
*/
private void validateParameters()
throws MojoExecutionException
{
if ( ( StringUtils.isEmpty( grammars ) ) && ( ( grammarDefs == null ) || ( grammarDefs.length == 0 ) ) )
{
StringBuffer msg = new StringBuffer();
msg.append( "Antlr plugin parameters are invalid/missing." ).append( '\n' );
msg.append( "Inside the definition for plugin 'antlr-maven-plugin' specify the following:" ).append( '\n' );
msg.append( '\n' );
msg.append( "" ).append( '\n' );
msg.append( " VALUE " ).append( '\n' );
msg.append( "- OR - " ).append( '\n' );
msg.append( " VALUE " ).append( '\n' );
msg.append( " " ).append( '\n' );
throw new MojoExecutionException( msg.toString() );
}
}
/**
* Get the list of all grammars to be compiled. The grammars variable
* can be a list of file or patterns. For instance, one can use *.g instead of
* a full list of grammar names.
*
* Be aware that sometime the grammar order is important, and that patterns won't
* keep this order, but we can still combine both elements( ordered names first, then
* the patterns).
*
* File name won't be added twice in the list of files.
*
* @return an array of grammar from grammars
and grammarDefs
variables
*/
private Grammar[] getGrammars()
{
List grammarList = new ArrayList();
Set grammarSet = new HashSet();
if ( StringUtils.isNotEmpty( grammars ) )
{
StringTokenizer st = new StringTokenizer( grammars, ", " );
while ( st.hasMoreTokens() )
{
String currentGrammar = st.nextToken().trim();
if ( StringUtils.isNotEmpty( currentGrammar ) )
{
// Check if some pattern has been used
if ( ( currentGrammar.indexOf( '*' ) != -1 ) || ( currentGrammar.indexOf( '?' ) != -1 ) )
{
// We first have to 'protect' the '.', and transform patterns
// to regexp, substituting '*' to '.*' and '?' to '.'
final String transformedGrammar = currentGrammar.replaceAll( "\\.", "\\\\." )
.replaceAll( "\\*", ".*" ).replaceAll( "\\?", "." );
// Filter the source directory
String[] dir = sourceDirectory.list( new FilenameFilter()
{
public boolean accept( File dir, String s )
{
return Pattern.matches( transformedGrammar, s );
}
} );
if ( ( dir != null ) && ( dir.length != 0 ) )
{
for ( int i = 0; i < dir.length; i++ )
{
// Just add fles which are not in the set
// of files already seen.
if ( !grammarSet.contains( dir[i] ) )
{
Grammar grammar = new Grammar();
grammar.setName( dir[i] );
grammarList.add( grammar );
}
}
}
}
else
{
if ( !grammarSet.contains( currentGrammar ) )
{
Grammar grammar = new Grammar();
grammar.setName( currentGrammar );
grammarList.add( grammar );
}
}
}
}
}
if ( grammarDefs != null )
{
grammarList.addAll( Arrays.asList( grammarDefs ) );
}
return (Grammar[]) grammarList.toArray( new Grammar[0] );
}
public static class NoAntlrDependencyDefinedException extends MojoExecutionException {
public NoAntlrDependencyDefinedException(String s) {
super( s );
}
}
}
© 2015 - 2025 Weber Informatics LLC | Privacy Policy