/*
 *    GeoTools - OpenSource mapping toolkit
 *    http://geotools.org
 *    (C) 2004-2006, GeoTools Project Managment Committee (PMC)
 *    
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License as published by the Free Software Foundation;
 *    version 2.1 of the License.
 *
 *    This library 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
 *    Lesser General Public License for more details.
 */
package org.geotools.xml.handlers.xsi;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.TreeSet;

import org.geotools.xml.SchemaFactory;
import org.geotools.xml.XSIElementHandler;
import org.geotools.xml.schema.All;
import org.geotools.xml.schema.Attribute;
import org.geotools.xml.schema.AttributeGroup;
import org.geotools.xml.schema.Choice;
import org.geotools.xml.schema.ComplexType;
import org.geotools.xml.schema.Element;
import org.geotools.xml.schema.ElementGrouping;
import org.geotools.xml.schema.Facet;
import org.geotools.xml.schema.Group;
import org.geotools.xml.schema.Schema;
import org.geotools.xml.schema.Sequence;
import org.geotools.xml.schema.SimpleType;
import org.geotools.xml.schema.Type;
import org.geotools.xml.xsi.XSISimpleTypes;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;

/**
 * SchemaHandler purpose.
 * <p>
 * represents a Schema element
 * </p>
 * 
 * @author dzwiers, Refractions Research, Inc. http://www.refractions.net
 * @author $Author:$ (last modification)
 * @source $URL: http://svn.geotools.org/geotools/tags/2.4.1/modules/library/xml/src/main/java/org/geotools/xml/handlers/xsi/SchemaHandler.java $
 * @version $Id: SchemaHandler.java 22266 2006-10-19 11:30:55Z acuster $
 */
public class SchemaHandler extends XSIElementHandler {
    /** "http://www.w3.org/2001/XMLSchema" */
    public static final String namespaceURI = "http://www.w3.org/2001/XMLSchema";

    /** 'schema' */
    public final static String LOCALNAME = "schema";
    private String id;
    private String prefix;
    private URI targetNamespace;
    private String version;
    private boolean elementFormDefault;
    private boolean attributeFormDefault;
    private int finalDefault;
    private int blockDefault;
    private List includes;
    private List imports;
    private List redefines;
    private List attributes;
    private List attributeGroups;
    private List complexTypes;
    private List elements;
    private List groups;
    private List simpleTypes;
    private URI uri;
    private Schema schema = null;
    private HashMap prefixCache;

    /**
     * @see java.lang.Object#hashCode()
     */
    public int hashCode() {
        return LOCALNAME.hashCode() * ((id == null) ? 1 : id.hashCode()) * ((version == null) ? 1 : version.hashCode())
                * ((targetNamespace == null) ? 1 : targetNamespace.hashCode());
    }

    /**
     * @see org.xml.sax.ContentHandler#startPrefixMapping(java.lang.String, java.lang.String)
     */
    public void startPrefixMapping( String pref, String uri1 ) {
        if (targetNamespace == null) {
            if (prefixCache == null) {
                prefixCache = new HashMap();
            }
            
            if( !pref.trim().equals("") || !prefixCache.containsKey(uri1) )
                prefixCache.put(uri1, pref);
                
            if (this.uri == null && (pref == null || "".equals(pref)))
                try {
                    this.uri = new URI(uri1);
                } catch (URISyntaxException e) {
                    logger.warning(e.getMessage());
                }
        } else {
            // we have already started
            if (targetNamespace.equals(uri1.toString())) {
                prefix = pref;
            }
        }
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#startElement(java.lang.String, java.lang.String, org.xml.sax.Attributes)
     */
    public void startElement( String namespaceURI1, String localName, Attributes atts ) throws SAXException {
        // targetNamespace
        String targetNamespace1 = atts.getValue("", "targetNamespace");

        if (targetNamespace1 == null) {
            targetNamespace1 = atts.getValue(namespaceURI1, "targetNamespace");
        }

        try {
            this.targetNamespace = new URI(targetNamespace1);
        } catch (URISyntaxException e) {
            logger.warning(e.toString());
            throw new SAXException(e);
        }

        if ((prefixCache != null) && (targetNamespace1 != null) && (!targetNamespace1.equals(""))) {
            Iterator i = prefixCache.keySet().iterator();

            while( (i != null) && i.hasNext() ) {
                String uriT = (String) i.next();

                if (targetNamespace1.equals(uriT)) {
                    prefix = (String) prefixCache.get(uriT);
                    i = null;
                }
            }
        }

        // prefixCache = null;

        // attributeFormDefault
        String attributeFormDefault1 = atts.getValue("", "attributeFormDefault");

        if (attributeFormDefault1 == null) {
            attributeFormDefault1 = atts.getValue(namespaceURI1, "attributeFormDefault");
        }

        this.attributeFormDefault = "qualified".equalsIgnoreCase(attributeFormDefault1);

        // blockDefault
        String blockDefault1 = atts.getValue("", "blockDefault");

        if (blockDefault1 == null) {
            blockDefault1 = atts.getValue(namespaceURI1, "blockDefault");
        }

        this.blockDefault = ComplexTypeHandler.findBlock(blockDefault1);

        // elementFormDefault
        String elementFormDefault1 = atts.getValue("", "elementFormDefault");

        if (elementFormDefault1 == null) {
            elementFormDefault1 = atts.getValue(namespaceURI1, "elementFormDefault");
        }

        this.elementFormDefault = "qualified".equalsIgnoreCase(elementFormDefault1);

        // finalDefault
        String finalDefault1 = atts.getValue("", "finalDefault");

        if (finalDefault1 == null) {
            finalDefault1 = atts.getValue(namespaceURI1, "finalDefault");
        }

        this.finalDefault = SimpleTypeHandler.findFinal(finalDefault1);

        // id
        id = atts.getValue("", "id");

        if (id == null) {
            id = atts.getValue(namespaceURI1, "id");
        }

        // version
        version = atts.getValue("", "version");

        if (version == null) {
            version = atts.getValue(namespaceURI1, "version");
        }
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#getHandler(java.lang.String, java.lang.String)
     */
    public XSIElementHandler getHandler( String namespaceURI1, String localName ) {
        // check that we are working with a known namespace
        if (SchemaHandler.namespaceURI.equalsIgnoreCase(namespaceURI1)) {
            // child elements:
            //
            // This list order has been picked in an adhock manner
            // attempting to improve performance. Re-order the
            // child elements if this order does not appear optimal.
            // complexType
            if (ComplexTypeHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (complexTypes == null) {
                    complexTypes = new LinkedList();
                }

                ComplexTypeHandler cth = new ComplexTypeHandler();
                complexTypes.add(cth);

                return cth;
            }

            // simpleType
            if (SimpleTypeHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (simpleTypes == null) {
                    simpleTypes = new LinkedList();
                }

                SimpleTypeHandler sth = new SimpleTypeHandler();
                simpleTypes.add(sth);

                return sth;
            }

            // element
            if (ElementTypeHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (elements == null) {
                    elements = new LinkedList();
                }

                ElementTypeHandler eth = new ElementTypeHandler();
                elements.add(eth);

                return eth;
            }

            // attribute
            if (AttributeHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (attributes == null) {
                    attributes = new LinkedList();
                }

                AttributeHandler ah = new AttributeHandler();
                attributes.add(ah);

                return ah;
            }

            // include
            if (IncludeHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (includes == null) {
                    includes = new LinkedList();
                }

                IncludeHandler ih = new IncludeHandler();
                includes.add(ih);

                return ih;
            }

            // import
            if (ImportHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (imports == null) {
                    imports = new LinkedList();
                }

                ImportHandler ih = new ImportHandler();
                imports.add(ih);

                return ih;
            }

            // group
            if (GroupHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (groups == null) {
                    groups = new LinkedList();
                }

                GroupHandler gh = new GroupHandler();
                groups.add(gh);

                return gh;
            }

            // redefine
            if (RedefineHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (redefines == null) {
                    redefines = new LinkedList();
                }

                RedefineHandler rh = new RedefineHandler();
                redefines.add(rh);

                return rh;
            }

            // attributeGroup
            if (AttributeGroupHandler.LOCALNAME.equalsIgnoreCase(localName)) {
                if (attributeGroups == null) {
                    attributeGroups = new LinkedList();
                }

                AttributeGroupHandler agh = new AttributeGroupHandler();
                attributeGroups.add(agh);

                return agh;
            }
        }

        return null;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#getLocalName()
     */
    public String getLocalName() {
        return LOCALNAME;
    }

    /**
     * <p>
     * creates a smaller, more compact version of the schema
     * </p>
     * 
     * @param thisURI
     * @throws SAXException
     */
    protected Schema compress( URI thisURI ) throws SAXException {
        if (schema != null) {
            return schema; // already compressed.
        }

        if (uri == null) {
            uri = thisURI;
        } else {
            if (thisURI != null) {
                uri = thisURI.resolve(uri);
            }
        }

        if (prefix == null && prefixCache != null) {
            prefix = (String) prefixCache.get(targetNamespace);
        }

        Iterator it = null;

        if (includes != null) {
            // do these first
            it = includes.iterator();

            while( it.hasNext() ) {
                IncludeHandler inc = (IncludeHandler) it.next();
                logger.finest("compressing include " + inc.getSchemaLocation());

                if (inc != null && inc.getSchemaLocation() != null) {
                    Schema cs;
                    URI incURI = null;
                    if (thisURI == null) {
                        try {
                            incURI = new URI(inc.getSchemaLocation());
                        } catch (URISyntaxException e) {
                            logger.warning(e.getMessage());
                        }
                    } else {
                        incURI = thisURI.normalize().resolve(inc.getSchemaLocation());
                    }
                    cs = SchemaFactory.getInstance(targetNamespace, incURI, logger.getLevel());

                    if (uri != null) {
                        uri = incURI.resolve(uri);
                    } else {
                        uri = incURI;
                    }

                    // already compressed
                    addSchema(cs);
                }
            }
        }

        includes = null;

        // imports may be schema or schemaHandler
        if (this.imports != null) {
            // have now loaded the included stuff.
            LinkedList imports1 = new LinkedList();
            it = this.imports.iterator();

            while( it.hasNext() ) {
                Object obj = it.next();

                if (obj instanceof ImportHandler) {
                    ImportHandler imp = (ImportHandler) obj;
                    URI incURI = null;

                    // if ((imp.getSchemaLocation() != null) && (thisURI != null)) {
                    // incURI = thisURI.normalize().resolve(imp
                    // .getSchemaLocation());
                    // }
                    // fix from chris dillard
                    if (imp.getSchemaLocation() != null) {
                        if (thisURI != null) {
                            // For resolving relative URIs
                            incURI = thisURI.normalize().resolve(imp.getSchemaLocation());
                        } else {
                            // If thisURI is null, we have to assume the
                            // URI is absolute.
                            incURI = imp.getSchemaLocation();
                        }
                    }

                    Schema cs = SchemaFactory.getInstance(imp.getNamespace(), incURI, logger.getLevel());

                    imports1.add(cs);
                } else {
                    imports1.add(obj);
                }
            }

            this.imports = imports1;
        }

        // TODO redefines?
        // should do over-writing here
        // build schema object ... be thrown out
        DefaultSchema schema1 = new DefaultSchema();
        schema1.attributeFormDefault = attributeFormDefault;
        schema1.elementFormDefault = elementFormDefault;
        schema1.finalDefault = finalDefault;
        schema1.blockDefault = blockDefault;
        schema1.id = id;
        schema1.prefix = prefix;
        schema1.targetNamespace = targetNamespace;
        schema1.version = version;
        schema1.uri = uri;

        if (imports != null) {
            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(imports);
            schema1.imports = (Schema[]) tmp.toArray(new Schema[tmp.size()]);
        }

        // these need to be retyped

        if (simpleTypes != null) {
            it = simpleTypes.iterator();
            HashSet cache = new HashSet();
            while( it.hasNext() ) {
                Object t = it.next();
                if (t instanceof SimpleTypeHandler) {
                    SimpleTypeHandler tt = (SimpleTypeHandler) t;
                    cache.add(tt.compress(this));
                    it.remove();
                }
            }
            it = cache.iterator();
            while( it.hasNext() )
                simpleTypes.add(it.next());

            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(simpleTypes);
            schema1.simpleTypes = (SimpleType[]) tmp.toArray(new SimpleType[tmp.size()]);
        }

        if (attributeGroups != null) {
            it = attributeGroups.iterator();
            HashSet cache = new HashSet();
            while( it.hasNext() ) {
                Object t = it.next();
                if (t instanceof AttributeGroupHandler) {
                    AttributeGroupHandler tt = (AttributeGroupHandler) t;
                    cache.add(tt.compress(this));
                    it.remove();
                }
            }
            it = cache.iterator();
            while( it.hasNext() )
                attributeGroups.add(it.next());

            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(attributeGroups);
            schema1.attributeGroups = (AttributeGroup[]) tmp.toArray(new AttributeGroup[tmp
                    .size()]);
        }

        if (attributes != null) {
            it = attributes.iterator();
            HashSet cache = new HashSet();
            while( it.hasNext() ) {
                Object t = it.next();
                if (t instanceof AttributeHandler) {
                    AttributeHandler tt = (AttributeHandler) t;
                    cache.add(tt.compress(this));
                    it.remove();
                }
            }
            it = cache.iterator();
            while( it.hasNext() )
                attributes.add(it.next());

            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(attributes);
            schema1.attributes = (Attribute[]) tmp.toArray(new Attribute[tmp.size()]);
        }

        if (complexTypes != null) {
            it = complexTypes.iterator();
            HashSet cache = new HashSet();
            while( it.hasNext() ) {
                Object t = it.next();
                if (t instanceof ComplexTypeHandler) {
                    ComplexTypeHandler tt = (ComplexTypeHandler) t;
                    cache.add(tt.compress(this));
                }
            }
            complexTypes.clear();
            it = cache.iterator();
            while( it.hasNext() )
                complexTypes.add(it.next());

            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(complexTypes);
            schema1.complexTypes = (ComplexType[]) tmp.toArray(new ComplexType[tmp.size()]);
        }

        if (elements != null) {
            it = elements.iterator();
            HashSet cache = new HashSet();
            while( it.hasNext() ) {
                Object t = it.next();
                if (t instanceof ElementTypeHandler) {
                    ElementTypeHandler tt = (ElementTypeHandler) t;
                    cache.add(tt.compress(this));
                    it.remove();
                }
            }
            it = cache.iterator();
            while( it.hasNext() )
                elements.add(it.next());

            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(elements);
            schema1.elements = (Element[]) tmp.toArray(new Element[tmp.size()]);
        }

        if (groups != null) {
            it = groups.iterator();
            HashSet cache = new HashSet();
            while( it.hasNext() ) {
                Object t = it.next();
                if (t instanceof GroupHandler) {
                    GroupHandler tt = (GroupHandler) t;
                    cache.add(tt.compress(this));
                    it.remove();
                }
            }
            it = cache.iterator();
            while( it.hasNext() )
                groups.add(it.next());

            TreeSet tmp = new TreeSet(SchemaComparator.getInstance());
            tmp.addAll(groups);
            schema1.groups = (Group[]) tmp.toArray(new Group[tmp.size()]);
        }

        attributeGroups = attributes = complexTypes = simpleTypes = elements = groups = imports = includes = redefines = null;

        return schema1;
    }

    /*
     * Helper method for lookUpSimpleType(String)
     */
    private SimpleType lookUpSimpleType( String localName, Schema s, TreeSet targets ) {
        if (s == null) {
            return null;
        }

        targets.add(s.getTargetNamespace());

        if (s.getSimpleTypes() != null) {
            SimpleType[] sts = s.getSimpleTypes();

            for( int i = 0; (sts != null) && (i < sts.length); i++ ) {
                if (localName.equalsIgnoreCase(sts[i].getName())) {
                    return sts[i];
                }
            }
        }

        if (s.getImports() != null) {
            Schema[] ss = s.getImports();

            for( int i = 0; (ss != null) && (i < ss.length); i++ ) {
                if (!targets.contains(ss[i].getTargetNamespace())) {
                    SimpleType st = lookUpSimpleType(localName, ss[i], targets);

                    if (st != null) {
                        return st;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for package classes
     * </p>
     * 
     * @param qname
     */
    protected SimpleType lookUpSimpleType( String qname ) {
        int index = qname.indexOf(":");
        String localName, prefix1;
        localName = prefix1 = null;
        if (index >= 0) {
            localName = qname.substring(index + 1);
            prefix1 = qname.substring(0, index);
        } else {
            prefix1 = "";
            localName = qname;
        }
        logger.finest("prefix is " + prefix1);
        logger.finest("localName is " + localName);
        Iterator it;
        if ((this.prefix == null && prefix1 == null) || (this.prefix != null && this.prefix.equals(prefix1))) {
            if (schema != null)
                return lookUpSimpleType(localName, schema, new TreeSet());
        } else {
            if (imports != null) {
                it = imports.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();

                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        SimpleType st = lookUpSimpleType(localName, s, new TreeSet());
                        if (st != null) {
                            return st;
                        }
                    }
                }
            }
        }

        if (simpleTypes != null) {
            it = simpleTypes.iterator();

            while( it.hasNext() ) {
                Object o = it.next();

                if (o instanceof SimpleTypeHandler) {
                    SimpleTypeHandler sst = (SimpleTypeHandler) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst.compress(this);
                    }
                } else {
                    SimpleType sst = (SimpleType) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst;
                    }
                }
            }
        }

        SimpleType sti = XSISimpleTypes.find(localName);

        if (sti != null) {
            return sti;
        }

        return null;
    }

    /*
     * helper for lookUpComplexType(String)
     */
    private ComplexType lookUpComplexType( String localName, Schema s, TreeSet targets ) {
        if (s == null) {
            return null;
        }

        targets.add(s.getTargetNamespace());

        if (s.getComplexTypes() != null) {
            ComplexType[] sts = s.getComplexTypes();

            for( int i = 0; (sts != null) && (i < sts.length); i++ ) {
                String name = sts[i].getName();
				if (localName.equalsIgnoreCase(name)) {
                    return sts[i];
                }
            }
        }

        if (s.getImports() != null) {
            Schema[] ss = s.getImports();

            for( int i = 0; (ss != null) && (i < ss.length); i++ ) {
                if (!targets.contains(ss[i].getTargetNamespace())) {
                    ComplexType st = lookUpComplexType(localName, ss[i], targets);

                    if (st != null) {
                        return st;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for package
     * </p>
     * 
     * @param qname
     * @throws SAXException
     */
    protected ComplexType lookUpComplexType( String qname ) throws SAXException {
        int index = qname.indexOf(":");
        String localName, prefix1;
        localName = prefix1 = null;
        if (index >= 0) {
            localName = qname.substring(index + 1);
            prefix1 = qname.substring(0, index);
        } else {
            prefix1 = "";
            localName = qname;
        }
        logger.finest("prefix is " + prefix1);
        logger.finest("localName is " + localName);

        Iterator it;
        if ((this.prefix == null && prefix1 == null) || (this.prefix != null && this.prefix.equals(prefix1))) {
            if (schema != null)
                return lookUpComplexType(localName, schema, new TreeSet());
        } else {
            if (imports != null) {
                it = imports.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();
                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        ComplexType ct = lookUpComplexType(localName, s, new TreeSet());
                        if (ct != null) {
                            return ct;
                        }
                    }
                }
            }
        }

        if (complexTypes != null) {
            it = complexTypes.iterator();

            while( it.hasNext() ) {
                Object o = it.next();

                if (o instanceof ComplexTypeHandler) {
                    ComplexTypeHandler sst = (ComplexTypeHandler) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst.compress(this);
                    }
                } else {
                    ComplexType sst = (ComplexType) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst;
                    }
                }
            }
        }

        return null;
    }

    /*
     * helper method for lookupElement(String)
     */
    private Element lookupElement( String localName, Schema s, TreeSet targets ) {
        if (s == null) {
            return null;
        }

        logger.finest("looking for element in " + s.getTargetNamespace());
        targets.add(s.getTargetNamespace());

        if (s.getElements() != null) {
            Element[] sts = s.getElements();

            for( int i = 0; (sts != null) && (i < sts.length); i++ ) {
                String name = sts[i].getName();
				logger.finest("checking element " + name);

                if (localName.equalsIgnoreCase(name)) {
                    return sts[i];
                }
            }
        }

        if (s.getImports() != null) {
            Schema[] ss = s.getImports();

            for( int i = 0; (ss != null) && (i < ss.length); i++ ) {
                if (!targets.contains(ss[i].getTargetNamespace())) {
                    Element st = lookupElement(localName, ss[i], targets);

                    if (st != null) {
                        return st;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for package
     * </p>
     * 
     * @param qname
     * @throws SAXException
     */
    protected Element lookUpElement( String qname ) throws SAXException {
        int index = qname.indexOf(":");
        String localName, prefix1;
        localName = prefix1 = null;
        if (index >= 0) {
            localName = qname.substring(index + 1);
            prefix1 = qname.substring(0, index);
        } else {
            prefix1 = "";
            localName = qname;
        }
        logger.finest("prefix is " + prefix1);
        logger.finest("localName is " + localName);

        Iterator it;
        if ((this.prefix == null && prefix1 == null) || (this.prefix != null && this.prefix.equals(prefix1))) {
            if (schema != null)
                return lookupElement(localName, schema, new TreeSet());
        } else {
            if (imports != null) {
                it = imports.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();
                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        Element ct = lookupElement(localName, s, new TreeSet());
                        if (ct != null) {
                            return ct;
                        }
                    }
                }
            }
            if (includes != null) {
                it = includes.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();
                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        Element ct = lookupElement(localName, s, new TreeSet());
                        if (ct != null) {
                            return ct;
                        }
                    }
                }
            }
        }

        it = elements.iterator();

        while( it.hasNext() ) {
            Object o = it.next();

            if (o instanceof ElementTypeHandler) {
                ElementTypeHandler sst = (ElementTypeHandler) o;

                if (localName.equalsIgnoreCase(sst.getName())) {
                    return (Element) sst.compress(this);
                }
            } else {
                Element sst = (Element) o;

                if (localName.equalsIgnoreCase(sst.getName())) {
                    return sst;
                }
            }
        }
        return null;
    }

    /*
     * helper for lookUpGroup
     */
    private Group lookUpGroup( String localName, Schema s, TreeSet targets ) {
        if (s == null) {
            return null;
        }

        targets.add(s.getTargetNamespace());

        if (s.getGroups() != null) {
            Group[] sts = s.getGroups();

            for( int i = 0; (sts != null) && (i < sts.length); i++ ) {
                if (localName.equalsIgnoreCase(sts[i].getName())) {
                    return sts[i];
                }
            }
        }

        if (s.getImports() != null) {
            Schema[] ss = s.getImports();

            for( int i = 0; (ss != null) && (i < ss.length); i++ ) {
                if (!targets.contains(ss[i].getTargetNamespace())) {
                    Group st = lookUpGroup(localName, ss[i], targets);

                    if (st != null) {
                        return st;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for package
     * </p>
     * 
     * @param qname
     * @throws SAXException
     */
    protected Group lookUpGroup( String qname ) throws SAXException {
        int index = qname.indexOf(":");
        String localName, prefix1;
        localName = prefix1 = null;
        if (index >= 0) {
            localName = qname.substring(index + 1);
            prefix1 = qname.substring(0, index);
        } else {
            prefix1 = "";
            localName = qname;
        }
        logger.finest("prefix is " + prefix1);
        logger.finest("localName is " + localName);

        Iterator it;
        if ((this.prefix == null && prefix1 == null) || (this.prefix != null && this.prefix.equals(prefix1))) {
            if (schema != null)
                return lookUpGroup(localName, schema, new TreeSet());
        } else {
            if (imports != null) {
                it = imports.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();
                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        Group ct = lookUpGroup(localName, s, new TreeSet());
                        if (ct != null) {
                            return ct;
                        }
                    }
                }
            }
        }

        if (groups != null) {
            it = groups.iterator();

            while( it.hasNext() ) {
                Object o = it.next();

                if (o instanceof GroupHandler) {
                    GroupHandler sst = (GroupHandler) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return (Group) sst.compress(this);
                    }
                } else {
                    Group sst = (Group) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst;
                    }
                }
            }
        }

        return null;
    }

    /*
     * helper method for lookUpAttributeGroup
     */
    private AttributeGroup lookUpAttributeGroup( String localName, Schema s, TreeSet targets ) {
        if (s == null) {
            return null;
        }

        targets.add(s.getTargetNamespace());

        if (s.getAttributeGroups() != null) {
            AttributeGroup[] sts = s.getAttributeGroups();

            for( int i = 0; (sts != null) && (i < sts.length); i++ ) {
                if (localName.equalsIgnoreCase(sts[i].getName())) {
                    return sts[i];
                }
            }
        }

        if (s.getImports() != null) {
            Schema[] ss = s.getImports();

            for( int i = 0; (ss != null) && (i < ss.length); i++ ) {
                if (!targets.contains(ss[i].getTargetNamespace())) {
                    AttributeGroup st = lookUpAttributeGroup(localName, ss[i], targets);

                    if (st != null) {
                        return st;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for the package
     * </p>
     * 
     * @param qname
     * @throws SAXException
     */
    protected AttributeGroup lookUpAttributeGroup( String qname ) throws SAXException {
        int index = qname.indexOf(":");
        String localName, prefix1;
        localName = prefix1 = null;
        if (index >= 0) {
            localName = qname.substring(index + 1);
            prefix1 = qname.substring(0, index);
        } else {
            prefix1 = "";
            localName = qname;
        }
        logger.finest("prefix is " + prefix1);
        logger.finest("localName is " + localName);

        Iterator it;
        if ((this.prefix == null && prefix1 == null) || (this.prefix != null && this.prefix.equals(prefix1))) {
            if (schema != null)
                return lookUpAttributeGroup(localName, schema, new TreeSet());
        } else {
            if (imports != null) {
                it = imports.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();
                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        AttributeGroup ct = lookUpAttributeGroup(localName, s, new TreeSet());
                        if (ct != null) {
                            return ct;
                        }
                    }
                }
            }
        }

        if (attributeGroups != null) {
            it = attributeGroups.iterator();

            while( it.hasNext() ) {
                Object o = it.next();

                if (o instanceof AttributeGroupHandler) {
                    AttributeGroupHandler sst = (AttributeGroupHandler) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst.compress(this);
                    }
                } else {
                    AttributeGroup sst = (AttributeGroup) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst;
                    }
                }
            }
        }

        return null;
    }

    /*
     * helper method for lookUpAttribute
     */
    private Attribute lookUpAttribute( String localName, Schema s, TreeSet targets ) {
        if (s == null) {
            return null;
        }

        targets.add(s.getTargetNamespace());

        if (s.getAttributes() != null) {
            Attribute[] sts = s.getAttributes();

            for( int i = 0; (sts != null) && (i < sts.length); i++ ) {
                if (sts[i] != null && sts[i].getName() != null) {
                    if (localName.equalsIgnoreCase(sts[i].getName())) {
                        return sts[i];
                    }
                }
            }
        }

        if (s.getImports() != null) {
            Schema[] ss = s.getImports();

            for( int i = 0; (ss != null) && (i < ss.length); i++ ) {
                if (!targets.contains(ss[i].getTargetNamespace())) {
                    Attribute st = lookUpAttribute(localName, ss[i], targets);

                    if (st != null) {
                        return st;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for package
     * </p>
     * 
     * @param qname
     * @throws SAXException
     */
    protected Attribute lookUpAttribute( String qname ) throws SAXException {
        int index = qname.indexOf(":");
        String localName, prefix1;
        localName = prefix1 = null;
        if (index >= 0) {
            localName = qname.substring(index + 1);
            prefix1 = qname.substring(0, index);
        } else {
            prefix1 = "";
            localName = qname;
        }
        logger.finest("prefix is " + prefix1);
        logger.finest("localName is " + localName);

        Iterator it;
        if ((this.prefix == null && prefix1 == null) || (this.prefix != null && this.prefix.equals(prefix1))) {
            if (schema != null)
                return lookUpAttribute(localName, schema, new TreeSet());
        } else {
            if (imports != null) {
                it = imports.iterator();
                while( it.hasNext() ) {
                    Schema s = (Schema) it.next();
                    String ns = s.getTargetNamespace().toString();
                    String prefixLookup = prefixCache != null ? (String) prefixCache.get(ns) : null;
                    if (prefix1 == null || prefixLookup == null || prefix1.equals(prefixLookup)) {
                        Attribute ct = lookUpAttribute(localName, s, new TreeSet());
                        if (ct != null) {
                            return ct;
                        }
                    }
                }
            }
        }

        if (attributes != null) {
            it = attributes.iterator();

            while( it.hasNext() ) {
                Object o = it.next();

                if (o instanceof AttributeHandler) {
                    AttributeHandler sst = (AttributeHandler) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst.compress(this);
                    }
                } else {
                    Attribute sst = (Attribute) o;

                    if (localName.equalsIgnoreCase(sst.getName())) {
                        return sst;
                    }
                }
            }
        }

        return null;
    }

    /**
     * <p>
     * convinience method for package
     * </p>
     * 
     * @param qname
     * @throws SAXException
     */
    protected Type lookUpType( String qname ) throws SAXException {
        if (qname == null)
            return null;
        Type t = null;
        t = lookUpComplexType(qname);
        t = t == null ? lookUpSimpleType(qname) : t;
        return t;
    }

    /*
     * helper method that merges the provided Schema into this Schema
     */
    private void addSchema( Schema s ) {
        Object[] objs = null;

        objs = s.getAttributes();

        if (objs != null) {
            if ((attributes == null) && (objs.length > 0)) {
                attributes = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                attributes.add(objs[i]);
        }

        objs = s.getAttributeGroups();

        if (objs != null) {
            if ((attributeGroups == null) && (objs.length > 0)) {
                attributeGroups = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                attributeGroups.add(objs[i]);
        }

        objs = s.getComplexTypes();

        if (objs != null) {
            if ((complexTypes == null) && (objs.length > 0)) {
                complexTypes = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                complexTypes.add(objs[i]);
        }

        objs = s.getElements();

        if (objs != null) {
            if ((elements == null) && (objs.length > 0)) {
                elements = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                elements.add(objs[i]);
        }

        objs = s.getGroups();

        if (objs != null) {
            if ((groups == null) && (objs.length > 0)) {
                groups = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                groups.add(objs[i]);
        }

        objs = s.getImports();

        if (objs != null) {
            if ((imports == null) && (objs.length > 0)) {
                imports = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                imports.add(objs[i]);
        }

        objs = s.getSimpleTypes();

        if (objs != null) {
            if ((simpleTypes == null) && (objs.length > 0)) {
                simpleTypes = new LinkedList();
            }

            for( int i = 0; i < objs.length; i++ )
                simpleTypes.add(objs[i]);
        }

        URI tempuri = s.getURI();

        if (uri == null) {
            uri = tempuri;
        } else {
            if (tempuri != null) {
                uri = tempuri.resolve(uri);
            }
        }
    }

    /**
     * DOCUMENT ME!
     *
     * @return Returns the targetNamespace.
     */
    public URI getTargetNamespace() {
        return targetNamespace;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#getHandlerType()
     */
    public int getHandlerType() {
        return DEFAULT;
    }

    /**
     * @see org.geotools.xml.XSIElementHandler#endElement(java.lang.String,
     * java.lang.String)
     */
    public void endElement( String namespaceURI1, String localName ) {
        // do nothing
    }

    /**
     * <p>
     * Default implementation of a Schema for a parsed schema.
     * </p>
     *
     * @author dzwiers
     *
     * @see Schema
     */
    private static class DefaultSchema implements Schema {
        // file visible to avoid set* methods
        boolean attributeFormDefault;
        boolean elementFormDefault;
        String id;
        URI targetNamespace;
        String version;
        int finalDefault;
        int blockDefault;
        URI uri;
        Schema[] imports;
        SimpleType[] simpleTypes;
        ComplexType[] complexTypes;
        AttributeGroup[] attributeGroups;
        Attribute[] attributes;
        Element[] elements;
        Group[] groups;
        String prefix;

        /**
         * @see org.geotools.xml.xsi.Schema#isAttributeFormDefault()
         */
        public boolean isAttributeFormDefault() {
            return attributeFormDefault;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getAttributeGroups()
         */
        public AttributeGroup[] getAttributeGroups() {
            return attributeGroups;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getAttributes()
         */
        public Attribute[] getAttributes() {
            return attributes;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getBlockDefault()
         */
        public int getBlockDefault() {
            return blockDefault;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getComplexTypes()
         */
        public ComplexType[] getComplexTypes() {
            return complexTypes;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#isElementFormDefault()
         */
        public boolean isElementFormDefault() {
            return elementFormDefault;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getElements()
         */
        public Element[] getElements() {
            return elements;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getFinalDefault()
         */
        public int getFinalDefault() {
            return finalDefault;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getId()
         */
        public String getId() {
            return id;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getImports()
         */
        public Schema[] getImports() {
            return imports;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getSimpleTypes()
         */
        public SimpleType[] getSimpleTypes() {
            return simpleTypes;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getTargetNamespace()
         */
        public URI getTargetNamespace() {
            return targetNamespace;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getURI()
         */
        public URI getURI() {
            return uri;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getVersion()
         */
        public String getVersion() {
            return version;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#getGroups()
         */
        public Group[] getGroups() {
            return groups;
        }

        /**
         * @see org.geotools.xml.xsi.Schema#includesURI(java.net.URI)
         */
        public boolean includesURI( URI uri1 ) {
            if (this.uri == null) {
                return false;
            }
            return this.uri.equals(uri1);
        }

        /**
         * @see org.geotools.xml.schema.Schema#getPrefix()
         */
        public String getPrefix() {
            return prefix;
        }

        /**
         * Returns the implementation hints. The default implementation returns en empty map.
         */
        public Map getImplementationHints() {
            return Collections.EMPTY_MAP;
        }
    }

    /**
     *
     * This class breaks both the collections api and the comparable api.
     * When an object is a temp ... thus we don't care ... it's always less
     than.
     * Please do not use this unless you fully understand it. It is intended to
     * be used for compressing two schemas and to remove duplicates resulting
     values (not placeholders).
     * Making this evaluate place holders would cause the parser to fail !!!
     *
     * @author dzwiers
     *
     */
    private static class SchemaComparator implements Comparator {
        private static SchemaComparator instance = new SchemaComparator();
        public static SchemaComparator getInstance() {
            return instance;
        }
        /* (non-Javadoc)
         * @see java.util.Comparator#compare(java.lang.Object, java.lang.Object)
         */
        public int compare( Object arg0, Object arg1 ) {
            // attribute
            if (arg0 instanceof Attribute && arg1 instanceof Attribute)
                return compareAttribute((Attribute) arg0, (Attribute) arg1);
            // attrbute group
            if (arg0 instanceof AttributeGroup && arg1 instanceof AttributeGroup)
                return compareAttributeGroup((AttributeGroup) arg0, (AttributeGroup) arg1);
            // complex type
            if (arg0 instanceof ComplexType && arg1 instanceof ComplexType)
                return compareComplexType((ComplexType) arg0, (ComplexType) arg1);
            // simpletype
            if (arg0 instanceof SimpleType && arg1 instanceof SimpleType)
                return compareSimpleType((SimpleType) arg0, (SimpleType) arg1);
            // group
            if (arg0 instanceof Group && arg1 instanceof Group)
                return compareGroup((Group) arg0, (Group) arg1);
            // element
            if (arg0 instanceof Element && arg1 instanceof Element)
                return compareElement((Element) arg0, (Element) arg1);
            // imports
            if (arg0 instanceof Schema && arg1 instanceof Schema)
                return compareImport((Schema) arg0, (Schema) arg1);

            return -1; // hack for unresolved portions
            // throw new ClassCastException("Unknown type "+arg0.getClass().getName());
        }

        private int compareAttribute( Attribute arg0, Attribute arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getName() == null ? arg1.getName() == null ? 0 : 1 : arg0.getName().compareTo(arg1.getName());
            if (i != 0)
                return i;
            i = arg0.getNamespace() == null ? arg1.getNamespace() == null ? 0 : 1 : arg0.getNamespace().compareTo(
                    arg1.getNamespace());
            if (i != 0)
                return i;
            i = arg0.getUse() - arg1.getUse();
            if (i != 0)
                return i;
            return compareSimpleType(arg0.getSimpleType(), arg1.getSimpleType());
        }

        private int compareAttributeGroup( AttributeGroup arg0, AttributeGroup arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getName() == null ? arg1.getName() == null ? 0 : 1 : arg0.getName().compareTo(arg1.getName());
            if (i != 0)
                return i;
            i = arg0.getNamespace() == null ? arg1.getNamespace() == null ? 0 : 1 : arg0.getNamespace().compareTo(
                    arg1.getNamespace());
            if (i != 0)
                return i;
            i = arg0.getAnyAttributeNameSpace() == null ? arg1.getAnyAttributeNameSpace() == null ? 0 : 1 : arg0
                    .getAnyAttributeNameSpace().compareTo(arg1.getAnyAttributeNameSpace());
            if (i != 0)
                return i;

            Attribute[] a0 = arg0.getAttributes();
            Arrays.sort(a0, this);
            Attribute[] a1 = arg1.getAttributes();
            Arrays.sort(a1, this);

            if (a0 == a1)
                return 0;
            if (a0 == null)
                return 1;
            if (a1 == null)
                return -1;

            if (a0.length < a1.length)
                return -1;
            if (a0.length > a1.length)
                return 1;

            for( int j = 0; j < a0.length && i == 0; j++ )
                i = compareAttribute(a0[j], a1[j]);

            return i;
        }

        private int compareGroup( Group arg0, Group arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getName() == null ? arg1.getName() == null ? 0 : 1 : arg0.getName().compareTo(arg1.getName());
            if (i != 0)
                return i;
            i = arg0.getNamespace() == null ? arg1.getNamespace() == null ? 0 : 1 : arg0.getNamespace().compareTo(
                    arg1.getNamespace());
            if (i != 0)
                return i;

            i = arg0.getMaxOccurs() - arg1.getMaxOccurs();
            if (i != 0)
                return i;

            i = arg0.getMinOccurs() - arg1.getMinOccurs();
            if (i != 0)
                return i;

            ElementGrouping a0 = arg0.getChild();
            ElementGrouping a1 = arg1.getChild();

            if (a0 == a1)
                return 0;
            if (a0 == null)
                return 1;
            if (a1 == null)
                return -1;

            return compareElementGrouping(a0, a1);
        }

        private int compareElement( Element arg0, Element arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getName() == null ? arg1.getName() == null ? 0 : 1 : arg0.getName().compareTo(arg1.getName());
            if (i != 0)
                return i;
            i = arg0.getNamespace() == null ? arg1.getNamespace() == null ? 0 : 1 : arg0.getNamespace().compareTo(
                    arg1.getNamespace());
            if (i != 0)
                return i;

            i = arg0.getMaxOccurs() - arg1.getMaxOccurs();
            if (i != 0)
                return i;

            i = arg0.getMinOccurs() - arg1.getMinOccurs();
            if (i != 0)
                return i;

            i = compareElement(arg0.getSubstitutionGroup(), arg1.getSubstitutionGroup());
            if (i != 0)
                return i;

            // ignore a few things here ... might need them back

            return compare(arg0.getType(), arg1.getType());
        }

        private int compareElementGrouping( ElementGrouping arg0, ElementGrouping arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;

            int i = 0;
            i = arg0.getGrouping() - arg1.getGrouping();
            if (i != 0)
                return i;

            i = arg0.getMaxOccurs() - arg1.getMaxOccurs();
            if (i != 0)
                return i;

            i = arg0.getMinOccurs() - arg1.getMinOccurs();
            if (i != 0)
                return i;

            ElementGrouping[] eg0 = null;
            ElementGrouping[] eg1 = null;

            switch( arg0.getGrouping() ) {
            case ElementGrouping.ELEMENT:
                return compareElement((Element) arg0, (Element) arg1);
            case ElementGrouping.GROUP:
                return compareGroup((Group) arg0, (Group) arg1);
            case ElementGrouping.CHOICE:
                Choice c0 = (Choice) arg0;
                Choice c1 = (Choice) arg1;
                eg0 = c0.getChildren();
                eg1 = c1.getChildren();
            case ElementGrouping.SEQUENCE:
                Sequence s0 = (Sequence) arg0;
                Sequence s1 = (Sequence) arg1;
                eg0 = s0.getChildren();
                eg1 = s1.getChildren();
            case ElementGrouping.ALL:
                All a0 = (All) arg0;
                All a1 = (All) arg1;
                eg0 = a0.getElements();
                eg1 = a1.getElements();
            }

            if (eg0.length < eg1.length)
                return -1;
            if (eg0.length > eg1.length)
                return 1;

            for( int j = 0; j < eg0.length && i != 0; j++ )
                i = compareElementGrouping(eg0[j], eg1[j]);
            return 0;
        }

        private int compareImport( Schema arg0, Schema arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getTargetNamespace() == null ? arg1.getTargetNamespace() == null ? 0 : 1 : arg0
                    .getTargetNamespace().compareTo(arg1.getTargetNamespace());
            if (i != 0)
                return i;
            i = arg0.getURI() == null ? arg1.getURI() == null ? 0 : 1 : arg0.getURI().compareTo(arg1.getURI());
            if (i != 0)
                return i;

            i = arg0.getElements().length - arg1.getElements().length;
            if (i != 0)
                return i;

            i = arg0.getComplexTypes().length - arg1.getComplexTypes().length;
            if (i != 0)
                return i;

            i = arg0.getSimpleTypes().length - arg1.getSimpleTypes().length;
            if (i != 0)
                return i;

            i = arg0.getAttributes().length - arg1.getAttributes().length;
            if (i != 0)
                return i;

            i = arg0.getAttributeGroups().length - arg1.getAttributeGroups().length;
            if (i != 0)
                return i;

            i = arg0.getGroups().length - arg1.getGroups().length;
            if (i != 0)
                return i;

            // Yes we are making a huge assumption here.

            return 0;
        }

        private int compareSimpleType( SimpleType arg0, SimpleType arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getName() == null ? arg1.getName() == null ? 0 : 1 : arg0.getName().compareTo(arg1.getName());
            if (i != 0)
                return i;
            i = arg0.getNamespace() == null ? arg1.getNamespace() == null ? 0 : 1 : arg0.getNamespace().compareTo(
                    arg1.getNamespace());
            if (i != 0)
                return i;

            SimpleType[] a0 = arg0.getParents();
            Arrays.sort(a0, this);
            SimpleType[] a1 = arg1.getParents();
            Arrays.sort(a1, this);

            if (a0 == a1)
                return 0;
            if (a0 == null)
                return 1;
            if (a1 == null)
                return -1;

            if (a0.length < a1.length)
                return -1;
            if (a0.length > a1.length)
                return 1;

            for( int j = 0; j < a0.length && i == 0; j++ )
                i = compareSimpleType(a0[j], a1[j]);

            Facet[] a01 = arg0.getFacets();
            Arrays.sort(a0, this);
            Facet[] a11 = arg1.getFacets();
            Arrays.sort(a1, this);

            if (a01 == a11)
                return 0;
            if (a01 == null)
                return 1;
            if (a11 == null)
                return -1;

            if (a01.length < a11.length)
                return -1;
            if (a01.length > a11.length)
                return 1;

            for( int j = 0; j < a01.length && i == 0; j++ )
                i = compareFacet(a01[j], a11[j]);

            return i;
        }

        private int compareFacet( Facet arg0, Facet arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;

            int i = 0;
            i = arg0.getFacetType() - arg1.getFacetType();
            if (i != 0)
                return i;

            i = arg0.getValue() == null ? arg1.getValue() == null ? 0 : 1 : arg0.getValue().compareTo(arg1.getValue());
            return i;
        }

        private int compareComplexType( ComplexType arg0, ComplexType arg1 ) {
            if (arg0 == arg1)
                return 0;
            if (arg0 == null)
                return 1;
            if (arg1 == null)
                return -1;
            int i = arg0.getName() == null ? arg1.getName() == null ? 0 : 1 : arg0.getName().compareTo(arg1.getName());
            if (i != 0)
                return i;
            i = arg0.getNamespace() == null ? arg1.getNamespace() == null ? 0 : 1 : arg0.getNamespace().compareTo(
                    arg1.getNamespace());
            if (i != 0)
                return i;

            Type a00 = arg0.getParent();
            Type a01 = arg1.getParent();

            if (a00 == a01)
                return 0;
            if (a00 == null)
                return 1;
            if (a01 == null)
                return -1;

            i = compare(a00, a01);
            if (i != 0)
                return i;

            i = arg0.getAnyAttributeNameSpace() == null ? arg1.getAnyAttributeNameSpace() == null ? 0 : 1 : arg0
                    .getAnyAttributeNameSpace().compareTo(arg1.getAnyAttributeNameSpace());
            if (i != 0)
                return i;

            Attribute[] a10 = arg0.getAttributes();
            Arrays.sort(a10, this);
            Attribute[] a11 = arg1.getAttributes();
            Arrays.sort(a11, this);

            if (a10 == a11)
                return 0;
            if (a10 == null)
                return 1;
            if (a11 == null)
                return -1;

            if (a10.length < a11.length)
                return -1;
            if (a10.length > a11.length)
                return 1;

            for( int j = 0; j < a10.length && i == 0; j++ )
                i = compareAttribute(a10[j], a11[j]);
            if (i != 0)
                return i;

            Element[] a0 = arg0.getChildElements();
            Arrays.sort(a0, this);
            Element[] a1 = arg1.getChildElements();
            Arrays.sort(a1, this);

            if (a0 == a1)
                return 0;
            if (a0 == null)
                return 1;
            if (a1 == null)
                return -1;

            if (a0.length < a1.length)
                return -1;
            if (a0.length > a1.length)
                return 1;

            for( int j = 0; j < a0.length && i == 0; j++ )
                i = compareElement(a0[j], a1[j]);

            return i;
        }
    }
}
