src/share/jaxws_classes/com/sun/xml/internal/bind/v2/schemagen/XmlSchemaGenerator.java

Thu, 12 Oct 2017 19:44:07 +0800

author
aoqi
date
Thu, 12 Oct 2017 19:44:07 +0800
changeset 760
e530533619ec
parent 637
9c07ef4934dd
child 1288
f4ace6971570
permissions
-rw-r--r--

merge

     1 /*
     2  * Copyright (c) 1997, 2013, Oracle and/or its affiliates. All rights reserved.
     3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
     4  *
     5  * This code is free software; you can redistribute it and/or modify it
     6  * under the terms of the GNU General Public License version 2 only, as
     7  * published by the Free Software Foundation.  Oracle designates this
     8  * particular file as subject to the "Classpath" exception as provided
     9  * by Oracle in the LICENSE file that accompanied this code.
    10  *
    11  * This code is distributed in the hope that it will be useful, but WITHOUT
    12  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
    13  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
    14  * version 2 for more details (a copy is included in the LICENSE file that
    15  * accompanied this code).
    16  *
    17  * You should have received a copy of the GNU General Public License version
    18  * 2 along with this work; if not, write to the Free Software Foundation,
    19  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
    20  *
    21  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
    22  * or visit www.oracle.com if you need additional information or have any
    23  * questions.
    24  */
    26 package com.sun.xml.internal.bind.v2.schemagen;
    28 import java.io.IOException;
    29 import java.io.OutputStream;
    30 import java.io.Writer;
    31 import java.io.File;
    32 import java.net.URI;
    33 import java.net.URISyntaxException;
    34 import java.util.Comparator;
    35 import java.util.HashMap;
    36 import java.util.LinkedHashSet;
    37 import java.util.Map;
    38 import java.util.Set;
    39 import java.util.TreeMap;
    40 import java.util.ArrayList;
    41 import java.util.logging.Level;
    42 import java.util.logging.Logger;
    44 import javax.activation.MimeType;
    45 import javax.xml.bind.SchemaOutputResolver;
    46 import javax.xml.bind.annotation.XmlElement;
    47 import javax.xml.namespace.QName;
    48 import javax.xml.transform.Result;
    49 import javax.xml.transform.stream.StreamResult;
    51 import com.sun.istack.internal.Nullable;
    52 import com.sun.istack.internal.NotNull;
    53 import com.sun.xml.internal.bind.Util;
    54 import com.sun.xml.internal.bind.api.CompositeStructure;
    55 import com.sun.xml.internal.bind.api.ErrorListener;
    56 import com.sun.xml.internal.bind.v2.TODO;
    57 import com.sun.xml.internal.bind.v2.WellKnownNamespace;
    58 import com.sun.xml.internal.bind.v2.util.CollisionCheckStack;
    59 import com.sun.xml.internal.bind.v2.util.StackRecorder;
    60 import static com.sun.xml.internal.bind.v2.WellKnownNamespace.XML_SCHEMA;
    61 import com.sun.xml.internal.bind.v2.model.core.Adapter;
    62 import com.sun.xml.internal.bind.v2.model.core.ArrayInfo;
    63 import com.sun.xml.internal.bind.v2.model.core.AttributePropertyInfo;
    64 import com.sun.xml.internal.bind.v2.model.core.ClassInfo;
    65 import com.sun.xml.internal.bind.v2.model.core.Element;
    66 import com.sun.xml.internal.bind.v2.model.core.ElementInfo;
    67 import com.sun.xml.internal.bind.v2.model.core.ElementPropertyInfo;
    68 import com.sun.xml.internal.bind.v2.model.core.EnumConstant;
    69 import com.sun.xml.internal.bind.v2.model.core.EnumLeafInfo;
    70 import com.sun.xml.internal.bind.v2.model.core.MapPropertyInfo;
    71 import com.sun.xml.internal.bind.v2.model.core.MaybeElement;
    72 import com.sun.xml.internal.bind.v2.model.core.NonElement;
    73 import com.sun.xml.internal.bind.v2.model.core.NonElementRef;
    74 import com.sun.xml.internal.bind.v2.model.core.PropertyInfo;
    75 import com.sun.xml.internal.bind.v2.model.core.ReferencePropertyInfo;
    76 import com.sun.xml.internal.bind.v2.model.core.TypeInfo;
    77 import com.sun.xml.internal.bind.v2.model.core.TypeInfoSet;
    78 import com.sun.xml.internal.bind.v2.model.core.TypeRef;
    79 import com.sun.xml.internal.bind.v2.model.core.ValuePropertyInfo;
    80 import com.sun.xml.internal.bind.v2.model.core.WildcardMode;
    81 import com.sun.xml.internal.bind.v2.model.impl.ClassInfoImpl;
    82 import com.sun.xml.internal.bind.v2.model.nav.Navigator;
    83 import com.sun.xml.internal.bind.v2.runtime.SwaRefAdapter;
    84 import static com.sun.xml.internal.bind.v2.schemagen.Util.*;
    85 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Any;
    86 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttrDecls;
    87 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexExtension;
    88 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexType;
    89 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ComplexTypeHost;
    90 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ExplicitGroup;
    91 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Import;
    92 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.List;
    93 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalAttribute;
    94 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.LocalElement;
    95 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.Schema;
    96 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleExtension;
    97 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleRestrictionModel;
    98 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleType;
    99 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.SimpleTypeHost;
   100 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelAttribute;
   101 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TopLevelElement;
   102 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeHost;
   103 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.ContentModelContainer;
   104 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.TypeDefParticle;
   105 import com.sun.xml.internal.bind.v2.schemagen.xmlschema.AttributeType;
   106 import com.sun.xml.internal.bind.v2.schemagen.episode.Bindings;
   107 import com.sun.xml.internal.txw2.TXW;
   108 import com.sun.xml.internal.txw2.TxwException;
   109 import com.sun.xml.internal.txw2.TypedXmlWriter;
   110 import com.sun.xml.internal.txw2.output.ResultFactory;
   111 import com.sun.xml.internal.txw2.output.XmlSerializer;
   112 import java.util.Collection;
   113 import org.xml.sax.SAXParseException;
   115 /**
   116  * Generates a set of W3C XML Schema documents from a set of Java classes.
   117  *
   118  * <p>
   119  * A client must invoke methods in the following order:
   120  * <ol>
   121  *  <li>Create a new {@link XmlSchemaGenerator}
   122  *  <li>Invoke {@link #add} methods, multiple times if necessary.
   123  *  <li>Invoke {@link #write}
   124  *  <li>Discard the {@link XmlSchemaGenerator}.
   125  * </ol>
   126  *
   127  * @author Ryan Shoemaker
   128  * @author Kohsuke Kawaguchi (kk@kohsuke.org)
   129  */
   130 public final class XmlSchemaGenerator<T,C,F,M> {
   132     private static final Logger logger = Util.getClassLogger();
   134     /**
   135      * Java classes to be written, organized by their namespace.
   136      *
   137      * <p>
   138      * We use a {@link TreeMap} here so that the suggested names will
   139      * be consistent across JVMs.
   140      *
   141      * @see SchemaOutputResolver#createOutput(String, String)
   142      */
   143     private final Map<String,Namespace> namespaces = new TreeMap<String,Namespace>(NAMESPACE_COMPARATOR);
   145     /**
   146      * {@link ErrorListener} to send errors to.
   147      */
   148     private ErrorListener errorListener;
   150     /** model navigator **/
   151     private Navigator<T,C,F,M> navigator;
   153     private final TypeInfoSet<T,C,F,M> types;
   155     /**
   156      * Representation for xs:string.
   157      */
   158     private final NonElement<T,C> stringType;
   160     /**
   161      * Represents xs:anyType.
   162      */
   163     private final NonElement<T,C> anyType;
   165     /**
   166      * Used to detect cycles in anonymous types.
   167      */
   168     private final CollisionCheckStack<ClassInfo<T,C>> collisionChecker = new CollisionCheckStack<ClassInfo<T,C>>();
   170     public XmlSchemaGenerator( Navigator<T,C,F,M> navigator, TypeInfoSet<T,C,F,M> types ) {
   171         this.navigator = navigator;
   172         this.types = types;
   174         this.stringType = types.getTypeInfo(navigator.ref(String.class));
   175         this.anyType = types.getAnyTypeInfo();
   177         // populate the object
   178         for( ClassInfo<T,C> ci : types.beans().values() )
   179             add(ci);
   180         for( ElementInfo<T,C> ei1 : types.getElementMappings(null).values() )
   181             add(ei1);
   182         for( EnumLeafInfo<T,C> ei : types.enums().values() )
   183             add(ei);
   184         for( ArrayInfo<T,C> a : types.arrays().values())
   185             add(a);
   186     }
   188     private Namespace getNamespace(String uri) {
   189         Namespace n = namespaces.get(uri);
   190         if(n==null)
   191             namespaces.put(uri,n=new Namespace(uri));
   192         return n;
   193     }
   195     /**
   196      * Adds a new class to the list of classes to be written.
   197      *
   198      * <p>
   199      * A {@link ClassInfo} may have two namespaces --- one for the element name
   200      * and the other for the type name. If they are different, we put the same
   201      * {@link ClassInfo} to two {@link Namespace}s.
   202      */
   203     public void add( ClassInfo<T,C> clazz ) {
   204         assert clazz!=null;
   206         String nsUri = null;
   208         if(clazz.getClazz()==navigator.asDecl(CompositeStructure.class))
   209             return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
   211         if(clazz.isElement()) {
   212             // put element -> type reference
   213             nsUri = clazz.getElementName().getNamespaceURI();
   214             Namespace ns = getNamespace(nsUri);
   215             ns.classes.add(clazz);
   216             ns.addDependencyTo(clazz.getTypeName());
   218             // schedule writing this global element
   219             add(clazz.getElementName(),false,clazz);
   220         }
   222         QName tn = clazz.getTypeName();
   223         if(tn!=null) {
   224             nsUri = tn.getNamespaceURI();
   225         } else {
   226             // anonymous type
   227             if(nsUri==null)
   228                 return;
   229         }
   231         Namespace n = getNamespace(nsUri);
   232         n.classes.add(clazz);
   234         // search properties for foreign namespace references
   235         for( PropertyInfo<T,C> p : clazz.getProperties()) {
   236             n.processForeignNamespaces(p, 1);
   237             if (p instanceof AttributePropertyInfo) {
   238                 AttributePropertyInfo<T,C> ap = (AttributePropertyInfo<T,C>) p;
   239                 String aUri = ap.getXmlName().getNamespaceURI();
   240                 if(aUri.length()>0) {
   241                     // global attribute
   242                     getNamespace(aUri).addGlobalAttribute(ap);
   243                     n.addDependencyTo(ap.getXmlName());
   244                 }
   245             }
   246             if (p instanceof ElementPropertyInfo) {
   247                 ElementPropertyInfo<T,C> ep = (ElementPropertyInfo<T,C>) p;
   248                 for (TypeRef<T,C> tref : ep.getTypes()) {
   249                     String eUri = tref.getTagName().getNamespaceURI();
   250                     if(eUri.length()>0 && !eUri.equals(n.uri)) {
   251                         getNamespace(eUri).addGlobalElement(tref);
   252                         n.addDependencyTo(tref.getTagName());
   253                     }
   254                 }
   255             }
   257             if(generateSwaRefAdapter(p))
   258                 n.useSwaRef = true;
   260             MimeType mimeType = p.getExpectedMimeType();
   261             if( mimeType != null ) {
   262                 n.useMimeNs = true;
   263             }
   265         }
   267         // recurse on baseTypes to make sure that we can refer to them in the schema
   268         ClassInfo<T,C> bc = clazz.getBaseClass();
   269         if (bc != null) {
   270             add(bc);
   271             n.addDependencyTo(bc.getTypeName());
   272         }
   273     }
   275     /**
   276      * Adds a new element to the list of elements to be written.
   277      */
   278     public void add( ElementInfo<T,C> elem ) {
   279         assert elem!=null;
   281         @SuppressWarnings("UnusedAssignment")
   282         boolean nillable = false; // default value
   284         QName name = elem.getElementName();
   285         Namespace n = getNamespace(name.getNamespaceURI());
   286         ElementInfo ei;
   288         if (elem.getScope() != null) { // (probably) never happens
   289             ei = this.types.getElementInfo(elem.getScope().getClazz(), name);
   290         } else {
   291             ei = this.types.getElementInfo(null, name);
   292         }
   294         XmlElement xmlElem = ei.getProperty().readAnnotation(XmlElement.class);
   296         if (xmlElem == null) {
   297             nillable = false;
   298         } else {
   299             nillable = xmlElem.nillable();
   300         }
   302         n.elementDecls.put(name.getLocalPart(),n.new ElementWithType(nillable, elem.getContentType()));
   304         // search for foreign namespace references
   305         n.processForeignNamespaces(elem.getProperty(), 1);
   306     }
   308     public void add( EnumLeafInfo<T,C> envm ) {
   309         assert envm!=null;
   311         String nsUri = null;
   313         if(envm.isElement()) {
   314             // put element -> type reference
   315             nsUri = envm.getElementName().getNamespaceURI();
   316             Namespace ns = getNamespace(nsUri);
   317             ns.enums.add(envm);
   318             ns.addDependencyTo(envm.getTypeName());
   320             // schedule writing this global element
   321             add(envm.getElementName(),false,envm);
   322         }
   324         final QName typeName = envm.getTypeName();
   325         if (typeName != null) {
   326             nsUri = typeName.getNamespaceURI();
   327         } else {
   328             if(nsUri==null)
   329                 return; // anonymous type
   330         }
   332         Namespace n = getNamespace(nsUri);
   333         n.enums.add(envm);
   335         // search for foreign namespace references
   336         n.addDependencyTo(envm.getBaseType().getTypeName());
   337     }
   339     public void add( ArrayInfo<T,C> a ) {
   340         assert a!=null;
   342         final String namespaceURI = a.getTypeName().getNamespaceURI();
   343         Namespace n = getNamespace(namespaceURI);
   344         n.arrays.add(a);
   346         // search for foreign namespace references
   347         n.addDependencyTo(a.getItemType().getTypeName());
   348     }
   350     /**
   351      * Adds an additional element declaration.
   352      *
   353      * @param tagName
   354      *      The name of the element declaration to be added.
   355      * @param type
   356      *      The type this element refers to.
   357      *      Can be null, in which case the element refers to an empty anonymous complex type.
   358      */
   359     public void add( QName tagName, boolean isNillable, NonElement<T,C> type ) {
   361         if(type!=null && type.getType()==navigator.ref(CompositeStructure.class))
   362             return; // this is a special class we introduced for JAX-WS that we *don't* want in the schema
   365         Namespace n = getNamespace(tagName.getNamespaceURI());
   366         n.elementDecls.put(tagName.getLocalPart(), n.new ElementWithType(isNillable,type));
   368         // search for foreign namespace references
   369         if(type!=null)
   370             n.addDependencyTo(type.getTypeName());
   371     }
   373     /**
   374      * Writes out the episode file.
   375      */
   376     public void writeEpisodeFile(XmlSerializer out) {
   377         Bindings root = TXW.create(Bindings.class, out);
   379         if(namespaces.containsKey("")) // otherwise jaxb binding NS should be the default namespace
   380             root._namespace(WellKnownNamespace.JAXB,"jaxb");
   381         root.version("2.1");
   382         // TODO: don't we want to bake in versions?
   384         // generate listing per schema
   385         for (Map.Entry<String,Namespace> e : namespaces.entrySet()) {
   386             Bindings group = root.bindings();
   388             String prefix;
   389             String tns = e.getKey();
   390             if(!tns.equals("")) {
   391                 group._namespace(tns,"tns");
   392                 prefix = "tns:";
   393             } else {
   394                 prefix = "";
   395             }
   397             group.scd("x-schema::"+(tns.equals("")?"":"tns"));
   398             group.schemaBindings().map(false);
   400             for (ClassInfo<T,C> ci : e.getValue().classes) {
   401                 if(ci.getTypeName()==null)  continue;   // local type
   403                 if(ci.getTypeName().getNamespaceURI().equals(tns)) {
   404                     Bindings child = group.bindings();
   405                     child.scd('~'+prefix+ci.getTypeName().getLocalPart());
   406                     child.klass().ref(ci.getName());
   407                 }
   409                 if(ci.isElement() && ci.getElementName().getNamespaceURI().equals(tns)) {
   410                     Bindings child = group.bindings();
   411                     child.scd(prefix+ci.getElementName().getLocalPart());
   412                     child.klass().ref(ci.getName());
   413                 }
   414             }
   416             for (EnumLeafInfo<T,C> en : e.getValue().enums) {
   417                 if(en.getTypeName()==null)  continue;   // local type
   419                 Bindings child = group.bindings();
   420                 child.scd('~'+prefix+en.getTypeName().getLocalPart());
   421                 child.klass().ref(navigator.getClassName(en.getClazz()));
   422             }
   424             group.commit(true);
   425         }
   427         root.commit();
   428     }
   430     /**
   431      * Write out the schema documents.
   432      */
   433     public void write(SchemaOutputResolver resolver, ErrorListener errorListener) throws IOException {
   434         if(resolver==null)
   435             throw new IllegalArgumentException();
   437         if(logger.isLoggable(Level.FINE)) {
   438             // debug logging to see what's going on.
   439             logger.log(Level.FINE,"Wrigin XML Schema for "+toString(),new StackRecorder());
   440         }
   442         // make it fool-proof
   443         resolver = new FoolProofResolver(resolver);
   444         this.errorListener = errorListener;
   446         Map<String, String> schemaLocations = types.getSchemaLocations();
   448         Map<Namespace,Result> out = new HashMap<Namespace,Result>();
   449         Map<Namespace,String> systemIds = new HashMap<Namespace,String>();
   451         // we create a Namespace object for the XML Schema namespace
   452         // as a side-effect, but we don't want to generate it.
   453         namespaces.remove(WellKnownNamespace.XML_SCHEMA);
   455         // first create the outputs for all so that we can resolve references among
   456         // schema files when we write
   457         for( Namespace n : namespaces.values() ) {
   458             String schemaLocation = schemaLocations.get(n.uri);
   459             if(schemaLocation!=null) {
   460                 systemIds.put(n,schemaLocation);
   461             } else {
   462                 Result output = resolver.createOutput(n.uri,"schema"+(out.size()+1)+".xsd");
   463                 if(output!=null) {  // null result means no schema for that namespace
   464                     out.put(n,output);
   465                     systemIds.put(n,output.getSystemId());
   466                 }
   467             }
   468         }
   470         // then write'em all
   471         for( Map.Entry<Namespace,Result> e : out.entrySet() ) {
   472             Result result = e.getValue();
   473             e.getKey().writeTo( result, systemIds );
   474             if(result instanceof StreamResult) {
   475                 OutputStream outputStream = ((StreamResult)result).getOutputStream();
   476                 if(outputStream != null) {
   477                     outputStream.close(); // fix for bugid: 6291301
   478                 } else {
   479                     final Writer writer = ((StreamResult)result).getWriter();
   480                     if(writer != null) writer.close();
   481                 }
   482             }
   483         }
   484     }
   488     /**
   489      * Schema components are organized per namespace.
   490      */
   491     private class Namespace {
   492         final @NotNull String uri;
   494         /**
   495          * Other {@link Namespace}s that this namespace depends on.
   496          */
   497         private final Set<Namespace> depends = new LinkedHashSet<Namespace>();
   499         /**
   500          * If this schema refers to components from this schema by itself.
   501          */
   502         private boolean selfReference;
   504         /**
   505          * List of classes in this namespace.
   506          */
   507         private final Set<ClassInfo<T,C>> classes = new LinkedHashSet<ClassInfo<T,C>>();
   509         /**
   510          * Set of enums in this namespace
   511          */
   512         private final Set<EnumLeafInfo<T,C>> enums = new LinkedHashSet<EnumLeafInfo<T,C>>();
   514         /**
   515          * Set of arrays in this namespace
   516          */
   517         private final Set<ArrayInfo<T,C>> arrays = new LinkedHashSet<ArrayInfo<T,C>>();
   519         /**
   520          * Global attribute declarations keyed by their local names.
   521          */
   522         private final MultiMap<String,AttributePropertyInfo<T,C>> attributeDecls = new MultiMap<String,AttributePropertyInfo<T,C>>(null);
   524         /**
   525          * Global element declarations to be written, keyed by their local names.
   526          */
   527         private final MultiMap<String,ElementDeclaration> elementDecls =
   528                 new MultiMap<String,ElementDeclaration>(new ElementWithType(true,anyType));
   530         private Form attributeFormDefault;
   531         private Form elementFormDefault;
   533         /**
   534          * Does schema in this namespace uses swaRef? If so, we need to generate import
   535          * statement.
   536          */
   537         private boolean useSwaRef;
   539         /**
   540          * Import for mime namespace needs to be generated.
   541          * See #856
   542          */
   543         private boolean useMimeNs;
   545         public Namespace(String uri) {
   546             this.uri = uri;
   547             assert !XmlSchemaGenerator.this.namespaces.containsKey(uri);
   548             XmlSchemaGenerator.this.namespaces.put(uri,this);
   549         }
   551         /**
   552          * Process the given PropertyInfo looking for references to namespaces that
   553          * are foreign to the given namespace.  Any foreign namespace references
   554          * found are added to the given namespaces dependency list and an &lt;import>
   555          * is generated for it.
   556          *
   557          * @param p the PropertyInfo
   558          */
   559         private void processForeignNamespaces(PropertyInfo<T, C> p, int processingDepth) {
   560             for (TypeInfo<T, C> t : p.ref()) {
   561                 if ((t instanceof ClassInfo) && (processingDepth > 0)) {
   562                     java.util.List<PropertyInfo> l = ((ClassInfo) t).getProperties();
   563                     for (PropertyInfo subp : l) {
   564                         processForeignNamespaces(subp, --processingDepth);
   565                     }
   566                 }
   567                 if (t instanceof Element) {
   568                     addDependencyTo(((Element) t).getElementName());
   569                 }
   570                 if (t instanceof NonElement) {
   571                     addDependencyTo(((NonElement) t).getTypeName());
   572                 }
   573             }
   574         }
   576         private void addDependencyTo(@Nullable QName qname) {
   577             // even though the Element interface says getElementName() returns non-null,
   578             // ClassInfo always implements Element (even if an instance of ClassInfo might not be an Element).
   579             // so this check is still necessary
   580             if (qname==null) {
   581                 return;
   582             }
   584             String nsUri = qname.getNamespaceURI();
   586             if (nsUri.equals(XML_SCHEMA)) {
   587                 // no need to explicitly refer to XSD namespace
   588                 return;
   589             }
   591             if (nsUri.equals(uri)) {
   592                 selfReference = true;
   593                 return;
   594             }
   596             // found a type in a foreign namespace, so make sure we generate an import for it
   597             depends.add(getNamespace(nsUri));
   598         }
   600         /**
   601          * Writes the schema document to the specified result.
   602          *
   603          * @param systemIds
   604          *      System IDs of the other schema documents. "" indicates 'implied'.
   605          */
   606         private void writeTo(Result result, Map<Namespace,String> systemIds) throws IOException {
   607             try {
   608                 Schema schema = TXW.create(Schema.class,ResultFactory.createSerializer(result));
   610                 // additional namespace declarations to be made.
   611                 Map<String, String> xmlNs = types.getXmlNs(uri);
   613                 for (Map.Entry<String, String> e : xmlNs.entrySet()) {
   614                     schema._namespace(e.getValue(),e.getKey());
   615                 }
   617                 if(useSwaRef)
   618                     schema._namespace(WellKnownNamespace.SWA_URI,"swaRef");
   620                 if(useMimeNs)
   621                     schema._namespace(WellKnownNamespace.XML_MIME_URI,"xmime");
   623                 attributeFormDefault = Form.get(types.getAttributeFormDefault(uri));
   624                 attributeFormDefault.declare("attributeFormDefault",schema);
   626                 elementFormDefault = Form.get(types.getElementFormDefault(uri));
   627                 // TODO: if elementFormDefault is UNSET, figure out the right default value to use
   628                 elementFormDefault.declare("elementFormDefault",schema);
   631                 // declare XML Schema namespace to be xs, but allow the user to override it.
   632                 // if 'xs' is used for other things, we'll just let TXW assign a random prefix
   633                 if(!xmlNs.containsValue(WellKnownNamespace.XML_SCHEMA)
   634                 && !xmlNs.containsKey("xs"))
   635                     schema._namespace(WellKnownNamespace.XML_SCHEMA,"xs");
   636                 schema.version("1.0");
   638                 if(uri.length()!=0)
   639                     schema.targetNamespace(uri);
   641                 // declare prefixes for them at this level, so that we can avoid redundant
   642                 // namespace declarations
   643                 for (Namespace ns : depends) {
   644                     schema._namespace(ns.uri);
   645                 }
   647                 if(selfReference && uri.length()!=0) {
   648                     // use common 'tns' prefix for the own namespace
   649                     // if self-reference is needed
   650                     schema._namespace(uri,"tns");
   651                 }
   653                 schema._pcdata(newline);
   655                 // refer to other schemas
   656                 for( Namespace n : depends ) {
   657                     Import imp = schema._import();
   658                     if(n.uri.length()!=0)
   659                         imp.namespace(n.uri);
   660                     String refSystemId = systemIds.get(n);
   661                     if(refSystemId!=null && !refSystemId.equals("")) {
   662                         // "" means implied. null if the SchemaOutputResolver said "don't generate!"
   663                         imp.schemaLocation(relativize(refSystemId,result.getSystemId()));
   664                     }
   665                     schema._pcdata(newline);
   666                 }
   667                 if(useSwaRef) {
   668                     schema._import().namespace(WellKnownNamespace.SWA_URI).schemaLocation("http://ws-i.org/profiles/basic/1.1/swaref.xsd");
   669                 }
   670                 if(useMimeNs) {
   671                     schema._import().namespace(WellKnownNamespace.XML_MIME_URI).schemaLocation("http://www.w3.org/2005/05/xmlmime");
   672                 }
   674                 // then write each component
   675                 for (Map.Entry<String,ElementDeclaration> e : elementDecls.entrySet()) {
   676                     e.getValue().writeTo(e.getKey(),schema);
   677                     schema._pcdata(newline);
   678                 }
   679                 for (ClassInfo<T, C> c : classes) {
   680                     if (c.getTypeName()==null) {
   681                         // don't generate anything if it's an anonymous type
   682                         continue;
   683                     }
   684                     if(uri.equals(c.getTypeName().getNamespaceURI()))
   685                         writeClass(c, schema);
   686                     schema._pcdata(newline);
   687                 }
   688                 for (EnumLeafInfo<T, C> e : enums) {
   689                     if (e.getTypeName()==null) {
   690                         // don't generate anything if it's an anonymous type
   691                         continue;
   692                     }
   693                     if(uri.equals(e.getTypeName().getNamespaceURI()))
   694                         writeEnum(e,schema);
   695                     schema._pcdata(newline);
   696                 }
   697                 for (ArrayInfo<T, C> a : arrays) {
   698                     writeArray(a,schema);
   699                     schema._pcdata(newline);
   700                 }
   701                 for (Map.Entry<String,AttributePropertyInfo<T,C>> e : attributeDecls.entrySet()) {
   702                     TopLevelAttribute a = schema.attribute();
   703                     a.name(e.getKey());
   704                     if(e.getValue()==null)
   705                         writeTypeRef(a,stringType,"type");
   706                     else
   707                         writeAttributeTypeRef(e.getValue(),a);
   708                     schema._pcdata(newline);
   709                 }
   711                 // close the schema
   712                 schema.commit();
   713             } catch( TxwException e ) {
   714                 logger.log(Level.INFO,e.getMessage(),e);
   715                 throw new IOException(e.getMessage());
   716             }
   717         }
   719         /**
   720          * Writes a type attribute (if the referenced type is a global type)
   721          * or writes out the definition of the anonymous type in place (if the referenced
   722          * type is not a global type.)
   723          *
   724          * Also provides processing for ID/IDREF, MTOM @xmime, and swa:ref
   725          *
   726          * ComplexTypeHost and SimpleTypeHost don't share an api for creating
   727          * and attribute in a type-safe way, so we will compromise for now and
   728          * use _attribute().
   729          */
   730         private void writeTypeRef(TypeHost th, NonElementRef<T, C> typeRef, String refAttName) {
   731             // ID / IDREF handling
   732             switch(typeRef.getSource().id()) {
   733             case ID:
   734                 th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "ID"));
   735                 return;
   736             case IDREF:
   737                 th._attribute(refAttName, new QName(WellKnownNamespace.XML_SCHEMA, "IDREF"));
   738                 return;
   739             case NONE:
   740                 // no ID/IDREF, so continue on and generate the type
   741                 break;
   742             default:
   743                 throw new IllegalStateException();
   744             }
   746             // MTOM handling
   747             MimeType mimeType = typeRef.getSource().getExpectedMimeType();
   748             if( mimeType != null ) {
   749                 th._attribute(new QName(WellKnownNamespace.XML_MIME_URI, "expectedContentTypes", "xmime"), mimeType.toString());
   750             }
   752             // ref:swaRef handling
   753             if(generateSwaRefAdapter(typeRef)) {
   754                 th._attribute(refAttName, new QName(WellKnownNamespace.SWA_URI, "swaRef", "ref"));
   755                 return;
   756             }
   758             // type name override
   759             if(typeRef.getSource().getSchemaType()!=null) {
   760                 th._attribute(refAttName,typeRef.getSource().getSchemaType());
   761                 return;
   762             }
   764             // normal type generation
   765             writeTypeRef(th, typeRef.getTarget(), refAttName);
   766         }
   768         /**
   769          * Writes a type attribute (if the referenced type is a global type)
   770          * or writes out the definition of the anonymous type in place (if the referenced
   771          * type is not a global type.)
   772          *
   773          * @param th
   774          *      the TXW interface to which the attribute will be written.
   775          * @param type
   776          *      type to be referenced.
   777          * @param refAttName
   778          *      The name of the attribute used when referencing a type by QName.
   779          */
   780         private void writeTypeRef(TypeHost th, NonElement<T,C> type, String refAttName) {
   781             Element e = null;
   782             if (type instanceof MaybeElement) {
   783                 MaybeElement me = (MaybeElement)type;
   784                 boolean isElement = me.isElement();
   785                 if (isElement) e = me.asElement();
   786             }
   787             if (type instanceof Element) {
   788                 e = (Element)type;
   789             }
   790             if (type.getTypeName()==null) {
   791                 if ((e != null) && (e.getElementName() != null)) {
   792                     th.block(); // so that the caller may write other attributes
   793                     if(type instanceof ClassInfo) {
   794                         writeClass( (ClassInfo<T,C>)type, th );
   795                     } else {
   796                         writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th);
   797                     }
   798                 } else {
   799                     // anonymous
   800                     th.block(); // so that the caller may write other attributes
   801                     if(type instanceof ClassInfo) {
   802                         if(collisionChecker.push((ClassInfo<T,C>)type)) {
   803                             errorListener.warning(new SAXParseException(
   804                                 Messages.ANONYMOUS_TYPE_CYCLE.format(collisionChecker.getCycleString()),
   805                                 null
   806                             ));
   807                         } else {
   808                             writeClass( (ClassInfo<T,C>)type, th );
   809                         }
   810                         collisionChecker.pop();
   811                     } else {
   812                         writeEnum( (EnumLeafInfo<T,C>)type, (SimpleTypeHost)th);
   813                     }
   814                 }
   815             } else {
   816                 th._attribute(refAttName,type.getTypeName());
   817             }
   818         }
   820         /**
   821          * writes the schema definition for the given array class
   822          */
   823         private void writeArray(ArrayInfo<T, C> a, Schema schema) {
   824             ComplexType ct = schema.complexType().name(a.getTypeName().getLocalPart());
   825             ct._final("#all");
   826             LocalElement le = ct.sequence().element().name("item");
   827             le.type(a.getItemType().getTypeName());
   828             le.minOccurs(0).maxOccurs("unbounded");
   829             le.nillable(true);
   830             ct.commit();
   831         }
   833         /**
   834          * writes the schema definition for the specified type-safe enum in the given TypeHost
   835          */
   836         private void writeEnum(EnumLeafInfo<T, C> e, SimpleTypeHost th) {
   837             SimpleType st = th.simpleType();
   838             writeName(e,st);
   840             SimpleRestrictionModel base = st.restriction();
   841             writeTypeRef(base, e.getBaseType(), "base");
   843             for (EnumConstant c : e.getConstants()) {
   844                 base.enumeration().value(c.getLexicalValue());
   845             }
   846             st.commit();
   847         }
   849         /**
   850          * Writes the schema definition for the specified class to the schema writer.
   851          *
   852          * @param c the class info
   853          * @param parent the writer of the parent element into which the type will be defined
   854          */
   855         private void writeClass(ClassInfo<T,C> c, TypeHost parent) {
   856             // special handling for value properties
   857             if (containsValueProp(c)) {
   858                 if (c.getProperties().size() == 1) {
   859                     // [RESULT 2 - simpleType if the value prop is the only prop]
   860                     //
   861                     // <simpleType name="foo">
   862                     //   <xs:restriction base="xs:int"/>
   863                     // </>
   864                     ValuePropertyInfo<T,C> vp = (ValuePropertyInfo<T,C>)c.getProperties().get(0);
   865                     SimpleType st = ((SimpleTypeHost)parent).simpleType();
   866                     writeName(c, st);
   867                     if(vp.isCollection()) {
   868                         writeTypeRef(st.list(),vp.getTarget(),"itemType");
   869                     } else {
   870                         writeTypeRef(st.restriction(),vp.getTarget(),"base");
   871                     }
   872                     return;
   873                 } else {
   874                     // [RESULT 1 - complexType with simpleContent]
   875                     //
   876                     // <complexType name="foo">
   877                     //   <simpleContent>
   878                     //     <extension base="xs:int"/>
   879                     //       <attribute name="b" type="xs:boolean"/>
   880                     //     </>
   881                     //   </>
   882                     // </>
   883                     // ...
   884                     //   <element name="f" type="foo"/>
   885                     // ...
   886                     ComplexType ct = ((ComplexTypeHost)parent).complexType();
   887                     writeName(c,ct);
   888                     if(c.isFinal())
   889                         ct._final("extension restriction");
   891                     SimpleExtension se = ct.simpleContent().extension();
   892                     se.block(); // because we might have attribute before value
   893                     for (PropertyInfo<T,C> p : c.getProperties()) {
   894                         switch (p.kind()) {
   895                         case ATTRIBUTE:
   896                             handleAttributeProp((AttributePropertyInfo<T,C>)p,se);
   897                             break;
   898                         case VALUE:
   899                             TODO.checkSpec("what if vp.isCollection() == true?");
   900                             ValuePropertyInfo vp = (ValuePropertyInfo) p;
   901                             se.base(vp.getTarget().getTypeName());
   902                             break;
   903                         case ELEMENT:   // error
   904                         case REFERENCE: // error
   905                         default:
   906                             assert false;
   907                             throw new IllegalStateException();
   908                         }
   909                     }
   910                     se.commit();
   911                 }
   912                 TODO.schemaGenerator("figure out what to do if bc != null");
   913                 TODO.checkSpec("handle sec 8.9.5.2, bullet #4");
   914                 // Java types containing value props can only contain properties of type
   915                 // ValuePropertyinfo and AttributePropertyInfo which have just been handled,
   916                 // so return.
   917                 return;
   918             }
   920             // we didn't fall into the special case for value props, so we
   921             // need to initialize the ct.
   922             // generate the complexType
   923             ComplexType ct = ((ComplexTypeHost)parent).complexType();
   924             writeName(c,ct);
   925             if(c.isFinal())
   926                 ct._final("extension restriction");
   927             if(c.isAbstract())
   928                 ct._abstract(true);
   930             // these are where we write content model and attributes
   931             AttrDecls contentModel = ct;
   932             TypeDefParticle contentModelOwner = ct;
   934             // if there is a base class, we need to generate an extension in the schema
   935             final ClassInfo<T,C> bc = c.getBaseClass();
   936             if (bc != null) {
   937                 if(bc.hasValueProperty()) {
   938                     // extending complex type with simple content
   939                     SimpleExtension se = ct.simpleContent().extension();
   940                     contentModel = se;
   941                     contentModelOwner = null;
   942                     se.base(bc.getTypeName());
   943                 } else {
   944                     ComplexExtension ce = ct.complexContent().extension();
   945                     contentModel = ce;
   946                     contentModelOwner = ce;
   948                     ce.base(bc.getTypeName());
   949                     // TODO: what if the base type is anonymous?
   950                 }
   951             }
   953             if(contentModelOwner!=null) {
   954                 // build the tree that represents the explicit content model from iterate over the properties
   955                 ArrayList<Tree> children = new ArrayList<Tree>();
   956                 for (PropertyInfo<T,C> p : c.getProperties()) {
   957                     // handling for <complexType @mixed='true' ...>
   958                     if(p instanceof ReferencePropertyInfo && ((ReferencePropertyInfo)p).isMixed()) {
   959                         ct.mixed(true);
   960                     }
   961                     Tree t = buildPropertyContentModel(p);
   962                     if(t!=null)
   963                         children.add(t);
   964                 }
   966                 Tree top = Tree.makeGroup( c.isOrdered() ? GroupKind.SEQUENCE : GroupKind.ALL, children);
   968                 // write the content model
   969                 top.write(contentModelOwner);
   970             }
   972             // then attributes
   973             for (PropertyInfo<T,C> p : c.getProperties()) {
   974                 if (p instanceof AttributePropertyInfo) {
   975                     handleAttributeProp((AttributePropertyInfo<T,C>)p, contentModel);
   976                 }
   977             }
   978             if( c.hasAttributeWildcard()) {
   979                 contentModel.anyAttribute().namespace("##other").processContents("skip");
   980             }
   981             ct.commit();
   982         }
   984         /**
   985          * Writes the name attribute if it's named.
   986          */
   987         private void writeName(NonElement<T,C> c, TypedXmlWriter xw) {
   988             QName tn = c.getTypeName();
   989             if(tn!=null)
   990                 xw._attribute("name",tn.getLocalPart());  // named
   991         }
   993         private boolean containsValueProp(ClassInfo<T, C> c) {
   994             for (PropertyInfo p : c.getProperties()) {
   995                 if (p instanceof ValuePropertyInfo) return true;
   996             }
   997             return false;
   998         }
  1000         /**
  1001          * Builds content model writer for the specified property.
  1002          */
  1003         private Tree buildPropertyContentModel(PropertyInfo<T,C> p) {
  1004             switch(p.kind()) {
  1005             case ELEMENT:
  1006                 return handleElementProp((ElementPropertyInfo<T,C>)p);
  1007             case ATTRIBUTE:
  1008                 // attribuets are handled later
  1009                 return null;
  1010             case REFERENCE:
  1011                 return handleReferenceProp((ReferencePropertyInfo<T,C>)p);
  1012             case MAP:
  1013                 return handleMapProp((MapPropertyInfo<T,C>)p);
  1014             case VALUE:
  1015                 // value props handled above in writeClass()
  1016                 assert false;
  1017                 throw new IllegalStateException();
  1018             default:
  1019                 assert false;
  1020                 throw new IllegalStateException();
  1024         /**
  1025          * Generate the proper schema fragment for the given element property into the
  1026          * specified schema compositor.
  1028          * The element property may or may not represent a collection and it may or may
  1029          * not be wrapped.
  1031          * @param ep the element property
  1032          */
  1033         private Tree handleElementProp(final ElementPropertyInfo<T,C> ep) {
  1034             if (ep.isValueList()) {
  1035                 return new Tree.Term() {
  1036                     protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1037                         TypeRef<T,C> t = ep.getTypes().get(0);
  1038                         LocalElement e = parent.element();
  1039                         e.block(); // we will write occurs later
  1040                         QName tn = t.getTagName();
  1041                         e.name(tn.getLocalPart());
  1042                         List lst = e.simpleType().list();
  1043                         writeTypeRef(lst,t, "itemType");
  1044                         elementFormDefault.writeForm(e,tn);
  1045                         writeOccurs(e,isOptional||!ep.isRequired(),repeated);
  1047                 };
  1050             ArrayList<Tree> children = new ArrayList<Tree>();
  1051             for (final TypeRef<T,C> t : ep.getTypes()) {
  1052                 children.add(new Tree.Term() {
  1053                     protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1054                         LocalElement e = parent.element();
  1056                         QName tn = t.getTagName();
  1058                         PropertyInfo propInfo = t.getSource();
  1059                         TypeInfo parentInfo = (propInfo == null) ? null : propInfo.parent();
  1061                         if (canBeDirectElementRef(t, tn, parentInfo)) {
  1062                             if ((!t.getTarget().isSimpleType()) && (t.getTarget() instanceof ClassInfo) && collisionChecker.findDuplicate((ClassInfo<T, C>) t.getTarget())) {
  1063                                 e.ref(new QName(uri, tn.getLocalPart()));
  1064                             } else {
  1066                                 QName elemName = null;
  1067                                 if (t.getTarget() instanceof Element) {
  1068                                     Element te = (Element) t.getTarget();
  1069                                     elemName = te.getElementName();
  1072                                 Collection<TypeInfo> refs = propInfo.ref();
  1073                                 TypeInfo ti;
  1074                                 if ((refs != null) && (!refs.isEmpty()) && (elemName != null)
  1075                                         && ((ti = refs.iterator().next()) == null || ti instanceof ClassInfoImpl)) {
  1076                                     ClassInfoImpl cImpl = (ClassInfoImpl)ti;
  1077                                     if ((cImpl != null) && (cImpl.getElementName() != null)) {
  1078                                         e.ref(new QName(cImpl.getElementName().getNamespaceURI(), tn.getLocalPart()));
  1079                                     } else {
  1080                                         e.ref(new QName("", tn.getLocalPart()));
  1082                                 } else {
  1083                                     e.ref(tn);
  1086                         } else {
  1087                             e.name(tn.getLocalPart());
  1088                             writeTypeRef(e,t, "type");
  1089                             elementFormDefault.writeForm(e,tn);
  1092                         if (t.isNillable()) {
  1093                             e.nillable(true);
  1095                         if(t.getDefaultValue()!=null)
  1096                             e._default(t.getDefaultValue());
  1097                         writeOccurs(e,isOptional,repeated);
  1099                 });
  1102             final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children)
  1103                     .makeOptional(!ep.isRequired())
  1104                     .makeRepeated(ep.isCollection()); // see Spec table 8-13
  1107             final QName ename = ep.getXmlName();
  1108             if (ename != null) { // wrapped collection
  1109                 return new Tree.Term() {
  1110                     protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1111                         LocalElement e = parent.element();
  1112                         if(ename.getNamespaceURI().length()>0) {
  1113                             if (!ename.getNamespaceURI().equals(uri)) {
  1114                                 // TODO: we need to generate the corresponding element declaration for this
  1115                                 // table 8-25: Property/field element wrapper with ref attribute
  1116                                 e.ref(new QName(ename.getNamespaceURI(), ename.getLocalPart()));
  1117                                 return;
  1120                         e.name(ename.getLocalPart());
  1121                         elementFormDefault.writeForm(e,ename);
  1123                         if(ep.isCollectionNillable()) {
  1124                             e.nillable(true);
  1126                         writeOccurs(e,!ep.isCollectionRequired(),repeated);
  1128                         ComplexType p = e.complexType();
  1129                         choice.write(p);
  1131                 };
  1132             } else {// non-wrapped
  1133                 return choice;
  1137         /**
  1138          * Checks if we can collapse
  1139          * &lt;element name='foo' type='t' /> to &lt;element ref='foo' />.
  1141          * This is possible if we already have such declaration to begin with.
  1142          */
  1143         private boolean canBeDirectElementRef(TypeRef<T, C> t, QName tn, TypeInfo parentInfo) {
  1144             Element te = null;
  1145             ClassInfo ci = null;
  1146             QName targetTagName = null;
  1148             if(t.isNillable() || t.getDefaultValue()!=null) {
  1149                 // can't put those attributes on <element ref>
  1150                 return false;
  1153             if (t.getTarget() instanceof Element) {
  1154                 te = (Element) t.getTarget();
  1155                 targetTagName = te.getElementName();
  1156                 if (te instanceof ClassInfo) {
  1157                     ci = (ClassInfo)te;
  1161             String nsUri = tn.getNamespaceURI();
  1162             if ((!nsUri.equals(uri) && nsUri.length()>0) && (!((parentInfo instanceof ClassInfo) && (((ClassInfo)parentInfo).getTypeName() == null)))) {
  1163                 return true;
  1166             if ((ci != null) && ((targetTagName != null) && (te.getScope() == null) && (targetTagName.getNamespaceURI() == null))) {
  1167                 if (targetTagName.equals(tn)) {
  1168                     return true;
  1172             // we have the precise element defined already
  1173             if (te != null) { // it is instanceof Element
  1174                 return targetTagName!=null && targetTagName.equals(tn);
  1177             return false;
  1181         /**
  1182          * Generate an attribute for the specified property on the specified complexType
  1184          * @param ap the attribute
  1185          * @param attr the schema definition to which the attribute will be added
  1186          */
  1187         private void handleAttributeProp(AttributePropertyInfo<T,C> ap, AttrDecls attr) {
  1188             // attr is either a top-level ComplexType or a ComplexExtension
  1189             //
  1190             // [RESULT]
  1191             //
  1192             // <complexType ...>
  1193             //   <...>...</>
  1194             //   <attribute name="foo" type="xs:int"/>
  1195             // </>
  1196             //
  1197             // or
  1198             //
  1199             // <complexType ...>
  1200             //   <complexContent>
  1201             //     <extension ...>
  1202             //       <...>...</>
  1203             //     </>
  1204             //   </>
  1205             //   <attribute name="foo" type="xs:int"/>
  1206             // </>
  1207             //
  1208             // or it could also be an in-lined type (attr ref)
  1209             //
  1210             LocalAttribute localAttribute = attr.attribute();
  1212             final String attrURI = ap.getXmlName().getNamespaceURI();
  1213             if (attrURI.equals("") /*|| attrURI.equals(uri) --- those are generated as global attributes anyway, so use them.*/) {
  1214                 localAttribute.name(ap.getXmlName().getLocalPart());
  1216                 writeAttributeTypeRef(ap, localAttribute);
  1218                 attributeFormDefault.writeForm(localAttribute,ap.getXmlName());
  1219             } else { // generate an attr ref
  1220                 localAttribute.ref(ap.getXmlName());
  1223             if(ap.isRequired()) {
  1224                 // TODO: not type safe
  1225                 localAttribute.use("required");
  1229         private void writeAttributeTypeRef(AttributePropertyInfo<T,C> ap, AttributeType a) {
  1230             if( ap.isCollection() )
  1231                 writeTypeRef(a.simpleType().list(), ap, "itemType");
  1232             else
  1233                 writeTypeRef(a, ap, "type");
  1236         /**
  1237          * Generate the proper schema fragment for the given reference property into the
  1238          * specified schema compositor.
  1240          * The reference property may or may not refer to a collection and it may or may
  1241          * not be wrapped.
  1242          */
  1243         private Tree handleReferenceProp(final ReferencePropertyInfo<T, C> rp) {
  1244             // fill in content model
  1245             ArrayList<Tree> children = new ArrayList<Tree>();
  1247             for (final Element<T,C> e : rp.getElements()) {
  1248                 children.add(new Tree.Term() {
  1249                     protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1250                         LocalElement eref = parent.element();
  1252                         boolean local=false;
  1254                         QName en = e.getElementName();
  1255                         if(e.getScope()!=null) {
  1256                             // scoped. needs to be inlined
  1257                             boolean qualified = en.getNamespaceURI().equals(uri);
  1258                             boolean unqualified = en.getNamespaceURI().equals("");
  1259                             if(qualified || unqualified) {
  1260                                 // can be inlined indeed
  1262                                 // write form="..." if necessary
  1263                                 if(unqualified) {
  1264                                     if(elementFormDefault.isEffectivelyQualified)
  1265                                         eref.form("unqualified");
  1266                                 } else {
  1267                                     if(!elementFormDefault.isEffectivelyQualified)
  1268                                         eref.form("qualified");
  1271                                 local = true;
  1272                                 eref.name(en.getLocalPart());
  1274                                 // write out type reference
  1275                                 if(e instanceof ClassInfo) {
  1276                                     writeTypeRef(eref,(ClassInfo<T,C>)e,"type");
  1277                                 } else {
  1278                                     writeTypeRef(eref,((ElementInfo<T,C>)e).getContentType(),"type");
  1282                         if(!local)
  1283                             eref.ref(en);
  1284                         writeOccurs(eref,isOptional,repeated);
  1286                 });
  1289             final WildcardMode wc = rp.getWildcard();
  1290             if( wc != null ) {
  1291                 children.add(new Tree.Term() {
  1292                     protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1293                         Any any = parent.any();
  1294                         final String pcmode = getProcessContentsModeName(wc);
  1295                         if( pcmode != null ) any.processContents(pcmode);
  1296                         any.namespace("##other");
  1297                         writeOccurs(any,isOptional,repeated);
  1299                 });
  1303             final Tree choice = Tree.makeGroup(GroupKind.CHOICE, children).makeRepeated(rp.isCollection()).makeOptional(!rp.isRequired());
  1305             final QName ename = rp.getXmlName();
  1307             if (ename != null) { // wrapped
  1308                 return new Tree.Term() {
  1309                     protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1310                         LocalElement e = parent.element().name(ename.getLocalPart());
  1311                         elementFormDefault.writeForm(e,ename);
  1312                         if(rp.isCollectionNillable())
  1313                             e.nillable(true);
  1314                         writeOccurs(e,true,repeated);
  1316                         ComplexType p = e.complexType();
  1317                         choice.write(p);
  1319                 };
  1320             } else { // unwrapped
  1321                 return choice;
  1325         /**
  1326          * Generate the proper schema fragment for the given map property into the
  1327          * specified schema compositor.
  1329          * @param mp the map property
  1330          */
  1331         private Tree handleMapProp(final MapPropertyInfo<T,C> mp) {
  1332             return new Tree.Term() {
  1333                 protected void write(ContentModelContainer parent, boolean isOptional, boolean repeated) {
  1334                     QName ename = mp.getXmlName();
  1336                     LocalElement e = parent.element();
  1337                     elementFormDefault.writeForm(e,ename);
  1338                     if(mp.isCollectionNillable())
  1339                         e.nillable(true);
  1341                     e = e.name(ename.getLocalPart());
  1342                     writeOccurs(e,isOptional,repeated);
  1343                     ComplexType p = e.complexType();
  1345                     // TODO: entry, key, and value are always unqualified. that needs to be fixed, too.
  1346                     // TODO: we need to generate the corresponding element declaration, if they are qualified
  1347                     e = p.sequence().element();
  1348                     e.name("entry").minOccurs(0).maxOccurs("unbounded");
  1350                     ExplicitGroup seq = e.complexType().sequence();
  1351                     writeKeyOrValue(seq, "key", mp.getKeyType());
  1352                     writeKeyOrValue(seq, "value", mp.getValueType());
  1354             };
  1357         private void writeKeyOrValue(ExplicitGroup seq, String tagName, NonElement<T, C> typeRef) {
  1358             LocalElement key = seq.element().name(tagName);
  1359             key.minOccurs(0);
  1360             writeTypeRef(key, typeRef, "type");
  1363         public void addGlobalAttribute(AttributePropertyInfo<T,C> ap) {
  1364             attributeDecls.put( ap.getXmlName().getLocalPart(), ap );
  1365             addDependencyTo(ap.getTarget().getTypeName());
  1368         public void addGlobalElement(TypeRef<T,C> tref) {
  1369             elementDecls.put( tref.getTagName().getLocalPart(), new ElementWithType(false,tref.getTarget()) );
  1370             addDependencyTo(tref.getTarget().getTypeName());
  1373         @Override
  1374         public String toString() {
  1375             StringBuilder buf = new StringBuilder();
  1376             buf.append("[classes=").append(classes);
  1377             buf.append(",elementDecls=").append(elementDecls);
  1378             buf.append(",enums=").append(enums);
  1379             buf.append("]");
  1380             return super.toString();
  1383         /**
  1384          * Represents a global element declaration to be written.
  1386          * <p>
  1387          * Because multiple properties can name the same global element even if
  1388          * they have different Java type, the schema generator first needs to
  1389          * walk through the model and decide what to generate for the given
  1390          * element declaration.
  1392          * <p>
  1393          * This class represents what will be written, and its {@link #equals(Object)}
  1394          * method is implemented in such a way that two identical declarations
  1395          * are considered as the same.
  1396          */
  1397         abstract class ElementDeclaration {
  1398             /**
  1399              * Returns true if two {@link ElementDeclaration}s are representing
  1400              * the same schema fragment.
  1401              */
  1402             @Override
  1403             public abstract boolean equals(Object o);
  1404             @Override
  1405             public abstract int hashCode();
  1407             /**
  1408              * Generates the declaration.
  1409              */
  1410             public abstract void writeTo(String localName, Schema schema);
  1413         /**
  1414          * {@link ElementDeclaration} that refers to a {@link NonElement}.
  1415          */
  1416         class ElementWithType extends ElementDeclaration {
  1417             private final boolean nillable;
  1418             private final NonElement<T,C> type;
  1420             public ElementWithType(boolean nillable,NonElement<T, C> type) {
  1421                 this.type = type;
  1422                 this.nillable = nillable;
  1425             public void writeTo(String localName, Schema schema) {
  1426                 TopLevelElement e = schema.element().name(localName);
  1427                 if(nillable)
  1428                     e.nillable(true);
  1429                 if (type != null) {
  1430                     writeTypeRef(e,type, "type");
  1431                 } else {
  1432                     e.complexType();    // refer to the nested empty complex type
  1434                 e.commit();
  1437             public boolean equals(Object o) {
  1438                 if (this == o) return true;
  1439                 if (o == null || getClass() != o.getClass()) return false;
  1441                 final ElementWithType that = (ElementWithType) o;
  1442                 return type.equals(that.type);
  1445             public int hashCode() {
  1446                 return type.hashCode();
  1451     /**
  1452      * Examine the specified element ref and determine if a swaRef attribute needs to be generated
  1453      * @param typeRef
  1454      */
  1455     private boolean generateSwaRefAdapter(NonElementRef<T,C> typeRef) {
  1456         return generateSwaRefAdapter(typeRef.getSource());
  1459     /**
  1460      * Examine the specified element ref and determine if a swaRef attribute needs to be generated
  1461      */
  1462     private boolean generateSwaRefAdapter(PropertyInfo<T,C> prop) {
  1463         final Adapter<T,C> adapter = prop.getAdapter();
  1464         if (adapter == null) return false;
  1465         final Object o = navigator.asDecl(SwaRefAdapter.class);
  1466         if (o == null) return false;
  1467         return (o.equals(adapter.adapterType));
  1470     /**
  1471      * Debug information of what's in this {@link XmlSchemaGenerator}.
  1472      */
  1473     @Override
  1474     public String toString() {
  1475         StringBuilder buf = new StringBuilder();
  1476         for (Namespace ns : namespaces.values()) {
  1477             if(buf.length()>0)  buf.append(',');
  1478             buf.append(ns.uri).append('=').append(ns);
  1480         return super.toString()+'['+buf+']';
  1483     /**
  1484      * return the string representation of the processContents mode of the
  1485      * give wildcard, or null if it is the schema default "strict"
  1487      */
  1488     private static String getProcessContentsModeName(WildcardMode wc) {
  1489         switch(wc) {
  1490         case LAX:
  1491         case SKIP:
  1492             return wc.name().toLowerCase();
  1493         case STRICT:
  1494             return null;
  1495         default:
  1496             throw new IllegalStateException();
  1501     /**
  1502      * Relativizes a URI by using another URI (base URI.)
  1504      * <p>
  1505      * For example, {@code relative("http://www.sun.com/abc/def","http://www.sun.com/pqr/stu") => "../abc/def"}
  1507      * <p>
  1508      * This method only works on hierarchical URI's, not opaque URI's (refer to the
  1509      * <a href="http://java.sun.com/j2se/1.5.0/docs/api/java/net/URI.html">java.net.URI</a>
  1510      * javadoc for complete definitions of these terms.
  1512      * <p>
  1513      * This method will not normalize the relative URI.
  1515      * @return the relative URI or the original URI if a relative one could not be computed
  1516      */
  1517     protected static String relativize(String uri, String baseUri) {
  1518         try {
  1519             assert uri!=null;
  1521             if(baseUri==null)   return uri;
  1523             URI theUri = new URI(escapeURI(uri));
  1524             URI theBaseUri = new URI(escapeURI(baseUri));
  1526             if (theUri.isOpaque() || theBaseUri.isOpaque())
  1527                 return uri;
  1529             if (!equalsIgnoreCase(theUri.getScheme(), theBaseUri.getScheme()) ||
  1530                     !equal(theUri.getAuthority(), theBaseUri.getAuthority()))
  1531                 return uri;
  1533             String uriPath = theUri.getPath();
  1534             String basePath = theBaseUri.getPath();
  1536             // normalize base path
  1537             if (!basePath.endsWith("/")) {
  1538                 basePath = normalizeUriPath(basePath);
  1541             if( uriPath.equals(basePath))
  1542                 return ".";
  1544             String relPath = calculateRelativePath(uriPath, basePath, fixNull(theUri.getScheme()).equals("file"));
  1546             if (relPath == null)
  1547                 return uri; // recursion found no commonality in the two uris at all
  1548             StringBuilder relUri = new StringBuilder();
  1549             relUri.append(relPath);
  1550             if (theUri.getQuery() != null)
  1551                 relUri.append('?').append(theUri.getQuery());
  1552             if (theUri.getFragment() != null)
  1553                 relUri.append('#').append(theUri.getFragment());
  1555             return relUri.toString();
  1556         } catch (URISyntaxException e) {
  1557             throw new InternalError("Error escaping one of these uris:\n\t"+uri+"\n\t"+baseUri);
  1561     private static String fixNull(String s) {
  1562         if(s==null)     return "";
  1563         else            return s;
  1566     private static String calculateRelativePath(String uri, String base, boolean fileUrl) {
  1567         // if this is a file URL (very likely), and if this is on a case-insensitive file system,
  1568         // then treat it accordingly.
  1569         boolean onWindows = File.pathSeparatorChar==';';
  1571         if (base == null) {
  1572             return null;
  1574         if ((fileUrl && onWindows && startsWithIgnoreCase(uri,base)) || uri.startsWith(base)) {
  1575             return uri.substring(base.length());
  1576         } else {
  1577             return "../" + calculateRelativePath(uri, getParentUriPath(base), fileUrl);
  1581     private static boolean startsWithIgnoreCase(String s, String t) {
  1582         return s.toUpperCase().startsWith(t.toUpperCase());
  1585     /**
  1586      * JAX-RPC wants the namespaces to be sorted in the reverse order
  1587      * so that the empty namespace "" comes to the very end. Don't ask me why.
  1588      */
  1589     private static final Comparator<String> NAMESPACE_COMPARATOR = new Comparator<String>() {
  1590         public int compare(String lhs, String rhs) {
  1591             return -lhs.compareTo(rhs);
  1593     };
  1595     private static final String newline = "\n";

mercurial