Java tutorial
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.apache.commons.exec.DefaultExecutor; import org.apache.commons.exec.CommandLine; import org.codehaus.mojo.antlr.options.Grammar; import org.codehaus.mojo.antlr.proxy.Helper; import org.codehaus.mojo.antlr.metadata.MetadataExtracter; import org.codehaus.mojo.antlr.metadata.XRef; import org.codehaus.mojo.antlr.plan.GenerationPlan; import org.codehaus.mojo.antlr.plan.GenerationPlanBuilder; import org.codehaus.plexus.util.StringOutputStream; import org.codehaus.plexus.util.StringUtils; /** * Base class with majority of Antlr functionalities. * * @author <a href="mailto:vincent.siveton@gmail.com">Vincent Siveton</a> * @version $Id$ */ public abstract class AbstractAntlrMojo extends AbstractMojo implements Environment { // ---------------------------------------------------------------------- // Mojo parameters // ---------------------------------------------------------------------- /** * Specifies the Antlr directory containing grammar files. * * @parameter default-value="${basedir}/src/main/antlr" */ protected File sourceDirectory; /** * The Maven Project Object * * @parameter expression="${project}" * @readonly */ protected MavenProject project; /** * The maven project's helper. * * @component role="org.apache.maven.project.MavenProjectHelper" * @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. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter default-value="${project.build.directory}/generated-sources/antlr" */ protected File outputDirectory; /** * Comma separated grammar file names or grammar pattern file names present in the <code>sourceDirectory</code> * directory. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${grammars}" */ protected String grammars; /** * Grammar list presents in the <code>sourceDirectory</code> directory. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> <br/> * Example: * * <pre> * <grammarDefs><br/> * <grammar><br/> * <name>myGrammar.g</name><br/> * <glib>mySuperGrammar.g;myOtherSuperGrammar.g</glib><br/> * </grammar><br/> * </grammarDefs> * </pre> * * @parameter expression="${grammarDefs}" */ protected Grammar[] grammarDefs; /** * Launch the ParseView debugger upon parser invocation. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${debug}" default-value="false" */ private boolean debug; /** * Generate a text file from your grammar with a lot of debugging info. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${diagnostic}" default-value="false" */ private boolean diagnostic; /** * Have all rules call traceIn/traceOut. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${trace}" default-value="false" */ private boolean trace; /** * Have parser rules call traceIn/traceOut. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${traceParser}" default-value="false" */ private boolean traceParser; /** * Have lexer rules call traceIn/traceOut. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${traceLexer}" default-value="false" */ private boolean traceLexer; /** * Have tree rules call traceIn/traceOut. <br/> * See <a href="http://www.antlr2.org/doc/options.html#Command%20Line%20Options">Command Line Options</a> * * @parameter expression="${traceTreeParser}" default-value="false" */ private boolean traceTreeParser; public File getSourceDirectory() { return sourceDirectory; } public File getOutputDirectory() { return outputDirectory; } /** * @throws MojoExecutionException */ protected void executeAntlr() throws MojoExecutionException { validateParameters(); Artifact antlrArtifact = locateAntlrArtifact(); MetadataExtracter metadataExtracter = new MetadataExtracter(this, new Helper(antlrArtifact)); XRef metadata = metadataExtracter.processMetadata(getGrammars()); Iterator generationPlans = new GenerationPlanBuilder(this).buildGenerationPlans(metadata).iterator(); while (generationPlans.hasNext()) { final GenerationPlan plan = (GenerationPlan) generationPlans.next(); if (!plan.isOutOfDate()) { getLog().info("grammar [" + plan.getId() + "] was up-to-date; skipping"); continue; } getLog().info("performing grammar generation [" + plan.getId() + "]"); performGeneration(plan, antlrArtifact); } if (project != null) { projectHelper.addResource(project, outputDirectory.getAbsolutePath(), Collections.singletonList("**/**.txt"), new ArrayList()); project.addCompileSourceRoot(outputDirectory.getAbsolutePath()); } } protected final Artifact locateAntlrArtifact() throws NoAntlrDependencyDefinedException { 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) ??? return antlrArtifact; } protected void performGeneration(GenerationPlan plan, Artifact antlrArtifact) throws MojoExecutionException { if (!plan.getGenerationDirectory().getParentFile().exists()) { plan.getGenerationDirectory().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(plan.getGenerationDirectory().getPath()); if (plan.getCollectedSuperGrammarIds().size() > 0) { arguments.add("-glib"); StringBuffer buffer = new StringBuffer(); Iterator ids = plan.getCollectedSuperGrammarIds().iterator(); while (ids.hasNext()) { buffer.append(new File(sourceDirectory, (String) ids.next())); if (ids.hasNext()) { buffer.append(';'); } } arguments.add(buffer.toString()); } arguments.add(plan.getSource().getPath()); String[] args = (String[]) arguments.toArray(new String[arguments.size()]); if (plan.getImportVocabTokenTypesDirectory() != null && !plan.getImportVocabTokenTypesDirectory().equals(plan.getGenerationDirectory())) { // we need to spawn a new process to properly set up PWD CommandLine commandLine = new CommandLine("java"); commandLine.addArgument("-classpath", false); commandLine.addArgument(generateClasspathForProcessSpawning(antlrArtifact), true); commandLine.addArgument("antlr.Tool", false); commandLine.addArguments(args, true); DefaultExecutor executor = new DefaultExecutor(); executor.setWorkingDirectory(plan.getImportVocabTokenTypesDirectory()); try { executor.execute(commandLine); } catch (IOException e) { getLog().warn("Error spawning process to execute antlr tool : " + e.getMessage()); } return; } // ---------------------------------------------------------------------- // Call Antlr // ---------------------------------------------------------------------- if (getLog().isDebugEnabled()) { getLog().debug("antlr args=\n" + StringUtils.join(args, "\n")); } 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."); } String originalUserDir = null; if (plan.getImportVocabTokenTypesDirectory() != null) { originalUserDir = System.getProperty("user.dir"); System.setProperty("user.dir", plan.getImportVocabTokenTypesDirectory().getPath()); } 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 (originalUserDir != null) { System.setProperty("user.dir", originalUserDir); } if (!failedSetManager) { System.setSecurityManager(oldSm); } System.setErr(oldErr); System.err.println(errOS.toString()); } } private String generateClasspathForProcessSpawning(Artifact antlrArtifact) { // todo : is maven by itself enough for the generation??? return antlrArtifact.getFile().getPath(); } 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("<configuration>").append('\n'); msg.append(" <grammars>VALUE</grammars>").append('\n'); msg.append("- OR - ").append('\n'); msg.append(" <grammarDefs>VALUE</grammarDefs>").append('\n'); msg.append("</configuration>").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 <code>grammars</code> and <code>grammarDefs</code> 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); } } }