blob: bd7dd10f88a4617e465788862a384d686b12adb9 [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.tools.ant.BuildException;
import org.apache.tools.ant.FileScanner;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.taskdefs.Jar;
import org.apache.tools.ant.taskdefs.Javac;
import org.apache.tools.ant.taskdefs.MatchingTask;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.Path;
import org.apache.tools.ant.types.Reference;
import org.apache.xmlbeans.XmlError;
import org.apache.xmlbeans.impl.common.IOUtil;
import java.io.File;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URI;
import java.util.*;
/**
* Modeled after Ant's javac and zip tasks.
* <p>
* Schema files to process, or directories of schema files, are set with the 'schema'
* attribute, and can be filtered with 'includes' and 'excludes'.
* Alternatively, one or more nested &lt;fileset&gt; elements can specify the
* files and directories to be used to generate this XMLBean.
* The include set can also define .java files that should be built as well.
* See the FileSet documentation at http://jakarta.apache.org/ant/manual/index.html
* for instructions on FileSets if you are unfamiliar with their usage.
*/
public class XMLBean extends MatchingTask {
private final List<FileSet> schemas = new ArrayList<>();
private Set<String> mdefnamespaces;
private Path classpath;
private File destfile,
schema,
srcgendir,
classgendir;
private boolean quiet,
verbose,
debug,
optimize,
download,
srconly,
noupa,
nopvr,
noann,
novdoc,
noext = false,
failonerror = true,
fork = true,
includeAntRuntime = true,
noSrcRegen,
includeJavaRuntime = false,
nowarn = false;
private String typesystemname,
forkedExecutable,
compiler,
debugLevel,
memoryInitialSize,
memoryMaximumSize,
catalog,
repackage;
private final List<Extension> extensions = new ArrayList<>();
private final Map<String, Set<File>> _extRouter = new HashMap<>(5);
private static final String XSD = ".xsd",
WSDL = ".wsdl",
JAVA = ".java",
XSDCONFIG = ".xsdconfig";
public void execute() throws BuildException {
/* VALIDATION */
//required
if (schemas.size() == 0
&& schema == null
&& fileset.getDir(project) == null) {
String msg = "The 'schema' or 'dir' attribute or a nested fileset is required.";
if (failonerror) {
throw new BuildException(msg);
} else {
log(msg, Project.MSG_ERR);
return;
}
}
_extRouter.put(XSD, new HashSet<>());
_extRouter.put(WSDL, new HashSet<>());
_extRouter.put(JAVA, new HashSet<>());
_extRouter.put(XSDCONFIG, new HashSet<>());
File theBasedir = schema;
if (schema != null) {
if (schema.isDirectory()) {
FileScanner scanner = getDirectoryScanner(schema);
String[] paths = scanner.getIncludedFiles();
processPaths(paths, scanner.getBasedir());
} else {
theBasedir = schema.getParentFile();
processPaths(new String[]{schema.getName()}, theBasedir);
}
}
if (fileset.getDir(project) != null) {
schemas.add(fileset);
}
for (FileSet fs : schemas) {
FileScanner scanner = fs.getDirectoryScanner(project);
File basedir = scanner.getBasedir();
String[] paths = scanner.getIncludedFiles();
processPaths(paths, basedir);
}
Set<File> xsdList = _extRouter.get(XSD);
Set<File> wsdlList = _extRouter.get(WSDL);
if (xsdList.size() + wsdlList.size() == 0) {
log("Could not find any xsd or wsdl files to process.", Project.MSG_WARN);
return;
}
//optional
Set<File> javaList = _extRouter.get(JAVA);
Set<File> xsdconfigList = _extRouter.get(XSDCONFIG);
if (srcgendir == null && srconly) {
srcgendir = classgendir;
}
if (destfile == null && classgendir == null && !srconly) {
destfile = new File("xmltypes.jar");
}
if (verbose) {
quiet = false;
}
/* EXECUTION */
File[] xsdArray = xsdList.toArray(new File[0]);
File[] wsdlArray = wsdlList.toArray(new File[0]);
File[] javaArray = javaList.toArray(new File[0]);
File[] xsdconfigArray = xsdconfigList.toArray(new File[0]);
ErrorLogger err = new ErrorLogger(verbose);
boolean success = false;
try {
// create a temp directory
File tmpdir = null;
if (srcgendir == null || classgendir == null) {
tmpdir = SchemaCodeGenerator.createTempDir();
}
if (srcgendir == null) {
srcgendir = IOUtil.createDir(tmpdir, "src");
}
if (classgendir == null) {
classgendir = IOUtil.createDir(tmpdir, "classes");
}
// use the system classpath if user didn't provide any
if (classpath == null) {
classpath = new Path(project);
classpath.concatSystemClasspath();
}
// prepend the output directory on the classpath
Path.PathElement pathElement = classpath.createPathElement();
pathElement.setLocation(classgendir);
String[] paths = classpath.list();
File[] cp = new File[paths.length];
for (int i = 0; i < paths.length; i++) {
cp[i] = new File(paths[i]);
}
// generate the source
SchemaCompiler.Parameters params = new SchemaCompiler.Parameters();
params.setBaseDir(theBasedir);
params.setXsdFiles(xsdArray);
params.setWsdlFiles(wsdlArray);
params.setJavaFiles(javaArray);
params.setConfigFiles(xsdconfigArray);
params.setClasspath(cp);
params.setName(typesystemname);
params.setSrcDir(srcgendir);
params.setClassesDir(classgendir);
params.setNojavac(true); // always compile using javac task below
params.setDebug(debug);
params.setVerbose(verbose);
params.setQuiet(quiet);
params.setDownload(download);
params.setExtensions(extensions);
params.setErrorListener(err);
params.setCatalogFile(catalog);
params.setIncrementalSrcGen(noSrcRegen);
params.setMdefNamespaces(mdefnamespaces);
params.setNoUpa(noupa);
params.setNoPvr(nopvr);
params.setNoAnn(noann);
params.setNoVDoc(novdoc);
params.setNoExt(noext);
params.setRepackage(repackage);
success = SchemaCompiler.compile(params);
if (success && !srconly) {
long start = System.currentTimeMillis();
// compile the source
Javac javac = new Javac();
javac.setProject(project);
javac.setTaskName(getTaskName());
javac.setClasspath(classpath);
if (compiler != null) {
javac.setCompiler(compiler);
}
javac.setDebug(debug);
if (debugLevel != null) {
javac.setDebugLevel(debugLevel);
}
javac.setDestdir(classgendir);
javac.setExecutable(forkedExecutable);
javac.setFailonerror(failonerror);
javac.setFork(fork);
javac.setSource("1.8");
javac.setTarget("1.8");
javac.setIncludeantruntime(includeAntRuntime);
javac.setIncludejavaruntime(includeJavaRuntime);
javac.setNowarn(nowarn);
javac.setSrcdir(new Path(project, srcgendir.getAbsolutePath()));
if (memoryInitialSize != null) {
javac.setMemoryInitialSize(memoryInitialSize);
}
if (memoryMaximumSize != null) {
javac.setMemoryMaximumSize(memoryMaximumSize);
}
javac.setOptimize(optimize);
javac.setVerbose(verbose);
javac.execute();
long finish = System.currentTimeMillis();
if (!quiet) {
log("Time to compile code: " + ((double) (finish - start) / 1000.0) + " seconds");
}
if (destfile != null) {
// jar the compiled classes
Jar jar = new Jar();
jar.setProject(project);
jar.setTaskName(getTaskName());
jar.setBasedir(classgendir);
jar.setDestFile(destfile);
jar.execute();
}
}
if (tmpdir != null) {
SchemaCodeGenerator.tryHardToDelete(tmpdir);
}
} catch (BuildException e) {
// re-throw anything thrown from javac or jar task
throw e;
} catch (Throwable e) {
//interrupted means cancel
if (e instanceof InterruptedException || failonerror) {
throw new BuildException(e);
}
log("Exception while building schemas: " + e.getMessage(), Project.MSG_ERR);
StringWriter sw = new StringWriter();
e.printStackTrace(new PrintWriter(sw));
log(sw.toString(), Project.MSG_VERBOSE);
}
if (!success && failonerror) {
throw new BuildException(); //stop the build
}
}
private void processPaths(String[] paths, File baseDir) {
for (String s : paths) {
int dot = s.lastIndexOf('.');
if (dot > -1) {
String possExt = s.substring(dot).toLowerCase();
Set<File> set = _extRouter.get(possExt);
if (set != null) {
set.add(new File(baseDir, s));
}
}
}
}
public void addFileset(FileSet fileset) {
schemas.add(fileset);
}
/////////////////////////////
//Getter/Setters
public File getSchema() {
return schema;
}
/**
* A file that points to either an individual schema file or a directory of files.
* It is optional only if one or more &lt;fileset&gt; elements are nested in this
* task.
*
* @param schema Required, unless a fileset element is nested.
*/
public void setSchema(File schema) {
this.schema = schema;
}
/**
* The classpath to use if schemas in the fileset import definitions that are
* supplied by other compiled xml beans JAR files, or if .java files are in the
* schema fileset.
*
* @param classpath Optional.
*/
public void setClasspath(Path classpath) {
if (this.classpath != null) {
this.classpath.append(classpath);
} else {
this.classpath = classpath;
}
}
/**
* Adds a path to the classpath.
*/
public Path createClasspath() {
if (classpath == null) {
classpath = new Path(project);
}
return classpath.createPath();
}
/**
* Adds a reference to a classpath defined elsewhere.
*
* @param classpathref Optional.
*/
public void setClasspathRef(Reference classpathref) {
if (classpath == null) {
classpath = new Path(project);
}
classpath.createPath().setRefid(classpathref);
}
public Path getClasspath() {
return classpath;
}
public File getDestfile() {
return destfile;
}
/**
* Define the name of the jar file created. For instance, "myXMLBean.jar"
* will output the results of this task into a jar with the same name.
* Optional, defaults to "xmltypes.jar".
*
* @param destfile Optional.
*/
public void setDestfile(File destfile) {
this.destfile = destfile;
}
public File getSrcgendir() {
return srcgendir;
}
/**
* Set a location to generate .java files into. Optional, defaults to
* a temp dir.
*
* @param srcgendir Optional.
*/
public void setSrcgendir(File srcgendir) {
this.srcgendir = srcgendir;
}
public File getClassgendir() {
return classgendir;
}
/**
* Set a location to generate .class files into. Optional, defaults to
* a temp dir.
*
* @param classgendir Optional.
*/
public void setClassgendir(File classgendir) {
this.classgendir = classgendir;
}
/**
* Choose the implementation for this particular task.
*
* @since Ant 1.5
*/
public void setCompiler(String compiler) {
this.compiler = compiler;
}
public boolean isDownload() {
return download;
}
/**
* Set to true to permit the compiler to download URLs for imports
* and includes. Defaults to false, meaning all imports and includes
* must be copied locally.
*
* @param download Optional.
*/
public void setDownload(boolean download) {
this.download = download;
}
/**
* If true, compiles with optimization enabled.
*/
public void setOptimize(boolean optimize) {
this.optimize = optimize;
}
/**
* Gets the optimize flag.
*/
public boolean getOptimize() {
return optimize;
}
public boolean isVerbose() {
return verbose;
}
/**
* Controls the amount of output. Defaults to true.
*
* @param verbose Optional.
*/
public void setVerbose(boolean verbose) {
this.verbose = verbose;
}
public boolean isQuiet() {
return quiet;
}
/**
* Controls the amount of output. Defaults to false.
*
* @param quiet Optional.
*/
public void setQuiet(boolean quiet) {
this.quiet = quiet;
}
public boolean isDebug() {
return debug;
}
/**
* Get the value of debugLevel.
*
* @return value of debugLevel.
*/
public String getDebugLevel() {
return debugLevel;
}
/**
* Keyword list to be appended to the -g command-line switch.
* <p>
* This will be ignored by all implementations except modern
* and classic(ver >= 1.2). Legal values are none or a
* comma-separated list of the following keywords: lines, vars,
* and source. If debuglevel is not specified, by default, :none
* will be appended to -g. If debug is not turned on, this attribute
* will be ignored.
*
* @param v Value to assign to debugLevel.
*/
public void setDebugLevel(String v) {
this.debugLevel = v;
}
/**
* Generate debugging symbols.
*
* @param debug Optional.
*/
public void setDebug(boolean debug) {
this.debug = debug;
}
/**
* If true, forks the javac compiler.
*
* @param f "true|false|on|off|yes|no"
*/
public void setFork(boolean f) {
fork = f;
}
/**
* Sets the the name of the javac executable.
*
* <p>Ignored unless fork is true or extJavac has been specified
* as the compiler.</p>
*/
public void setExecutable(String forkExec) {
forkedExecutable = forkExec;
}
public String getExecutable() {
return forkedExecutable;
}
public boolean isSrconly() {
return srconly;
}
/**
* A value of true means that only source will be generated. Optional,
* default is false.
*
* @param srconly Optional.
*/
public void setSrconly(boolean srconly) {
this.srconly = srconly;
}
public String getTypesystemname() {
return typesystemname;
}
/**
* One or more SchemaCompiler extensions can be passed in via the &lt;extension> subelement.
* Schema Compiler extensions must implement the interface com.xbean.too.SchemaCompilerExtension
*/
public Extension createExtension() {
Extension e = new Extension();
extensions.add(e);
return e;
}
/**
* One or more namespaces in which duplicate definitions are to be ignored
* can be passed in via the &lt;ignoreDuplicatesInNamespaces> subelement.
*/
public void setIgnoreDuplicatesInNamespaces(String namespaces) {
mdefnamespaces = new HashSet<>();
StringTokenizer st = new StringTokenizer(namespaces, ",");
while (st.hasMoreTokens()) {
String namespace = st.nextToken().trim();
mdefnamespaces.add(namespace);
}
}
public String getIgnoreDuplicatesInNamespaces() {
return mdefnamespaces == null ? null : String.join(",", mdefnamespaces);
}
/**
* The name of the package that the TypeSystemHolder class should be
* generated in. Normally this should be left unspecified. None of
* the xml beans are generated in this package.
* <BR><BR>Use .xsdconfig files to modify xml bean package or class names.
*
* @param typesystemname Optional.
*/
public void setTypesystemname(String typesystemname) {
this.typesystemname = typesystemname;
}
public boolean isFailonerror() {
return failonerror;
}
/**
* Determines whether or not the ant target will continue if the XMLBean
* creation encounters a build error. Defaults to true. Optional.
*
* @param failonerror Optional.
*/
public void setFailonerror(boolean failonerror) {
this.failonerror = failonerror;
}
public boolean isIncludeAntRuntime() {
return includeAntRuntime;
}
public void setIncludeAntRuntime(boolean includeAntRuntime) {
this.includeAntRuntime = includeAntRuntime;
}
public boolean isIncludeJavaRuntime() {
return includeJavaRuntime;
}
public void setIncludeJavaRuntime(boolean includeJavaRuntime) {
this.includeJavaRuntime = includeJavaRuntime;
}
public boolean isNowarn() {
return nowarn;
}
public void setNowarn(boolean nowarn) {
this.nowarn = nowarn;
}
public boolean isNoSrcRegen() {
return noSrcRegen;
}
public void setNoSrcRegen(boolean noSrcRegen) {
this.noSrcRegen = noSrcRegen;
}
/**
* Set the initial memory size of the underlying javac process.
*/
public String getMemoryInitialSize() {
return memoryInitialSize;
}
public void setMemoryInitialSize(String memoryInitialSize) {
this.memoryInitialSize = memoryInitialSize;
}
/**
* Set the maximum memory size of the underlying javac process.
*/
public String getMemoryMaximumSize() {
return memoryMaximumSize;
}
public void setMemoryMaximumSize(String memoryMaximumSize) {
this.memoryMaximumSize = memoryMaximumSize;
}
/**
* Do not enforce the unique particle attribution rule.
*/
public void setNoUpa(boolean noupa) {
this.noupa = noupa;
}
public boolean isNoUpa() {
return noupa;
}
/**
* Do not enforce the particle valid (restriction) rule.
*/
public void setNoPvr(boolean nopvr) {
this.nopvr = nopvr;
}
public boolean isNoPvr() {
return nopvr;
}
/**
* Skip over schema &lt;annotation%gt; elements.
*/
public void setNoAnnotations(boolean noann) {
this.noann = noann;
}
public boolean isNoAnnotations() {
return noann;
}
/**
* Do not validate the contents of schema &lt;documentation&gt; elements.
*/
public void setNoValidateDoc(boolean novdoc) {
this.novdoc = novdoc;
}
public boolean isNoValidateDoc() {
return novdoc;
}
/**
* Ignore extensions found in .xsdconfig files
*
* @param noext Ignore extensions
*/
public void setNoExt(boolean noext) {
this.noext = noext;
}
public boolean isNoExt() {
return noext;
}
//REVIEW this allows people to deal with the case where they drag in
//more files for compilation than they should. not sure if this is
//a good thing or not
private String source = null;
public void setSource(String s) {
source = s;
}
/**
* Gets the XML Catalog file for org.apache.xml.resolver.tools.CatalogResolver. (Note: needs resolver.jar from http://xml.apache.org/commons/components/resolver/index.html)
*/
public String getCatalog() {
return catalog;
}
/**
* Sets the XML Catalog file for org.apache.xml.resolver.tools.CatalogResolver. (Note: needs resolver.jar from http://xml.apache.org/commons/components/resolver/index.html)
*/
public void setCatalog(String catalog) {
this.catalog = catalog;
}
public String getRepackage() {
return repackage;
}
public void setRepackage(String repackage) {
this.repackage = repackage;
}
private static URI uriFromFile(File f) {
if (f == null) {
return null;
}
try {
return f.getCanonicalFile().toURI();
} catch (java.io.IOException e) {
// Don't spit out an exception here because on Windows you'll get one
// if the filename is "aux", "lpt1", etc. It's the caller's responsibility
// to deal with those cases correctly, usually by calling FileSvc.invalidPathCheck()
// MessageSvc.get().logException(e);
return f.getAbsoluteFile().toURI();
}
}
public class ErrorLogger extends AbstractCollection<XmlError> {
private final boolean _noisy;
private final URI _baseURI;
public ErrorLogger(boolean noisy) {
_noisy = noisy;
_baseURI = uriFromFile(project.getBaseDir());
}
public boolean add(XmlError err) {
if (err.getSeverity() == XmlError.SEVERITY_ERROR) {
log(err.toString(_baseURI), Project.MSG_ERR);
} else if (err.getSeverity() == XmlError.SEVERITY_WARNING) {
log(err.toString(_baseURI), Project.MSG_WARN);
} else if (_noisy) {
log(err.toString(_baseURI), Project.MSG_INFO);
}
return false;
}
public Iterator<XmlError> iterator() {
return Collections.emptyIterator();
}
public int size() {
return 0;
}
}
}