| /* 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.schema; |
| |
| import org.apache.xmlbeans.*; |
| import org.apache.xmlbeans.impl.common.XmlErrorWatcher; |
| import org.apache.xmlbeans.impl.xb.xsdschema.SchemaDocument; |
| import org.apache.xmlbeans.impl.xb.xsdschema.SchemaDocument.Schema; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.Writer; |
| import java.net.URI; |
| import java.util.*; |
| |
| public class SchemaTypeSystemCompiler { |
| public static class Parameters { |
| private SchemaTypeSystem existingSystem; |
| private String name; |
| private Schema[] schemas; |
| private BindingConfig config; |
| private SchemaTypeLoader linkTo; |
| private XmlOptions options; |
| private Collection<XmlError> errorListener; |
| private boolean javaize; |
| private URI baseURI; |
| private Map<String, String> sourcesToCopyMap; |
| private File schemasDir; |
| |
| public SchemaTypeSystem getExistingTypeSystem() { |
| return existingSystem; |
| } |
| |
| public void setExistingTypeSystem(SchemaTypeSystem system) { |
| this.existingSystem = system; |
| } |
| |
| public String getName() { |
| return name; |
| } |
| |
| public void setName(String name) { |
| this.name = name; |
| } |
| |
| public SchemaDocument.Schema[] getSchemas() { |
| return schemas; |
| } |
| |
| public void setSchemas(SchemaDocument.Schema[] schemas) { |
| this.schemas = schemas; |
| } |
| |
| public BindingConfig getConfig() { |
| return config; |
| } |
| |
| public void setConfig(BindingConfig config) { |
| this.config = config; |
| } |
| |
| public SchemaTypeLoader getLinkTo() { |
| return linkTo; |
| } |
| |
| public void setLinkTo(SchemaTypeLoader linkTo) { |
| this.linkTo = linkTo; |
| } |
| |
| public XmlOptions getOptions() { |
| return options; |
| } |
| |
| public void setOptions(XmlOptions options) { |
| this.options = options; |
| } |
| |
| public Collection<XmlError> getErrorListener() { |
| return errorListener; |
| } |
| |
| public void setErrorListener(Collection<XmlError> errorListener) { |
| this.errorListener = errorListener; |
| } |
| |
| public boolean isJavaize() { |
| return javaize; |
| } |
| |
| public void setJavaize(boolean javaize) { |
| this.javaize = javaize; |
| } |
| |
| public URI getBaseURI() { |
| return baseURI; |
| } |
| |
| public void setBaseURI(URI baseURI) { |
| this.baseURI = baseURI; |
| } |
| |
| public Map<String, String> getSourcesToCopyMap() { |
| return sourcesToCopyMap; |
| } |
| |
| public void setSourcesToCopyMap(Map<String, String> sourcesToCopyMap) { |
| this.sourcesToCopyMap = sourcesToCopyMap; |
| } |
| |
| public File getSchemasDir() { |
| return schemasDir; |
| } |
| |
| public void setSchemasDir(File schemasDir) { |
| this.schemasDir = schemasDir; |
| } |
| |
| } |
| |
| /** |
| * Compiles a SchemaTypeSystem. Use XmlBeans.compileXmlBeans() if you can. |
| */ |
| public static SchemaTypeSystem compile(Parameters params) { |
| return compileImpl(params.getExistingTypeSystem(), params.getName(), |
| params.getSchemas(), params.getConfig(), params.getLinkTo(), |
| params.getOptions(), params.getErrorListener(), params.isJavaize(), |
| params.getBaseURI(), params.getSourcesToCopyMap(), params.getSchemasDir()); |
| } |
| |
| /** |
| * Please do not invoke this method directly as the signature could change unexpectedly. |
| * Use one of |
| * {@link XmlBeans#loadXsd(XmlObject[])}, |
| * {@link XmlBeans#compileXsd(XmlObject[], SchemaTypeLoader, XmlOptions)}, |
| * or |
| * {@link XmlBeans#compileXmlBeans(String, SchemaTypeSystem, XmlObject[], BindingConfig, SchemaTypeLoader, Filer, XmlOptions)} |
| */ |
| public static SchemaTypeSystemImpl compile(String name, SchemaTypeSystem existingSTS, |
| XmlObject[] input, BindingConfig config, SchemaTypeLoader linkTo, Filer filer, XmlOptions options) |
| throws XmlException { |
| options = XmlOptions.maskNull(options); |
| ArrayList<Schema> schemas = new ArrayList<>(); |
| |
| if (input != null) { |
| for (int i = 0; i < input.length; i++) { |
| if (input[i] instanceof Schema) { |
| schemas.add((Schema) input[i]); |
| } else if (input[i] instanceof SchemaDocument && ((SchemaDocument) input[i]).getSchema() != null) { |
| schemas.add(((SchemaDocument) input[i]).getSchema()); |
| } else { |
| throw new XmlException("Thread " + Thread.currentThread().getName() + ": The " + i + "th supplied input is not a schema document: its type is " + input[i].schemaType()); |
| } |
| } |
| } |
| |
| Collection<XmlError> userErrors = options.getErrorListener(); |
| XmlErrorWatcher errorWatcher = new XmlErrorWatcher(userErrors); |
| |
| SchemaTypeSystemImpl stsi = compileImpl(existingSTS, name, |
| schemas.toArray(new Schema[0]), |
| config, linkTo, options, errorWatcher, filer != null, options.getBaseURI(), |
| null, null); |
| |
| // if there is an error and compile didn't recover (stsi==null), throw exception |
| if (errorWatcher.hasError() && stsi == null) { |
| throw new XmlException(errorWatcher.firstError()); |
| } |
| |
| if (stsi != null && !stsi.isIncomplete() && filer != null) { |
| stsi.save(filer); |
| generateTypes(stsi, filer, options); |
| } |
| |
| return stsi; |
| } |
| |
| // |
| // Compiles a SchemaTypeSystem |
| // |
| /* package */ |
| static SchemaTypeSystemImpl compileImpl(SchemaTypeSystem system, String name, |
| Schema[] schemas, BindingConfig config, SchemaTypeLoader linkTo, |
| XmlOptions options, Collection<XmlError> outsideErrors, boolean javaize, |
| URI baseURI, Map<String, String> sourcesToCopyMap, File schemasDir) { |
| if (linkTo == null) { |
| throw new IllegalArgumentException("Must supply linkTo"); |
| } |
| |
| XmlErrorWatcher errorWatcher = new XmlErrorWatcher(outsideErrors); |
| boolean incremental = system != null; |
| |
| // construct the state |
| StscState state = StscState.start(); |
| boolean validate = (options == null || !options.isCompileNoValidation()); |
| try { |
| state.setErrorListener(errorWatcher); |
| state.setBindingConfig(config); |
| state.setOptions(options); |
| state.setGivenTypeSystemName(name); |
| state.setSchemasDir(schemasDir); |
| if (baseURI != null) { |
| state.setBaseUri(baseURI); |
| } |
| |
| // construct the classpath (you always get the builtin types) |
| linkTo = SchemaTypeLoaderImpl.build(new SchemaTypeLoader[]{BuiltinSchemaTypeSystem.get(), linkTo}, null, null); |
| state.setImportingTypeLoader(linkTo); |
| |
| List<Schema> validSchemas = new ArrayList<>(schemas.length); |
| |
| // load all the xsd files into it |
| if (validate) { |
| XmlOptions validateOptions = new XmlOptions().setErrorListener(errorWatcher); |
| if (options != null && options.isValidateTreatLaxAsSkip()) { |
| validateOptions.setValidateTreatLaxAsSkip(); |
| } |
| for (Schema schema : schemas) { |
| if (schema.validate(validateOptions)) { |
| validSchemas.add(schema); |
| } |
| } |
| } else { |
| validSchemas.addAll(Arrays.asList(schemas)); |
| } |
| |
| Schema[] startWith = validSchemas.toArray(new Schema[0]); |
| |
| if (incremental) { |
| Set<String> namespaces = new HashSet<>(); |
| startWith = getSchemasToRecompile((SchemaTypeSystemImpl) system, startWith, namespaces); |
| state.initFromTypeSystem((SchemaTypeSystemImpl) system, namespaces); |
| } else { |
| state.setDependencies(new SchemaDependencies()); |
| } |
| |
| // deal with imports and includes |
| StscImporter.SchemaToProcess[] schemasAndChameleons = StscImporter.resolveImportsAndIncludes(startWith, incremental); |
| |
| // call the translator so that it may also perform magic |
| StscTranslator.addAllDefinitions(schemasAndChameleons); |
| |
| // call the resolver to do its magic |
| StscResolver.resolveAll(); |
| |
| // call the checker to check both restrictions and defaults |
| StscChecker.checkAll(); |
| |
| // call the javaizer to do its magic |
| StscJavaizer.javaizeAllTypes(javaize); |
| |
| // construct the loader out of the state |
| StscState.get().sts().loadFromStscState(state); |
| |
| // fill in the source-copy map |
| if (sourcesToCopyMap != null) { |
| sourcesToCopyMap.putAll(state.sourceCopyMap()); |
| } |
| |
| if (errorWatcher.hasError()) { |
| // EXPERIMENTAL: recovery from compilation errors and partial type system |
| if (state.allowPartial() && state.getRecovered() == errorWatcher.size()) { |
| // if partial type system allowed and all errors were recovered |
| StscState.get().sts().setIncomplete(true); |
| } else { |
| // if any non-recoverable errors, return null |
| return null; |
| } |
| } |
| |
| if (system != null) { |
| ((SchemaTypeSystemImpl) system).setIncomplete(true); |
| } |
| |
| return StscState.get().sts(); |
| } finally { |
| StscState.end(); |
| } |
| } |
| |
| /** |
| * Get the list of Schemas to be recompiled, based on the list of Schemas that |
| * were modified. |
| * We make use of the depencency information that we stored in the typesystem |
| * and of the entity resolvers that have been set up |
| */ |
| private static Schema[] getSchemasToRecompile(SchemaTypeSystemImpl system, |
| Schema[] modified, Set<String> namespaces) { |
| Set<String> modifiedFiles = new HashSet<>(); |
| Map<String, SchemaDocument.Schema> haveFile = new HashMap<>(); |
| List<SchemaDocument.Schema> result = new ArrayList<>(); |
| for (Schema schema : modified) { |
| String fileURL = schema.documentProperties().getSourceName(); |
| if (fileURL == null) { |
| throw new IllegalArgumentException("One of the Schema files passed in" + |
| " doesn't have the source set, which prevents it to be incrementally" + |
| " compiled"); |
| } |
| modifiedFiles.add(fileURL); |
| haveFile.put(fileURL, schema); |
| result.add(schema); |
| } |
| SchemaDependencies dep = system.getDependencies(); |
| List<String> nss = dep.getNamespacesTouched(modifiedFiles); |
| namespaces.addAll(dep.computeTransitiveClosure(nss)); |
| List<String> needRecompilation = dep.getFilesTouched(namespaces); |
| StscState.get().setDependencies(new SchemaDependencies(dep, namespaces)); |
| for (String url : needRecompilation) { |
| Schema have = haveFile.get(url); |
| if (have == null) { |
| // We have to load the file from the entity resolver |
| try { |
| XmlObject xdoc = StscImporter.DownloadTable. |
| downloadDocument(StscState.get().getS4SLoader(), null, url); |
| XmlOptions voptions = new XmlOptions(); |
| voptions.setErrorListener(StscState.get().getErrorListener()); |
| if (!(xdoc instanceof SchemaDocument) || !xdoc.validate(voptions)) { |
| StscState.get().error("Referenced document is not a valid schema, URL = " + url, XmlErrorCodes.CANNOT_FIND_RESOURCE, null); |
| continue; |
| } |
| |
| SchemaDocument sDoc = (SchemaDocument) xdoc; |
| |
| result.add(sDoc.getSchema()); |
| } catch (java.net.MalformedURLException mfe) { |
| StscState.get().error(XmlErrorCodes.EXCEPTION_LOADING_URL, new Object[]{"MalformedURLException", url, mfe.getMessage()}, null); |
| } catch (IOException ioe) { |
| StscState.get().error(XmlErrorCodes.EXCEPTION_LOADING_URL, new Object[]{"IOException", url, ioe.getMessage()}, null); |
| } catch (XmlException xmle) { |
| StscState.get().error(XmlErrorCodes.EXCEPTION_LOADING_URL, new Object[]{"XmlException", url, xmle.getMessage()}, null); |
| } |
| } |
| } |
| return result.toArray(new Schema[0]); |
| } |
| |
| |
| /** |
| * Generate java source files for a SchemaTypeSystem. |
| * Please do not invoke this method directly as the signature could change unexpectedly. |
| * Use {@link org.apache.xmlbeans.XmlBeans#compileXmlBeans} |
| * |
| * @param system the SchemaTypeSystem to generated java source for |
| * @param filer to create the java source files |
| * @param options See {@link XmlOptions#setSchemaCodePrinter(org.apache.xmlbeans.SchemaCodePrinter)} |
| * @return true if saving the generated source succeeded. |
| */ |
| public static boolean generateTypes(SchemaTypeSystem system, Filer filer, XmlOptions options) { |
| // partial type systems not allowed to be saved |
| if (system instanceof SchemaTypeSystemImpl && ((SchemaTypeSystemImpl) system).isIncomplete()) { |
| return false; |
| } |
| |
| boolean success = true; |
| |
| List<SchemaType> types = new ArrayList<>(); |
| types.addAll(Arrays.asList(system.globalTypes())); |
| types.addAll(Arrays.asList(system.documentTypes())); |
| types.addAll(Arrays.asList(system.attributeTypes())); |
| |
| for (SchemaType type : types) { |
| if (type.isBuiltinType()) { |
| continue; |
| } |
| if (type.getFullJavaName() == null) { |
| continue; |
| } |
| |
| String fjn = type.getFullJavaName(); |
| |
| try (Writer writer = filer.createSourceFile(fjn)) { |
| // Generate interface class |
| SchemaTypeCodePrinter.printType(writer, type, options); |
| } catch (IOException e) { |
| System.err.println("IO Error " + e); |
| success = false; |
| } |
| |
| fjn = type.getFullJavaImplName(); |
| |
| try (Writer writer = filer.createSourceFile(fjn)) { |
| // Generate Implementation class |
| SchemaTypeCodePrinter.printTypeImpl(writer, type, options); |
| } catch (IOException e) { |
| System.err.println("IO Error " + e); |
| success = false; |
| } |
| } |
| |
| return success; |
| } |
| } |