/*
* Copyright 2013 Guidewire Software, Inc.
*/
package gw.internal.xml.xsd.typeprovider;
import gw.config.CommonServices;
import gw.fs.IDirectory;
import gw.fs.IFile;
import gw.internal.ext.org.apache.commons.collections.map.AbstractReferenceMap;
import gw.internal.ext.org.apache.commons.collections.map.ReferenceMap;
import gw.internal.ext.org.apache.xerces.jaxp.validation.XMLSchemaFactory;
import gw.internal.schema.gw.xsd.config.xml.schemalocations.Schemalocations;
import gw.internal.schema.gw.xsd.config.xml.schemalocations.anonymous.elements.Schemalocations_Schema;
import gw.internal.schema.gw.xsd.w3c.wsdl.Definitions;
import gw.internal.schema.gw.xsd.w3c.wsdl.anonymous.elements.TDefinitions_Import;
import gw.internal.schema.gw.xsd.w3c.wsdl.anonymous.elements.TDefinitions_Types;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Attribute;
import gw.internal.schema.gw.xsd.w3c.xmlschema.AttributeGroup;
import gw.internal.schema.gw.xsd.w3c.xmlschema.ComplexContent;
import gw.internal.schema.gw.xsd.w3c.xmlschema.ComplexType;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Element;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Group;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Import;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Include;
import gw.internal.schema.gw.xsd.w3c.xmlschema.Redefine;
import gw.internal.schema.gw.xsd.w3c.xmlschema.SimpleContent;
import gw.internal.schema.gw.xsd.w3c.xmlschema.SimpleType;
import gw.internal.schema.gw.xsd.w3c.xmlschema.anonymous.elements.ComplexContent_Extension;
import gw.internal.schema.gw.xsd.w3c.xmlschema.anonymous.elements.ComplexContent_Restriction;
import gw.internal.xml.XmlConstants;
import gw.internal.xml.XmlElementInternals;
import gw.internal.xml.XmlSchemaAccessImpl;
import gw.internal.xml.XmlSchemaLocalResourceResolver;
import gw.internal.xml.xsd.XmlSchemaSource;
import gw.internal.xml.xsd.typeprovider.primitive.XmlSchemaPrimitiveType;
import gw.internal.xml.xsd.typeprovider.schema.WsdlBinding;
import gw.internal.xml.xsd.typeprovider.schema.WsdlDefinitions;
import gw.internal.xml.xsd.typeprovider.schema.WsdlImport;
import gw.internal.xml.xsd.typeprovider.schema.WsdlMessage;
import gw.internal.xml.xsd.typeprovider.schema.WsdlPortType;
import gw.internal.xml.xsd.typeprovider.schema.WsdlTypes;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchema;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaAny;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaAttribute;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaAttributeGroup;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaCollection;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaComplexType;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaContent;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaElement;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaFacet;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaGroup;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaImport;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaObject;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleContent;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleContentExtension;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleContentRestriction;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleType;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleTypeList;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleTypeRestriction;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaSimpleTypeUnion;
import gw.internal.xml.xsd.typeprovider.schema.XmlSchemaType;
import gw.internal.xml.xsd.typeprovider.schemaindexer.XmlSchemaIndexer;
import gw.internal.xml.xsd.typeprovider.schemaparser.XmlSchemaParseContext;
import gw.internal.xml.xsd.typeprovider.schemaparser.XmlSchemaParser;
import gw.internal.xml.xsd.typeprovider.simplevaluefactory.XmlSchemaEnumSimpleValueFactory;
import gw.internal.xml.xsd.typeprovider.simplevaluefactory.XmlSchemaListSimpleValueFactory;
import gw.internal.xml.xsd.typeprovider.simplevaluefactory.XmlSimpleValueFactory;
import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleListValueValidator;
import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleNoopValueValidator;
import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleTypeSimpleValueValidator;
import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleUnionValueValidator;
import gw.internal.xml.xsd.typeprovider.validator.XmlSimpleValueValidator;
import gw.lang.reflect.IType;
import gw.lang.reflect.TypeSystem;
import gw.lang.reflect.gs.IFileSystemGosuClassRepository;
import gw.lang.reflect.gs.IGosuClass;
import gw.lang.reflect.java.IAsmJavaClassInfo;
import gw.lang.reflect.java.IJavaClassInfo;
import gw.lang.reflect.module.IExecutionEnvironment;
import gw.lang.reflect.module.IModule;
import gw.util.GosuExceptionUtil;
import gw.util.Pair;
import gw.util.StreamUtil;
import gw.util.concurrent.LockingLazyVar;
import gw.util.fingerprint.FP64;
import gw.xml.XmlElement;
import gw.xml.XmlException;
import gw.xml.XmlNamespace;
import gw.xml.XmlSimpleValue;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;
import javax.xml.XMLConstants;
import javax.xml.namespace.QName;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.transform.Source;
import javax.xml.transform.stream.StreamSource;
import javax.xml.validation.Schema;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.MalformedURLException;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedHashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.WeakHashMap;
/**
* Represents an index of a schema or schemas retrieved from a particular file, which could be an XSD or a WSDL.
* An XSD can by definition only contain one schema, but a WSDL can contain multiple schemas. Since every reference
* to a QName is relative to the schema where the QName is located, it's important to go back to the original
* XmlSchemaIndex where the QName is defined to look up the schema object ( group ref, attribute group, etc ).
*
* The bulk of this class is devoted to providing various ways to look up information about the schemas represented
* by this schema index, such as looking up a top level xsd type by QName, or looking up an IType by XmlSchemaObject,
* etc. All methods that take an XmlSchemaObject are static (or should be), since every XmlSchemaObject knows its
* schema index. Any methods that return an XmlSchemaObject can possibly return one from a schema index
* other than the one that was queried. This is due to the way that schemas can interact with each
* other. For example, if this is a user-defined schema in the urn:foo namespace, asking the schema index for the
* type xsd:int ( using getXmlSchemaTypeByQName() ) will in fact return an XmlSchemaObject that contains a
* reference to the "xml schema" schema index ( the one in the gw.xsd.w3c.xmlschema gosu namespace ).
*/
public class XmlSchemaIndex<T> {
public static final String SOAP11_WSDL_NAMESPACE = gw.internal.schema.gw.xsd.w3c.soap11.Body.$QNAME.getNamespaceURI();
public static final String SOAP12_WSDL_NAMESPACE = gw.internal.schema.gw.xsd.w3c.soap12.Body.$QNAME.getNamespaceURI();
private static final Map<String, String> _normalizedSchemaNamespacesByPath = new HashMap<String, String>();
private static final Set<String> _normalizedSchemaNamespaces = new HashSet<String>();
// {http://foo.bar} -> gw.xml.whatever -- for finding a target schema by XML namespace relative to this schema
private LockingLazyVar<LinkedHashMap<String, LinkedHashSet<String>>> _gosuNamespacesByXMLNamespace = new LockingLazyVar<LinkedHashMap<String, LinkedHashSet<String>>>(TypeSystem.getGlobalLock()) {
protected LinkedHashMap<String, LinkedHashSet<String>> init() {
try {
maybeIndex( null );
LinkedHashMap<String, LinkedHashSet<String>> gosuNamespacesByXMLNamespace = new LinkedHashMap<String, LinkedHashSet<String>>();
String targetGosuNamespace = _packageName;
LinkedHashSet<XmlSchema> schemas = _collection.getXmlSchemas();
addSchemasToNamespaceMappings( targetGosuNamespace, schemas, gosuNamespacesByXMLNamespace );
if (getWsdlDefinitions() != null) {
for ( WsdlImport parsedImport : getWsdlDefinitions().getWsdlImports() ){
String location = parsedImport.getLocation();
URL url = makeLocalIfValid( _schemaEF, location, parsedImport.getNameSpace(), getTypeLoader().getModule() );
if ( url == null ) {
throw new RuntimeException( "WSDL " + location + " with namespace " + parsedImport.getNameSpace() + " not found relative to " + _schemaEF );
}
String gosuNamespace = getGosuNamespace( url, getTypeLoader().getModule() );
XmlSchemaIndex importIndex = XmlSchemaResourceTypeLoaderBase.findSchemaForNamespace( getTypeLoader().getModule(), gosuNamespace );
addSchemasToNamespaceMappings( gosuNamespace, importIndex.getXmlSchemaCollection().getXmlSchemas(), gosuNamespacesByXMLNamespace );
}
addGosuNamespaceByXmlNamespace( gosuNamespacesByXMLNamespace, "http://schemas.xmlsoap.org/soap/encoding/", "gw.xsd.w3c.soap11_encoding" );
}
return gosuNamespacesByXMLNamespace;
}
catch ( Exception ex ) {
throw new RuntimeException( "Error indexing namespaces in schema " + _packageName, ex );
}
}
};
private LockingLazyVar<LinkedHashMap<String, String>> _gosuNamespacesByWsdlNamespace = new LockingLazyVar<LinkedHashMap<String, String>>() {
protected LinkedHashMap<String, String> init() {
try {
maybeIndex( null );
LinkedHashMap<String, String> gosuNamespacesByWsdlNamespace = new LinkedHashMap<String, String>();
if (getWsdlDefinitions() != null){
gosuNamespacesByWsdlNamespace.put( getWsdlDefinitions().getTargetNamespace(), _packageName );
for ( WsdlImport parsedImport : getWsdlDefinitions().getWsdlImports() ){
String location = parsedImport.getLocation();
String importNamespace = parsedImport.getNameSpace();
URL url = makeLocalIfValid( _schemaEF, location, importNamespace, getTypeLoader().getModule() );
if ( url == null ) {
throw new RuntimeException( "WSDL " + location + " with namespace " + importNamespace + " not found relative to " + _schemaEF );
}
String gosuNamespace = getGosuNamespace( url, getTypeLoader().getModule() );
gosuNamespacesByWsdlNamespace.put( importNamespace, gosuNamespace );
}
}
return gosuNamespacesByWsdlNamespace;
}
catch ( Exception ex ) {
throw new RuntimeException( "Error indexing namespaces in WSDL " + _packageName, ex );
}
}
};
public static void clear() {
_normalizedSchemaNamespacesByPath.clear();
_normalizedSchemaNamespaces.clear();
_multiCompiledSchemaCache.clear();
_singleCompiledSchemaCache.clear();
_defaultSchemaLocations.clear();
_codegenSchemasByModuleRoot.clear();
IGNORE_JAVA_CLASSES.clear();
}
//Storing the wsdl definition parsed from xml to use during lazy initialization
private WsdlDefinitions _wsdlDefinition = null;
// The package name of this schema index... For example gw.xsd.w3c.xmlschema
private String _packageName;
// The typeloader that created this XmlSchemaIndex
protected final XmlSchemaResourceTypeLoaderBase<T> _typeLoader;
// An optional context object - to be used by GX and other XSD-based systems
private final T _context;
// The source of this schema ( url it was loaded from, the actual schema bytes, etc )
private final XmlSchemaSource _xmlSchemaSource;
// A cache of all type names created by this schema index
private final Set<String> _allTypeNames = new HashSet<String>();
// A cache of all type data created by this schema index, indexed by name
private final Map<String, IXmlSchemaTypeData> _typesByName = new HashMap<String, IXmlSchemaTypeData>();
// Translation from a schema object ( such as a complexType ) to an IType representing it
private final Map<XmlSchemaObject, IXmlSchemaTypeData> _typesBySchemaObject = new HashMap<XmlSchemaObject, IXmlSchemaTypeData>();
// The original schema collection for the schemas represented by this schema index
private final XmlSchemaCollection _collection;
// The flattened possible child elements of each schema type ( complex/simple )
private Map<XmlSchemaType, List<XmlSchemaFlattenedChild>> _flattenedChildrenBySchemaType = new HashMap<XmlSchemaType, List<XmlSchemaFlattenedChild>>();
// The plurality of the children of each schema type
private Map<XmlSchemaType, Map<Pair<XmlSchemaPropertyType, QName>, Boolean>> _childPluralityBySchemaType = new HashMap<XmlSchemaType, Map<Pair<XmlSchemaPropertyType, QName>, Boolean>>();
// A flag to indicate whether or not this schema index has been indexed yet ( types created )
private boolean _indexed = false;
// xsd:anySimpleType - bootstrap type, not actually defined in the schema schema
// resist the urge to replace this with AnySimpleType.$QNAME, as that would cause generated code to rely on generated code in the case of a codegen
public static final QName ANY_SIMPLE_TYPE_QNAME = new QName( XMLConstants.W3C_XML_SCHEMA_NS_URI, "anySimpleType", "xs" );
// xsd:anyType
// resist the urge to replace this with AnyType.$QNAME, as that would cause generated code to rely on generated code in the case of a codegen
public static final QName ANY_TYPE_QNAME = new QName( XMLConstants.W3C_XML_SCHEMA_NS_URI, "anyType", "xs" );
// Enumerations defined in the schema index, mapped by their xsd type
private final Map<XmlSchemaType, XmlSchemaEnumerationTypeData> _enumerations = new HashMap<XmlSchemaType, XmlSchemaEnumerationTypeData>();
public static final String WSDL_NAMESPACE = gw.internal.schema.gw.xsd.w3c.wsdl.Definitions.$QNAME.getNamespaceURI();
// TODO dlank - could use a better caching strategy in the future
private static final ReferenceMap _multiCompiledSchemaCache = new ReferenceMap( AbstractReferenceMap.SOFT, AbstractReferenceMap.HARD );
private static final WeakHashMap<XmlSchemaIndex, Schema> _singleCompiledSchemaCache = new WeakHashMap<XmlSchemaIndex, Schema>();
// the list of "built-in" datatypes. Not every simple type defined in the schema schema is considered present in
// every schema, except the following. The only way to distinguish the simples types that are available from
// those that are not available is an annotation in the schema schema that has some human-readable documentation
// which says a particular simple type is not available, or by reading the schema specification. So I believe we
// need to inline this list here. See XmlSchemaBuiltInTypesAreUpToDateTest, which ensures that this list is
// up to date with what Xerces thinks it should be.
public static Set<String> BUILT_IN_DATATYPES = Collections.unmodifiableSet( new HashSet<String>( Arrays.asList(
"string", "boolean", "float", "double", "decimal", "duration", "dateTime", "time", "date",
"gYearMonth", "gYear", "gMonthDay", "gDay", "gMonth", "hexBinary", "base64Binary",
"anyURI", "QName", "NOTATION", "normalizedString", "token", "language", "IDREFS", "ENTITIES",
"NMTOKEN", "NMTOKENS", "Name", "NCName", "ID", "IDREF", "ENTITY", "integer", "nonPositiveInteger",
"negativeInteger", "long", "int", "short", "byte", "nonNegativeInteger", "unsignedLong",
"unsignedInt", "unsignedShort", "unsignedByte", "positiveInteger",
"anyType", "anySimpleType"
)
) );
private static final Map<IModule, XmlSchemaDefaultSchemaLocations> _defaultSchemaLocations = new HashMap<IModule, XmlSchemaDefaultSchemaLocations>();
private static final Map<IDirectory,Set<String>> _codegenSchemasByModuleRoot = new HashMap<IDirectory, Set<String>>();
private LockingLazyVar<XmlSchemaAccessImpl> _xmlSchemaAccess = new LockingLazyVar<XmlSchemaAccessImpl>() {
@Override
protected XmlSchemaAccessImpl init() {
IType type = TypeSystem.getByFullName( getTypeLoader().getSchemaSchemaTypeName() );
try {
return new XmlSchemaAccessImpl( type, _xmlSchemaSource, XmlSchemaIndex.this );
}
catch ( URISyntaxException e ) {
throw new RuntimeException( e );
}
}
};
public static final String REDEFINE_PREFIX = "::redefine";
private final boolean _usesJavaBackedTypes;
private final URL _schemaEF;
private final LockingLazyVar<Long> _schemaFP64 = new LockingLazyVar<Long>() {
@Override
protected Long init() {
try {
byte[] bytes = StreamUtil.getContent( _xmlSchemaSource.getInputStream( true ) );
FP64 fp64 = new FP64();
for ( byte b : bytes ) {
if ( b != 10 && b != 13 ) {
fp64.extend( b ); // a failure to notice a difference is not catastrophic, so we just strip 13/10
}
}
return fp64.getRawFingerprint();
}
catch ( IOException ex ) {
throw new RuntimeException( ex );
}
}
};
private static final LockingLazyVar<Boolean> IGNORE_JAVA_CLASSES = new LockingLazyVar<Boolean>() {
@Override
protected Boolean init() {
return System.getProperty( XmlSchemaResourceTypeLoaderBase.IGNORE_JAVA_CLASSES_PROPERTY ) != null;
}
};
public XmlSchemaIndex( XmlSchemaResourceTypeLoaderBase<T> typeLoader, String packageName, XmlSchemaSource xmlSchemaSource, T context) {
this( typeLoader, packageName, xmlSchemaSource, context, null );
}
public XmlSchemaIndex( XmlSchemaResourceTypeLoaderBase<T> typeLoader, String packageName, XmlSchemaSource xmlSchemaSource, T context, Map<Pair<URL,String>, XmlSchema> caches ) {
_typeLoader = typeLoader;
_usesJavaBackedTypes = ! IGNORE_JAVA_CLASSES.get() && getAllCodegenSchemas().contains( packageName );
_context = context;
_packageName = packageName;
_xmlSchemaSource = xmlSchemaSource;
_collection = new XmlSchemaCollection();
_schemaEF = _xmlSchemaSource.getBlueprintURL();
//TODO APEX-PULL cauuses stack overflow
// try {
// InputStream inputStream = _xmlSchemaSource.getInputStream( true );
// readSchema( _schemaEF, inputStream, null, caches );
// if ( _wsdlDefinition == null ) {
// parseSchemasButDoNotCache( Collections.<XmlSchemaIndex>singletonList( this ), null, null );
// }
// else {
// // XmlElement additionalWsdl = XmlElement.parse( _xmlSchemaSource.getInputStream( true ) );
// // parseSchemasButDoNotCache( Collections.<XmlSchemaIndex>emptyList(), additionalWsdl );
// }
// }
// catch ( Exception ex ) {
// ex.printStackTrace();
// if ( getXSDSource().getBlueprintURL().getProtocol().equals( "jar" ) || getTypeLoader().getClass().getName().equals( "com.guidewire.commons.system.gx.GXTypeLoader" ) ) {
// clearExternalJarXSD();
// }
// else {
// StringBuilder sb = new StringBuilder();
// sb.append( "Could not parse schema " );
// sb.append( _xmlSchemaSource.getDescription() );
// throw new XmlException( sb.toString(), ex );
// }
// }
}
public static Schema parseSchemas( List<XmlSchemaIndex> schemaIndexes ) throws SAXException, MalformedURLException, URISyntaxException {
Schema compiledSchema;
if ( schemaIndexes.size() == 1 ) {
compiledSchema = _singleCompiledSchemaCache.get( schemaIndexes.get( 0 ) );
}
else {
compiledSchema = (Schema) _multiCompiledSchemaCache.get( schemaIndexes );
}
if ( compiledSchema == null ) {
compiledSchema = parseSchemasButDoNotCache( schemaIndexes, null, null );
if ( schemaIndexes.size() == 1 ) {
_singleCompiledSchemaCache.put( schemaIndexes.get( 0 ), compiledSchema );
}
else {
_multiCompiledSchemaCache.put( schemaIndexes, compiledSchema );
}
}
return compiledSchema;
}
public static Schema parseSchemasButDoNotCache( List<XmlSchemaIndex> schemaIndexes, XmlElement additionalWsdl, IType webserviceType ) throws MalformedURLException, URISyntaxException, SAXException {
Map<String, List<XmlElement>> schemaGraph = getSchemaGraphAndInlineIncludes( schemaIndexes, additionalWsdl, webserviceType );
Map<String, XmlElement> reducedSchemaGraph = reduceSchemaGraph( schemaGraph );
return compileSchemas( reducedSchemaGraph, schemaIndexes, webserviceType, additionalWsdl );
}
private static Schema compileSchemas( Map<String, XmlElement> reducedSchemaGraph, List<XmlSchemaIndex> schemaIndexes, IType webserviceType, XmlElement additionalWsdl ) throws SAXException {
Map<String, byte[]> reducedSchemaGraphBytes = new HashMap<String, byte[]>();
List<Source> sources = new ArrayList<Source>();
for ( Map.Entry<String, XmlElement> entry : reducedSchemaGraph.entrySet() ) {
byte[] bytes = entry.getValue().bytes();
reducedSchemaGraphBytes.put( entry.getKey(), bytes );
sources.add( new StreamSource( new ByteArrayInputStream( bytes ), "{" + entry.getKey() + "}" ) );
}
XMLSchemaFactory schemaFactory = new XMLSchemaFactory();
schemaFactory.setResourceResolver( new XmlSchemaCachedResourceResolver( reducedSchemaGraphBytes ) );
// Our XSD featureset heavily assumes only valid schemas are being used, following all rules of the schema
// specification, such as the Unique Particle Attribution rule.
schemaFactory.setFeature( "http://apache.org/xml/features/validation/schema-full-checking", true );
try {
return schemaFactory.newSchema( sources.toArray( new Source[ sources.size() ] ) );
}
catch ( SAXParseException ex ) {
System.out.println( "---------------------------------------" );
System.out.println( "[XSD] Error encountered while parsing schema for namespace " + ex.getSystemId() );
System.out.println( "[XSD] " + ex.getMessage() );
System.out.println( "[XSD] at line " + ex.getLineNumber() + " column " + ex.getColumnNumber() );
System.out.println( "[XSD] Gosu namespaces processed:" );
for ( XmlSchemaIndex schemaIndex : schemaIndexes ) {
System.out.println( "[XSD] " + schemaIndex.getPackageName() );
}
if ( additionalWsdl != null ) {
System.out.println( "[XSD] Additional WSDL: " + webserviceType.getName() );
}
System.out.println( "---------------------------------------" );
System.out.println( "[XSD] Begin Problematic schemas" );
for ( XmlElement schema : reducedSchemaGraph.values() ) {
System.out.println( "---------------------------------------" );
schema.print();
}
System.out.println( "---------------------------------------" );
System.out.println( "[XSD] End Problematic schemas" );
System.out.println( "---------------------------------------" );
ex.printStackTrace();
throw ex;
}
}
private static Map<String, XmlElement> reduceSchemaGraph( Map<String, List<XmlElement>> schemaGraph ) throws URISyntaxException {
Map<String,XmlElement> reducedSchemaGraph = new HashMap<String, XmlElement>();
for ( Map.Entry<String, List<XmlElement>> entry : schemaGraph.entrySet() ) {
String targetNamespace = entry.getKey();
List<XmlElement> schemas = entry.getValue();
if ( schemas.size() == 1 ) {
for ( XmlElement schema : schemas ) {
for ( XmlElement importElement : schema.getChildren( Import.$QNAME ) ) {
importElement.setAttributeValue( "schemaLocation", null ); // clear schemaLocation attribute
}
}
reducedSchemaGraph.put( targetNamespace, schemas.get( 0 ) );
}
else {
// combine schemas with same targetNamespace into a single schema
XmlNamespace schemaNS = XmlNamespace.forQName( gw.internal.schema.gw.xsd.w3c.xmlschema.Schema.$QNAME );
Map<String,Map<String,XmlElement>> componentTypeToNameToElementMap = new HashMap<String, Map<String, XmlElement>>();
Set<String> importedNamespaces = new HashSet<String>();
List<XmlElement> schemaComponentsToAdd = new ArrayList<XmlElement>();
for ( XmlElement schema : schemas ) {
for ( XmlElement child : schema.getChildren( schemaNS.qualify( "import" ) ) ) {
importedNamespaces.add( child.getAttributeValue( "namespace" ) );
}
for ( XmlElement child : schema.getChildren() ) {
String componentName = child.getAttributeValue( "name" );
if ( componentName != null ) {
String componentType = child.getQName().getLocalPart();
if ( componentType.equals( "complexType" ) || componentType.equals( "simpleType" ) ) {
componentType = "type"; // complexType and simpleType share a namespace
}
Map<String, XmlElement> nameToElementMap = componentTypeToNameToElementMap.get( componentType );
if ( nameToElementMap == null ) {
nameToElementMap = new HashMap<String, XmlElement>();
componentTypeToNameToElementMap.put( componentType, nameToElementMap );
}
XmlElement oldChild = nameToElementMap.put( componentName, child );
if ( oldChild == null ) {
schemaComponentsToAdd.add( child );
}
}
}
}
XmlElement newSchema = new XmlElement( schemaNS.qualify( "schema" ) );
newSchema.setAttributeValue( "targetNamespace", targetNamespace );
// create import statements
for ( String importedNamespace : importedNamespaces ) {
XmlElement importElement = new XmlElement( schemaNS.qualify( "import" ) );
importElement.setAttributeValue( "namespace", importedNamespace );
newSchema.addChild( importElement );
}
for ( XmlElement schemaComponent : schemaComponentsToAdd ) {
newSchema.addChild( schemaComponent );
}
reducedSchemaGraph.put( targetNamespace, newSchema );
}
}
// step through and change each "import namespace" and each "targetNamespace" from empty string to null
for ( XmlElement schema : reducedSchemaGraph.values() ) {
if ( "".equals( schema.getAttributeValue( "targetNamespace" ) ) ) {
schema.setAttributeValue( "targetNamespace", null );
}
for ( XmlElement imprt : schema.getChildren( Import.$QNAME ) ) {
if ( "".equals( imprt.getAttributeValue( "namespace" ) ) ) {
imprt.setAttributeValue( "namespace", null );
}
}
}
return reducedSchemaGraph;
}
private static void walkSchemaBeforeCombination( XmlElement element, boolean qualifyElements, boolean qualifyAttributes, boolean topLevel, String elementBlockDefault, String complexTypeBlockDefault, String elementFinalDefault, String complexTypeFinalDefault, String simpleTypeFinalDefault ) {
if ( element.getQName().equals( Element.$QNAME ) ) {
if ( ! topLevel && qualifyElements && element.getAttributeValue( "name" ) != null && element.getAttributeValue( "form" ) == null ) {
element.setAttributeValue( "form", "qualified" );
}
if ( elementBlockDefault != null && element.getAttributeValue( "block" ) == null && element.getAttributeValue( "name" ) != null ) {
element.setAttributeValue( "block", elementBlockDefault );
}
if ( topLevel && elementFinalDefault != null && element.getAttributeValue( "final" ) == null ) {
element.setAttributeValue( "final", elementFinalDefault );
}
}
else if ( element.getQName().equals( Attribute.$QNAME ) ) {
if ( ! topLevel && qualifyAttributes && element.getAttributeValue( "name" ) != null && element.getAttributeValue( "form" ) == null ) {
element.setAttributeValue( "form", "qualified" );
}
}
else if ( element.getQName().equals( ComplexType.$QNAME ) ) {
if ( topLevel && complexTypeBlockDefault != null && element.getAttributeValue( "block" ) == null ) {
element.setAttributeValue( "block", complexTypeBlockDefault );
}
if ( topLevel && complexTypeFinalDefault != null && element.getAttributeValue( "final" ) == null ) {
element.setAttributeValue( "final", complexTypeFinalDefault );
}
}
else if ( element.getQName().equals( SimpleType.$QNAME ) ) {
if ( topLevel && simpleTypeFinalDefault != null && element.getAttributeValue( "final" ) == null ) {
element.setAttributeValue( "final", simpleTypeFinalDefault );
}
}
for ( XmlElement child : element.getChildren() ) {
walkSchemaBeforeCombination( child, qualifyElements, qualifyAttributes, false, elementBlockDefault, complexTypeBlockDefault, elementFinalDefault, complexTypeFinalDefault, simpleTypeFinalDefault );
}
}
private static Map<String, List<XmlElement>> getSchemaGraphAndInlineIncludes( List<XmlSchemaIndex> schemaIndexes, XmlElement additionalWsdl, IType webserviceType ) throws MalformedURLException {
Set<Pair<URL,String>> newSchemaIndexesToProcess = new HashSet<Pair<URL, String>>();
Map<URL,String> resourceUrls = new HashMap<URL, String>();
Map<String,List<XmlElement>> schemasByTargetNamespace = new HashMap<String, List<XmlElement>>();
if ( additionalWsdl != null ) {
IModule currentModule = webserviceType.getTypeLoader().getModule();
String webserviceTypePath = webserviceType.getName().replace( '.', '/' );
String webserviceTypeResourcePath = webserviceTypePath + ".gs";
IGosuClass gosuClass = (IGosuClass) webserviceType;
URL schemaUrl = gosuClass.getTypeLoader().getRepository().findResource( webserviceTypeResourcePath );
if ( schemaUrl == null ) {
throw new IllegalStateException( "Unable to find resource " + webserviceTypeResourcePath + " in module " + currentModule );
}
for ( XmlElement wsdlImportElement : additionalWsdl.getChildren( TDefinitions_Import.$QNAME ) ) {
String location = wsdlImportElement.getAttributeValue( "location" );
URL newUrl = makeLocal( schemaUrl, location, null, currentModule );
newSchemaIndexesToProcess.add( new Pair<URL, String>( newUrl, null ) );
}
for ( XmlElement typesElement : additionalWsdl.getChildren( TDefinitions_Types.$QNAME ) ) {
for ( XmlElement schema : typesElement.getChildren() ) {
addSchemaToSchemasByNamespaceMapAndInlineIncludes( schemasByTargetNamespace, schema, newSchemaIndexesToProcess, schemaUrl );
}
}
}
for ( XmlSchemaIndex<?> schemaIndexToProcess : schemaIndexes ) {
URL blueprintURL = schemaIndexToProcess.getXSDSource().getBlueprintURL();
newSchemaIndexesToProcess.add( new Pair<URL, String>( blueprintURL, null ) );
}
Set<Pair<URL,String>> schemaIndexesToProcess = newSchemaIndexesToProcess;
while ( ! schemaIndexesToProcess.isEmpty() ) {
newSchemaIndexesToProcess = new HashSet<Pair<URL, String>>();
for ( Pair<URL, String> pair : schemaIndexesToProcess ) {
URL blueprintUrl = pair.getFirst();
String requiredTargetNamespace = pair.getSecond();
if ( resourceUrls.containsKey( blueprintUrl ) ) {
if ( requiredTargetNamespace != null ) {
String oldTargetNamespace = resourceUrls.put( blueprintUrl, requiredTargetNamespace );
if ( oldTargetNamespace != null ) {
if ( ! requiredTargetNamespace.equals( oldTargetNamespace ) ) {
throw new XmlException( "The namespace attribute, '" + requiredTargetNamespace + "', of an <import> element information item must be identical to the targetNamespace attribute, '" + oldTargetNamespace + "', of the imported document" );
}
}
}
}
else {
// convert URL to schema index and process it
getSchemaAndInlineIncludes( blueprintUrl, schemasByTargetNamespace, newSchemaIndexesToProcess, requiredTargetNamespace, resourceUrls );
}
}
schemaIndexesToProcess = newSchemaIndexesToProcess;
}
return schemasByTargetNamespace;
}
private static void getSchemaAndInlineIncludes( URL blueprintURL, Map<String, List<XmlElement>> schemasByTargetNamespace, Set<Pair<URL, String>> newSchemaIndexesToProcess, String requiredNamespace, Map<URL, String> resourceUrls ) throws MalformedURLException {
IModule currentModule = TypeSystem.getGlobalModule();
String gosuNamespace = XmlSchemaIndex.getGosuNamespace( blueprintURL, currentModule );
XmlSchemaIndex<?> referencedSchemaIndex = XmlSchemaResourceTypeLoaderBase.findSchemaForNamespace( currentModule, gosuNamespace );
if ( referencedSchemaIndex == null ) {
if ( CommonServices.getXmlSchemaCompatibilityConfig().useCompatibilityMode( gosuNamespace ) ) {
throw new XmlException( "A non-compatibility-mode schema cannot import a compatibility-mode schema (" + gosuNamespace + ")" );
}
else {
throw new IllegalStateException( "Schema not found for Gosu namespace " + gosuNamespace );
}
}
XmlElement schemaOrWsdl = XmlElement.parse( referencedSchemaIndex.getXSDSource().getInputStream( false ) );
if ( schemaOrWsdl.getQName().equals( Definitions.$QNAME ) ) {
resourceUrls.put( blueprintURL, null );
for ( XmlElement wsdlImportElement : schemaOrWsdl.getChildren( TDefinitions_Import.$QNAME ) ) {
String location = wsdlImportElement.getAttributeValue( "location" );
URL newUrl = makeLocal( blueprintURL, location, null, referencedSchemaIndex.getTypeLoader().getModule() );
newSchemaIndexesToProcess.add( new Pair<URL, String>( newUrl, null ) );
}
for ( XmlElement typesElement : schemaOrWsdl.getChildren( TDefinitions_Types.$QNAME ) ) {
for ( XmlElement schema : typesElement.getChildren() ) {
addSchemaToSchemasByNamespaceMapAndInlineIncludes( schemasByTargetNamespace, schema, newSchemaIndexesToProcess, blueprintURL );
}
}
}
else {
String tns = schemaOrWsdl.getAttributeValue( "targetNamespace" );
if ( tns == null ) {
tns = "";
}
if ( requiredNamespace != null ) {
if ( ! tns.equals( requiredNamespace ) ) {
throw new XmlException( "The namespace attribute, '" + requiredNamespace + "', of an <import> element information item must be identical to the targetNamespace attribute, '" + tns + "', of the imported document" );
}
}
resourceUrls.put( blueprintURL, tns );
addSchemaToSchemasByNamespaceMapAndInlineIncludes( schemasByTargetNamespace, schemaOrWsdl, newSchemaIndexesToProcess, blueprintURL );
}
}
private static void addSchemaToSchemasByNamespaceMapAndInlineIncludes( Map<String, List<XmlElement>> schemasByTargetNamespace, XmlElement schema, Set<Pair<URL, String>> newSchemaIndexesToProcess, URL blueprintUrl ) throws MalformedURLException {
String targetNamespace = schema.getAttributeValue( "targetNamespace" );
if ( targetNamespace == null ) {
targetNamespace = "";
}
Set<URL> alreadyIncluded = new HashSet<URL>();
Map<String,Map<String,XmlElement>> resultingComponents = new HashMap<String, Map<String, XmlElement>>();
Set<String> resultingImportedNamespaces = new HashSet<String>();
processIncludedSchema( schema, blueprintUrl, targetNamespace, resultingComponents, resultingImportedNamespaces, newSchemaIndexesToProcess, alreadyIncluded );
XmlElement resultingSchema = new XmlElement( gw.internal.schema.gw.xsd.w3c.xmlschema.Schema.$QNAME );
resultingSchema.setAttributeValue( "targetNamespace", targetNamespace );
for ( String resultingImportedNamespace : resultingImportedNamespaces ) {
XmlElement importElement = new XmlElement( Import.$QNAME );
importElement.setAttributeValue( "namespace", resultingImportedNamespace );
resultingSchema.addChild( importElement );
}
for ( Map<String, XmlElement> submap : resultingComponents.values() ) {
for ( XmlElement componentElement : submap.values() ) {
resultingSchema.addChild( componentElement );
}
}
List<XmlElement> schemas = schemasByTargetNamespace.get( targetNamespace );
if ( schemas == null ) {
schemas = new ArrayList<XmlElement>();
schemasByTargetNamespace.put( targetNamespace, schemas );
}
schemas.add( resultingSchema );
}
private static void processIncludedSchema( XmlElement schema, URL blueprintUrl, String targetNamespace, Map<String, Map<String, XmlElement>> resultingComponents, Set<String> resultingImportedNamespaces, Set<Pair<URL, String>> newSchemaIndexesToProcess, Set<URL> alreadyIncluded ) throws MalformedURLException {
boolean elementFormDefault = "qualified".equals( schema.getAttributeValue( "elementFormDefault" ) );
boolean attributeFormDefault = "qualified".equals( schema.getAttributeValue( "attributeFormDefault" ) );
String blockDefault = schema.getAttributeValue( "blockDefault" );
boolean blockDefaultExtension = blockDefault != null && ( blockDefault.contains( "extension" ) || blockDefault.contains( "#all" ) );
boolean blockDefaultRestriction = blockDefault != null && ( blockDefault.contains( "restriction" ) || blockDefault.contains( "#all" ) );
boolean blockDefaultSubstitution = blockDefault != null && ( blockDefault.contains( "substitution" ) || blockDefault.contains( "#all" ) );
String finalDefault = schema.getAttributeValue( "finalDefault" );
boolean finalDefaultExtension = finalDefault != null && ( finalDefault.contains( "extension" ) || finalDefault.contains( "#all" ) );
boolean finalDefaultRestriction = finalDefault != null && ( finalDefault.contains( "restriction" ) || finalDefault.contains( "#all" ) );
boolean finalDefaultList = finalDefault != null && ( finalDefault.contains( "list" ) || finalDefault.contains( "#all" ) );
boolean finalDefaultUnion = finalDefault != null && ( finalDefault.contains( "union" ) || finalDefault.contains( "#all" ) );
final String elementBlockDefault;
final String complexTypeBlockDefault;
final String elementFinalDefault;
final String complexTypeFinalDefault;
final String simpleTypeFinalDefault;
if ( blockDefaultExtension && blockDefaultRestriction && blockDefaultSubstitution ) {
elementBlockDefault = "#all";
}
else if ( blockDefaultExtension || blockDefaultRestriction || blockDefaultSubstitution ) {
StringBuilder elementBlockValue = new StringBuilder();
addToList( blockDefaultExtension, elementBlockValue, "extension" );
addToList( blockDefaultRestriction, elementBlockValue, "restriction" );
addToList( blockDefaultSubstitution, elementBlockValue, "substitution" );
elementBlockDefault = elementBlockValue.toString();
}
else {
elementBlockDefault = null;
}
if ( blockDefaultExtension && blockDefaultRestriction ) {
complexTypeBlockDefault = "#all";
}
else if ( blockDefaultExtension || blockDefaultRestriction || blockDefaultSubstitution ) {
StringBuilder complexTypeBlockValue = new StringBuilder();
addToList( blockDefaultExtension, complexTypeBlockValue, "extension" );
addToList( blockDefaultRestriction, complexTypeBlockValue, "restriction" );
complexTypeBlockDefault = complexTypeBlockValue.toString();
}
else {
complexTypeBlockDefault = null;
}
if ( finalDefaultExtension && finalDefaultRestriction ) {
elementFinalDefault = "#all";
}
else if ( finalDefaultExtension || finalDefaultRestriction ) {
StringBuilder elementFinalValue = new StringBuilder();
addToList( finalDefaultExtension, elementFinalValue, "extension" );
addToList( finalDefaultRestriction, elementFinalValue, "restriction" );
elementFinalDefault = elementFinalValue.toString();
}
else {
elementFinalDefault = null;
}
if ( finalDefaultExtension && finalDefaultRestriction ) {
complexTypeFinalDefault = "#all";
}
else if ( finalDefaultExtension || finalDefaultRestriction ) {
StringBuilder complexTypeFinalValue = new StringBuilder();
addToList( finalDefaultExtension, complexTypeFinalValue, "extension" );
addToList( finalDefaultRestriction, complexTypeFinalValue, "restriction" );
complexTypeFinalDefault = complexTypeFinalValue.toString();
}
else {
complexTypeFinalDefault = null;
}
if ( finalDefaultRestriction && finalDefaultList && finalDefaultUnion ) {
simpleTypeFinalDefault = "#all";
}
else if ( finalDefaultRestriction && finalDefaultList && finalDefaultUnion ) {
StringBuilder simpleTypeFinalValue = new StringBuilder();
addToList( finalDefaultRestriction, simpleTypeFinalValue, "restriction" );
addToList( finalDefaultList, simpleTypeFinalValue, "list" );
addToList( finalDefaultUnion, simpleTypeFinalValue, "union" );
simpleTypeFinalDefault = simpleTypeFinalValue.toString();
}
else {
simpleTypeFinalDefault = null;
}
for ( XmlElement child : schema.getChildren() ) {
walkSchemaBeforeCombination( child, elementFormDefault, attributeFormDefault, true, elementBlockDefault, complexTypeBlockDefault, elementFinalDefault, complexTypeFinalDefault, simpleTypeFinalDefault );
}
// move all components from included schema into "resulting components" list
for ( XmlElement componentElement : schema.getChildren() ) {
String componentName = componentElement.getAttributeValue( "name" );
if ( componentName != null ) {
String componentType = componentElement.getQName().getLocalPart();
if ( componentType.equals( "complexType" ) || componentType.equals( "simpleType" ) ) {
componentType = "type"; // complexType and simpleType share a namespace
}
Map<String, XmlElement> nameToComponentElementMap = resultingComponents.get( componentType );
if ( nameToComponentElementMap == null ) {
nameToComponentElementMap = new HashMap<String, XmlElement>();
resultingComponents.put( componentType, nameToComponentElementMap );
}
nameToComponentElementMap.put( componentName, componentElement );
}
}
for ( XmlElement importElement : schema.getChildren( Import.$QNAME ) ) {
String tns = importElement.getAttributeValue( "namespace" );
if ( tns == null ) {
tns = "";
}
resultingImportedNamespaces.add( tns );
String schemaLocation = importElement.getAttributeValue( "schemaLocation" );
URL newUrl = makeLocal( blueprintUrl, schemaLocation, tns, TypeSystem.getExecutionEnvironment().getModule( blueprintUrl ) );
if ( newUrl != null ) { // in the case of an xs:import without a specified schemaLocation, we won't be able to find the imported resource
newSchemaIndexesToProcess.add( new Pair<URL, String>( newUrl, tns ) );
}
}
for ( XmlElement include : schema.removeChildren( Include.$QNAME ) ) {
inlineIncludeOrRedefine( blueprintUrl, targetNamespace, resultingComponents, resultingImportedNamespaces, newSchemaIndexesToProcess, include, alreadyIncluded );
}
for ( XmlElement redefine : schema.removeChildren( Redefine.$QNAME ) ) {
inlineIncludeOrRedefine( blueprintUrl, targetNamespace, resultingComponents, resultingImportedNamespaces, newSchemaIndexesToProcess, redefine, alreadyIncluded );
}
}
private static void addToList( boolean blockDefaultExtension, StringBuilder complexTypeBlockValue, String blockName ) {
if ( blockDefaultExtension ) {
if ( complexTypeBlockValue.length() > 0 ) {
complexTypeBlockValue.append( ' ' );
}
complexTypeBlockValue.append( blockName );
}
}
private static void inlineIncludeOrRedefine( URL blueprintUrl, String targetNamespace, Map<String, Map<String, XmlElement>> resultingComponents, Set<String> resultingImportedNamespaces, Set<Pair<URL, String>> newSchemaIndexesToProcess, XmlElement includeOrRedefine, Set<URL> alreadyIncluded ) throws MalformedURLException {
String schemaLocation = includeOrRedefine.getAttributeValue( "schemaLocation" );
URL includedUrl = makeLocal( blueprintUrl, schemaLocation, null, TypeSystem.getExecutionEnvironment().getModule( blueprintUrl ) );
if ( ! alreadyIncluded.add( includedUrl ) ) {
return; // already included
}
XmlElement includedSchema = XmlElement.parse( includedUrl );
String includedTargetNamespace = includedSchema.getAttributeValue( "targetNamespace" );
if ( includedTargetNamespace == null ) {
includedTargetNamespace = "";
}
if ( includedTargetNamespace.equals( "" ) && ! targetNamespace.equals( "" ) ) {
// perform chameleon transformation - all references without namespace gain the targetNamespace of the including schema
performChameleonTransformation( includedSchema, targetNamespace );
}
else if ( ! includedTargetNamespace.equals( targetNamespace ) ) {
throw new XmlException( "src-include.2.1: The targetNamespace of the referenced schema, '" + includedTargetNamespace + "', must be identical to that of the including schema, '" + targetNamespace + "'" );
}
if ( includeOrRedefine.getQName().equals( Redefine.$QNAME ) ) {
processIncludedSchema( includedSchema, includedUrl, targetNamespace, resultingComponents, resultingImportedNamespaces, newSchemaIndexesToProcess, alreadyIncluded ); // recurse as needed
// now actually redefine specified components
for ( XmlElement complexTypeRedefinitionElement : includeOrRedefine.getChildren( ComplexType.$QNAME ) ) {
XmlElement content = complexTypeRedefinitionElement.getChild( ComplexContent.$QNAME );
if ( content == null ) {
content = complexTypeRedefinitionElement.getChild( SimpleContent.$QNAME );
}
if ( content == null ) {
throw new XmlException( "xs:complexType expected to contain either xs:complexContent or xs:simpleContent" );
}
XmlElement extensionOrRestriction = content.getChild( ComplexContent_Extension.$QNAME );
if ( extensionOrRestriction == null ) {
extensionOrRestriction = content.getChild( ComplexContent_Restriction.$QNAME );
}
if ( extensionOrRestriction == null ) {
throw new XmlException( "xs:complexType expected to contain either xs:complexContent or xs:simpleContent containing either xs:extension or xs:restriction" );
}
String baseString = (String) extensionOrRestriction.getAttributeSimpleValue( "base" ).getGosuValue();
QName baseQName = XmlSchemaParser.parseQName( baseString, extensionOrRestriction.getNamespaceContext(), targetNamespace, true );
QName expectedQName = new QName( targetNamespace, complexTypeRedefinitionElement.getAttributeValue( "name" ) );
if ( ! expectedQName.equals( baseQName ) ) {
throw new XmlException( "Expected base type name to be " + expectedQName + ", but was " + baseQName );
}
// invent a new, unique name for the redefined base component
String newBaseName = "Redefine-" + baseQName.getLocalPart() + "-" + UUID.randomUUID().toString();
extensionOrRestriction.setAttributeSimpleValue( "base", XmlSimpleValue.makeQNameInstance( new QName( targetNamespace, newBaseName ) ) ); // rename the reference
XmlElement oldComponent = resultingComponents.get( "type" ).put( baseQName.getLocalPart(), complexTypeRedefinitionElement );
oldComponent.setAttributeValue( "name", newBaseName );
resultingComponents.get( "type" ).put( newBaseName, oldComponent );
}
for ( XmlElement simpleTypeRedefinitionElement : includeOrRedefine.getChildren( SimpleType.$QNAME ) ) {
XmlElement restriction = simpleTypeRedefinitionElement.getChild( ComplexContent_Restriction.$QNAME );
if ( restriction == null ) {
throw new XmlException( "xs:simpleType expected to contain xs:restriction" );
}
String baseString = (String) restriction.getAttributeSimpleValue( "base" ).getGosuValue();
QName baseQName = XmlSchemaParser.parseQName( baseString, restriction.getNamespaceContext(), targetNamespace, true );
QName expectedQName = new QName( targetNamespace, simpleTypeRedefinitionElement.getAttributeValue( "name" ) );
if ( ! expectedQName.equals( baseQName ) ) {
throw new XmlException( "Expected base type name to be " + expectedQName + ", but was " + baseQName );
}
// invent a new, unique name for the redefined base component
String newBaseName = "Redefine-" + baseQName.getLocalPart() + "-" + UUID.randomUUID().toString();
restriction.setAttributeSimpleValue( "base", XmlSimpleValue.makeQNameInstance( new QName( targetNamespace, newBaseName ) ) ); // rename the reference
XmlElement oldComponent = resultingComponents.get( "type" ).put( baseQName.getLocalPart(), simpleTypeRedefinitionElement );
oldComponent.setAttributeValue( "name", newBaseName );
resultingComponents.get( "type" ).put( newBaseName, oldComponent );
}
for ( XmlElement groupRedefinitionElement : includeOrRedefine.getChildren( Group.$QNAME ) ) {
String groupName = groupRedefinitionElement.getAttributeValue( "name" );
String newBaseName = "Redefine-" + groupName + "-" + UUID.randomUUID().toString();
// invent a new, unique name for the redefined base component
redefineGroup( targetNamespace, groupRedefinitionElement, groupName, newBaseName );
}
for ( XmlElement attributeGroupRedefinitionElement : includeOrRedefine.getChildren( AttributeGroup.$QNAME ) ) {
String groupName = attributeGroupRedefinitionElement.getAttributeValue( "name" );
String newBaseName = "Redefine-" + groupName + "-" + UUID.randomUUID().toString();
// invent a new, unique name for the redefined base component
for ( XmlElement attributeGroupElement : attributeGroupRedefinitionElement.getChildren( AttributeGroup.$QNAME ) ) {
XmlSimpleValue refValue = attributeGroupElement.getAttributeSimpleValue( "ref" );
if ( refValue != null ) {
String baseString = (String) refValue.getGosuValue();
QName baseQName = XmlSchemaParser.parseQName( baseString, attributeGroupElement.getNamespaceContext(), targetNamespace, true );
QName expectedQName = new QName( targetNamespace, groupName );
if ( expectedQName.equals( baseQName ) ) {
attributeGroupElement.setAttributeSimpleValue( "ref", XmlSimpleValue.makeQNameInstance( new QName( targetNamespace, newBaseName ) ) );
}
}
}
}
}
else {
processIncludedSchema( includedSchema, includedUrl, targetNamespace, resultingComponents, resultingImportedNamespaces, newSchemaIndexesToProcess, alreadyIncluded ); // recurse as needed
}
}
private static void redefineGroup( String targetNamespace, XmlElement groupRedefinitionElement, String groupName, String newBaseName ) {
for ( XmlElement groupElement : groupRedefinitionElement.getChildren() ) {
if ( groupElement.getQName().equals( Group.$QNAME ) ) {
XmlSimpleValue refValue = groupElement.getAttributeSimpleValue( "ref" );
if ( refValue != null ) {
String baseString = (String) refValue.getGosuValue();
QName baseQName = XmlSchemaParser.parseQName( baseString, groupElement.getNamespaceContext(), targetNamespace, true );
QName expectedQName = new QName( targetNamespace, groupName );
if ( expectedQName.equals( baseQName ) ) {
groupElement.setAttributeSimpleValue( "ref", XmlSimpleValue.makeQNameInstance( new QName( targetNamespace, newBaseName ) ) );
}
}
}
redefineGroup( targetNamespace, groupElement, groupName, newBaseName );
}
}
private static void performChameleonTransformation( XmlElement element, String targetNamespace ) {
String refName = element.getAttributeValue( "ref" );
if ( refName != null ) {
element.setAttributeSimpleValue( "ref", XmlSimpleValue.makeQNameInstance( XmlSchemaParser.parseQName( refName, element.getNamespaceContext(), targetNamespace, true ) ) );
}
String typeName = element.getAttributeValue( "type" );
if ( typeName != null ) {
element.setAttributeSimpleValue( "type", XmlSimpleValue.makeQNameInstance( XmlSchemaParser.parseQName( typeName, element.getNamespaceContext(), targetNamespace, true ) ) );
}
String baseName = element.getAttributeValue( "base" );
if ( baseName != null ) {
element.setAttributeSimpleValue( "base", XmlSimpleValue.makeQNameInstance( XmlSchemaParser.parseQName( baseName, element.getNamespaceContext(), targetNamespace, true ) ) );
}
String itemTypeName = element.getAttributeValue( "itemType" );
if ( itemTypeName != null ) {
element.setAttributeSimpleValue( "itemType", XmlSimpleValue.makeQNameInstance( XmlSchemaParser.parseQName( itemTypeName, element.getNamespaceContext(), targetNamespace, true ) ) );
}
String referName = element.getAttributeValue( "refer" );
if ( referName != null ) {
element.setAttributeSimpleValue( "refer", XmlSimpleValue.makeQNameInstance( XmlSchemaParser.parseQName( referName, element.getNamespaceContext(), targetNamespace, true ) ) );
}
String memberTypes = element.getAttributeValue( "memberTypes" );
if ( memberTypes != null ) {
String[] parts = memberTypes.split( "\\w" );
List<QName> qnames = new ArrayList<QName>( parts.length );
for ( String part : parts ) {
qnames.add( XmlSchemaParser.parseQName( part, element.getNamespaceContext(), targetNamespace, true ) );
}
element.setAttributeSimpleValue( "memberTypes", XmlSimpleValue.makeListOfQNameInstance( qnames ) );
}
for ( XmlElement child : element.getChildren() ) {
performChameleonTransformation( child, targetNamespace );
}
}
private static void getAllSchemas( URL schemaUrl, Set<URL> seenResources, List<Pair<XmlElement, String>> allSchemas, Map<String, String> fakeExternalLocationAliases, boolean isInclude ) {
if ( ! seenResources.contains( schemaUrl ) ) {
seenResources.add( schemaUrl );
XmlElement schema;
IModule currentModule = TypeSystem.getGlobalModule();
// IModule currentModule = TypeSystem.getExecutionEnvironment().getModule( schemaUrl );
if ( isInclude ) {
schema = XmlElement.parse( schemaUrl );
String fakeUrl = "file:/" + currentModule.getFileRepository().getResourceName( schemaUrl );
addSchema( allSchemas, schema, fakeUrl, fakeExternalLocationAliases, seenResources, schemaUrl, currentModule );
}
else {
String gosuNamespace = XmlSchemaIndex.getGosuNamespace( schemaUrl, currentModule );
XmlSchemaIndex<?> schemaIndex = XmlSchemaResourceTypeLoaderBase.findSchemaForNamespace( currentModule, gosuNamespace );
if ( schemaIndex == null ) {
if ( CommonServices.getXmlSchemaCompatibilityConfig().useCompatibilityMode( gosuNamespace ) ) {
throw new XmlException( "A non-compatibility-mode schema cannot import a compatibility-mode schema (" + gosuNamespace + ")" );
}
else {
throw new IllegalStateException( "Schema not found for Gosu namespace " + gosuNamespace );
}
}
schema = XmlElement.parse( schemaIndex.getXSDSource().getInputStream( false ) );
addSchemaOrWsdl( schemaUrl, seenResources, allSchemas, fakeExternalLocationAliases, schema, currentModule );
}
}
}
private static void addSchemaOrWsdl( URL schemaUrl, Set<URL> seenResources, List<Pair<XmlElement, String>> allSchemas, Map<String, String> fakeExternalLocationAliases, XmlElement schema, IModule currentModule ) {
String fakeUrl = "file:/" + currentModule.getFileRepository().getResourceName( schemaUrl );
if ( schema.getQName().equals( Definitions.$QNAME ) ) {
for ( XmlElement importElement : schema.getChildren( TDefinitions_Import.$QNAME ) ) {
String wsdlLocation = importElement.getAttributeValue( "location" );
URL url = makeLocal( schemaUrl, wsdlLocation, null, currentModule );
getAllSchemas( url, seenResources, allSchemas, fakeExternalLocationAliases, false );
}
// extract schemas from WSDL
for ( XmlElement typesElement : schema.getChildren( TDefinitions_Types.$QNAME ) ) {
for ( XmlElement schemaElement : typesElement.getChildren( gw.internal.schema.gw.xsd.w3c.xmlschema.Schema.$QNAME ) ) {
addSchema( allSchemas, schemaElement, fakeUrl, fakeExternalLocationAliases, seenResources, schemaUrl, currentModule );
}
}
}
else if ( schema.getQName().equals( gw.internal.schema.gw.xsd.w3c.xmlschema.Schema.$QNAME ) ) {
addSchema( allSchemas, schema, fakeUrl, fakeExternalLocationAliases, seenResources, schemaUrl, currentModule );
}
else {
throw new XmlException( "Unexpected root element " + schema.getQName() + ", expected " + gw.internal.schema.gw.xsd.w3c.xmlschema.Schema.$QNAME + " or " + Definitions.$QNAME );
}
}
private static void addSchema( List<Pair<XmlElement, String>> allSchemas, XmlElement schema, String fakeUrl, Map<String, String> fakeExternalLocationAliases, Set<URL> seenResources, URL schemaUrl, IModule currentModule ) {
allSchemas.add( new Pair<XmlElement, String>( schema, fakeUrl ) );
Map<String,String> externalLocationAliases = new HashMap<String, String>();
for ( XmlElement include : schema.getChildren( Include.$QNAME ) ) {
String schemaLocation = include.getAttributeValue( "schemaLocation" );
URL url = makeLocal( schemaUrl, schemaLocation, null, currentModule, externalLocationAliases );
getAllSchemas( url, seenResources, allSchemas, fakeExternalLocationAliases, true );
}
for ( XmlElement redefine : schema.getChildren( Redefine.$QNAME ) ) {
String schemaLocation = redefine.getAttributeValue( "schemaLocation" );
URL url = makeLocal( schemaUrl, schemaLocation, null, currentModule, externalLocationAliases );
getAllSchemas( url, seenResources, allSchemas, fakeExternalLocationAliases, true );
}
for ( XmlElement imprt : schema.getChildren( Import.$QNAME ) ) {
String schemaLocation = imprt.getAttributeValue( "schemaLocation" );
String namespace = imprt.getAttributeValue( "namespace" );
// Work around possible Xerces bug. If imported schema has an empty string namespace, the importing schema
// *can not* specify the namespace attribute
if ( namespace != null && namespace.equals( XmlConstants.NULL_NS_URI ) ) {
namespace = null;
imprt.setAttributeValue( "namespace", null );
}
URL url = makeLocal( schemaUrl, schemaLocation, namespace, currentModule, externalLocationAliases );
if ( url != null ) {
// url can be null in the case of an <xs:import> without a schemaLocation, such as if the imported schema
// is mapped as an external location, or if another imported schema specifies the schemaLocation
getAllSchemas( url, seenResources, allSchemas, fakeExternalLocationAliases, false );
}
}
for ( Map.Entry<String, String> entry : externalLocationAliases.entrySet()) {
try {
String externalLocation = entry.getKey();
String localPath = entry.getValue();
String fakeUrl2 = "file:/" + currentModule.getFileRepository().getResourceName( new URL( localPath ) );
fakeExternalLocationAliases.put( externalLocation, fakeUrl2 );
}
catch ( MalformedURLException ex ) {
throw GosuExceptionUtil.forceThrow( ex );
}
}
}
public StreamSource getSchemaStreamSource() {
URL schemaURL = _xmlSchemaSource.getBlueprintURL();
String schemaEF = schemaURL == null ? null : schemaURL.toExternalForm();
return new StreamSource( _xmlSchemaSource.getInputStream( false ), schemaEF );
}
private void readSchema( URL schemaEF, String tns, Map<Pair<URL,String>, XmlSchema> caches ) throws ParserConfigurationException, SAXException, IOException {
LocationMap locationMap = _xmlSchemaSource.createLocationMap();
XmlElement rootElement;
InputStream inputStream = _xmlSchemaSource.getInputStream( true );
try {
rootElement = XmlElementInternals.instance().parse( inputStream, schemaEF, new XmlSchemaLocalResourceResolver(),
new LocationMapCallback(locationMap) );
}
finally {
inputStream.close();
}
XmlSchemaParseContext context = new XmlSchemaParseContext();
context.setLocationMap( locationMap );
WsdlDefinitions def;
List<XmlSchema> schemas;
if ( rootElement.getQName().equals( gw.internal.schema.gw.xsd.w3c.xmlschema.Schema.$QNAME ) ) {
schemas = Collections.singletonList( XmlSchemaParser.parseSchema( this, rootElement, tns, schemaEF, context, false, caches ) );
}
else if ( rootElement.getQName().equals( gw.internal.schema.gw.xsd.w3c.wsdl.Definitions.$QNAME ) ) {
def = XmlSchemaParser.parseWsdlDefinitions( this, rootElement, schemaEF, context, locationMap );
_wsdlDefinition = def;
List<XmlSchema> result = new ArrayList<XmlSchema>();
for ( WsdlTypes types : def.getTypes()){
result.addAll( types.getSchemas());
}
schemas = result;
}
else {
throw new IllegalArgumentException( "Expected an xs:schema or wsdl:definitions, but found " + rootElement.getQName() );
}
_collection.addSchemas( schemas );
}
private void maybeIndex( Map<Pair<URL, String>, XmlSchema> caches ) {
if ( _indexed ) {
return;
}
TypeSystem.lock();
try {
if ( _indexed ) {
return;
}
_indexed = true;
try {
readSchema( _schemaEF, null, caches );
if (!CommonServices.getPlatformHelper().isInIDE()) {
validate( caches );
}
String schemaAccessTypeName = _packageName + ".util";
_allTypeNames.addAll( getAdditionalTypeNames() );
_allTypeNames.add( schemaAccessTypeName );
_typesByName.put( schemaAccessTypeName, new XmlSchemaAccessTypeData<T>( schemaAccessTypeName, getTypeLoader().getSchemaSchemaTypeName(), getContext(), this ) );
List<Runnable> todo = new ArrayList<Runnable>();
for ( XmlSchema schema : _collection.getXmlSchemas() ) {
index( schema.getTypes(), todo );
index( schema.getElements(), todo );
index( schema.getAttributes(), todo );
}
for ( Runnable runnable : todo ) {
runnable.run();
}
}
finally {
// memory optimization - initialize all type data so we can throw out the intermediary indexing info
Runnable runnable = new Runnable() {
public void run() {
IExecutionEnvironment env = TypeSystem.getExecutionEnvironment();
TypeSystem.pushModule(env.getGlobalModule());
TypeSystem.lock();
try {
XmlSchemaIndex<?> schemaIndex = XmlSchemaResourceTypeLoaderBase.findSchemaForNamespace( getTypeLoader().getModule(), _packageName );
// ensure the wsdl actually validated and has not been removed before executing the following code
//noinspection ObjectEquality
if ( schemaIndex == XmlSchemaIndex.this ) {
for ( IXmlSchemaTypeData typeData : _typesByName.values() ) {
typeData.maybeInit(); // initialize all type data before clearing intermediary indexing info
}
_flattenedChildrenBySchemaType = null;
_childPluralityBySchemaType = null;
}
}
finally {
TypeSystem.unlock();
TypeSystem.popModule(env.getGlobalModule());
}
}
};
MemoryCleanerThread.invokeLater( runnable );
}
}
catch ( Exception ex ) {
getTypeLoader().schemaIndexingExceptionOccurred( _packageName, getXSDSource().getResourceFile(), ex );
clearExternalJarXSD();
if ( ! getXSDSource().getBlueprintURL().getProtocol().equals( "jar" ) ) {
//noinspection ThrowableInstanceNeverThrown
new XmlException( "Error indexing schema '" + _xmlSchemaSource.getDescription() + "'", ex ).printStackTrace();
// we can't throw here
}
}
finally {
TypeSystem.unlock();
}
}
private void clearExternalJarXSD() {
// pretend like this xsd never existed
_allTypeNames.clear();
_typesByName.clear();
_typesBySchemaObject.clear();
_flattenedChildrenBySchemaType.clear();
_childPluralityBySchemaType.clear();
}
private void index( Map<QName, ? extends XmlSchemaObject> schemaObjects, List<Runnable> todo ) {
for ( XmlSchemaObject xmlSchemaObject : schemaObjects.values() ) {
XmlSchemaIndexer.invokeIndexer( xmlSchemaObject, "", new ArrayList<XmlSchemaFlattenedChild>(), true, false, null, true, new HashMap<QName, XmlSchemaElementTypeData>(), new HashMap<Pair<XmlSchemaPropertyType, QName>, Boolean>(), getContext(), todo );
}
}
public static String getGosuNamespace( URL resourceURL, IModule module ) {
for (IModule m : module.getModuleTraversalList()) {
// Skip global module, but only if requested module is not global module itself
if (m == TypeSystem.getGlobalModule() && module != m) {
continue;
}
IFile file = CommonServices.getFileSystem().getIFile(resourceURL);
String resourceName = m.pathRelativeToRoot(file);
if (resourceName != null) {
return normalizeSchemaNamespace( getGosuNamespaceWithoutNormalization( resourceURL, m ), resourceName );
}
}
throw new IllegalArgumentException("Cannot find resource " + resourceURL);
}
public static String getGosuNamespaceWithoutNormalization( URL resourceURL, IModule module ) {
if (isLocal(resourceURL)) {
IFileSystemGosuClassRepository fileRepository = module.getFileRepository();
String path = fileRepository.getResourceName(resourceURL);
if ( ! Character.isLetter( path.charAt( 0 ) ) ) {
// logic below relies on this... It's changed over time to starting with ./, /, etc
throw new IllegalStateException( "Expected " + path + " to start with a letter" );
}
return path.replace( '.', '_' ).replace( '/', '.' );
}
return null;
}
public String makeUniqueTypeName( String packageName, String originalTypeName ) {
originalTypeName = packageName + '.' + normalizeName( originalTypeName, NormalizationMode.PROPERCASE );
String typeName = originalTypeName;
int suffix = 2;
while ( _allTypeNames.contains( typeName ) || _typeLoader.hasNamespace( typeName ) ) {
typeName = originalTypeName + suffix++;
}
_allTypeNames.add( typeName );
// FIXME-isd: too hacky?
// Need to record newly created type since there will be no refresh on them!
getTypeLoader().createdType( typeName );
return typeName;
}
public static String normalizeName( String name, NormalizationMode mode ) {
StringBuilder sb = new StringBuilder();
boolean firstChar = true;
for ( int i = 0; i < name.length(); i++ ) {
char ch = name.charAt( i );
if ( mode == NormalizationMode.LOWERCASE ) {
ch = Character.toLowerCase( ch );
}
else if ( mode == NormalizationMode.UPPERCASE ) {
ch = Character.toUpperCase( ch );
}
if ( firstChar ) {
if ( mode == NormalizationMode.PROPERCASE ) {
ch = Character.toUpperCase( ch );
}
boolean ignore = false;
if ( ! Character.isJavaIdentifierStart( ch ) ) {
if ( Character.isJavaIdentifierPart( ch ) ) {
// valid part, but not valid start - prepend underscore
sb.append( '_' );
}
else {
// not valid in any form - ignore
ignore = true;
}
}
if ( ! ignore ) {
sb.append( ch );
firstChar = false;
}
}
else {
if ( Character.isJavaIdentifierPart( ch ) ) {
sb.append( ch );
}
else {
sb.append( '_' );
}
}
}
if ( firstChar ) {
sb.append( "_" ); // no valid characters in name - ouch
}
return sb.toString();
}
public XmlSchemaSource getXSDSource() {
return _xmlSchemaSource;
}
public T getContext() {
return _context;
}
public String getPackageName() {
return _packageName;
}
public String getGosuNamespaceByWsdlNamespace( String wsdlNamespace ) {
return getGosuNamespaceByWsdlNamespace( wsdlNamespace, new HashSet<String>() );
}
private String getGosuNamespaceByWsdlNamespace( String xmlNamespace, Set<String> checkedGosuNamespaces ) {
String gosuNamespace = _gosuNamespacesByWsdlNamespace.get().get( xmlNamespace );
if ( gosuNamespace == null ) {
for ( String childGosuNamespace : _gosuNamespacesByWsdlNamespace.get().values() ) {
if ( checkedGosuNamespaces.add( childGosuNamespace ) ) {
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForGosuNamespace( childGosuNamespace );
gosuNamespace = schemaIndex.getGosuNamespaceByWsdlNamespace( xmlNamespace, checkedGosuNamespaces );
if ( gosuNamespace != null ) {
break;
}
}
}
}
return gosuNamespace;
}
public Set<String> getGosuNamespacesByXMLNamespace( String xmlNamespace ) {
List<Set<String>> results = new ArrayList<Set<String>>();
getGosuNamespacesByXMLNamespace( xmlNamespace, new HashSet<String>(), results );
if ( results.size() == 0 ) {
return Collections.emptySet();
}
else if ( results.size() == 1 ) {
return results.get( 0 );
}
else {
LinkedHashSet<String> allGosuNamespaces = new LinkedHashSet<String>();
for ( Set<String> result : results ) {
allGosuNamespaces.addAll( result );
}
return allGosuNamespaces;
}
}
private void getGosuNamespacesByXMLNamespace( String xmlNamespace, Set<String> checkedGosuNamespaces, List<Set<String>> results ) {
checkedGosuNamespaces.add( _packageName );
LinkedHashMap<String, LinkedHashSet<String>> gosuNamespacesByXmlNamespace = _gosuNamespacesByXMLNamespace.get();
Set<String> gosuNamespaces = gosuNamespacesByXmlNamespace.get( xmlNamespace );
if ( gosuNamespaces != null ) {
results.add( gosuNamespaces );
}
for ( Set<String> childGosuNamespaces : gosuNamespacesByXmlNamespace.values() ) {
for ( String childGosuNamespace : childGosuNamespaces ) {
if ( checkedGosuNamespaces.add( childGosuNamespace ) ) {
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForGosuNamespace( childGosuNamespace );
schemaIndex.getGosuNamespacesByXMLNamespace( xmlNamespace, checkedGosuNamespaces, results );
}
}
}
}
public Set<String> getAllTypeNames( Map<Pair<URL, String>, XmlSchema> caches ) {
maybeIndex( caches );
return Collections.unmodifiableSet( _allTypeNames );
}
public IXmlTypeData getTypeData( String fullyQualifiedTypeName ) {
maybeIndex( null );
IXmlTypeData typeData = _typesByName.get( fullyQualifiedTypeName );
if ( typeData == null ) {
typeData = getAdditionalTypeData( fullyQualifiedTypeName );
}
return typeData;
}
public XmlSchemaIndex<?> getSchemaIndexForWsdlNamespace( String namespaceURI ) {
String gosuNamespace = getGosuNamespaceByWsdlNamespace( namespaceURI );
if ( gosuNamespace == null ) {
throw new NotFoundException( "Wsdl {" + namespaceURI + "} not available in wsdl " + getPackageName() );
}
return getSchemaIndexForGosuNamespace( gosuNamespace );
}
private XmlSchemaIndex<?> getSchemaIndexForGosuNamespace( String gosuNamespace ) {
XmlSchemaIndex<?> schemaForNamespace = XmlSchemaResourceTypeLoaderBase.findSchemaForNamespace( getTypeLoader().getModule(), gosuNamespace );
if ( schemaForNamespace == null ) {
throw new NotFoundException( "Schema not found for Gosu namespace " + gosuNamespace + " in schema " + _packageName );
}
return schemaForNamespace;
}
public Set<XmlSchemaIndex<?>> getSchemaIndexesForXmlNamespace( String namespaceURI ) {
Set<String> gosuNamespaces = getGosuNamespacesByXMLNamespace( namespaceURI );
Set<XmlSchemaIndex<?>> ret = Collections.emptySet();
if ( gosuNamespaces != null ) {
for ( String gosuNamespace : gosuNamespaces ) {
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForGosuNamespace( gosuNamespace );
if ( schemaIndex == null ) {
throw new IllegalStateException( "Expected schema index to be found for gosu namespace: " + gosuNamespace );
}
if ( ret.isEmpty() ) {
ret = Collections.<XmlSchemaIndex<?>>singleton( schemaIndex );
}
else {
if ( ret.size() == 1 ) {
ret = new LinkedHashSet<XmlSchemaIndex<?>>( ret );
}
ret.add( schemaIndex );
}
}
}
return ret;
}
public static IType getGosuTypeBySchemaObject( XmlSchemaObject schemaObject ) {
XmlSchemaIndex<?> schemaIndex = schemaObject.getSchemaIndex();
schemaIndex.maybeIndex( null );
if ( schemaObject instanceof XmlSchemaAny ) {
return TypeSystem.get( XmlElement.class );
}
IXmlSchemaTypeData typeData = schemaIndex._typesBySchemaObject.get( schemaObject );
IType type = typeData.getType();
if ( type == null ) {
throw new NotFoundException( "Type not found for schema object: " + schemaObject );
}
return type;
}
public static IXmlSchemaTypeData getGosuTypeDataBySchemaObject( XmlSchemaObject schemaObject ) {
schemaObject.getSchemaIndex().maybeIndex( null );
return getGosuTypeDataBySchemaObjectWithoutIndexingSchema( schemaObject );
}
public static IXmlSchemaTypeData getGosuTypeDataBySchemaObjectWithoutIndexingSchema( XmlSchemaObject schemaObject ) {
return (IXmlSchemaTypeData)schemaObject.getSchemaIndex()._typesBySchemaObject.get( schemaObject );
}
public static XmlSchemaElement getActualElement( XmlSchemaElement ref ) {
if ( ref.getRefName() == null ) {
return ref; // not a reference
}
return ref.getSchemaIndex().getXmlSchemaElementByQName( ref.getRefName() );
}
public static XmlSchemaAttribute getActualAttribute( XmlSchemaAttribute ref ) {
if ( ref.getRefName() == null ) {
return ref; // not a reference
}
return ref.getSchemaIndex().getXmlSchemaAttributeByQName( ref.getRefName() );
}
public XmlSchemaGroup getXmlSchemaGroupByQName( QName qname ) {
maybeIndex( null );
Set<XmlSchemaIndex<?>> schemaIndexes = getSchemaIndexesForXmlNamespace( qname.getNamespaceURI() );
for ( XmlSchemaIndex<?> schemaIndex : schemaIndexes ) {
for ( XmlSchema schema : schemaIndex.getXmlSchemaCollection().getXmlSchemas()) {
if ( schema.getTargetNamespace().equals( qname.getNamespaceURI() ) ) {
XmlSchemaGroup group = schema.getGroups().get( qname );
if ( group != null ) {
return group;
}
}
}
}
throw throwNotFoundException( "Group", qname );
}
public XmlSchemaAttributeGroup getXmlSchemaAttributeGroupByQName( QName qname ) {
maybeIndex( null );
Set<XmlSchemaIndex<?>> schemaIndexes = getSchemaIndexesForXmlNamespace( qname.getNamespaceURI() );
for ( XmlSchemaIndex<?> schemaIndex : schemaIndexes ) {
for (XmlSchema schema : schemaIndex.getXmlSchemaCollection().getXmlSchemas()) {
if ( schema.getTargetNamespace().equals( qname.getNamespaceURI() ) ) {
XmlSchemaAttributeGroup group = schema.getAttributeGroups().get( qname );
if ( group != null ) {
return group;
}
}
}
}
throw throwNotFoundException( "AttributeGroup", qname );
}
public XmlSchemaResourceTypeLoaderBase<T> getTypeLoader() {
return _typeLoader;
}
public void putTypeDataByName( String typeName, IXmlSchemaTypeData typeData ) {
IXmlSchemaTypeData oldTypeData = _typesByName.put( typeName, typeData );
if ( oldTypeData != null ) {
throw new IllegalArgumentException( "Type name already registered: " + typeName );
}
}
public void putTypeDataBySchemaObject( XmlSchemaObject xsdObject, IXmlSchemaTypeData typeData ) {
IXmlSchemaTypeData oldTypeData = _typesBySchemaObject.put( xsdObject, typeData );
if ( oldTypeData != null ) {
throw new IllegalArgumentException( "Schema object already registered. Old: " + oldTypeData.getName() + ", New: " + typeData.getName() );
}
}
public XmlSchemaElement getXmlSchemaElementByQNameIfValid( QName schemaElementName ) {
maybeIndex( null );
Set<XmlSchemaIndex<?>> schemaIndexes = getSchemaIndexesForXmlNamespace( schemaElementName.getNamespaceURI() );
for ( XmlSchemaIndex<?> schemaIndex : schemaIndexes ) {
XmlSchemaElement element = schemaIndex.getXmlSchemaCollection().getElementByQNameIfValid( schemaElementName );
if ( element != null ) {
return element;
}
}
return null;
}
public XmlSchemaElement getXmlSchemaElementByQName( QName schemaElementName ) {
XmlSchemaElement element = getXmlSchemaElementByQNameIfValid( schemaElementName );
if ( element != null ) {
return element;
}
throw throwNotFoundException( "Element", schemaElementName );
}
public XmlSchemaAttribute getXmlSchemaAttributeByQNameIfValid( QName schemaAttributeName ) {
maybeIndex( null );
Set<XmlSchemaIndex<?>> schemaIndexes = getSchemaIndexesForXmlNamespace( schemaAttributeName.getNamespaceURI() );
for ( XmlSchemaIndex<?> schemaIndex : schemaIndexes ) {
XmlSchemaAttribute attribute = schemaIndex.getXmlSchemaCollection().getAttributeByQNameIfValid( schemaAttributeName );
if ( attribute != null ) {
return attribute;
}
}
return null;
}
public XmlSchemaAttribute getXmlSchemaAttributeByQName( QName schemaAttributeName ) {
XmlSchemaAttribute attribute = getXmlSchemaAttributeByQNameIfValid( schemaAttributeName );
if ( attribute != null ) {
return attribute;
}
throw throwNotFoundException( "Attribute", schemaAttributeName );
}
public WsdlBinding getWsdlBindingByQName( QName bindingName ) {
maybeIndex( null );
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForWsdlNamespace( bindingName.getNamespaceURI() );
WsdlBinding binding = schemaIndex.getWsdlDefinitions().getBindingByQName(bindingName);
if ( binding == null ) {
throw throwNotFoundException( "Binding", bindingName );
}
return binding;
}
public WsdlMessage getWsdlMessageByQName( QName messageQName ) {
maybeIndex( null );
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForWsdlNamespace( messageQName.getNamespaceURI() );
WsdlMessage message = schemaIndex.getWsdlDefinitions().getMessageByQName( messageQName );
if ( message == null ) {
throw throwNotFoundException( "Message", messageQName );
}
return message;
}
public WsdlPortType getWsdlPortTypeByQName( QName portTypeName ) {
maybeIndex( null );
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForWsdlNamespace( portTypeName.getNamespaceURI() );
WsdlPortType portType = schemaIndex.getWsdlDefinitions().getPortTypeByQName(portTypeName);
if ( portType == null ) {
throw throwNotFoundException( "PortType", portTypeName );
}
return portType;
}
public XmlSchemaType getXmlSchemaTypeByQName( QName schemaTypeName ) {
XmlSchemaType type = getXmlSchemaTypeByQNameIfValid( schemaTypeName );
if ( type != null ) {
return type;
}
throw throwNotFoundException( "Type", schemaTypeName );
}
public XmlSchemaType getXmlSchemaTypeByQNameIfValid( QName schemaTypeName ) {
maybeIndex( null );
if ( isBuiltInDatatype( schemaTypeName ) ) {
XmlSchemaIndex<?> schemaIndex = getSchemaIndexForGosuNamespace( "gw.xsd.w3c.xmlschema" );
return schemaIndex.getXmlSchemaCollection().getTypeByQName( schemaTypeName );
}
else {
Set<XmlSchemaIndex<?>> schemaIndexes = getSchemaIndexesForXmlNamespace( schemaTypeName.getNamespaceURI() );
for ( XmlSchemaIndex<?> schemaIndex : schemaIndexes ) {
XmlSchemaType type = schemaIndex.getXmlSchemaCollection().getTypeByQNameIfValid( schemaTypeName );
if ( type != null ) {
return type;
}
}
return null;
}
}
public static boolean isBuiltInDatatype( QName schemaTypeName ) {
return schemaTypeName.getNamespaceURI().equals( XMLConstants.W3C_XML_SCHEMA_NS_URI ) && BUILT_IN_DATATYPES.contains(schemaTypeName.getLocalPart());
}
public static boolean isBuiltInDatatype( IType type ) {
XmlSchemaTypeSchemaInfo schemaInfo = getSchemaInfoByType( type );
if ( schemaInfo == null ) {
return false;
}
QName qname = schemaInfo.getXsdType().getQName();
return qname != null && isBuiltInDatatype( qname );
}
public XmlSchemaSimpleType getXmlSchemaSimpleTypeByQName( QName schemaTypeName ) {
if ( schemaTypeName == null ) {
schemaTypeName = ANY_SIMPLE_TYPE_QNAME; // the default
}
XmlSchemaType xsdType = getXmlSchemaTypeByQName( schemaTypeName );
if ( ! ( xsdType instanceof XmlSchemaSimpleType ) ) {
throw new NotFoundException( "Expected simple type, but found " + xsdType );
}
return (XmlSchemaSimpleType) xsdType;
}
public XmlSchemaComplexType getXmlSchemaComplexTypeByQName( QName schemaTypeName ) {
if ( schemaTypeName == null ) {
return null;
}
XmlSchemaType xsdType = getXmlSchemaTypeByQName( schemaTypeName );
if ( ! ( xsdType instanceof XmlSchemaComplexType ) ) {
throw new NotFoundException( "Expected complex type, but found " + xsdType );
}
return (XmlSchemaComplexType) xsdType;
}
public List<XmlSchemaFlattenedChild> getFlattenedChildrenBySchemaType( XmlSchemaType xsdType ) {
maybeIndex( null );
return _flattenedChildrenBySchemaType.get(xsdType);
}
public boolean getChildPluralityBySchemaType( XmlSchemaType xsdType, XmlSchemaPropertyType propertyType, QName childName ) {
maybeIndex( null );
return _childPluralityBySchemaType.get( xsdType ).get( new Pair<XmlSchemaPropertyType, QName>( propertyType, childName ) );
}
public void registerFlattenedChildrenBySchemaType( XmlSchemaType xsdType, List<XmlSchemaFlattenedChild> flattenedChildren ) {
_flattenedChildrenBySchemaType.put( xsdType, flattenedChildren );
}
public void registerChildrenPluralityBySchemaType( XmlSchemaType xsdType, Map<Pair<XmlSchemaPropertyType, QName>, Boolean> pluralityMap ) {
_childPluralityBySchemaType.put( xsdType, pluralityMap );
}
private static XmlSimpleValueFactory getSimpleValueFactoryForSimpleType( XmlSchemaSimpleType simpleType ) {
if ( simpleType.getGwTypeName() != null ) {
return XmlSchemaTypeToGosuTypeMappings.gosuToSchema( TypeSystem.getByFullName( simpleType.getGwTypeName() ) ).getSecond();
}
XmlSchemaEnumerationTypeData enumType = simpleType.getSchemaIndex().getEnumerationForSchemaType( simpleType );
if ( enumType != null ) {
return new XmlSchemaEnumSimpleValueFactory( enumType );
}
XmlSimpleValueFactory factory = XmlSchemaTypeToGosuTypeMappings.schemaToGosu( simpleType.getQName() );
if ( factory == null ) {
if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeUnion) {
factory = getSimpleValueFactoryForSimpleTypeUnion( simpleType.getSchemaIndex(), (XmlSchemaSimpleTypeUnion) simpleType.getContent() );
}
else if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeList ) {
factory = getSimpleValueFactoryForSimpleTypeList( simpleType.getSchemaIndex(), (XmlSchemaSimpleTypeList) simpleType.getContent() );
}
else {
XmlSchemaSimpleType baseType = getSimpleTypeBase( simpleType );
factory = getSimpleValueFactoryForSimpleType( baseType );
}
}
return factory;
}
private XmlSchemaEnumerationTypeData getEnumerationForSchemaType( XmlSchemaType simpleType ) {
maybeIndex( null );
return _enumerations.get( simpleType );
}
private static XmlSchemaSimpleType getSimpleTypeBase( final XmlSchemaSimpleType simpleType ) {
XmlSchemaSimpleType baseType = null;
if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeRestriction) {
XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) simpleType.getContent();
baseType = restriction.getBaseType();
if ( baseType == null ) {
baseType = simpleType.getSchemaIndex().getXmlSchemaSimpleTypeByQName( restriction.getBaseTypeName() );
}
}
if ( baseType == null && ! ANY_SIMPLE_TYPE_QNAME.equals( simpleType.getQName() ) ) {
baseType = simpleType.getSchemaIndex().getXmlSchemaSimpleTypeByQName( ANY_SIMPLE_TYPE_QNAME );
}
return baseType;
}
private static XmlSimpleValueFactory getSimpleValueFactoryForSimpleTypeList( XmlSchemaIndex<?> schemaIndex, XmlSchemaSimpleTypeList simpleTypeList ) {
XmlSchemaSimpleType itemType;
if ( simpleTypeList.getItemTypeName() != null ) {
itemType = schemaIndex.getXmlSchemaSimpleTypeByQName( simpleTypeList.getItemTypeName() );
}
else {
itemType = simpleTypeList.getItemType();
}
return new XmlSchemaListSimpleValueFactory( getSimpleValueFactoryForSimpleType( itemType ), getSimpleValueValidatorForType( itemType ) );
}
private static XmlSimpleValueFactory getSimpleValueFactoryForSimpleTypeUnion( XmlSchemaIndex<?> schemaIndex, XmlSchemaSimpleTypeUnion xmlSchemaSimpleTypeUnion ) {
List<LinkedList<XmlSimpleValueFactory>> hierarchies = new ArrayList<LinkedList<XmlSimpleValueFactory>>();
if ( xmlSchemaSimpleTypeUnion.getMemberTypesQNames() != null ) {
for ( QName simpleTypeName : xmlSchemaSimpleTypeUnion.getMemberTypesQNames() ) {
LinkedList<XmlSimpleValueFactory> hierarchy = getSimpleTypeHierarchy( schemaIndex.getXmlSchemaSimpleTypeByQName( simpleTypeName ) );
hierarchies.add(hierarchy);
}
}
for ( XmlSchemaSimpleType xmlSchemaSimpleType : xmlSchemaSimpleTypeUnion.getBaseTypes() ) {
hierarchies.add( getSimpleTypeHierarchy( xmlSchemaSimpleType ) );
}
Iterator<LinkedList<XmlSimpleValueFactory>> iter = hierarchies.iterator();
LinkedList<XmlSimpleValueFactory> remainingHierarchy = iter.next();
while ( iter.hasNext() ) {
List<XmlSimpleValueFactory> nextHierarchy = iter.next();
XmlSimpleValueFactory commonWrapper = null;
for ( XmlSimpleValueFactory wrapper : nextHierarchy ) {
if ( remainingHierarchy.contains( wrapper ) ) {
commonWrapper = wrapper;
break;
}
}
Iterator<XmlSimpleValueFactory> it = remainingHierarchy.iterator();
while ( it.hasNext() ) {
XmlSimpleValueFactory wrapper = it.next();
if ( wrapper.equals( commonWrapper ) ) {
break;
}
it.remove();
}
}
return remainingHierarchy.getFirst();
}
private static LinkedList<XmlSimpleValueFactory> getSimpleTypeHierarchy( XmlSchemaSimpleType xmlSchemaSimpleType ) {
LinkedList<XmlSimpleValueFactory> hierarchy = new LinkedList<XmlSimpleValueFactory>();
while ( xmlSchemaSimpleType != null ) {
XmlSimpleValueFactory typeWrapper = getSimpleValueFactoryForSimpleType( xmlSchemaSimpleType );
hierarchy.add( typeWrapper );
xmlSchemaSimpleType = getSimpleTypeBase( xmlSchemaSimpleType );
}
return hierarchy;
}
public static XmlSchemaType getSchemaTypeForElement( XmlSchemaElement xsdElement ) {
xsdElement.getSchemaIndex().maybeIndex( null );
xsdElement = getActualElement(xsdElement);
XmlSchemaType schemaTypeSpec;
QName schemaTypeName = xsdElement.getSchemaTypeName();
if ( schemaTypeName != null ) {
schemaTypeSpec = xsdElement.getSchemaIndex().getXmlSchemaTypeByQName(schemaTypeName);
}
else if ( xsdElement.getSchemaType() != null ){
schemaTypeSpec = xsdElement.getSchemaType();
}
else if ( xsdElement.getSubstitutionGroup() != null ) {
// pick up type from substitution group head by default
return getSchemaTypeForElement( xsdElement.getSchemaIndex().getXmlSchemaElementByQName( xsdElement.getSubstitutionGroup() ) );
}
else {
schemaTypeName = ANY_TYPE_QNAME;
schemaTypeSpec = xsdElement.getSchemaIndex().getXmlSchemaTypeByQName( schemaTypeName );
}
return schemaTypeSpec;
}
public static XmlSchemaType getSchemaTypeForAttribute( XmlSchemaAttribute xsdAttribute ) {
xsdAttribute.getSchemaIndex().maybeIndex( null );
xsdAttribute = getActualAttribute( xsdAttribute );
XmlSchemaType schemaTypeSpec;
if ( xsdAttribute.getSchemaType() != null ) {
schemaTypeSpec = xsdAttribute.getSchemaType();
}
else {
QName schemaTypeName = xsdAttribute.getSchemaTypeName();
if ( schemaTypeName == null ) {
schemaTypeName = ANY_SIMPLE_TYPE_QNAME;
}
schemaTypeSpec = xsdAttribute.getSchemaIndex().getXmlSchemaTypeByQName( schemaTypeName );
}
return schemaTypeSpec;
}
public static URL makeLocalIfValid( URL baseURL, String systemId, String namespaceURI, IModule module ) {
return makeLocalIfValid( baseURL, systemId, namespaceURI, module, null );
}
public static URL makeLocalIfValid( URL baseURL, String systemId, String namespaceURI, IModule module, Map<String, String> externalLocationAliases ) {
if ( systemId == null && namespaceURI != null ) {
return getUrlForNamespace( namespaceURI, module );
}
else if ( baseURL == null ) {
return null;
}
URL url;
try {
URL targetURL = new URL(baseURL, systemId);
if ( !isLocal(targetURL) ) {
url = getUrlForExternalLocation( systemId, module );
if ( url != null && externalLocationAliases != null ) {
externalLocationAliases.put( systemId, url.toExternalForm() );
}
}
else {
IFileSystemGosuClassRepository fileRepository = module.getFileRepository();
String resourceName = fileRepository.getResourceName(targetURL);
IFile foundFile = findFile(module, resourceName);
if ( foundFile == null ) {
throw new XmlException( "Unable to resolve resource with location '" + systemId + "' relative to '" + baseURL + "' with namespace '" + namespaceURI + "'" );
}
url = foundFile.toURI().toURL();
}
}
catch ( MalformedURLException ex ) {
throw new RuntimeException( "Unable to resolve resource " + systemId + " relative to " + baseURL, ex );
}
return url;
}
private static IFile findFile(IModule module, String resourceName) {
for (IModule m : module.getModuleTraversalList()) {
IFile firstFile = m.getFileRepository().findFirstFile(resourceName);
if (firstFile != null) {
return firstFile;
}
}
return null;
}
private static URL getUrlForNamespace( String namespaceURI, IModule module ) {
XmlSchemaDefaultSchemaLocations locations = getDefaultSchemaLocations( module );
IFile iFile = locations.getLocalResourcePathByXmlNamespace().get( namespaceURI );
if ( iFile == null ) {
return null;
}
try {
return iFile.toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException( e );
}
}
public static URL getUrlForExternalLocation( String externalLocation, IModule module ) {
XmlSchemaDefaultSchemaLocations locations = getDefaultSchemaLocations( module );
IFile iFile = locations.getLocalResourcePathsByExternalLocation().get( externalLocation );
if ( iFile == null ) {
throw new RuntimeException( "External location " + externalLocation + " is not configured in schemalocations.xml." );
}
try {
return iFile.toURI().toURL();
} catch (MalformedURLException e) {
throw new RuntimeException( e );
}
}
private static XmlSchemaDefaultSchemaLocations getDefaultSchemaLocations( IModule module ) {
TypeSystem.lock();
try {
XmlSchemaDefaultSchemaLocations defaultSchemaLocations = _defaultSchemaLocations.get( module );
if ( defaultSchemaLocations == null ) {
defaultSchemaLocations = new XmlSchemaDefaultSchemaLocations();
for ( IFile file : getSchemaLocationsFiles(module)) {
try {
// We can't use Schemalocations.parse() here, since that would cause a lookup of types, yet this
// gets called while resolving types
XmlElement root = XmlElement.parse( file.openInputStream() );
QName rootQName = Schemalocations.$QNAME;
if ( ! root.getQName().equals( rootQName ) ) {
throw new RuntimeException( file + " has wrong root element. Expected: " + rootQName + ", Actual: " + root.getQName() );
}
for ( XmlElement child : root.getChildren( Schemalocations_Schema.$QNAME ) ) {
String xmlNamespace = child.getAttributeValue( Schemalocations_Schema.$ATTRIBUTE_QNAME_XmlNamespace );
String resourcePath = child.getAttributeValue( Schemalocations_Schema.$ATTRIBUTE_QNAME_ResourcePath );
String externalLocations = child.getAttributeValue( Schemalocations_Schema.$ATTRIBUTE_QNAME_ExternalLocations );
String[] parts = externalLocations.split( " " );
IFile resource = findFile(module, resourcePath);
for ( String part : parts ) {
defaultSchemaLocations.getLocalResourcePathsByExternalLocation().put( part, resource );
}
defaultSchemaLocations.getLocalResourcePathByXmlNamespace().put( xmlNamespace, resource );
defaultSchemaLocations.getExternalLocationsByLocalResourcePath().put( resourcePath, Arrays.asList(externalLocations.split(" ")) );
}
} catch (IOException ex) {
throw new RuntimeException(ex);
}
}
_defaultSchemaLocations.put( module, defaultSchemaLocations );
}
return defaultSchemaLocations;
}
finally {
TypeSystem.unlock();
}
}
private static Iterable<? extends IFile> getSchemaLocationsFiles(IModule module) {
if (CommonServices.getPlatformHelper().isInIDE()) {
module = TypeSystem.getGlobalModule();
}
Set<IFile> result = new LinkedHashSet<IFile>();
result.addAll(findAllFiles("config/xml/schemalocations.xml", module.getRoots()));
// XML module itself now has this file inside "sources path"
result.addAll(findAllFiles("xml/schemalocations.xml", module.getSourcePath()));
return result;
}
private static List<? extends IFile> findAllFiles(String resourceName, List<? extends IDirectory> searchPath) {
List<IFile> results = new ArrayList<IFile>();
for (IDirectory dir : searchPath) {
IFile file = dir.file(resourceName);
if (file != null && file.exists()) {
results.add(file);
}
}
return results;
}
public static boolean isLocal( URL url ) {
return url.getProtocol().equals("file") || url.getProtocol().equals("jar");
}
public IType getGosuTypeByXmlSchemaElementQName( QName qname ) {
XmlSchemaElement element = getXmlSchemaElementByQName( qname );
if ( element == null ) {
throw new NotFoundException( "Element not found: " + qname );
}
return XmlSchemaIndex.getGosuTypeBySchemaObject( element );
}
public XmlSchemaTypeSchemaInfo getElementInfoByXmlSchemaElementQName( QName qname ) {
// first find the actual non-typeref element type for this QName
IType actualElementRef = getGosuTypeByXmlSchemaElementQName( qname );
XmlSchemaIndex<?> actualElementSchemaIndex = XmlSchemaIndex.getSchemaIndexByType( actualElementRef );
XmlSchemaElementTypeData<?> actualElementTypeData = (XmlSchemaElementTypeData) actualElementSchemaIndex._typesByName.get( actualElementRef.getName() );
// then find the actual non-typeref typeinstance type for that element
IType actualTypeTypeRef = XmlSchemaIndex.getGosuTypeBySchemaObject( actualElementTypeData.getXsdType() );
XmlSchemaIndex<?> actualTypeSchemaIndex = XmlSchemaIndex.getSchemaIndexByType( actualTypeTypeRef );
XmlSchemaTypeInstanceTypeData actualTypeType = (XmlSchemaTypeInstanceTypeData) actualTypeSchemaIndex._typesByName.get( actualTypeTypeRef.getName() );
return actualTypeType.getSchemaInfo();
}
public XmlSchemaTypeSchemaInfo getSchemaInfoByTypeName( String typeName ) {
IXmlSchemaTypeData actualType = _typesByName.get( typeName );
return actualType instanceof XmlSchemaTypeInstanceTypeData ? ( (XmlSchemaTypeInstanceTypeData) actualType ).getSchemaInfo() : null;
}
// TODO - this duplicates another method in this class, getTypeData() - remove?
public IXmlSchemaTypeData getActualTypeByName( String name ) {
return _typesByName.get( name );
}
public static void clearNormalizedSchemaNamespaces() {
_normalizedSchemaNamespaces.clear();
}
public static String normalizeSchemaNamespace( String ns, String path ) {
TypeSystem.lock();
try {
String normalizedSchemaNamespace = _normalizedSchemaNamespacesByPath.get( path );
if ( normalizedSchemaNamespace == null ) {
normalizedSchemaNamespace = normalizeSchemaNamespace( ns );
if ( _normalizedSchemaNamespaces.contains( normalizedSchemaNamespace ) ) {
throw new XmlException( "Multiple schemas found with package name " + normalizedSchemaNamespace );
}
_normalizedSchemaNamespacesByPath.put( path, normalizedSchemaNamespace );
_normalizedSchemaNamespaces.add( normalizedSchemaNamespace );
}
return normalizedSchemaNamespace;
}
finally {
TypeSystem.unlock();
}
}
public static String normalizeSchemaNamespace( String ns ) {
String normalizedSchemaNamespace;
StringBuilder ret = new StringBuilder( ns.length() );
String[] parts = ns.split( "\\." );
for ( String part : parts ) {
part = XmlSchemaIndex.normalizeName( part, NormalizationMode.LOWERCASE );
if ( ret.length() > 0 ) {
ret.append( '.' );
}
ret.append( part );
}
normalizedSchemaNamespace = ret.toString();
return normalizedSchemaNamespace;
}
private static XmlSimpleValueValidator getSimpleValueValidatorForSimpleContent( XmlSchemaSimpleContent simpleContent, XmlSchemaComplexType complexType ) {
XmlSimpleValueValidator validator;
final XmlSchemaContent content = simpleContent.getContent();
if ( content instanceof XmlSchemaSimpleContentRestriction ) {
XmlSchemaSimpleContentRestriction restriction = (XmlSchemaSimpleContentRestriction) content;
List<XmlSchemaFacet> facets = new ArrayList<XmlSchemaFacet>();
for ( XmlSchemaFacet xmlSchemaFacet : restriction.getFacets() ) {
facets.add( xmlSchemaFacet );
}
XmlSchemaType baseTypeSpec = simpleContent.getSchemaIndex().getXmlSchemaTypeByQName( restriction.getBaseTypeName() );
XmlSimpleValueValidator parentValidator = getSimpleValueValidatorForType( baseTypeSpec );
XmlSimpleValueFactory valueFactory = getSimpleValueFactoryForSimpleContent( complexType );
validator = new XmlSimpleTypeSimpleValueValidator( null, parentValidator, facets, valueFactory );
}
else if ( content instanceof XmlSchemaSimpleContentExtension ) {
// simple content extension can only add attributes
XmlSchemaSimpleContentExtension extension = (XmlSchemaSimpleContentExtension) content;
XmlSchemaType baseTypeSpec = simpleContent.getSchemaIndex().getXmlSchemaTypeByQName( extension.getBaseTypeName() );
return getSimpleValueValidatorForSchemaType( baseTypeSpec );
}
else {
throw new RuntimeException( "Unknown simple content content: " + content.getClass() );
}
return validator;
}
private static XmlSimpleValueValidator getSimpleValueValidatorForType( XmlSchemaType type ) {
XmlSimpleValueValidator validator = null;
if ( type instanceof XmlSchemaSimpleType ) {
XmlSchemaSimpleType simpleType = (XmlSchemaSimpleType) type;
if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeRestriction ) {
XmlSchemaSimpleTypeRestriction restriction = (XmlSchemaSimpleTypeRestriction) simpleType.getContent();
List<XmlSchemaFacet> facets = new ArrayList<XmlSchemaFacet>();
for ( XmlSchemaFacet facet : restriction.getFacets() ) {
facets.add( facet );
}
XmlSchemaSimpleType baseTypeSpec = getSimpleTypeBase( simpleType );
XmlSimpleValueValidator parentValidator = getSimpleValueValidatorForType( baseTypeSpec );
XmlSimpleValueFactory valueFactory = getSimpleValueFactoryForSimpleType( simpleType );
validator = new XmlSimpleTypeSimpleValueValidator( XmlSchemaPrimitiveType.get( simpleType.getQName() ), parentValidator, facets, valueFactory );
}
else if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeUnion ) {
List<XmlSimpleValueValidator> validators = new ArrayList<XmlSimpleValueValidator>();
XmlSchemaSimpleTypeUnion xmlSchemaSimpleTypeUnion = (XmlSchemaSimpleTypeUnion) simpleType.getContent();
if ( xmlSchemaSimpleTypeUnion.getMemberTypesQNames() != null ) {
for ( QName simpleTypeName : xmlSchemaSimpleTypeUnion.getMemberTypesQNames() ) {
XmlSchemaSimpleType baseType = simpleType.getSchemaIndex().getXmlSchemaSimpleTypeByQName( simpleTypeName );
validators.add( getSimpleValueValidatorForType( baseType ) );
}
}
for ( XmlSchemaSimpleType baseType : xmlSchemaSimpleTypeUnion.getBaseTypes() ) {
validators.add( getSimpleValueValidatorForType( baseType ) );
}
validator = new XmlSimpleUnionValueValidator( validators );
}
else if ( simpleType.getContent() instanceof XmlSchemaSimpleTypeList ) {
XmlSchemaSimpleTypeList simpleTypeList = (XmlSchemaSimpleTypeList) simpleType.getContent();
XmlSchemaSimpleType itemType;
if ( simpleTypeList.getItemTypeName() != null ) {
itemType = simpleType.getSchemaIndex().getXmlSchemaSimpleTypeByQName( simpleTypeList.getItemTypeName() );
}
else {
itemType = simpleTypeList.getItemType();
}
validator = new XmlSimpleListValueValidator( getSimpleValueValidatorForType( itemType ) );
}
}
else {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) type;
XmlSchemaSimpleContent content = (XmlSchemaSimpleContent) complexType.getContentModel();
XmlSimpleValueFactory valueFactory = getSimpleValueFactoryForSimpleContent( complexType );
if ( content.getContent() instanceof XmlSchemaSimpleContentRestriction ) {
XmlSchemaSimpleContentRestriction restriction = (XmlSchemaSimpleContentRestriction) content.getContent();
List<XmlSchemaFacet> facets = new ArrayList<XmlSchemaFacet>();
for ( XmlSchemaFacet facet : restriction.getFacets() ) {
facets.add( facet );
}
XmlSchemaType baseType = complexType.getSchemaIndex().getXmlSchemaTypeByQName( restriction.getBaseTypeName() );
XmlSimpleValueValidator parentValidator = getSimpleValueValidatorForType( baseType );
validator = new XmlSimpleTypeSimpleValueValidator( XmlSchemaPrimitiveType.get( complexType.getQName() ), parentValidator, facets, valueFactory );
}
else {
XmlSchemaSimpleContentExtension extension = (XmlSchemaSimpleContentExtension) content.getContent();
XmlSchemaType baseType = complexType.getSchemaIndex().getXmlSchemaTypeByQName( extension.getBaseTypeName() );
XmlSimpleValueValidator parentValidator = getSimpleValueValidatorForType( baseType );
validator = new XmlSimpleTypeSimpleValueValidator( XmlSchemaPrimitiveType.get( complexType.getQName() ), parentValidator, Collections.<XmlSchemaFacet>emptyList(), valueFactory );
}
}
if ( validator == null ) {
validator = new XmlSimpleNoopValueValidator();
}
return validator;
}
private static XmlSimpleValueFactory getSimpleValueFactoryForSimpleContent( XmlSchemaComplexType complexType ) {
XmlSchemaEnumerationTypeData enumTypeData = complexType.getSchemaIndex().getEnumerationForSchemaType( complexType );
if ( enumTypeData != null ) {
return new XmlSchemaEnumSimpleValueFactory( enumTypeData );
}
final QName baseTypeName;
final XmlSchemaContent content = complexType.getContentModel().getContent();
if ( content instanceof XmlSchemaSimpleContentRestriction ) {
baseTypeName = ( (XmlSchemaSimpleContentRestriction) content ).getBaseTypeName();
}
else if ( content instanceof XmlSchemaSimpleContentExtension ) {
baseTypeName = ( (XmlSchemaSimpleContentExtension) content ).getBaseTypeName();
}
else {
throw new RuntimeException( "Unknown simple content content: " + content );
}
XmlSchemaType baseType = complexType.getSchemaIndex().getXmlSchemaTypeByQName( baseTypeName );
return getSimpleValueFactoryForSchemaType( baseType );
}
public static boolean hasSimpleContent( XmlSchemaObject xsdTypeOrElement ) {
boolean hasSimpleContent = false;
if ( xsdTypeOrElement instanceof XmlSchemaElement ) {
xsdTypeOrElement = getSchemaTypeForElement( (XmlSchemaElement) xsdTypeOrElement );
}
if ( xsdTypeOrElement instanceof XmlSchemaSimpleType ) {
hasSimpleContent = true;
}
else {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) xsdTypeOrElement;
if ( complexType.getContentModel() instanceof XmlSchemaSimpleContent ) {
hasSimpleContent = true;
}
}
return hasSimpleContent;
}
public static XmlSimpleValueFactory getSimpleValueFactoryForSchemaType( XmlSchemaType schemaType ) {
XmlSimpleValueFactory factory = null;
if ( schemaType instanceof XmlSchemaSimpleType ) {
factory = getSimpleValueFactoryForSimpleType( (XmlSchemaSimpleType) schemaType );
}
else {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) schemaType;
if ( complexType.getContentModel() instanceof XmlSchemaSimpleContent ) {
factory = getSimpleValueFactoryForSimpleContent( complexType );
}
}
return factory;
}
public static XmlSimpleValueValidator getSimpleValueValidatorForSchemaType( XmlSchemaType schemaType ) {
XmlSimpleValueValidator factory = null;
if ( schemaType instanceof XmlSchemaSimpleType ) {
factory = getSimpleValueValidatorForType( schemaType );
}
else {
XmlSchemaComplexType complexType = (XmlSchemaComplexType) schemaType;
if ( complexType.getContentModel() instanceof XmlSchemaSimpleContent ) {
factory = getSimpleValueValidatorForSimpleContent( ( XmlSchemaSimpleContent ) complexType.getContentModel(), complexType );
}
}
return factory;
}
public void registerEnumeration( XmlSchemaType xmlSchemaObject, XmlSchemaEnumerationTypeData enumTypeData ) {
_enumerations.put( xmlSchemaObject, enumTypeData );
}
public static XmlSchemaIndex<?> getSchemaIndexByType( IType type ) {
for ( XmlSchemaResourceTypeLoaderBase typeLoader : type.getTypeLoader().getModule().getTypeLoaders( XmlSchemaResourceTypeLoaderBase.class ) ) {
XmlSchemaIndex<?> schemaIndex = typeLoader.getSchemaForType( type.getName() );
if ( schemaIndex != null ) {
return schemaIndex;
}
}
return null;
}
public static XmlSchemaTypeSchemaInfo getSchemaInfoByType( IType type ) {
XmlSchemaIndex<?> schemaIndex = getSchemaIndexByType( type );
return schemaIndex == null ? null : schemaIndex.getSchemaInfoByTypeName( type.getName() );
}
public Map<String, LinkedHashSet<String>> getGosuNamespacesByXmlNamespace() {
return Collections.unmodifiableMap(_gosuNamespacesByXMLNamespace.get());
}
@Override
public String toString() {
return _packageName;
}
public XmlSchemaAccessImpl getXmlSchemaAccess() {
maybeIndex( null );
return _xmlSchemaAccess.get();
}
public static String makeCamelCase( String name, NormalizationMode mode ) {
if ( mode == NormalizationMode.PRESERVECASE ) {
return name;
}
if ( name == null ) {
return null;
}
StringBuilder sb = new StringBuilder();
boolean makeNextCharUppercase = true;
for ( int i = 0; i < name.length(); i++ ) {
char ch = name.charAt( i );
if ( makeNextCharUppercase ) {
ch = Character.toUpperCase( ch );
makeNextCharUppercase = false;
}
if ( ch == '_' || ! Character.isJavaIdentifierPart( ch ) ) {
if ( i + 1 < name.length() ) {
char nextChar = name.charAt( i + 1 );
if ( Character.toUpperCase( nextChar ) != nextChar ) {
makeNextCharUppercase = true;
continue;
}
}
}
sb.append( ch );
}
if ( sb.length() == 0 ) {
sb.append( "_" );
}
return sb.toString();
}
public Set<String> getAllCodegenSchemas() {
TypeSystem.lock();
try {
Set<String> schemas = new HashSet<String>();
for ( IDirectory root : getTypeLoader().getModule().getSourcePath() ) {
schemas.addAll( getCodegenSchemasForDir(root) );
}
return schemas;
}
finally {
TypeSystem.unlock();
}
}
public static Set<String> getCodegenSchemasForDir(IDirectory dir) {
TypeSystem.lock();
try {
Set<String> schemas = _codegenSchemasByModuleRoot.get( dir );
if ( schemas == null ) {
schemas = new HashSet<String>();
IFile configFile = dir.file( "gw/internal/xml/config/xsd-codegen.xml" );
if ( configFile == null || !configFile.exists() ) {
return Collections.emptySet();
}
XmlElement config;
try {
config = XmlElement.parse( configFile.openInputStream() );
}
catch ( IOException ex ) {
throw new RuntimeException( ex );
}
List<XmlElement> schemaElements = config.getChildren( new QName( "http://guidewire.com/config/xml/xsd-codegen", "schema" ) );
for ( XmlElement schemaElement : schemaElements ) {
schemas.add( schemaElement.getAttributeValue( "namespace" ) );
}
_codegenSchemasByModuleRoot.put( dir, schemas );
}
return schemas;
}
finally {
TypeSystem.unlock();
}
}
public long getFingerprint() {
return _schemaFP64.get();
}
public IJavaClassInfo getGeneratedClass( String name ) {
if ( ! _usesJavaBackedTypes ) {
return null;
}
String className = "gw.internal.schema." + name;
IJavaClassInfo clazz = TypeSystem.getJavaClassInfo(className, getTypeLoader().getModule());
if (clazz == null) {
throw new RuntimeException( "Class " + className + " does not exist. Please generate it using xsd-codegen." );
}
if (!(clazz instanceof IAsmJavaClassInfo) && clazz.getBackingClass() != null) {
try {
Field field = clazz.getBackingClass().getDeclaredField( "FINGERPRINT" );
field.setAccessible( true );
long fingerprint = (Long) field.get( null );
if ( fingerprint != getFingerprint() ) {
throw new RuntimeException( "Class " + clazz.getName() + " is out of date. Please regenerate it using xsd-codegen." );
}
}
catch ( NoSuchFieldException ex ) {
throw new RuntimeException( ex );
}
catch ( IllegalAccessException ex ) {
throw new RuntimeException( ex );
}
}
return clazz;
}
public String getTargetNamespaceAsDeclaredInSchema() {
if ( getXmlSchemaCollection().getXmlSchemas().size() != 1 ) {
throw new UnsupportedOperationException( "Only supported for single-schema indexes, such as those based on an XSD" );
}
return getXmlSchemaCollection().getXmlSchemas().iterator().next().getDeclaredTargetNamespace();
}
public String getXSDSourcePath() {
IFileSystemGosuClassRepository fileRepository = getTypeLoader().getModule().getFileRepository();
String resourceName = fileRepository.getResourceName( _xmlSchemaSource.getBlueprintURL() );
if ( resourceName.startsWith( "/" ) ) {
resourceName = resourceName.substring( 1 );
}
return resourceName;
}
public WsdlDefinitions getWsdlDefinitions() {
maybeIndex( null );
return _wsdlDefinition;
}
public void validate( Map<Pair<URL, String>, XmlSchema> caches ) {
try {
if ( getWsdlDefinitions() == null || System.getProperty( "gw.internal.xml.do_not_validate_wsdl" ) == null ) {
parseSchemasButDoNotCache( Collections.<XmlSchemaIndex>singletonList( this ), null, null );
}
}
catch ( Exception ex ) {
if ( getXSDSource().getBlueprintURL().getProtocol().equals( "jar" ) || getTypeLoader().getClass().getName().equals( "com.guidewire.commons.system.gx.GXTypeLoader" ) ) {
clearExternalJarXSD();
}
else {
StringBuilder sb = new StringBuilder();
sb.append( "Could not parse schema " );
sb.append( _xmlSchemaSource.getDescription() );
throw new XmlException( sb.toString(), ex );
}
}
}
public void removeTypeName( String typeName ) {
_allTypeNames.remove( typeName );
}
public XmlSchemaImportInfo getImportInfo() {
return new XmlSchemaImportInfo( getTargetNamespaceAsDeclaredInSchema(), getXSDSourcePath(), false );
}
public enum NormalizationMode {
PROPERCASE, PRESERVECASE, UPPERCASE, LOWERCASE
}
public XmlSchemaCollection getXmlSchemaCollection() {
maybeIndex( null );
return _collection;
}
private void addSchemasToNamespaceMappings( String myGosuNamespace, LinkedHashSet<XmlSchema> schemas, LinkedHashMap<String, LinkedHashSet<String>> gosuNamespacesByXMLNamespace ) {
Set<String> myNamespaces = new HashSet<String>();
if ( getWsdlDefinitions() != null ) {
// intra-wsdl import support
for ( XmlSchema schema : schemas ) {
myNamespaces.add( schema.getTargetNamespace() );
}
}
for ( XmlSchema schema : schemas ) {
// if schema does not define any top level components, don't add it -- this supports WSDL's empty-xsd-with-only-imports style
if ( ! schema.isEmpty() ) {
String targetNamespace = schema.getTargetNamespace();
addGosuNamespaceByXmlNamespace( gosuNamespacesByXMLNamespace, targetNamespace, myGosuNamespace );
}
for ( XmlSchemaImport imprt : schema.getImports() ) {
if ( myNamespaces.contains( imprt.getNamespaceURI() ) ) {
// intra-wsdl import support
addGosuNamespaceByXmlNamespace( gosuNamespacesByXMLNamespace, imprt.getNamespaceURI(), myGosuNamespace );
}
else {
String targetNamespace = imprt.getNamespaceURI();
String schemaLocation = imprt.getSchemaLocation();
URL url = makeLocal( imprt.getBaseUrl(), schemaLocation, targetNamespace, getTypeLoader().getModule() );
if ( url != null ) {
String targetGosuNamespace = getGosuNamespace( url, getTypeLoader().getModule() );
if ( targetGosuNamespace == null ) {
throw new RuntimeException( "Gosu namespace not found for imported schema " + url );
}
addGosuNamespaceByXmlNamespace( gosuNamespacesByXMLNamespace, targetNamespace, targetGosuNamespace );
}
}
}
}
}
private void addGosuNamespaceByXmlNamespace( LinkedHashMap<String, LinkedHashSet<String>> gosuNamespacesByXMLNamespace, String xmlNamespace, String gosuNamespace ) {
LinkedHashSet<String> nsList = gosuNamespacesByXMLNamespace.get( xmlNamespace );
if ( nsList == null ) {
nsList = new LinkedHashSet<String>();
gosuNamespacesByXMLNamespace.put( xmlNamespace, nsList );
}
nsList.add ( gosuNamespace );
}
/**
* This method will return null only if schemaLocation is null, and targetNamespace is not registered
* in schemalocations.xml.
*/
public static URL makeLocal( URL schemaEF, String schemaLocation, String targetNamespace, IModule module ) {
return makeLocal( schemaEF, schemaLocation, targetNamespace, module, null );
}
/**
* This method will return null only if schemaLocation is null, and targetNamespace is not registered
* in schemalocations.xml.
*/
public static URL makeLocal( URL schemaEF, String schemaLocation, String targetNamespace, IModule module, Map<String, String> externalLocationAliases ) {
URL url = makeLocalIfValid( schemaEF, schemaLocation, targetNamespace, module, externalLocationAliases );
if ( url == null && schemaLocation != null ) {
throw new RuntimeException( "Unable to map imported schema " + schemaLocation + " with targetNamespace " + targetNamespace + " relative to " + schemaEF + " to a local resource. This can be fixed by adding it to schemalocations.xml." );
}
return url;
}
public static XmlSchemaIndex getSchemaIndexForFilePath( String testPath ) {
XmlSchemaIndex source = null;
OUTER:
for ( XmlSchemaResourceTypeLoaderBase<?> typeLoader : TypeSystem.getGlobalModule().getTypeLoaders( XmlSchemaResourceTypeLoaderBase.class ) ) {
final Collection<String> namespaces = typeLoader.getAllSchemaNamespaces();
for (String ns : namespaces) {
final XmlSchemaIndex si = typeLoader.getSchemaForNamespace(ns);
if (si.getXSDSourcePath().equals(testPath)) {
source = si;
break OUTER;
}
}
}
return source;
}
public IXmlTypeData getAdditionalTypeData( String fullyQualifiedName ) {
return null;
}
public Set<String> getAdditionalTypeNames() {
return Collections.emptySet();
}
// used by tests written in Gosu
@SuppressWarnings( { "UnusedDeclaration" } )
public String getExternalLocationForTesting() {
int pos = _packageName.lastIndexOf('.');
String path = _packageName.substring(0, pos).replace('.','/') + "/" + _xmlSchemaSource.getResourceFile().getName();
XmlSchemaDefaultSchemaLocations locations = getDefaultSchemaLocations( getTypeLoader().getModule() );
final List<String> stringList = locations.getExternalLocationsByLocalResourcePath().get(path);
if (stringList != null && stringList.size() > 0) {
return stringList.get(0);
}
return null;
}
public static String makeUniquePropertyName( Set<String> usedPropertyNames, String propertyName, XmlSchemaIndex.NormalizationMode normalizationMode ) {
propertyName = XmlSchemaIndex.normalizeName( propertyName, normalizationMode );
if ( usedPropertyNames.contains( propertyName ) ) {
int suffix = 2;
String newPropertyName = propertyName;
while ( usedPropertyNames.contains( newPropertyName ) ) {
newPropertyName = propertyName + suffix++;
}
propertyName = newPropertyName;
}
usedPropertyNames.add( propertyName );
return propertyName;
}
private NotFoundException throwNotFoundException( String type, QName name ) {
throw new NotFoundException( type + " " + name + " not found in schema " + _packageName );
}
public URL getSchemaURL() {
return _schemaEF;
}
}