diff options
Diffstat (limited to 'src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java')
-rw-r--r-- | src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java | 1615 |
1 files changed, 0 insertions, 1615 deletions
diff --git a/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java b/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java deleted file mode 100644 index a9cb5bed..00000000 --- a/src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java +++ /dev/null @@ -1,1615 +0,0 @@ -/* - * Copyright (c) 1997, 2016, Oracle and/or its affiliates. All rights reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. Oracle designates this - * particular file as subject to the "Classpath" exception as provided - * by Oracle in the LICENSE file that accompanied this code. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA - * or visit www.oracle.com if you need additional information or have any - * questions. - */ - -package com.sun.xml.internal.bind.v2.schemagen; - -import java.io.IOException; -import java.io.OutputStream; -import java.io.Writer; -import java.io.File; -import java.net.URI; -import java.net.URISyntaxException; -import java.util.Comparator; -import java.util.HashMap; -import java.util.LinkedHashSet; -import java.util.Map; -import java.util.Set; -import java.util.TreeMap; -import java.util.ArrayList; -import java.util.logging.Level; -import java.util.logging.Logger; - -import javax.activation.MimeType; -import javax.xml.bind.SchemaOutputResolver; -import javax.xml.bind.annotation.XmlElement; -import javax.xml.namespace.QName; -import javax.xml.transform.Result; -import javax.xml.transform.stream.StreamResult; - -import com.sun.istack.internal.Nullable; -import com.sun.istack.internal.NotNull; -import com.sun.xml.internal.bind.Util; -import com.sun.xml.internal.bind.api.CompositeStructure; -import com.sun.xml.internal.bind.api.ErrorListener; -import com.sun.xml.internal.bind.v2.TODO; -import com.sun.xml.internal.bind.v2.WellKnownNamespace; -import com.sun.xml.internal.bind.v2.util.CollisionCheckStack; -import com.sun.xml.internal.bind.v2.util.StackRecorder; -import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_SCHEMA; -import com.sun.xml.internal.bind.v2.model.core.Adapter; -import com.sun.xml.internal.bind.v2.model.core.ArrayInfo; -import com.sun.xml.internal.bind.v2.model.core.AttributePropertyInfo; -import com.sun.xml.internal.bind.v2.model.core.ClassInfo; -import com.sun.xml.internal.bind.v2.model.core.Element; -import com.sun.xml.internal.bind.v2.model.core.ElementInfo; -import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo; -import com.sun.xml.internal.bind.v2.model.core.EnumConstant; -import com.sun.xml.internal.bind.v2.model.core.EnumLeafInfo; -import com.sun.xml.internal.bind.v2.model.core.MapPropertyInfo; -import com.sun.xml.internal.bind.v2.model.core.MaybeElement; -import com.sun.xml.internal.bind.v2.model.core.NonElement; -import com.sun.xml.internal.bind.v2.model.core.NonElementRef; -import com.sun.xml.internal.bind.v2.model.core.PropertyInfo; -import com.sun.xml.internal.bind.v2.model.core.ReferencePropertyInfo; -import com.sun.xml.internal.bind.v2.model.core.TypeInfo; -import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet; -import com.sun.xml.internal.bind.v2.model.core.TypeRef; -import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo; -import com.sun.xml.internal.bind.v2.model.core.WildcardMode; -import com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl; -import com.sun.xml.internal.bind.v2.model.nav.Navigator; -import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter; -import static com.sun.xml.internal.bind.v2.schemagen.Util.*; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Any; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttrDecls; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexExtension; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexType; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexTypeHost; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ExplicitGroup; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Import; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalAttribute; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalElement; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Schema; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleExtension; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleRestrictionModel; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleType; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleTypeHost; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelAttribute; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelElement; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeHost; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ContentModelContainer; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeDefParticle; -import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttributeType; -import com.sun.xml.internal.bind.v2.schemagen.episode.Bindings; -import com.sun.xml.internal.txw2.TXW; -import com.sun.xml.internal.txw2.TxwException; -import com.sun.xml.internal.txw2.TypedXmlWriter; -import com.sun.xml.internal.txw2.output.ResultFactory; -import com.sun.xml.internal.txw2.output.XmlSerializer; -import java.util.Collection; -import java.util.HashSet; -import org.xml.sax.SAXParseException; - -/** - * Generates a set of W3C XML Schema documents from a set of Java classes. - * - * <p> - * A client must invoke methods in the following order: - * <ol> - * <li>Create a new {@link XmlSchemaGenerator} - * <li>Invoke {@link #add} methods, multiple times if necessary. - * <li>Invoke {@link #write} - * <li>Discard the {@link XmlSchemaGenerator}. - * </ol> - * - * @author Ryan Shoemaker - * @author Kohsuke Kawaguchi (kk@kohsuke.org) - */ -public final class XmlSchemaGenerator<T,C,F,M> { - - private static final Logger logger = Util.getClassLogger(); - - /** - * Java classes to be written, organized by their namespace. - * - * <p> - * We use a {@link TreeMap} here so that the suggested names will - * be consistent across JVMs. - * - * @see SchemaOutputResolver#createOutput(String, String) - */ - private final Map<String,Namespace> namespaces = new TreeMap<String,Namespace>(NAMESPACE_COMPARATOR); - - /** - * {@link ErrorListener} to send errors to. - */ - private ErrorListener errorListener; - - /** model navigator **/ - private Navigator<T,C,F,M> navigator; - - private final TypeInfoSet<T,C,F,M> types; - - /** - * Representation for xs:string. - */ - private final NonElement<T,C> stringType; - - /** - * Represents xs:anyType. - */ - private final NonElement<T,C> anyType; - - /** - * Used to detect cycles in anonymous types. - */ - private final CollisionCheckStack<ClassInfo<T,C>> collisionChecker = new CollisionCheckStack<ClassInfo<T,C>>(); - - public XmlSchemaGenerator( Navigator<T,C,F,M> navigator, TypeInfoSet<T,C,F,M> types ) { - this.navigator = navigator; - this.types = types; - - this.stringType = types.getTypeInfo(navigator.ref(String.class)); - this.anyType = types.getAnyTypeInfo(); - - // populate the object - for( ClassInfo<T,C> ci : types.beans().values() ) - add(ci); - for( ElementInfo<T,C> ei1 : types.getElementMappings(null).values() ) - add(ei1); - for( EnumLeafInfo<T,C> ei : types.enums().values() ) - add(ei); - for( ArrayInfo<T,C> a : types.arrays().values()) - add(a); - } - - private Namespace getNamespace(String uri) { - Namespace n = namespaces.get(uri); - if(n==null) - namespaces.put(uri,n=new Namespace(uri)); - return n; - } - - /** - * Adds a new class to the list of classes to be written. - * - * <p> - * A {@link ClassInfo} may have two namespaces --- one for the element name - * and the other for the type name. If they are different, we put the same - * {@link ClassInfo} to two {@link Namespace}s. - */ - public void add( ClassInfo<T,C> clazz ) { - assert clazz!=null; - - String nsUri = null; - - if(clazz.getClazz()==navigator.asDecl(CompositeStructure.class)) - return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema - - if(clazz.isElement()) { - // put element -> type reference - nsUri = clazz.getElementName().getNamespaceURI(); - Namespace ns = getNamespace(nsUri); - ns.classes.add(clazz); - ns.addDependencyTo(clazz.getTypeName()); - - // schedule writing this global element - add(clazz.getElementName(),false,clazz); - } - - QName tn = clazz.getTypeName(); - if(tn!=null) { - nsUri = tn.getNamespaceURI(); - } else { - // anonymous type - if(nsUri==null) - return; - } - - Namespace n = getNamespace(nsUri); - n.classes.add(clazz); - - // search properties for foreign namespace references - for( PropertyInfo<T,C> p : clazz.getProperties()) { - n.processForeignNamespaces(p, 1); - if (p instanceof AttributePropertyInfo) { - AttributePropertyInfo<T,C> ap = (AttributePropertyInfo<T,C>) p; - String aUri = ap.getXmlName().getNamespaceURI(); - if(aUri.length()>0) { - // global attribute - getNamespace(aUri).addGlobalAttribute(ap); - n.addDependencyTo(ap.getXmlName()); - } - } - if (p instanceof ElementPropertyInfo) { - ElementPropertyInfo<T,C> ep = (ElementPropertyInfo<T,C>) p; - for (TypeRef<T,C> tref : ep.getTypes()) { - String eUri = tref.getTagName().getNamespaceURI(); - if(eUri.length()>0 && !eUri.equals(n.uri)) { - getNamespace(eUri).addGlobalElement(tref); - n.addDependencyTo(tref.getTagName()); - } - } - } - - if(generateSwaRefAdapter(p)) - n.useSwaRef = true; - - MimeType mimeType = p.getExpectedMimeType(); - if( mimeType != null ) { - n.useMimeNs = true; - } - - } - - // recurse on baseTypes to make sure that we can refer to them in the schema - ClassInfo<T,C> bc = clazz.getBaseClass(); - if (bc != null) { - add(bc); - n.addDependencyTo(bc.getTypeName()); - } - } - - /** - * Adds a new element to the list of elements to be written. - */ - public void add( ElementInfo<T,C> elem ) { - assert elem!=null; - - @SuppressWarnings("UnusedAssignment") - boolean nillable = false; // default value - - QName name = elem.getElementName(); - Namespace n = getNamespace(name.getNamespaceURI()); - ElementInfo ei; - - if (elem.getScope() != null) { // (probably) never happens - ei = this.types.getElementInfo(elem.getScope().getClazz(), name); - } else { - ei = this.types.getElementInfo(null, name); - } - - XmlElement xmlElem = ei.getProperty().readAnnotation(XmlElement.class); - - if (xmlElem == null) { - nillable = false; - } else { - nillable = xmlElem.nillable(); - } - - n.elementDecls.put(name.getLocalPart(),n.new ElementWithType(nillable, elem.getContentType())); - - // search for foreign namespace references - n.processForeignNamespaces(elem.getProperty(), 1); - } - - public void add( EnumLeafInfo<T,C> envm ) { - assert envm!=null; - - String nsUri = null; - - if(envm.isElement()) { - // put element -> type reference - nsUri = envm.getElementName().getNamespaceURI(); - Namespace ns = getNamespace(nsUri); - ns.enums.add(envm); - ns.addDependencyTo(envm.getTypeName()); - - // schedule writing this global element - add(envm.getElementName(),false,envm); - } - - final QName typeName = envm.getTypeName(); - if (typeName != null) { - nsUri = typeName.getNamespaceURI(); - } else { - if(nsUri==null) - return; // anonymous type - } - - Namespace n = getNamespace(nsUri); - n.enums.add(envm); - - // search for foreign namespace references - n.addDependencyTo(envm.getBaseType().getTypeName()); - } - - public void add( ArrayInfo<T,C> a ) { - assert a!=null; - - final String namespaceURI = a.getTypeName().getNamespaceURI(); - Namespace n = getNamespace(namespaceURI); - n.arrays.add(a); - - // search for foreign namespace references - n.addDependencyTo(a.getItemType().getTypeName()); - } - - /** - * Adds an additional element declaration. - * - * @param tagName - * The name of the element declaration to be added. - * @param type - * The type this element refers to. - * Can be null, in which case the element refers to an empty anonymous complex type. - */ - public void add( QName tagName, boolean isNillable, NonElement<T,C> type ) { - - if(type!=null && type.getType()==navigator.ref(CompositeStructure.class)) - return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema - - - Namespace n = getNamespace(tagName.getNamespaceURI()); - n.elementDecls.put(tagName.getLocalPart(), n.new ElementWithType(isNillable,type)); - - // search for foreign namespace references - if(type!=null) - n.addDependencyTo(type.getTypeName()); - } - - /** - * Writes out the episode file. - */ - public void writeEpisodeFile(XmlSerializer out) { - Bindings root = TXW.create(Bindings.class, out); - - if(namespaces.containsKey("")) // otherwise jaxb binding NS should be the default namespace - root._namespace(WellKnownNamespace.JAXB,"jaxb"); - root.version("2.1"); - // TODO: don't we want to bake in versions? - - // generate listing per schema - for (Map.Entry<String,Namespace> e : namespaces.entrySet()) { - Bindings group = root.bindings(); - - String prefix; - String tns = e.getKey(); - if(!tns.equals("")) { - group._namespace(tns,"tns"); - prefix = "tns:"; - } else { - prefix = ""; - } - - group.scd("x-schema::"+(tns.equals("")?"":"tns")); - group.schemaBindings().map(false); - - for (ClassInfo<T,C> ci : e.getValue().classes) { - if(ci.getTypeName()==null) continue; // local type - - if(ci.getTypeName().getNamespaceURI().equals(tns)) { - Bindings child = group.bindings(); - child.scd('~'+prefix+ci.getTypeName().getLocalPart()); - child.klass().ref(ci.getName()); - } - - if(ci.isElement() && ci.getElementName().getNamespaceURI().equals(tns)) { - Bindings child = group.bindings(); - child.scd(prefix+ci.getElementName().getLocalPart()); - child.klass().ref(ci.getName()); - } - } - - for (EnumLeafInfo<T,C> en : e.getValue().enums) { - if(en.getTypeName()==null) continue; // local type - - Bindings child = group.bindings(); - child.scd('~'+prefix+en.getTypeName().getLocalPart()); - child.klass().ref(navigator.getClassName(en.getClazz())); - } - - group.commit(true); - } - - root.commit(); - } - - /** - * Write out the schema documents. - */ - public void write(SchemaOutputResolver resolver, ErrorListener errorListener) throws IOException { - if(resolver==null) - throw new IllegalArgumentException(); - - if(logger.isLoggable(Level.FINE)) { - // debug logging to see what's going on. - logger.log(Level.FINE,"Writing XML Schema for "+toString(),new StackRecorder()); - } - - // make it fool-proof - resolver = new FoolProofResolver(resolver); - this.errorListener = errorListener; - - Map<String, String> schemaLocations = types.getSchemaLocations(); - - Map<Namespace,Result> out = new HashMap<Namespace,Result>(); - Map<Namespace,String> systemIds = new HashMap<Namespace,String>(); - - // we create a Namespace object for the XML Schema namespace - // as a side-effect, but we don't want to generate it. - namespaces.remove(WellKnownNamespace.XML_SCHEMA); - - // first create the outputs for all so that we can resolve references among - // schema files when we write - for( Namespace n : namespaces.values() ) { - String schemaLocation = schemaLocations.get(n.uri); - if(schemaLocation!=null) { - systemIds.put(n,schemaLocation); - } else { - Result output = resolver.createOutput(n.uri,"schema"+(out.size()+1)+".xsd"); - if(output!=null) { // null result means no schema for that namespace - out.put(n,output); - systemIds.put(n,output.getSystemId()); - } - } - //Clear the namespace specific set with already written classes - n.resetWritten(); - } - - // then write'em all - for( Map.Entry<Namespace,Result> e : out.entrySet() ) { - Result result = e.getValue(); - e.getKey().writeTo( result, systemIds ); - if(result instanceof StreamResult) { - OutputStream outputStream = ((StreamResult)result).getOutputStream(); - if(outputStream != null) { - outputStream.close(); // fix for bugid: 6291301 - } else { - final Writer writer = ((StreamResult)result).getWriter(); - if(writer != null) writer.close(); - } - } - } - } - - - - /** - * Schema components are organized per namespace. - */ - private class Namespace { - final @NotNull String uri; - - /** - * Other {@link Namespace}s that this namespace depends on. - */ - private final Set<Namespace> depends = new LinkedHashSet<Namespace>(); - - /** - * If this schema refers to components from this schema by itself. - */ - private boolean selfReference; - - /** - * List of classes in this namespace. - */ - private final Set<ClassInfo<T,C>> classes = new LinkedHashSet<ClassInfo<T,C>>(); - - /** - * Set of enums in this namespace - */ - private final Set<EnumLeafInfo<T,C>> enums = new LinkedHashSet<EnumLeafInfo<T,C>>(); - - /** - * Set of arrays in this namespace - */ - private final Set<ArrayInfo<T,C>> arrays = new LinkedHashSet<ArrayInfo<T,C>>(); - - /** - * Global attribute declarations keyed by their local names. - */ - private final MultiMap<String,AttributePropertyInfo<T,C>> attributeDecls = new MultiMap<String,AttributePropertyInfo<T,C>>(null); - - /** - * Global element declarations to be written, keyed by their local names. - */ - private final MultiMap<String,ElementDeclaration> elementDecls = - new MultiMap<String,ElementDeclaration>(new ElementWithType(true,anyType)); - - private Form attributeFormDefault; - private Form elementFormDefault; - - /** - * Does schema in this namespace uses swaRef? If so, we need to generate import - * statement. - */ - private boolean useSwaRef; - - /** - * Import for mime namespace needs to be generated. - * See #856 - */ - private boolean useMimeNs; - - /** - * Container for already processed classes - */ - private final Set<ClassInfo> written = new HashSet<ClassInfo>(); - - public Namespace(String uri) { - this.uri = uri; - assert !XmlSchemaGenerator.this.namespaces.containsKey(uri); - XmlSchemaGenerator.this.namespaces.put(uri,this); - } - - /** - * Clear out the set of already processed classes for this namespace - */ - void resetWritten() { - written.clear(); - } - - /** - * Process the given PropertyInfo looking for references to namespaces that - * are foreign to the given namespace. Any foreign namespace references - * found are added to the given namespaces dependency list and an <import> - * is generated for it. - * - * @param p the PropertyInfo - */ - private void processForeignNamespaces(PropertyInfo<T, C> p, int processingDepth) { - for (TypeInfo<T, C> t : p.ref()) { - if ((t instanceof ClassInfo) && (processingDepth > 0)) { - java.util.List<PropertyInfo> l = ((ClassInfo) t).getProperties(); - for (PropertyInfo subp : l) { - processForeignNamespaces(subp, --processingDepth); - } - } - if (t instanceof Element) { - addDependencyTo(((Element) t).getElementName()); - } - if (t instanceof NonElement) { - addDependencyTo(((NonElement) t).getTypeName()); - } - } - } - - private void addDependencyTo(@Nullable QName qname) { - // even though the Element interface says getElementName() returns non-null, - // ClassInfo always implements Element (even if an instance of ClassInfo might not be an Element). - // so this check is still necessary - if (qname==null) { - return; - } - - String nsUri = qname.getNamespaceURI(); - - if (nsUri.equals(XML_SCHEMA)) { - // no need to explicitly refer to XSD namespace - return; - } - - if (nsUri.equals(uri)) { - selfReference = true; - return; - } - - // found a type in a foreign namespace, so make sure we generate an import for it - depends.add(getNamespace(nsUri)); - } - - /** - * Writes the schema document to the specified result. - * - * @param systemIds - * System IDs of the other schema documents. "" indicates 'implied'. - */ - private void writeTo(Result result, Map<Namespace,String> systemIds) throws IOException { - try { - Schema schema = TXW.create(Schema.class,ResultFactory.createSerializer(result)); - - // additional namespace declarations to be made. - Map<String, String> xmlNs = types.getXmlNs(uri); - - for (Map.Entry<String, String> e : xmlNs.entrySet()) { - schema._namespace(e.getValue(),e.getKey()); - } - - if(useSwaRef) - schema._namespace(WellKnownNamespace.SWA_URI,"swaRef"); - - if(useMimeNs) - schema._namespace(WellKnownNamespace.XML_MIME_URI,"xmime"); - - attributeFormDefault = Form.get(types.getAttributeFormDefault(uri)); - attributeFormDefault.declare("attributeFormDefault",schema); - - elementFormDefault = Form.get(types.getElementFormDefault(uri)); - // TODO: if elementFormDefault is UNSET, figure out the right default value to use - elementFormDefault.declare("elementFormDefault",schema); - - - // declare XML Schema namespace to be xs, but allow the user to override it. - // if 'xs' is used for other things, we'll just let TXW assign a random prefix - if(!xmlNs.containsValue(WellKnownNamespace.XML_SCHEMA) - && !xmlNs.containsKey("xs")) - schema._namespace(WellKnownNamespace.XML_SCHEMA,"xs"); - schema.version("1.0"); - - if(uri.length()!=0) - schema.targetNamespace(uri); - - // declare prefixes for them at this level, so that we can avoid redundant - // namespace declarations - for (Namespace ns : depends) { - schema._namespace(ns.uri); - } - - if(selfReference && uri.length()!=0) { - // use common 'tns' prefix for the own namespace - // if self-reference is needed - schema._namespace(uri,"tns"); - } - - schema._pcdata(newline); - - // refer to other schemas - for( Namespace n : depends ) { - Import imp = schema._import(); - if(n.uri.length()!=0) - imp.namespace(n.uri); - String refSystemId = systemIds.get(n); - if(refSystemId!=null && !refSystemId.equals("")) { - // "" means implied. null if the SchemaOutputResolver said "don't generate!" - imp.schemaLocation(relativize(refSystemId,result.getSystemId())); - } - schema._pcdata(newline); - } - if(useSwaRef) { - schema._import().namespace(WellKnownNamespace.SWA_URI).schemaLocation("http://ws-i.org/profiles/basic/1.1/swaref.xsd"); - } - if(useMimeNs) { - schema._import().namespace(WellKnownNamespace.XML_MIME_URI).schemaLocation("http://www.w3.org/2005/05/xmlmime"); - } - - // then write each component - for (Map.Entry<String,ElementDeclaration> e : elementDecls.entrySet()) { - e.getValue().writeTo(e.getKey(),schema); - schema._pcdata(newline); - } - for (ClassInfo<T, C> c : classes) { - if (c.getTypeName()==null) { - // don't generate anything if it's an anonymous type - continue; - } - if(uri.equals(c.getTypeName().getNamespaceURI())) - writeClass(c, schema); - schema._pcdata(newline); - } - for (EnumLeafInfo<T, C> e : enums) { - if (e.getTypeName()==null) { - // don't generate anything if it's an anonymous type - continue; - } - if(uri.equals(e.getTypeName().getNamespaceURI())) - writeEnum(e,schema); - schema._pcdata(newline); - } - for (ArrayInfo<T, C> a : arrays) { - writeArray(a,schema); - schema._pcdata(newline); - } - for (Map.Entry<String,AttributePropertyInfo<T,C>> e : attributeDecls.entrySet()) { - TopLevelAttribute a = schema.attribute(); - a.name(e.getKey()); - if(e.getValue()==null) - writeTypeRef(a,stringType,"type"); - else - writeAttributeTypeRef(e.getValue(),a); - schema._pcdata(newline); - } - - // close the schema - schema.commit(); - } catch( TxwException e ) { - logger.log(Level.INFO,e.getMessage(),e); - throw new IOException(e.getMessage()); - } - } - - /** - * Writes a type attribute (if the referenced type is a global type) - * or writes out the definition of the anonymous type in place (if the referenced - * type is not a global type.) - * - * Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref - * - * ComplexTypeHost and SimpleTypeHost don't share an api for creating - * and attribute in a type-safe way, so we will compromise for now and - * use _attribute(). - */ - private void writeTypeRef(TypeHost th, NonElementRef<T, C> typeRef, String refAttName) { - // ID / IDREF handling - switch(typeRef.getSource().id()) { - case ID: - th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "ID")); - return; - case IDREF: - th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "IDREF")); - return; - case NONE: - // no ID/IDREF, so continue on and generate the type - break; - default: - throw new IllegalStateException(); - } - - // MTOM handling - MimeType mimeType = typeRef.getSource().getExpectedMimeType(); - if( mimeType != null ) { - th._attribute(new QName(WellKnownNamespace.XML_MIME_URI, "expectedContentTypes", "xmime"), mimeType.toString()); - } - - // ref:swaRef handling - if(generateSwaRefAdapter(typeRef)) { - th._attribute(refAttName, new QName(WellKnownNamespace.SWA_URI, "swaRef", "ref")); - return; - } - - // type name override - if(typeRef.getSource().getSchemaType()!=null) { - th._attribute(refAttName,typeRef.getSource().getSchemaType()); - return; - } - - // normal type generation - writeTypeRef(th, typeRef.getTarget(), refAttName); - } - - /** - * Writes a type attribute (if the referenced type is a global type) - * or writes out the definition of the anonymous type in place (if the referenced - * type is not a global type.) - * - * @param th - * the TXW interface to which the attribute will be written. - * @param type - * type to be referenced. - * @param refAttName - * The name of the attribute used when referencing a type by QName. - */ - private void writeTypeRef(TypeHost th, NonElement<T,C> type, String refAttName) { - Element e = null; - if (type instanceof MaybeElement) { - MaybeElement me = (MaybeElement)type; - boolean isElement = me.isElement(); - if (isElement) e = me.asElement(); - } - if (type instanceof Element) { - e = (Element)type; - } - if (type.getTypeName()==null) { - if ((e != null) && (e.getElementName() != null)) { - th.block(); // so that the caller may write other attributes - if(type instanceof ClassInfo) { - writeClass( (ClassInfo<T,C>)type, th ); - } else { - writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th); - } - } else { - // anonymous - th.block(); // so that the caller may write other attributes - if(type instanceof ClassInfo) { - if(collisionChecker.push((ClassInfo<T,C>)type)) { - errorListener.warning(new SAXParseException( - Messages.ANONYMOUS_TYPE_CYCLE.format(collisionChecker.getCycleString()), - null - )); - } else { - writeClass( (ClassInfo<T,C>)type, th ); - } - collisionChecker.pop(); - } else { - writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th); - } - } - } else { - th._attribute(refAttName,type.getTypeName()); - } - } - - /** - * writes the schema definition for the given array class - */ - private void writeArray(ArrayInfo<T, C> a, Schema schema) { - ComplexType ct = schema.complexType().name(a.getTypeName().getLocalPart()); - ct._final("#all"); - LocalElement le = ct.sequence().element().name("item"); - le.type(a.getItemType().getTypeName()); - le.minOccurs(0).maxOccurs("unbounded"); - le.nillable(true); - ct.commit(); - } - - /** - * writes the schema definition for the specified type-safe enum in the given TypeHost - */ - private void writeEnum(EnumLeafInfo<T, C> e, SimpleTypeHost th) { - SimpleType st = th.simpleType(); - writeName(e,st); - - SimpleRestrictionModel base = st.restriction(); - writeTypeRef(base, e.getBaseType(), "base"); - - for (EnumConstant c : e.getConstants()) { - base.enumeration().value(c.getLexicalValue()); - } - st.commit(); - } - - /** - * Writes the schema definition for the specified class to the schema writer. - * - * @param c the class info - * @param parent the writer of the parent element into which the type will be defined - */ - private void writeClass(ClassInfo<T,C> c, TypeHost parent) { - if (written.contains(c)) { // to avoid cycles let's check if we haven't already processed the class - return; - } - written.add(c); - // special handling for value properties - if (containsValueProp(c)) { - if (c.getProperties().size() == 1) { - // [RESULT 2 - simpleType if the value prop is the only prop] - // - // <simpleType name="foo"> - // <xs:restriction base="xs:int"/> - // </> - ValuePropertyInfo<T,C> vp = (ValuePropertyInfo<T,C>)c.getProperties().get(0); - SimpleType st = ((SimpleTypeHost)parent).simpleType(); - writeName(c, st); - if(vp.isCollection()) { - writeTypeRef(st.list(),vp.getTarget(),"itemType"); - } else { - writeTypeRef(st.restriction(),vp.getTarget(),"base"); - } - return; - } else { - // [RESULT 1 - complexType with simpleContent] - // - // <complexType name="foo"> - // <simpleContent> - // <extension base="xs:int"/> - // <attribute name="b" type="xs:boolean"/> - // </> - // </> - // </> - // ... - // <element name="f" type="foo"/> - // ... - ComplexType ct = ((ComplexTypeHost)parent).complexType(); - writeName(c,ct); - if(c.isFinal()) - ct._final("extension restriction"); - - SimpleExtension se = ct.simpleContent().extension(); - se.block(); // because we might have attribute before value - for (PropertyInfo<T,C> p : c.getProperties()) { - switch (p.kind()) { - case ATTRIBUTE: - handleAttributeProp((AttributePropertyInfo<T,C>)p,se); - break; - case VALUE: - TODO.checkSpec("what if vp.isCollection() == true?"); - ValuePropertyInfo vp = (ValuePropertyInfo) p; - se.base(vp.getTarget().getTypeName()); - break; - case ELEMENT: // error - case REFERENCE: // error - default: - assert false; - throw new IllegalStateException(); - } - } - se.commit(); - } - TODO.schemaGenerator("figure out what to do if bc != null"); - TODO.checkSpec("handle sec 8.9.5.2, bullet #4"); - // Java types containing value props can only contain properties of type - // ValuePropertyinfo and AttributePropertyInfo which have just been handled, - // so return. - return; - } - - // we didn't fall into the special case for value props, so we - // need to initialize the ct. - // generate the complexType - ComplexType ct = ((ComplexTypeHost)parent).complexType(); - writeName(c,ct); - if(c.isFinal()) - ct._final("extension restriction"); - if(c.isAbstract()) - ct._abstract(true); - - // these are where we write content model and attributes - AttrDecls contentModel = ct; - TypeDefParticle contentModelOwner = ct; - - // if there is a base class, we need to generate an extension in the schema - final ClassInfo<T,C> bc = c.getBaseClass(); - if (bc != null) { - if(bc.hasValueProperty()) { - // extending complex type with simple content - SimpleExtension se = ct.simpleContent().extension(); - contentModel = se; - contentModelOwner = null; - se.base(bc.getTypeName()); - } else { - ComplexExtension ce = ct.complexContent().extension(); - contentModel = ce; - contentModelOwner = ce; - - ce.base(bc.getTypeName()); - // TODO: what if the base type is anonymous? - } - } - - if(contentModelOwner!=null) { - // build the tree that represents the explicit content model from iterate over the properties - ArrayList<Tree> children = new ArrayList<Tree>(); - for (PropertyInfo<T,C> p : c.getProperties()) { - // handling for <complexType @mixed='true' ...> - if(p instanceof ReferencePropertyInfo && ((ReferencePropertyInfo)p).isMixed()) { - ct.mixed(true); - } - Tree t = buildPropertyContentModel(p); - if(t!=null) - children.add(t); - } - - Tree top = Tree.makeGroup( c.isOrdered() ? GroupKind.SEQUENCE : GroupKind.ALL, children); - - // write the content model - top.write(contentModelOwner); - } - - // then attributes - for (PropertyInfo<T,C> p : c.getProperties()) { - if (p instanceof AttributePropertyInfo) { - handleAttributeProp((AttributePropertyInfo<T,C>)p, contentModel); - } - } - if( c.hasAttributeWildcard()) { - contentModel.anyAttribute().namespace("##other").processContents("skip"); - } - ct.commit(); - } - - /** - * Writes the name attribute if it's named. - */ - private void writeName(NonElement<T,C> c, TypedXmlWriter xw) { - QName tn = c.getTypeName(); - if(tn!=null) - xw._attribute("name",tn.getLocalPart()); // named - } - - private boolean containsValueProp(ClassInfo<T, C> c) { - for (PropertyInfo p : c.getProperties()) { - if (p instanceof ValuePropertyInfo) return true; - } - return false; - } - - /** - * Builds content model writer for the specified property. - */ - private Tree buildPropertyContentModel(PropertyInfo<T,C> p) { - switch(p.kind()) { - case ELEMENT: - return handleElementProp((ElementPropertyInfo<T,C>)p); - case ATTRIBUTE: - // attribuets are handled later - return null; - case REFERENCE: - return handleReferenceProp((ReferencePropertyInfo<T,C>)p); - case MAP: - return handleMapProp((MapPropertyInfo<T,C>)p); - case VALUE: - // value props handled above in writeClass() - assert false; - throw new IllegalStateException(); - default: - assert false; - throw new IllegalStateException(); - } - } - - /** - * Generate the proper schema fragment for the given element property into the - * specified schema compositor. - * - * The element property may or may not represent a collection and it may or may - * not be wrapped. - * - * @param ep the element property - */ - private Tree handleElementProp(final ElementPropertyInfo<T,C> ep) { - if (ep.isValueList()) { - return new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - TypeRef<T,C> t = ep.getTypes().get(0); - LocalElement e = parent.element(); - e.block(); // we will write occurs later - QName tn = t.getTagName(); - e.name(tn.getLocalPart()); - List lst = e.simpleType().list(); - writeTypeRef(lst,t, "itemType"); - elementFormDefault.writeForm(e,tn); - writeOccurs(e,isOptional||!ep.isRequired(),repeated); - } - }; - } - - ArrayList<Tree> children = new ArrayList<Tree>(); - for (final TypeRef<T,C> t : ep.getTypes()) { - children.add(new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - LocalElement e = parent.element(); - - QName tn = t.getTagName(); - - PropertyInfo propInfo = t.getSource(); - TypeInfo parentInfo = (propInfo == null) ? null : propInfo.parent(); - - if (canBeDirectElementRef(t, tn, parentInfo)) { - if ((!t.getTarget().isSimpleType()) && (t.getTarget() instanceof ClassInfo) && collisionChecker.findDuplicate((ClassInfo<T, C>) t.getTarget())) { - e.ref(new QName(uri, tn.getLocalPart())); - } else { - - QName elemName = null; - if (t.getTarget() instanceof Element) { - Element te = (Element) t.getTarget(); - elemName = te.getElementName(); - } - - Collection<TypeInfo> refs = propInfo.ref(); - TypeInfo ti; - if ((refs != null) && (!refs.isEmpty()) && (elemName != null) - && ((ti = refs.iterator().next()) == null || ti instanceof ClassInfoImpl)) { - ClassInfoImpl cImpl = (ClassInfoImpl)ti; - if ((cImpl != null) && (cImpl.getElementName() != null)) { - e.ref(new QName(cImpl.getElementName().getNamespaceURI(), tn.getLocalPart())); - } else { - e.ref(new QName("", tn.getLocalPart())); - } - } else { - e.ref(tn); - } - } - } else { - e.name(tn.getLocalPart()); - writeTypeRef(e,t, "type"); - elementFormDefault.writeForm(e,tn); - } - - if (t.isNillable()) { - e.nillable(true); - } - if(t.getDefaultValue()!=null) - e._default(t.getDefaultValue()); - writeOccurs(e,isOptional,repeated); - } - }); - } - - final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children) - .makeOptional(!ep.isRequired()) - .makeRepeated(ep.isCollection()); // see Spec table 8-13 - - - final QName ename = ep.getXmlName(); - if (ename != null) { // wrapped collection - return new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - LocalElement e = parent.element(); - if(ename.getNamespaceURI().length()>0) { - if (!ename.getNamespaceURI().equals(uri)) { - // TODO: we need to generate the corresponding element declaration for this - // table 8-25: Property/field element wrapper with ref attribute - e.ref(new QName(ename.getNamespaceURI(), ename.getLocalPart())); - return; - } - } - e.name(ename.getLocalPart()); - elementFormDefault.writeForm(e,ename); - - if(ep.isCollectionNillable()) { - e.nillable(true); - } - writeOccurs(e,!ep.isCollectionRequired(),repeated); - - ComplexType p = e.complexType(); - choice.write(p); - } - }; - } else {// non-wrapped - return choice; - } - } - - /** - * Checks if we can collapse - * <element name='foo' type='t' /> to <element ref='foo' />. - * - * This is possible if we already have such declaration to begin with. - */ - private boolean canBeDirectElementRef(TypeRef<T, C> t, QName tn, TypeInfo parentInfo) { - Element te = null; - ClassInfo ci = null; - QName targetTagName = null; - - if(t.isNillable() || t.getDefaultValue()!=null) { - // can't put those attributes on <element ref> - return false; - } - - if (t.getTarget() instanceof Element) { - te = (Element) t.getTarget(); - targetTagName = te.getElementName(); - if (te instanceof ClassInfo) { - ci = (ClassInfo)te; - } - } - - String nsUri = tn.getNamespaceURI(); - if ((!nsUri.equals(uri) && nsUri.length()>0) && (!((parentInfo instanceof ClassInfo) && (((ClassInfo)parentInfo).getTypeName() == null)))) { - return true; - } - - if ((ci != null) && ((targetTagName != null) && (te.getScope() == null) && (targetTagName.getNamespaceURI() == null))) { - if (targetTagName.equals(tn)) { - return true; - } - } - - // we have the precise element defined already - if (te != null) { // it is instanceof Element - return targetTagName!=null && targetTagName.equals(tn); - } - - return false; - } - - - /** - * Generate an attribute for the specified property on the specified complexType - * - * @param ap the attribute - * @param attr the schema definition to which the attribute will be added - */ - private void handleAttributeProp(AttributePropertyInfo<T,C> ap, AttrDecls attr) { - // attr is either a top-level ComplexType or a ComplexExtension - // - // [RESULT] - // - // <complexType ...> - // <...>...</> - // <attribute name="foo" type="xs:int"/> - // </> - // - // or - // - // <complexType ...> - // <complexContent> - // <extension ...> - // <...>...</> - // </> - // </> - // <attribute name="foo" type="xs:int"/> - // </> - // - // or it could also be an in-lined type (attr ref) - // - LocalAttribute localAttribute = attr.attribute(); - - final String attrURI = ap.getXmlName().getNamespaceURI(); - if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) { - localAttribute.name(ap.getXmlName().getLocalPart()); - - writeAttributeTypeRef(ap, localAttribute); - - attributeFormDefault.writeForm(localAttribute,ap.getXmlName()); - } else { // generate an attr ref - localAttribute.ref(ap.getXmlName()); - } - - if(ap.isRequired()) { - // TODO: not type safe - localAttribute.use("required"); - } - } - - private void writeAttributeTypeRef(AttributePropertyInfo<T,C> ap, AttributeType a) { - if( ap.isCollection() ) - writeTypeRef(a.simpleType().list(), ap, "itemType"); - else - writeTypeRef(a, ap, "type"); - } - - /** - * Generate the proper schema fragment for the given reference property into the - * specified schema compositor. - * - * The reference property may or may not refer to a collection and it may or may - * not be wrapped. - */ - private Tree handleReferenceProp(final ReferencePropertyInfo<T, C> rp) { - // fill in content model - ArrayList<Tree> children = new ArrayList<Tree>(); - - for (final Element<T,C> e : rp.getElements()) { - children.add(new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - LocalElement eref = parent.element(); - - boolean local=false; - - QName en = e.getElementName(); - if(e.getScope()!=null) { - // scoped. needs to be inlined - boolean qualified = en.getNamespaceURI().equals(uri); - boolean unqualified = en.getNamespaceURI().equals(""); - if(qualified || unqualified) { - // can be inlined indeed - - // write form="..." if necessary - if(unqualified) { - if(elementFormDefault.isEffectivelyQualified) - eref.form("unqualified"); - } else { - if(!elementFormDefault.isEffectivelyQualified) - eref.form("qualified"); - } - - local = true; - eref.name(en.getLocalPart()); - - // write out type reference - if(e instanceof ClassInfo) { - writeTypeRef(eref,(ClassInfo<T,C>)e,"type"); - } else { - writeTypeRef(eref,((ElementInfo<T,C>)e).getContentType(),"type"); - } - } - } - if(!local) - eref.ref(en); - writeOccurs(eref,isOptional,repeated); - } - }); - } - - final WildcardMode wc = rp.getWildcard(); - if( wc != null ) { - children.add(new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - Any any = parent.any(); - final String pcmode = getProcessContentsModeName(wc); - if( pcmode != null ) any.processContents(pcmode); - any.namespace("##other"); - writeOccurs(any,isOptional,repeated); - } - }); - } - - - final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children).makeRepeated(rp.isCollection()).makeOptional(!rp.isRequired()); - - final QName ename = rp.getXmlName(); - - if (ename != null) { // wrapped - return new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - LocalElement e = parent.element().name(ename.getLocalPart()); - elementFormDefault.writeForm(e,ename); - if(rp.isCollectionNillable()) - e.nillable(true); - writeOccurs(e,true,repeated); - - ComplexType p = e.complexType(); - choice.write(p); - } - }; - } else { // unwrapped - return choice; - } - } - - /** - * Generate the proper schema fragment for the given map property into the - * specified schema compositor. - * - * @param mp the map property - */ - private Tree handleMapProp(final MapPropertyInfo<T,C> mp) { - return new Tree.Term() { - protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) { - QName ename = mp.getXmlName(); - - LocalElement e = parent.element(); - elementFormDefault.writeForm(e,ename); - if(mp.isCollectionNillable()) - e.nillable(true); - - e = e.name(ename.getLocalPart()); - writeOccurs(e,isOptional,repeated); - ComplexType p = e.complexType(); - - // TODO: entry, key, and value are always unqualified. that needs to be fixed, too. - // TODO: we need to generate the corresponding element declaration, if they are qualified - e = p.sequence().element(); - e.name("entry").minOccurs(0).maxOccurs("unbounded"); - - ExplicitGroup seq = e.complexType().sequence(); - writeKeyOrValue(seq, "key", mp.getKeyType()); - writeKeyOrValue(seq, "value", mp.getValueType()); - } - }; - } - - private void writeKeyOrValue(ExplicitGroup seq, String tagName, NonElement<T, C> typeRef) { - LocalElement key = seq.element().name(tagName); - key.minOccurs(0); - writeTypeRef(key, typeRef, "type"); - } - - public void addGlobalAttribute(AttributePropertyInfo<T,C> ap) { - attributeDecls.put( ap.getXmlName().getLocalPart(), ap ); - addDependencyTo(ap.getTarget().getTypeName()); - } - - public void addGlobalElement(TypeRef<T,C> tref) { - elementDecls.put( tref.getTagName().getLocalPart(), new ElementWithType(false,tref.getTarget()) ); - addDependencyTo(tref.getTarget().getTypeName()); - } - - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - buf.append("[classes=").append(classes); - buf.append(",elementDecls=").append(elementDecls); - buf.append(",enums=").append(enums); - buf.append("]"); - return super.toString(); - } - - /** - * Represents a global element declaration to be written. - * - * <p> - * Because multiple properties can name the same global element even if - * they have different Java type, the schema generator first needs to - * walk through the model and decide what to generate for the given - * element declaration. - * - * <p> - * This class represents what will be written, and its {@link #equals(Object)} - * method is implemented in such a way that two identical declarations - * are considered as the same. - */ - abstract class ElementDeclaration { - /** - * Returns true if two {@link ElementDeclaration}s are representing - * the same schema fragment. - */ - @Override - public abstract boolean equals(Object o); - @Override - public abstract int hashCode(); - - /** - * Generates the declaration. - */ - public abstract void writeTo(String localName, Schema schema); - } - - /** - * {@link ElementDeclaration} that refers to a {@link NonElement}. - */ - class ElementWithType extends ElementDeclaration { - private final boolean nillable; - private final NonElement<T,C> type; - - public ElementWithType(boolean nillable,NonElement<T, C> type) { - this.type = type; - this.nillable = nillable; - } - - public void writeTo(String localName, Schema schema) { - TopLevelElement e = schema.element().name(localName); - if(nillable) - e.nillable(true); - if (type != null) { - writeTypeRef(e,type, "type"); - } else { - e.complexType(); // refer to the nested empty complex type - } - e.commit(); - } - - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - - final ElementWithType that = (ElementWithType) o; - return type.equals(that.type); - } - - public int hashCode() { - return type.hashCode(); - } - } - } - - /** - * Examine the specified element ref and determine if a swaRef attribute needs to be generated - * @param typeRef - */ - private boolean generateSwaRefAdapter(NonElementRef<T,C> typeRef) { - return generateSwaRefAdapter(typeRef.getSource()); - } - - /** - * Examine the specified element ref and determine if a swaRef attribute needs to be generated - */ - private boolean generateSwaRefAdapter(PropertyInfo<T,C> prop) { - final Adapter<T,C> adapter = prop.getAdapter(); - if (adapter == null) return false; - final Object o = navigator.asDecl(SwaRefAdapter.class); - if (o == null) return false; - return (o.equals(adapter.adapterType)); - } - - /** - * Debug information of what's in this {@link XmlSchemaGenerator}. - */ - @Override - public String toString() { - StringBuilder buf = new StringBuilder(); - for (Namespace ns : namespaces.values()) { - if(buf.length()>0) buf.append(','); - buf.append(ns.uri).append('=').append(ns); - } - return super.toString()+'['+buf+']'; - } - - /** - * return the string representation of the processContents mode of the - * give wildcard, or null if it is the schema default "strict" - * - */ - private static String getProcessContentsModeName(WildcardMode wc) { - switch(wc) { - case LAX: - case SKIP: - return wc.name().toLowerCase(); - case STRICT: - return null; - default: - throw new IllegalStateException(); - } - } - - - /** - * Relativizes a URI by using another URI (base URI.) - * - * <p> - * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"} - * - * <p> - * This method only works on hierarchical URI's, not opaque URI's (refer to the - * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a> - * javadoc for complete definitions of these terms. - * - * <p> - * This method will not normalize the relative URI. - * - * @return the relative URI or the original URI if a relative one could not be computed - */ - protected static String relativize(String uri, String baseUri) { - try { - assert uri!=null; - - if(baseUri==null) return uri; - - URI theUri = new URI(escapeURI(uri)); - URI theBaseUri = new URI(escapeURI(baseUri)); - - if (theUri.isOpaque() || theBaseUri.isOpaque()) - return uri; - - if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) || - !equal(theUri.getAuthority(), theBaseUri.getAuthority())) - return uri; - - String uriPath = theUri.getPath(); - String basePath = theBaseUri.getPath(); - - // normalize base path - if (!basePath.endsWith("/")) { - basePath = normalizeUriPath(basePath); - } - - if( uriPath.equals(basePath)) - return "."; - - String relPath = calculateRelativePath(uriPath, basePath, fixNull(theUri.getScheme()).equals("file")); - - if (relPath == null) - return uri; // recursion found no commonality in the two uris at all - StringBuilder relUri = new StringBuilder(); - relUri.append(relPath); - if (theUri.getQuery() != null) - relUri.append('?').append(theUri.getQuery()); - if (theUri.getFragment() != null) - relUri.append('#').append(theUri.getFragment()); - - return relUri.toString(); - } catch (URISyntaxException e) { - throw new InternalError("Error escaping one of these uris:\n\t"+uri+"\n\t"+baseUri); - } - } - - private static String fixNull(String s) { - if(s==null) return ""; - else return s; - } - - private static String calculateRelativePath(String uri, String base, boolean fileUrl) { - // if this is a file URL (very likely), and if this is on a case-insensitive file system, - // then treat it accordingly. - boolean onWindows = File.pathSeparatorChar==';'; - - if (base == null) { - return null; - } - if ((fileUrl && onWindows && startsWithIgnoreCase(uri,base)) || uri.startsWith(base)) { - return uri.substring(base.length()); - } else { - return "../" + calculateRelativePath(uri, getParentUriPath(base), fileUrl); - } - } - - private static boolean startsWithIgnoreCase(String s, String t) { - return s.toUpperCase().startsWith(t.toUpperCase()); - } - - /** - * JAX-RPC wants the namespaces to be sorted in the reverse order - * so that the empty namespace "" comes to the very end. Don't ask me why. - */ - private static final Comparator<String> NAMESPACE_COMPARATOR = new Comparator<String>() { - public int compare(String lhs, String rhs) { - return -lhs.compareTo(rhs); - } - }; - - private static final String newline = "\n"; -} |