blob: b83eb5ebe553535fa3f431310ee6d78e9eca0dc3 [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" )
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 comma-seperated list of namespace URIs
*/
@Parameter
private 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;
/*
public String getSourceDir() {
return sourceDir;
}
public void setSourceDir(String sourceDir) {
this.sourceDir = sourceDir;
}
public String getSourceSchemas() {
return sourceSchemas;
}
public void setSourceSchemas(String sourceSchemas) {
this.sourceSchemas = sourceSchemas;
}
public String getXmlConfigs() {
return xmlConfigs;
}
public void setXmlConfigs(String xmlConfigs) {
this.xmlConfigs = xmlConfigs;
}
public String getJavaTargetDir() {
return javaTargetDir;
}
public void setJavaTargetDir(String javaTargetDir) {
this.javaTargetDir = javaTargetDir;
}
public String getClassTargetDir() {
return classTargetDir;
}
public void setClassTargetDir(String classTargetDir) {
this.classTargetDir = classTargetDir;
}
public String getCatalogLocation() {
return catalogLocation;
}
public void setCatalogLocation(String catalogLocation) {
this.catalogLocation = catalogLocation;
}
public String getClassPath() {
return classPath;
}
public void setClassPath(String classPath) {
this.classPath = classPath;
}
public List<Resource> getResources() {
return resources;
}
public void setResources(List<Resource> resources) {
this.resources = resources;
}
public boolean isBuildSchemas() {
return buildSchemas;
}
public void setBuildSchemas(boolean buildSchemas) {
this.buildSchemas = buildSchemas;
}
public String getBaseSchemaLocation() {
return baseSchemaLocation;
}
public void setBaseSchemaLocation(String baseSchemaLocation) {
if (baseSchemaLocation != null && !(baseSchemaLocation.length() == 0)) {
this.baseSchemaLocation = baseSchemaLocation;
}
}*/
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");
}
List<File> xsds = new ArrayList<>();
List<File> wsdls = 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) {
(sf.getName().endsWith(".wsdl") ? wsdls : xsds).add(sf);
resource.addInclude(sf.getName());
}
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();
if (!xsds.isEmpty()) {
params.setXsdFiles(xsds.toArray(new File[0]));
}
if (!wsdls.isEmpty()) {
params.setWsdlFiles(wsdls.toArray(new File[0]));
}
if (!configs.isEmpty()) {
params.setConfigFiles(configs.toArray(new File[0]));
}
if (!classPathList.isEmpty()) {
params.setClasspath(classPathList.toArray(new File[0]));
}
params.setName(name);
params.setSrcDir(new File(javaTargetDir));
params.setClassesDir(new File(classTargetDir));
params.setNojavac(true);
params.setVerbose(verbose);
params.setEntityResolver(entityResolver);
params.setQuiet(quite);
params.setNoUpa(noUpa);
params.setNoPvr(noPvr);
params.setNoAnn(noAnn);
params.setNoVDoc(noVDoc);
if (repackage != null && !repackage.isEmpty()) {
params.setRepackage("org.apache.xmlbeans.metadata:"+repackage);
}
if (mdefNamespaces != null && !mdefNamespaces.isEmpty()) {
params.setMdefNamespaces(new HashSet<>(Arrays.asList(mdefNamespaces.split(","))));
}
List<XmlError> errorList = new ArrayList<>();
params.setErrorListener(errorList);
if (partialMethods != null && !partialMethods.isEmpty()) {
params.setPartialMethods(parsePartialMethods(partialMethods));
}
// params.setBaseDir(null);
// params.setJavaFiles(new File[0]);
// params.setCompiler(null);
// params.setMemoryInitialSize(null);
// params.setMemoryMaximumSize(null);
// params.setOutputJar(null);
// params.setDownload(false);
// params.setDebug(debug);
// params.setExtensions(null);
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 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);
}
}
}