blob: c96042e85a7698cf367123b723fb088669ddb6c7 [file] [log] [blame]
/* Copyright 2004 The Apache Software Foundation
*
* Licensed 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.
*/
package org.apache.xmlbeans.impl.tool;
import org.apache.maven.model.Resource;
import org.apache.maven.plugin.AbstractMojo;
import org.apache.maven.plugin.MojoExecutionException;
import org.apache.maven.plugin.MojoFailureException;
import org.apache.maven.plugins.annotations.LifecyclePhase;
import org.apache.maven.plugins.annotations.Mojo;
import org.apache.maven.plugins.annotations.Parameter;
import org.apache.maven.project.MavenProject;
import org.apache.xmlbeans.XmlError;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.net.*;
import java.util.*;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import static org.apache.xmlbeans.impl.tool.SchemaCompiler.parsePartialMethods;
@SuppressWarnings("unused")
@Mojo(name = "compile", defaultPhase = LifecyclePhase.GENERATE_SOURCES)
public class MavenPlugin extends AbstractMojo {
// ******************************************************************************************
// As we don't use the maven plugin-plugin, the defaultValues and others need to be manually
// copied into resources/maven/plugin.xml
// ******************************************************************************************
/** The maven project */
@Parameter( readonly = true, defaultValue = "${project}" )
private MavenProject project;
/** sourceDir is a base directory for the list in sourceschema */
@Parameter( defaultValue = "${project.basedir}/src/main/schema" )
private String sourceDir;
/** sourceSchemas is a comma-delimited list of all the schemas you want to compile */
@Parameter( defaultValue = "*.xsd,*.wsdl,*.java" )
private String sourceSchemas;
/** xmlConfigs points to your xmlconfig.xml file */
@Parameter( defaultValue = "${project.basedir}/src/schema/xmlconfig.xml" )
private String xmlConfigs;
/** javaTargetdir is where you want generated java source to appear */
@Parameter( defaultValue = "${project.basedir}/target/generated-sources" )
private String javaTargetDir;
/** classTargetDir is where you want compiled class files to appear */
@Parameter( defaultValue = "${project.basedir}/target/generated-resources" )
private String classTargetDir;
/** catalogLocation is the location of an entity resolver catalog to use for resolving namespace to schema locations. */
@Parameter
private String catalogLocation;
@Parameter
private String classPath;
@Parameter
private List<Resource> resources;
/** buildSchemas sets build process of the generated sources */
@Parameter(defaultValue = "true")
private boolean buildSchemas;
/** destination directory of the copied xsd files - default: schemaorg_apache_xmlbeans/src */
@Parameter( defaultValue = "schemaorg_apache_xmlbeans/src" )
private String baseSchemaLocation;
/** schema system name - default: ${project.artifactId} */
@Parameter( defaultValue = "${project.artifactId}" )
private String name;
/** verbose output - default: false */
@Parameter( defaultValue = "false" )
private boolean verbose;
/** no output - default: true */
@Parameter( defaultValue = "true" )
private boolean quite;
/** deactivate unique particle attribution - default: false */
@Parameter( defaultValue = "false" )
private boolean noUpa;
/** deactivate particle valid (restriction) - default: false */
@Parameter( defaultValue = "false" )
private boolean noPvr;
/** deactivate annotation generation - default: false */
@Parameter( defaultValue = "false" )
private boolean noAnn;
/** do not validate contents of documentation-tags - default: false */
@Parameter( defaultValue = "false" )
private boolean noVDoc;
/** Metadata package name. If explicitly set empty, generates to org.apache.xmlbeans.metadata - default: ${project.groupId}.${project.artifactId}.metadata */
@Parameter( defaultValue = "${project.groupId}.${project.artifactId}.metadata" )
private String repackage;
/**
* If this option is set, then the schema compiler will permit and
* ignore multiple definitions of the same component (element, attribute,
* type, etc) names in the given namespaces. If multiple definitions
* with the same name appear, the definitions that happen to be processed
* last will be ignored.
*
* a list of namespace URIs
*/
@Parameter
private List<String> mdefNamespaces;
/**
* Only generate a subset of the bean methods. Comma-seperated list of the following method types:
* GET, XGET, IS_SET, IS_NIL, IS_NIL_IDX, SET, SET_NIL, SET_NIL_IDX, XSET, UNSET,
* GET_ARRAY, XGET_ARRAY, GET_IDX, XGET_IDX, XSET_ARRAY, XSET_IDX,
* SIZE_OF_ARRAY, SET_ARRAY, SET_IDX,
* INSERT_IDX, INSERT_NEW_IDX,
* ADD, ADD_NEW, REMOVE_IDX,
* GET_LIST, XGET_LIST, SET_LIST,
* INSTANCE_TYPE
*
* Example: "ALL,-GET_LIST,-XGET_LIST" excludes GET_LIST and XGET_LIST methods
*/
@Parameter
private String partialMethods;
@Parameter( defaultValue = "false" )
private boolean download;
@Parameter( defaultValue = "true" )
private boolean sourceOnly;
@Parameter
private File basedir;
@Parameter
private String compiler;
@Parameter( defaultValue = CodeGenUtil.DEFAULT_MEM_START )
private String memoryInitialSize;
@Parameter( defaultValue = CodeGenUtil.DEFAULT_MEM_MAX )
private String memoryMaximumSize;
@Parameter( defaultValue = "${project.basedir}/target/${project.artifactId}-${project.version}-xmltypes.jar" )
private File outputJar;
@Parameter( defaultValue = "false" )
private boolean debug;
/** copy annotations to javadoc of generated sources - default: false */
@Parameter( defaultValue = "false" )
private boolean copyAnn;
@Parameter
private List<Extension> extensions;
public void execute() throws MojoExecutionException, MojoFailureException {
if (sourceDir == null || sourceDir.isEmpty() || !new File(sourceDir).isDirectory()) {
throw new MojoFailureException("Set configuration <sourceDir> (='"+sourceDir+"') to a valid directory containing *.xsd,*.wsdl files.");
}
if (baseSchemaLocation == null || baseSchemaLocation.isEmpty()) {
throw new MojoFailureException("baseSchemaLocation is empty");
}
if (sourceSchemas == null) {
getLog().debug("sourceSchemas is null");
}
if (classPath == null) {
getLog().debug("classPath is null");
}
final List<File> xsds = new ArrayList<>();
final List<File> wsdls = new ArrayList<>();
final List<File> javas = new ArrayList<>();
File base = new File(sourceDir);
Resource resource = new Resource();
resource.setDirectory(sourceDir);
resource.setTargetPath(baseSchemaLocation);
// if sourceSchemas is not specified use all found schemas
// otherwise convert comma-separated string to regex including glob parameter
Pattern pat = Pattern.compile(sourceSchemas == null ? ".*" :
"(" + sourceSchemas
.replace(",","|")
.replace(".", "\\.")
.replace("*",".*") +
")");
File[] schemaFiles = Objects.requireNonNull(base.listFiles((dir, name) ->
!name.endsWith(".xsdconfig") && pat.matcher(name).matches()));
for (File sf : schemaFiles) {
String name = sf.getName();
switch (name.replaceAll(".*\\.", "")) {
case "wsdl":
wsdls.add(sf);
break;
case "java":
javas.add(sf);
break;
default:
xsds.add(sf);
break;
}
resource.addInclude(name);
}
resources = Collections.singletonList(resource);
if (buildSchemas) {
List<File> configs = (xmlConfigs == null || xmlConfigs.isEmpty()) ? Collections.emptyList()
: Stream.of(xmlConfigs.split(",")).flatMap(s ->
Stream.of(new File(s), new File(base, s)).filter(File::exists)
).collect(Collectors.toList());
List<File> classPathList = new ArrayList<>();
List<URL> urls = new ArrayList<>();
if (classPath != null) {
for (String classpathElement : classPath.split(",")) {
File file = new File(classpathElement);
classPathList.add(file);
try {
urls.add(file.toURI().toURL());
} catch (MalformedURLException e) {
throw new MojoFailureException("invalid classpath: "+file, e);
}
}
}
ClassLoader cl = new URLClassLoader(urls.toArray(new URL[0]));
EntityResolver entityResolver = MavenPluginResolver.getResolver(catalogLocation);
URI sourceDirURI = new File(sourceDir).toURI();
entityResolver = new PassThroughResolver(cl, entityResolver, sourceDirURI, baseSchemaLocation);
Parameters params = new Parameters();
params.setXsdFiles(files(xsds));
params.setWsdlFiles(files(wsdls));
params.setJavaFiles(files(javas));
params.setConfigFiles(files(configs));
params.setClasspath(files(classPathList));
params.setName(name);
params.setSrcDir(new File(javaTargetDir));
params.setClassesDir(new File(classTargetDir));
params.setNojavac(sourceOnly);
params.setVerbose(verbose);
params.setEntityResolver(entityResolver);
params.setQuiet(quite);
params.setNoUpa(noUpa);
params.setNoPvr(noPvr);
params.setNoAnn(noAnn);
params.setCopyAnn(copyAnn);
params.setNoVDoc(noVDoc);
if (repackage != null && !repackage.isEmpty()) {
params.setRepackage("org.apache.xmlbeans.metadata:"+repackage);
}
if (mdefNamespaces != null && !mdefNamespaces.isEmpty()) {
params.setMdefNamespaces(new HashSet<>(mdefNamespaces));
}
List<XmlError> errorList = new ArrayList<>();
params.setErrorListener(errorList);
if (partialMethods != null && !partialMethods.isEmpty()) {
params.setPartialMethods(parsePartialMethods(partialMethods));
}
params.setDownload(download);
params.setBaseDir(basedir);
params.setCompiler(compiler);
params.setMemoryInitialSize(memoryInitialSize);
params.setMemoryMaximumSize(memoryMaximumSize);
params.setOutputJar(outputJar);
params.setDebug(debug);
params.setExtensions(extensions);
boolean result = SchemaCompiler.compile(params);
if (!result) {
throw new MojoFailureException("Schema compilation failed!\n"+
errorList.stream().map(XmlError::toString).collect(Collectors.joining("\n"))
);
}
Resource genResource = new Resource();
genResource.setDirectory(classTargetDir);
project.addResource(genResource);
project.addCompileSourceRoot(javaTargetDir);
}
}
private static File[] files(List<File> files) {
return (files == null || files.isEmpty()) ? null : files.toArray(new File[0]);
}
private static class PassThroughResolver implements EntityResolver {
private final ClassLoader cl;
private final EntityResolver delegate;
private final URI sourceDir;
//this copy has an / appended
private final String baseSchemaLocation;
public PassThroughResolver(ClassLoader cl, EntityResolver delegate, URI sourceDir, String baseSchemaLocation) {
this.cl = cl;
this.delegate = delegate;
this.sourceDir = sourceDir;
this.baseSchemaLocation = baseSchemaLocation + "/";
}
public InputSource resolveEntity(String publicId, String systemId) throws SAXException, IOException {
if (delegate != null) {
InputSource is = delegate.resolveEntity(publicId, systemId);
if (is != null) {
return is;
}
}
System.out.println("Could not resolve publicId: " + publicId + ", systemId: " + systemId + " from catalog");
String localSystemId;
try {
localSystemId = sourceDir.relativize(new URI(systemId)).toString();
} catch (URISyntaxException e) {
throw new IOException("Could not relativeize systemId", e);
}
InputStream in = cl.getResourceAsStream(localSystemId);
if (in != null) {
System.out.println("found in classpath at: " + localSystemId);
return new InputSource(in);
}
in = cl.getResourceAsStream(baseSchemaLocation + localSystemId);
if (in != null) {
System.out.println("found in classpath at: META-INF/" + localSystemId);
return new InputSource(in);
}
System.out.println("Not found in classpath, looking in current directory: " + systemId);
return new InputSource(systemId);
}
}
}