gt:             QName tn = c.getTypeName();
context: /*
   * Copyright (c) 1997, 2014, 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 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,"Wrigin 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());
                  }
              }
          }
  
          // 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;
  
          public Namespace(String uri) {
              this.uri = uri;
              assert !XmlSchemaGenerator.this.namespaces.containsKey(uri);
              XmlSchemaGenerator.this.namespaces.put(uri,this);
          }
  
          /**
           * 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 &lt;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) {
              // 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) {
  