All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.codehaus.mojo.jaxb2.schemageneration.AbstractXsdGeneratorMojo Maven / Gradle / Ivy

Go to download

Mojo's JAXB-2 Maven plugin is used to create an object graph from XSDs based on the JAXB 2.x implementation and to generate XSDs from JAXB annotated Java classes.

There is a newer version: 3.2.0
Show newest version
package org.codehaus.mojo.jaxb2.schemageneration;

/*
 * 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 com.sun.tools.jxc.SchemaGenerator;
import com.thoughtworks.qdox.JavaProjectBuilder;
import com.thoughtworks.qdox.model.JavaClass;
import com.thoughtworks.qdox.model.JavaPackage;
import com.thoughtworks.qdox.model.JavaSource;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.Parameter;
import org.codehaus.mojo.jaxb2.AbstractJaxbMojo;
import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.DefaultJavaDocRenderer;
import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.JavaDocExtractor;
import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.JavaDocRenderer;
import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.SearchableDocumentation;
import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.schemaenhancement.SimpleNamespaceResolver;
import org.codehaus.mojo.jaxb2.schemageneration.postprocessing.schemaenhancement.TransformSchema;
import org.codehaus.mojo.jaxb2.shared.FileSystemUtilities;
import org.codehaus.mojo.jaxb2.shared.arguments.ArgumentBuilder;
import org.codehaus.mojo.jaxb2.shared.environment.EnvironmentFacet;
import org.codehaus.mojo.jaxb2.shared.environment.ToolExecutionEnvironment;
import org.codehaus.mojo.jaxb2.shared.environment.classloading.ThreadContextClassLoaderBuilder;
import org.codehaus.mojo.jaxb2.shared.environment.locale.LocaleFacet;
import org.codehaus.mojo.jaxb2.shared.environment.logging.LoggingHandlerEnvironmentFacet;
import org.codehaus.mojo.jaxb2.shared.filters.Filter;
import org.codehaus.mojo.jaxb2.shared.filters.pattern.PatternFileFilter;
import org.codehaus.plexus.classworlds.realm.ClassRealm;
import org.codehaus.plexus.util.FileUtils;

import javax.tools.ToolProvider;
import java.io.File;
import java.io.IOException;
import java.net.HttpURLConnection;
import java.net.URL;
import java.net.URLConnection;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;
import java.util.regex.Pattern;

/**
 * 

Abstract superclass for Mojos that generate XSD files from annotated Java Sources. * This Mojo delegates execution to the {@code schemagen} tool to perform the XSD file * generation. Moreover, the AbstractXsdGeneratorMojo provides an augmented processing * pipeline by optionally letting a set of NodeProcessors improve the 'vanilla' XSD files.

* * @author Lennart Jörelid * @see The JAXB Reference Implementation */ public abstract class AbstractXsdGeneratorMojo extends AbstractJaxbMojo { /** *

Pattern matching the names of files emitted by the JAXB/JDK SchemaGenerator. * According to the JAXB Schema Generator documentation:

*
There is no way to control the name of the generated schema files at this time.
*/ public static final Pattern SCHEMAGEN_EMITTED_FILENAME = Pattern.compile("schema\\p{javaDigit}+.xsd"); /** *

The default JavaDocRenderer used unless another JavaDocRenderer should be used.

* * @see #javaDocRenderer * @since 2.0 */ public static final JavaDocRenderer STANDARD_JAVADOC_RENDERER = new DefaultJavaDocRenderer(); /** *

Default exclude file name suffixes for testSources, used unless overridden by an * explicit configuration in the {@code testSourceExcludeSuffixes} parameter. *

*
     *     
     *         final List<Filter<File>> schemagenTmp = new ArrayList<Filter<File>>();
     *         schemagenTmp.addAll(AbstractJaxbMojo.STANDARD_EXCLUDE_FILTERS);
     *         schemagenTmp.add(new PatternFileFilter(Arrays.asList("\\.java", "\\.scala", "\\.mdo"), false));
     *         STANDARD_BYTECODE_EXCLUDE_FILTERS = Collections.unmodifiableList(schemagenTmp);
     *     
     * 
*/ public static final List> STANDARD_BYTECODE_EXCLUDE_FILTERS; /** * Filter list containing a PatternFileFilter including ".class" files. */ public static final List> CLASS_INCLUDE_FILTERS; /** * Specification for packages which must be loaded using the SystemToolClassLoader (and not in the plugin's * ThreadContext ClassLoader). The SystemToolClassLoader is used by SchemaGen to process some stuff from the * {@code tools.jar} archive, in particular its exception types used to signal JAXB annotation Exceptions. * * @see ToolProvider#getSystemToolClassLoader() */ public static final List SYSTEM_TOOLS_CLASSLOADER_PACKAGES = Arrays.asList( "com.sun.source.util", "com.sun.source.tree"); static { final List> schemagenTmp = new ArrayList>(); schemagenTmp.addAll(AbstractJaxbMojo.STANDARD_EXCLUDE_FILTERS); schemagenTmp.add(new PatternFileFilter(Arrays.asList("\\.java", "\\.scala", "\\.mdo"), false)); STANDARD_BYTECODE_EXCLUDE_FILTERS = Collections.unmodifiableList(schemagenTmp); CLASS_INCLUDE_FILTERS = new ArrayList>(); CLASS_INCLUDE_FILTERS.add(new PatternFileFilter(Arrays.asList("\\.class"), true)); } // Internal state private static final int SCHEMAGEN_INCORRECT_OPTIONS = -1; private static final int SCHEMAGEN_COMPLETED_OK = 0; private static final int SCHEMAGEN_JAXB_ERRORS = 1; /** *

A List holding desired schema mappings, each of which binds a schema namespace URI to its desired prefix * [optional] and the name of the resulting schema file [optional]. All given elements (uri, prefix, file) must be * unique within the configuration; no two elements may have the same values.

*

The example schema configuration below maps two namespace uris to prefixes and generated file names. This implies * that http://some/namespace will be represented by the prefix some within the generated XML * Schema files; creating namespace definitions on the form xmlns:some="http://some/namespace", and * corresponding uses on the form <xs:element minOccurs="0" * ref="some:anOptionalElementInSomeNamespace"/>. Moreover, the file element defines that the * http://some/namespace definitions will be written to the file some_schema.xsd, and that all * import references will be on the form <xs:import namespace="http://some/namespace" * schemaLocation="some_schema.xsd"/>

*

The example configuration below also performs identical operations for the namespace uri * http://another/namespace with the prefix another and the file another_schema.xsd. *

*
     *     
     * <transformSchemas>
     *   <transformSchema>
     *     <uri>http://some/namespace</uri>
     *     <toPrefix>some</toPrefix>
     *     <toFile>some_schema.xsd</toFile>
     *   <transformSchema>
     *     <uri>http://another/namespace</uri>
     *     <toPrefix>another</toPrefix>
     *     <toFile>another_schema.xsd</toFile>
     *   </transformSchema>
     * </transformSchemas>
     *     
     * 
* * @since 1.4 */ @Parameter private List transformSchemas; /** * Deprecated - will be removed in a future release *

From plugin version 2.4, this parameter will not be used. * Instead, episode files are generated by default with all JAXB operations.

*

Starting with plugin version 2.4, use the parameter {@link #episodeFileName} to provide a custom * name of the generated episode File (or rely on the standard file name {@link #STANDARD_EPISODE_FILENAME}).

* * @since 2.0 * @deprecated */ @Deprecated @Parameter(defaultValue = "true") protected boolean generateEpisode; /** *

Corresponding SchemaGen parameter: {@code episode}.

*

Generate an episode file with the supplied name from this XSD generation, so that other schemas that rely * on this schema can be compiled later and rely on classes that are generated from this compilation. * The generated episode file is simply a JAXB customization file (but with vendor extensions), normally known * as a binding file with the suffix .xjb.

*

If the episodeFileName parameter is not given, the episode file name is synthesized on the form * "episode_" + executionID + ".xjb" - typically something like episode_schemagen.xjb, but * it depends on the actual ID given in the execution element:

*
     *     
     * <executions>
     *     <execution>
     *         <id>schemagen</id>
     *         <goals>
     *             <goal>schemagen</goal>
     *         </goals>
     *     </execution>
     * </executions>
     *      
     * 
* * @see #STANDARD_EPISODE_FILENAME * @since 2.4 */ @Parameter protected String episodeFileName; /** *

If {@code true}, Elements or Attributes in the generated XSD files will be annotated with any * JavaDoc found for their respective properties. If {@code false}, no XML documentation annotations will be * generated in post-processing any results from the JAXB SchemaGenerator.

* * @since 2.0 */ @Parameter(defaultValue = "true") protected boolean createJavaDocAnnotations; /** *

A renderer used to create XML annotation text from JavaDoc comments found within the source code. * Unless another implementation is provided, the standard JavaDocRenderer used is * {@linkplain org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.DefaultJavaDocRenderer}.

* * @see org.codehaus.mojo.jaxb2.schemageneration.postprocessing.javadoc.DefaultJavaDocRenderer * @since 2.0 */ @Parameter protected JavaDocRenderer javaDocRenderer; /** *

Removes all files from the output directory before running SchemaGenerator.

* * @since 2.0 */ @Parameter(defaultValue = "true") protected boolean clearOutputDir; /** *

XSD schema files are not generated from POM projects or if no includes have been supplied.

* {@inheritDoc} */ @Override protected boolean shouldExecutionBeSkipped() { boolean toReturn = false; if ("pom".equalsIgnoreCase(getProject().getPackaging())) { warnAboutIncorrectPluginConfiguration("packaging", "POM-packaged projects should not generate XSDs."); toReturn = true; } if (getSources().isEmpty()) { warnAboutIncorrectPluginConfiguration("sources", "At least one Java Source file has to be included."); toReturn = true; } // All done. return toReturn; } /** * {@inheritDoc} */ @Override protected boolean isReGenerationRequired() { // // Use the stale flag method to identify if we should re-generate the XSDs from the sources. // Basically, we should re-generate the XSDs if: // // a) The staleFile does not exist // b) The staleFile exists and is older than one of the sources (Java or XJB files). // "Older" is determined by comparing the modification timestamp of the staleFile and the source files. // final File staleFile = getStaleFile(); final String debugPrefix = "StaleFile [" + FileSystemUtilities.getCanonicalPath(staleFile) + "]"; boolean stale = !staleFile.exists(); if (stale) { getLog().debug(debugPrefix + " not found. XML Schema (re-)generation required."); } else { final List sources = getSources(); if (getLog().isDebugEnabled()) { getLog().debug(debugPrefix + " found. Checking timestamps on source Java " + "files to determine if XML Schema (re-)generation is required."); } final long staleFileLastModified = staleFile.lastModified(); for (URL current : sources) { final URLConnection sourceFileConnection; try { sourceFileConnection = current.openConnection(); sourceFileConnection.connect(); } catch (Exception e) { if (getLog().isDebugEnabled()) { getLog().debug("Could not open a sourceFileConnection to [" + current + "]", e); } // Can't determine if the staleFile is younger than this source. // Re-generate to be on the safe side. stale = true; break; } try { if (sourceFileConnection.getLastModified() > staleFileLastModified) { if (getLog().isDebugEnabled()) { getLog().debug(current.toString() + " is newer than the stale flag file."); } stale = true; } } finally { if (sourceFileConnection instanceof HttpURLConnection) { ((HttpURLConnection) sourceFileConnection).disconnect(); } } } } // All done. return stale; } /** * {@inheritDoc} */ @Override protected boolean performExecution() throws MojoExecutionException, MojoFailureException { boolean updateStaleFileTimestamp = false; ToolExecutionEnvironment environment = null; try { // // Ensure that classes that SchemaGen expects to be loaded in the SystemToolClassLoader // is delegated to that ClassLoader, to comply with SchemaGen's internal reflective loading // of classes. Otherwise we will have ClassCastExceptions instead of proper execution. // final ClassRealm localRealm = (ClassRealm) getClass().getClassLoader(); for (String current : SYSTEM_TOOLS_CLASSLOADER_PACKAGES) { localRealm.importFrom(ToolProvider.getSystemToolClassLoader(), current); } // Configure the ThreadContextClassLoaderBuilder, to enable synthesizing a correct ClassPath for the tool. final ThreadContextClassLoaderBuilder classLoaderBuilder = ThreadContextClassLoaderBuilder .createFor(this.getClass(), getLog(), getEncoding(false)) .addPaths(getClasspath()) .addPaths(getProject().getCompileSourceRoots()); final LocaleFacet localeFacet = locale == null ? null : LocaleFacet.createFor(locale, getLog()); // Create the execution environment as required by the XJC tool. environment = new ToolExecutionEnvironment( getLog(), classLoaderBuilder, LoggingHandlerEnvironmentFacet.create(getLog(), getClass(), getEncoding(false)), localeFacet); final String projectBasedirPath = FileSystemUtilities.getCanonicalPath(getProject().getBasedir()); // Add any extra configured EnvironmentFacets, as configured in the POM. if (extraFacets != null) { for (EnvironmentFacet current : extraFacets) { environment.add(current); } } // Setup the environment. environment.setup(); // Compile the SchemaGen arguments final File episodeFile = getEpisodeFile(episodeFileName); final List sources = getSources(); final String[] schemaGenArguments = getSchemaGenArguments( environment.getClassPathAsArgument(), episodeFile, sources); // Ensure that the outputDirectory and workDirectory exists. // Clear them if configured to do so. FileSystemUtilities.createDirectory(getOutputDirectory(), clearOutputDir); FileSystemUtilities.createDirectory(getWorkDirectory(), clearOutputDir); // Re-generate the episode file's parent directory. getEpisodeFile(episodeFileName); // Do we need to re-create the episode file's parent directory? /*final boolean reCreateEpisodeFileParentDirectory = generateEpisode && clearOutputDir; if (reCreateEpisodeFileParentDirectory) { } */ try { // Check the system properties. // logSystemPropertiesAndBasedir(); // Fire the SchemaGenerator final int result = SchemaGenerator.run( schemaGenArguments, Thread.currentThread().getContextClassLoader()); if (SCHEMAGEN_INCORRECT_OPTIONS == result) { printSchemaGenCommandAndThrowException(projectBasedirPath, sources, schemaGenArguments, result, null); } else if (SCHEMAGEN_JAXB_ERRORS == result) { // TODO: Collect the error message(s) which was emitted by SchemaGen. How can this be done? throw new MojoExecutionException("JAXB errors arose while SchemaGen compiled sources to XML."); } // Copy generated XSDs and episode files from the WorkDirectory to the OutputDirectory, // but do not copy the intermediary bytecode files generated by schemagen. final List> exclusionFilters = PatternFileFilter.createIncludeFilterList( getLog(), "\\.class"); final List toCopy = FileSystemUtilities.resolveRecursively( Arrays.asList(getWorkDirectory()), exclusionFilters, getLog()); for (File current : toCopy) { // Get the path to the current file final String currentPath = FileSystemUtilities.getCanonicalPath(current.getAbsoluteFile()); final File target = new File(getOutputDirectory(), FileSystemUtilities.relativize(currentPath, getWorkDirectory(), true)); // Copy the file to the same relative structure within the output directory. FileSystemUtilities.createDirectory(target.getParentFile(), false); FileUtils.copyFile(current, target); } // // The XSD post-processing should be applied in the following order: // // 1. [XsdAnnotationProcessor]: Inject JavaDoc annotations for Classes. // 2. [XsdEnumerationAnnotationProcessor]: Inject JavaDoc annotations for Enums. // 3. [ChangeNamespacePrefixProcessor]: Change namespace prefixes within XSDs. // 4. [ChangeFilenameProcessor]: Change the fileNames of XSDs. // final boolean performPostProcessing = createJavaDocAnnotations || transformSchemas != null; if (performPostProcessing) { // Map the XML Namespaces to their respective XML URIs (and reverse) // The keys are the generated 'vanilla' XSD file names. final Map resolverMap = XsdGeneratorHelper.getFileNameToResolverMap(getOutputDirectory()); if (createJavaDocAnnotations) { if (getLog().isInfoEnabled()) { getLog().info("XSD post-processing: Adding JavaDoc annotations in generated XSDs."); } // Resolve the sources final List fileSources = new ArrayList(); for (URL current : sources) { if ("file".equalsIgnoreCase(current.getProtocol())) { final File toAdd = new File(current.getPath()); if (toAdd.exists()) { fileSources.add(toAdd); } else { if (getLog().isWarnEnabled()) { getLog().warn("Ignoring URL [" + current + "] as it is a nonexistent file."); } } } } final List files = FileSystemUtilities.resolveRecursively( fileSources, null, getLog()); // Acquire JavaDocs final JavaDocExtractor extractor = new JavaDocExtractor(getLog()).addSourceFiles(files); final SearchableDocumentation javaDocs = extractor.process(); // Modify the 'vanilla' generated XSDs by inserting the JavaDoc as annotations final JavaDocRenderer renderer = javaDocRenderer == null ? STANDARD_JAVADOC_RENDERER : javaDocRenderer; final int numProcessedFiles = XsdGeneratorHelper.insertJavaDocAsAnnotations(getLog(), getEncoding(false), getOutputDirectory(), javaDocs, renderer); if (getLog().isDebugEnabled()) { getLog().info("XSD post-processing: " + numProcessedFiles + " files processed."); } } if (transformSchemas != null) { if (getLog().isInfoEnabled()) { getLog().info("XSD post-processing: Renaming and converting XSDs."); } // Transform all namespace prefixes as requested. XsdGeneratorHelper.replaceNamespacePrefixes(resolverMap, transformSchemas, getLog(), getOutputDirectory(), getEncoding(false)); // Rename all generated schema files as requested. XsdGeneratorHelper.renameGeneratedSchemaFiles(resolverMap, transformSchemas, getLog(), getOutputDirectory(), getEncoding(false)); } } } catch (MojoExecutionException e) { throw e; } catch (Exception e) { // Find the root exception, and print its stack trace to the Maven Log. // These invocation target exceptions tend to produce really deep stack traces, // hiding the actual root cause of the exception. Throwable current = e; while (current.getCause() != null) { current = current.getCause(); } getLog().error("Execution failed."); // // Print a stack trace // StringBuilder rootCauseBuilder = new StringBuilder(); rootCauseBuilder.append("\n"); rootCauseBuilder.append("[Exception]: " + current.getClass().getName() + "\n"); rootCauseBuilder.append("[Message]: " + current.getMessage() + "\n"); for (StackTraceElement el : current.getStackTrace()) { rootCauseBuilder.append(" " + el.toString()).append("\n"); } getLog().error(rootCauseBuilder.toString().replaceAll("[\r\n]+", "\n")); printSchemaGenCommandAndThrowException(projectBasedirPath, sources, schemaGenArguments, -1, current); } // Indicate that the output directory was updated. getBuildContext().refresh(getOutputDirectory()); // Update the modification timestamp of the staleFile. updateStaleFileTimestamp = true; } finally { // Restore the environment if (environment != null) { environment.restore(); } } // Add generated directories to // All done. return updateStaleFileTimestamp; } /** * @return The working directory to which the SchemaGenerator should initially copy all its generated files, * including bytecode files, compiled from java sources. */ protected abstract File getWorkDirectory(); /** * Finds a List containing URLs to compiled bytecode files within this Compilation Unit. * Typically this equals the resolved files under the project's build directories, plus any * JAR artifacts found on the classpath. * * @return A non-null List containing URLs to bytecode files within this compilation unit. * Typically this equals the resolved files under the project's build directories, plus any JAR * artifacts found on the classpath. */ protected abstract List getCompiledClassNames(); /** * Override this method to acquire a List holding all URLs to the SchemaGen Java sources for which this * AbstractXsdGeneratorMojo should generate Xml Schema Descriptor files. * * @return A non-null List holding URLs to sources for the XSD generation. */ @Override protected abstract List getSources(); // // Private helpers // private String[] getSchemaGenArguments(final String classPath, final File episodeFile, final List sources) throws MojoExecutionException { final ArgumentBuilder builder = new ArgumentBuilder(); // Add all flags on the form '-flagName' // builder.withFlag(); // Add all arguments on the form '-argumentName argumentValue' // (i.e. in 2 separate elements of the returned String[]) builder.withNamedArgument("encoding", getEncoding(true)); builder.withNamedArgument("d", getWorkDirectory().getAbsolutePath()); builder.withNamedArgument("classpath", classPath); // From 2.4: Always generate an episode file. // builder.withNamedArgument("episode", FileSystemUtilities.getCanonicalPath(episodeFile)); try { // // The SchemaGenerator does not support directories as arguments: // "Caused by: java.lang.IllegalArgumentException: directories not supported" // ... implying we must resolve source files in the compilation unit. // // There seems to be two ways of adding sources to the SchemaGen tool: // 1) Using java source files // Define the relative paths to source files, calculated from the System.property "user.dir" // (i.e. *not* the Maven "basedir" property) on the form 'src/main/java/se/west/something/SomeClass.java'. // Sample: javac -d . ../github_jaxb2_plugin/src/it/schemagen-main/src/main/java/se/west/gnat/Foo.java // // 2) Using bytecode files // Define the CLASSPATH to point to build output directories (such as target/classes), and then use // package notation arguments on the form 'se.west.something.SomeClass'. // Sample: schemagen -d . -classpath brat se.west.gnat.Foo // // The jaxb2-maven-plugin uses these two methods in the order given. // builder.withPreCompiledArguments(getSchemaGeneratorSourceFiles(sources)); } catch (IOException e) { throw new MojoExecutionException("Could not compile source paths for the SchemaGenerator", e); } // All done. return logAndReturnToolArguments(builder.build(), "SchemaGen"); } /** *

The SchemaGenerator does not support directories as arguments, implying we must resolve source * files in the compilation unit. This fact is shown when supplying a directory argument as source, when * the tool emits: *

Caused by: java.lang.IllegalArgumentException: directories not supported

*

There seems to be two ways of adding sources to the SchemaGen tool:

*
*
1. Java Source files
*
Define the relative paths to source files, calculated from the System.property {@code user.dir} * (i.e. not the Maven {@code basedir} property) on the form * {@code src/main/java/se/west/something/SomeClass.java}.
* Sample: {@code javac -d . . * ./github_jaxb2_plugin/src/it/schemagen-main/src/main/java/se/west/gnat/Foo.java}
*
2. Bytecode files
*
Define the {@code CLASSPATH} to point to build output directories (such as target/classes), and then * use package notation arguments on the form {@code se.west.something.SomeClass}.
* Sample: {@code schemagen -d . -classpath brat se.west.gnat.Foo}
*
*

The jaxb2-maven-plugin uses these two methods in the order given

* * @param sources The compiled sources (as calculated from the local project's * source paths, {@code getSources()}). * @return A sorted List holding all sources to be used by the SchemaGenerator. According to the SchemaGenerator * documentation, the order in which the source arguments are provided is irrelevant. * The sources are to be rendered as the final (open-ended) argument to the schemagen execution. * @see #getSources() */ private List getSchemaGeneratorSourceFiles(final List sources) throws IOException, MojoExecutionException { final SortedMap className2SourcePath = new TreeMap(); final File baseDir = getProject().getBasedir(); final File userDir = new File(System.getProperty("user.dir")); final String encoding = getEncoding(true); // 1) Find/add all sources available in the compilation unit. for (URL current : sources) { final File sourceCodeFile = FileSystemUtilities.getFileFor(current, encoding); // Calculate the relative path for the current source final String relativePath = FileSystemUtilities.relativize( FileSystemUtilities.getCanonicalPath(sourceCodeFile), userDir, true); if (getLog().isDebugEnabled()) { getLog().debug("SourceCodeFile [" + FileSystemUtilities.getCanonicalPath(sourceCodeFile) + "] and userDir [" + FileSystemUtilities.getCanonicalPath(userDir) + "] ==> relativePath: " + relativePath + ". (baseDir: " + FileSystemUtilities.getCanonicalPath(baseDir) + "]"); } // Find the Java class(es) within the source. final JavaProjectBuilder builder = new JavaProjectBuilder(); builder.setEncoding(encoding); // // Ensure that we include package-info.java classes in the SchemaGen compilation. // if (sourceCodeFile.getName().trim().equalsIgnoreCase(PACKAGE_INFO_FILENAME)) { // For some reason, QDox requires the package-info.java to be added as a URL instead of a File. builder.addSource(current); final Collection packages = builder.getPackages(); if (packages.size() != 1) { throw new MojoExecutionException("Exactly one package should be present in file [" + sourceCodeFile.getPath() + "]"); } // Make the key indicate that this is the package-info.java file. final JavaPackage javaPackage = packages.iterator().next(); className2SourcePath.put("package-info for (" + javaPackage.getName() + ")", relativePath); continue; } // This is not a package-info.java file, so QDox lets us add this as a File. builder.addSource(sourceCodeFile); // Map any found FQCN to the relativized path of its source file. for (JavaSource currentJavaSource : builder.getSources()) { for (JavaClass currentJavaClass : currentJavaSource.getClasses()) { final String className = currentJavaClass.getFullyQualifiedName(); if (className2SourcePath.containsKey(className)) { if (getLog().isWarnEnabled()) { getLog().warn("Already mapped. Source class [" + className + "] within [" + className2SourcePath.get(className) + "]. Not overwriting with [" + relativePath + "]"); } } else { className2SourcePath.put(className, relativePath); } } } } /* // 2) Find any bytecode available in the compilation unit, and add its file as a SchemaGen argument. // // The algorithm is: // 1) Add bytecode classpath unless its class is already added in source form. // 2) SchemaGen cannot handle directory arguments, so any bytecode files in classpath directories // must be resolved. // 3) All JARs in the classpath should be added as arguments to SchemaGen. // // .... Gosh ... // for (URL current : getCompiledClassNames()) { getLog().debug(" (compiled ClassName) --> " + current.toExternalForm()); } Filters.initialize(getLog(), CLASS_INCLUDE_FILTERS); final List classPathURLs = new ArrayList(); for (String current : getClasspath()) { final File currentFile = new File(current); if (FileSystemUtilities.EXISTING_FILE.accept(currentFile)) { // This is a file/JAR. Simply add its path to SchemaGen's arguments. classPathURLs.add(FileSystemUtilities.getUrlFor(currentFile)); } else if (FileSystemUtilities.EXISTING_DIRECTORY.accept(currentFile)) { // Resolve all bytecode files within this directory. // FileSystemUtilities.filterFiles(baseDir, ) if (getLog().isDebugEnabled()) { getLog().debug("TODO: Resolve and add bytecode files within: [" + FileSystemUtilities.getCanonicalPath(currentFile) + "]"); } // Find the byte code files within the current directory. final List byteCodeFiles = new ArrayList(); for(File currentResolvedFile : FileSystemUtilities.resolveRecursively( Arrays.asList(currentFile), null, getLog())) { if(Filters.matchAtLeastOnce(currentResolvedFile, CLASS_INCLUDE_FILTERS)) { byteCodeFiles.add(currentResolvedFile); } } for(File currentByteCodeFile : byteCodeFiles) { final String currentCanonicalPath = FileSystemUtilities.getCanonicalPath( currentByteCodeFile.getAbsoluteFile()); final String relativized = FileSystemUtilities.relativize(currentCanonicalPath, FileSystemUtilities.getCanonicalFile(currentFile.getAbsoluteFile())); final String pathFromUserDir = FileSystemUtilities.relativize(currentCanonicalPath, userDir); final String className = relativized.substring(0, relativized.indexOf(".class")) .replace("/", ".") .replace(File.separator, "."); if(!className2SourcePath.containsKey(className)) { className2SourcePath.put(className, pathFromUserDir); if(getLog().isDebugEnabled()) { getLog().debug("Adding ByteCode [" + className + "] at relativized path [" + pathFromUserDir + "]"); } } else { if(getLog().isDebugEnabled()) { getLog().debug("ByteCode [" + className + "] already added. Not re-adding."); } } } } else if (getLog().isWarnEnabled()) { final String suffix = !currentFile.exists() ? " nonexistent" : " was neither a File nor a Directory"; getLog().warn("Classpath part [" + current + "] " + suffix + ". Ignoring it."); } } /* for (URL current : getCompiledClassNames()) { // TODO: FIX THIS! // Get the class information data from the supplied URL for (String currentClassPathElement : getClasspath()) { if(getLog().isDebugEnabled()) { getLog().debug("Checking class path element: [" + currentClassPathElement + "]"); } } if(getLog().isDebugEnabled()) { getLog().debug("Processing compiledClassName: [" + current + "]"); } // Find the Java class(es) within the source. final JavaProjectBuilder builder = new JavaProjectBuilder(); builder.setEncoding(getEncoding(true)); builder.addSource(current); for (JavaSource currentSource : builder.getSources()) { for (JavaClass currentClass : currentSource.getClasses()) { final String className = currentClass.getFullyQualifiedName(); if (className2SourcePath.containsKey(className)) { if (getLog().isWarnEnabled()) { getLog().warn("Already mapped. Source class [" + className + "] within [" + className2SourcePath.get(className) + "]. Not overwriting with [" + className + "]"); } } else { className2SourcePath.put(className, className); } } } } */ if (getLog().isDebugEnabled()) { final int size = className2SourcePath.size(); getLog().debug("[ClassName-2-SourcePath Map (size: " + size + ")] ..."); int i = 0; for (Map.Entry current : className2SourcePath.entrySet()) { getLog().debug(" " + (++i) + "/" + size + ": [" + current.getKey() + "]: " + current.getValue()); } getLog().debug("... End [ClassName-2-SourcePath Map]"); } // Sort the source paths and place them first in the argument array final ArrayList toReturn = new ArrayList(className2SourcePath.values()); Collections.sort(toReturn); // All Done. return toReturn; } private void printSchemaGenCommandAndThrowException(final String projectBasedirPath, final List sources, final String[] schemaGenArguments, final int result, final Throwable cause) throws MojoExecutionException { final StringBuilder errorMsgBuilder = new StringBuilder(); errorMsgBuilder.append("\n+=================== [SchemaGenerator Error '" + (result == -1 ? "" : result) + "']\n"); errorMsgBuilder.append("|\n"); errorMsgBuilder.append("| SchemaGen did not complete its operation correctly.\n"); errorMsgBuilder.append("|\n"); errorMsgBuilder.append("| To re-create the error (and get a proper error message), cd to:\n"); errorMsgBuilder.append("| ").append(projectBasedirPath).append("\n"); errorMsgBuilder.append("| ... and fire the following on a command line/in a shell:\n"); errorMsgBuilder.append("|\n"); final StringBuilder builder = new StringBuilder("schemagen "); for (String current : schemaGenArguments) { builder.append(current).append(" "); } errorMsgBuilder.append("| " + builder.toString() + "\n"); errorMsgBuilder.append("|\n"); errorMsgBuilder.append("| The following source files should be processed by schemagen:\n"); for (int i = 0; i < sources.size(); i++) { errorMsgBuilder.append("| " + i + ": ").append(sources.get(i).toString()).append("\n"); } errorMsgBuilder.append("|\n"); errorMsgBuilder.append("+=================== [End SchemaGenerator Error]\n"); final String msg = errorMsgBuilder.toString().replaceAll("[\r\n]+", "\n"); if (cause != null) { throw new MojoExecutionException(msg, cause); } else { throw new MojoExecutionException(msg); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy