org.alfresco.web.forms.xforms.SchemaUtil.java Source code

Java tutorial

Introduction

Here is the source code for org.alfresco.web.forms.xforms.SchemaUtil.java

Source

/*
 * Copyright (C) 2005-2010 Alfresco Software Limited.
 *
 * This file is part of Alfresco
 *
 * Alfresco 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, either version 3 of the License, or
 * (at your option) any later version.
 *
 * Alfresco 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.
 *
 * You should have received a copy of the GNU Lesser General Public License
 * along with Alfresco. If not, see <http://www.gnu.org/licenses/>. */
package org.alfresco.web.forms.xforms;

import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.TreeMap;
import java.util.TreeSet;

import org.alfresco.util.EqualsHelper;
import org.alfresco.util.XMLUtil;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.apache.xerces.xs.XSAttributeUse;
import org.apache.xerces.xs.XSComplexTypeDefinition;
import org.apache.xerces.xs.XSConstants;
import org.apache.xerces.xs.XSElementDeclaration;
import org.apache.xerces.xs.XSImplementation;
import org.apache.xerces.xs.XSLoader;
import org.apache.xerces.xs.XSModel;
import org.apache.xerces.xs.XSModelGroup;
import org.apache.xerces.xs.XSNamedMap;
import org.apache.xerces.xs.XSObjectList;
import org.apache.xerces.xs.XSParticle;
import org.apache.xerces.xs.XSSimpleTypeDefinition;
import org.apache.xerces.xs.XSTerm;
import org.apache.xerces.xs.XSTypeDefinition;
import org.w3c.dom.DOMConfiguration;
import org.w3c.dom.DOMError;
import org.w3c.dom.DOMErrorHandler;
import org.w3c.dom.DOMLocator;
import org.w3c.dom.Document;
import org.w3c.dom.bootstrap.DOMImplementationRegistry;
import org.w3c.dom.ls.DOMImplementationLS;
import org.w3c.dom.ls.LSInput;

/**
 * Provides utility functions for xml schema parsing.
 */
public class SchemaUtil {

    ////////////////////////////////////////////////////////////////////////////

    public static class Occurrence {
        public final static int UNBOUNDED = -1;

        public final int minimum;
        public final int maximum;

        public Occurrence(final XSParticle particle) {
            this(particle.getMinOccurs(),
                    (particle.getMaxOccursUnbounded() ? Occurrence.UNBOUNDED : particle.getMaxOccurs()));
        }

        public Occurrence(final int minimum) {
            this(minimum, Occurrence.UNBOUNDED);
        }

        public Occurrence(final int minimum, final int maximum) {
            this.minimum = minimum;
            this.maximum = maximum;
        }

        public boolean isRepeated() {
            return this.isUnbounded() || this.maximum > 1;
        }

        public boolean isOptional() {
            return this.minimum == 0 && this.maximum == 1;
        }

        public boolean isUnbounded() {
            return this.maximum == UNBOUNDED;
        }

        public String toString() {
            return "minimum=" + minimum + ", maximum=" + maximum;
        }
    }

    ////////////////////////////////////////////////////////////////////////////

    private final static Comparator<XSTypeDefinition> TYPE_EXTENSION_SORTER = new Comparator<XSTypeDefinition>() {
        public int compare(final XSTypeDefinition type1, final XSTypeDefinition type2) {
            int result = 0;
            if (type1 == null && type2 != null) {
                result = -1;
            } else if (type1 != null && type2 == null) {
                result = 1;
            } else if (type1 == type2 || (type1 == null && type2 == null)) {
                result = 0;
            } else {
                result = (type1.derivedFromType(type2, XSConstants.DERIVATION_EXTENSION) ? 1
                        : (type2.derivedFromType(type1, XSConstants.DERIVATION_EXTENSION) ? -1
                                : type1.getName().compareTo(type2.getName())));
            }
            if (LOGGER.isDebugEnabled() && false)
                LOGGER.debug("compare(" + type1 + ", " + type2 + ") = " + result);
            return result;
        }
    };

    ////////////////////////////////////////////////////////////////////////////

    private final static Log LOGGER = LogFactory.getLog(SchemaUtil.class);

    private final static HashMap<Short, String> DATA_TYPE_TO_NAME = new HashMap<Short, String>();
    static {
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.ANYSIMPLETYPE_DT, "anyType");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.ANYURI_DT, "anyURI");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.BASE64BINARY_DT, "base64Binary");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.BOOLEAN_DT, "boolean");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.BYTE_DT, "byte");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.DATETIME_DT, "dateTime");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.DATE_DT, "date");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.DECIMAL_DT, "decimal");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.DOUBLE_DT, "double");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.DURATION_DT, "duration");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.ENTITY_DT, "ENTITY");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.FLOAT_DT, "float");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.GDAY_DT, "gDay");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.GMONTHDAY_DT, "gMonthDay");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.GMONTH_DT, "gMonth");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.GYEARMONTH_DT, "gYearMonth");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.GYEAR_DT, "gYear");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.IDREF_DT, "IDREF");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.ID_DT, "ID");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.INTEGER_DT, "integer");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.INT_DT, "int");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.LANGUAGE_DT, "language");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.LONG_DT, "long");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NAME_DT, "Name");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NCNAME_DT, "NCName");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NEGATIVEINTEGER_DT, "negativeInteger");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NMTOKEN_DT, "NMTOKEN");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NONNEGATIVEINTEGER_DT, "nonNegativeInteger");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NONPOSITIVEINTEGER_DT, "nonPositiveInteger");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NORMALIZEDSTRING_DT, "normalizedString");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.NOTATION_DT, "NOTATION");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.POSITIVEINTEGER_DT, "positiveInteger");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.QNAME_DT, "QName");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.SHORT_DT, "short");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.STRING_DT, "string");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.TIME_DT, "time");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.TOKEN_DT, "TOKEN");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.UNSIGNEDBYTE_DT, "unsignedByte");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.UNSIGNEDINT_DT, "unsignedInt");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.UNSIGNEDLONG_DT, "unsignedLong");
        SchemaUtil.DATA_TYPE_TO_NAME.put(XSConstants.UNSIGNEDSHORT_DT, "unsignedShort");
    };

    /**
     * Returns the most-specific built-in base type for the provided type.
     */
    public static short getBuiltInType(final XSTypeDefinition type) {
        // type.getName() may be 'null' for anonymous types, so compare against
        // static string (see bug #1172541 on sf.net)
        if (SchemaUtil.DATA_TYPE_TO_NAME.get(XSConstants.ANYSIMPLETYPE_DT).equals(type.getName())) {
            return XSConstants.ANYSIMPLETYPE_DT;
        }
        final XSSimpleTypeDefinition simpleType = (XSSimpleTypeDefinition) type;
        final short result = simpleType.getBuiltInKind();
        return (result == XSConstants.LIST_DT ? SchemaUtil.getBuiltInType(simpleType.getItemType()) : result);
    }

    public static String getBuiltInTypeName(final XSTypeDefinition type) {
        final short s = SchemaUtil.getBuiltInType(type);
        return SchemaUtil.getBuiltInTypeName(s);
    }

    public static String getBuiltInTypeName(final short type) {
        return SchemaUtil.DATA_TYPE_TO_NAME.get(type);
    }

    public static XSModel parseSchema(final Document schemaDocument, final boolean failOnError)
            throws FormBuilderException {
        try {
            // Get DOM Implementation using DOM Registry
            System.setProperty(DOMImplementationRegistry.PROPERTY,
                    "org.apache.xerces.dom.DOMXSImplementationSourceImpl");

            final DOMImplementationRegistry registry = DOMImplementationRegistry.newInstance();

            final DOMImplementationLS lsImpl = (DOMImplementationLS) registry
                    .getDOMImplementation("XML 1.0 LS 3.0");
            if (lsImpl == null) {
                throw new FormBuilderException("unable to create DOMImplementationLS using " + registry);
            }
            final LSInput in = lsImpl.createLSInput();
            in.setStringData(XMLUtil.toString(schemaDocument));

            final XSImplementation xsImpl = (XSImplementation) registry.getDOMImplementation("XS-Loader");
            final XSLoader schemaLoader = xsImpl.createXSLoader(null);
            final DOMConfiguration config = (DOMConfiguration) schemaLoader.getConfig();
            final LinkedList<DOMError> errors = new LinkedList<DOMError>();
            config.setParameter("error-handler", new DOMErrorHandler() {
                public boolean handleError(final DOMError domError) {
                    errors.add(domError);
                    return true;
                }
            });

            final XSModel result = schemaLoader.load(in);
            if (failOnError && errors.size() != 0) {
                final HashSet<String> messages = new HashSet<String>();
                StringBuilder message = null;
                for (DOMError e : errors) {
                    message = new StringBuilder();
                    final DOMLocator dl = e.getLocation();
                    if (dl != null) {
                        message.append("at line ").append(dl.getLineNumber()).append(" column ")
                                .append(dl.getColumnNumber());
                        if (dl.getRelatedNode() != null) {
                            message.append(" node ").append(dl.getRelatedNode().getNodeName());
                        }
                        message.append(": ").append(e.getMessage());
                    }
                    messages.add(message.toString());
                }

                message = new StringBuilder();
                message.append(messages.size() > 1 ? "errors" : "error").append(" parsing schema: \n");
                for (final String s : messages) {
                    message.append(s).append("\n");
                }

                throw new FormBuilderException(message.toString());
            }

            if (result == null) {
                throw new FormBuilderException("invalid schema");
            }
            return result;
        } catch (ClassNotFoundException x) {
            throw new FormBuilderException(x);
        } catch (InstantiationException x) {
            throw new FormBuilderException(x);
        } catch (IllegalAccessException x) {
            throw new FormBuilderException(x);
        }
    }

    private static void buildTypeTree(final XSTypeDefinition type, final TreeSet<XSTypeDefinition> descendents,
            final TreeMap<String, TreeSet<XSTypeDefinition>> typeTree) {
        if (type == null) {
            return;
        }
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("buildTypeTree(" + type.getName() + ", " + descendents.size() + " descendents)");
        if (descendents.size() > 0) {
            TreeSet<XSTypeDefinition> compatibleTypes = typeTree.get(type.getName());

            if (compatibleTypes == null) {
                if (LOGGER.isDebugEnabled())
                    LOGGER.debug("no compatible types found for " + type.getName() + ", creating a new set");
                compatibleTypes = new TreeSet<XSTypeDefinition>(TYPE_EXTENSION_SORTER);
                typeTree.put(type.getName(), compatibleTypes);
            }
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("adding " + descendents.size() + " descendents to " + type.getName());
            compatibleTypes.addAll(descendents);
        }

        final XSTypeDefinition parentType = type.getBaseType();
        if (parentType == null || type.getTypeCategory() != parentType.getTypeCategory()) {
            return;
        }

        if (type != parentType && parentType.getName() != null && !parentType.getName().equals("anyType")) {
            TreeSet<XSTypeDefinition> newDescendents = typeTree.get(parentType.getName());
            if (newDescendents == null) {
                if (LOGGER.isDebugEnabled())
                    LOGGER.debug("type tree doesn't contain " + parentType.getName()
                            + ", creating a new descendant set");
                newDescendents = new TreeSet<XSTypeDefinition>(TYPE_EXTENSION_SORTER);
            }
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("adding " + descendents.size() + " descendants to existing " + newDescendents.size()
                        + " descendants of " + parentType.getName());
            newDescendents.addAll(descendents);

            //extension (we only add it to "newDescendants" because we don't want
            //to have a type descendant to itself, but to consider it for the parent
            if (type.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
                final XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) type;
                if (complexType.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION
                        && !complexType.getAbstract() && !descendents.contains(type)) {
                    if (LOGGER.isDebugEnabled())
                        LOGGER.debug("adding " + type.getName() + " to existing " + newDescendents.size()
                                + " descendents of " + parentType.getName());
                    newDescendents.add(type);
                }
            }

            //note: extensions are impossible on simpleTypes !
            SchemaUtil.buildTypeTree(parentType, newDescendents, typeTree);
        }
    }

    public static TreeMap<String, TreeSet<XSTypeDefinition>> buildTypeTree(final XSModel schema) {
        final TreeMap<String, TreeSet<XSTypeDefinition>> result = new TreeMap<String, TreeSet<XSTypeDefinition>>();
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("buildTypeTree " + schema);
        // build the type tree for complex types
        final XSNamedMap types = schema.getComponents(XSConstants.TYPE_DEFINITION);
        for (int i = 0; i < types.getLength(); i++) {
            final XSTypeDefinition t = (XSTypeDefinition) types.item(i);
            if (t.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
                final XSComplexTypeDefinition type = (XSComplexTypeDefinition) t;
                SchemaUtil.buildTypeTree(type, new TreeSet<XSTypeDefinition>(TYPE_EXTENSION_SORTER), result);
            }
        }

        // build the type tree for simple types
        for (int i = 0; i < types.getLength(); i++) {
            final XSTypeDefinition t = (XSTypeDefinition) types.item(i);
            if (t.getTypeCategory() == XSTypeDefinition.SIMPLE_TYPE) {
                SchemaUtil.buildTypeTree((XSSimpleTypeDefinition) t,
                        new TreeSet<XSTypeDefinition>(TYPE_EXTENSION_SORTER), result);
            }
        }

        // print out type hierarchy for debugging purposes
        if (LOGGER.isDebugEnabled()) {
            for (String typeName : result.keySet()) {
                TreeSet<XSTypeDefinition> descendents = result.get(typeName);
                LOGGER.debug(">>>> for " + typeName + " Descendants=\n ");
                Iterator<XSTypeDefinition> it = descendents.iterator();
                while (it.hasNext()) {
                    XSTypeDefinition desc = it.next();
                    LOGGER.debug("      " + desc.getName());
                }
            }
        }
        return result;
    }

    public static XSParticle findCorrespondingParticleInComplexType(final XSElementDeclaration elDecl) {
        final XSComplexTypeDefinition complexType = elDecl.getEnclosingCTDefinition();
        if (complexType == null) {
            LOGGER.warn("unable to find corresponding particle for " + elDecl.getName()
                    + ".  no enclosing complex type.");
            return null;
        }

        final XSParticle particle = complexType.getParticle();
        final XSTerm term = particle.getTerm();
        if (!(term instanceof XSModelGroup)) {
            LOGGER.warn("unable to find corresponding particle for " + elDecl.getName() + ".  term " + term
                    + " is not a model group.");
            return null;
        }
        final XSParticle result = SchemaUtil.findCorrespondingParticleInModelGroup((XSModelGroup) term, elDecl);
        if (result == null) {
            final String msg = ("unable to find corresponding particle for " + elDecl.getName() + " in term "
                    + term);
            if (LOGGER.isDebugEnabled()) {
                throw new NullPointerException(msg);
            } else {
                LOGGER.warn(msg);
            }
        }
        return result;
    }

    private static XSParticle findCorrespondingParticleInModelGroup(final XSModelGroup modelGroup,
            final XSElementDeclaration elDecl) {
        final XSObjectList particles = modelGroup.getParticles();
        if (particles == null || particles.getLength() == 0) {
            LOGGER.debug("unable to find corresponding particle for " + elDecl.getName() + ".  group " + modelGroup
                    + " contains no particles.");
            return null;
        }

        for (int i = 0; i < particles.getLength(); i++) {
            XSParticle part = (XSParticle) particles.item(i);
            final XSTerm thisTerm = part.getTerm();
            if (thisTerm instanceof XSModelGroup) {
                part = SchemaUtil.findCorrespondingParticleInModelGroup((XSModelGroup) thisTerm, elDecl);
                if (part != null) {
                    return part;
                }
            } else {
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("checking term " + thisTerm.getName() + " in " + modelGroup.getName() + "("
                            + modelGroup.getClass().getName() + ")");
                }
                if (thisTerm == elDecl) {
                    return part;
                }
            }
        }
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("unable to find corresponding particle for " + elDecl.getName() + ".  " + elDecl.getName()
                    + " not found in " + particles.getLength() + " particles");
        }
        return null;
    }

    /**
     * check that the element defined by this name is declared directly in the type
     */
    public static boolean isElementDeclaredIn(String name, String namespace, XSComplexTypeDefinition type,
            boolean recursive) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("isElement " + name + " declared in " + type.getName());
        }

        //test if extension + declared in parent + not recursive -> NOK
        if (!recursive && type.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) {
            XSComplexTypeDefinition parent = (XSComplexTypeDefinition) type.getBaseType();
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("testing if it is not on parent " + parent.getName());
            if (SchemaUtil.isElementDeclaredIn(name, namespace, parent, true))
                return false;
        }

        boolean found = false;
        XSParticle particle = type.getParticle();
        if (particle != null) {
            XSTerm term = particle.getTerm();
            if (term instanceof XSModelGroup) {
                XSModelGroup group = (XSModelGroup) term;
                found = SchemaUtil.isElementDeclaredIn(name, namespace, group);
            }
        }

        if (LOGGER.isDebugEnabled())
            LOGGER.debug("isElement " + name + " declared in " + type.getName() + ": " + found);

        return found;
    }

    /**
     * private recursive method called by isElementDeclaredIn(String name, XSComplexTypeDefinition type)
     */
    public static boolean isElementDeclaredIn(String name, String namespace, XSModelGroup group) {
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("isElement " + name + " declared in group " + group.getName());

        boolean found = false;
        XSObjectList particles = group.getParticles();
        for (int i = 0; i < particles.getLength(); i++) {
            XSParticle subPart = (XSParticle) particles.item(i);
            XSTerm subTerm = subPart.getTerm();
            if (subTerm instanceof XSElementDeclaration) {
                XSElementDeclaration elDecl = (XSElementDeclaration) subTerm;
                if (EqualsHelper.nullSafeEquals(namespace, elDecl.getNamespace()) && name.equals(elDecl.getName()))
                    found = true;
            } else if (subTerm instanceof XSModelGroup) {
                found = SchemaUtil.isElementDeclaredIn(name, namespace, (XSModelGroup) subTerm);
            }
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("isElement " + name + " declared in group " + group.getName() + ": " + found);
        }
        return found;
    }

    public static boolean doesElementComeFromExtension(final XSElementDeclaration element,
            final XSComplexTypeDefinition controlType) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("doesElementComeFromExtension for " + element.getName() + " and controlType="
                    + controlType.getName());
        }
        if (controlType.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) {
            final XSTypeDefinition baseType = controlType.getBaseType();
            if (baseType.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE) {
                final XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) baseType;
                if (SchemaUtil.isElementDeclaredIn(element.getName(), element.getNamespace(), complexType, true)) {
                    if (LOGGER.isDebugEnabled()) {
                        LOGGER.debug("doesElementComeFromExtension: yes");
                    }
                    return true;
                }
                if (LOGGER.isDebugEnabled()) {
                    LOGGER.debug("doesElementComeFromExtension: recursive call on previous level");
                }
                return SchemaUtil.doesElementComeFromExtension(element, complexType);
            }
        }
        return false;
    }

    /**
     * check that the element defined by this name is declared directly in the type
     */
    public static boolean isAttributeDeclaredIn(final XSAttributeUse attr, final XSComplexTypeDefinition type,
            final boolean recursive) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("is Attribute " + attr.getAttrDeclaration().getName() + " declared in " + type.getName());
        }

        //check on parent if not recursive
        if (!recursive && type.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) {
            XSComplexTypeDefinition parent = (XSComplexTypeDefinition) type.getBaseType();
            if (LOGGER.isDebugEnabled()) {
                LOGGER.debug("testing if it is not on parent " + parent.getName());
            }
            if (SchemaUtil.isAttributeDeclaredIn(attr, parent, true)) {
                return false;
            }
        }

        //check on this type  (also checks recursively)
        boolean found = false;
        final XSObjectList attrs = type.getAttributeUses();
        for (int i = 0; i < attrs.getLength(); i++) {
            if ((XSAttributeUse) attrs.item(i) == attr) {
                found = true;
                break;
            }
        }

        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("is Attribute " + attr.getName() + " declared in " + type.getName() + ": " + found);
        }

        return found;
    }

    /**
     * check that the element defined by this name is declared directly in the type
     * -> idem with string
     */
    public static boolean isAttributeDeclaredIn(String attrName, String namespace, XSComplexTypeDefinition type,
            boolean recursive) {
        boolean found = false;

        if (LOGGER.isDebugEnabled())
            LOGGER.debug("is Attribute " + attrName + " declared in " + type.getName());

        if (attrName.startsWith("@"))
            attrName = attrName.substring(1);

        //check on parent if not recursive
        if (!recursive && type.getDerivationMethod() == XSConstants.DERIVATION_EXTENSION) {
            XSComplexTypeDefinition parent = (XSComplexTypeDefinition) type.getBaseType();
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("testing if it is not on parent " + parent.getName());
            if (SchemaUtil.isAttributeDeclaredIn(attrName, namespace, parent, true))
                return false;
        }

        //check on this type (also checks recursively)
        final XSObjectList attrs = type.getAttributeUses();
        for (int i = 0; i < attrs.getLength() && !found; i++) {
            final XSAttributeUse anAttr = (XSAttributeUse) attrs.item(i);
            if (anAttr != null) {
                String name = anAttr.getName();
                if (name == null || name.length() == 0)
                    name = anAttr.getAttrDeclaration().getName();
                if (EqualsHelper.nullSafeEquals(namespace, anAttr.getAttrDeclaration().getNamespace())
                        && attrName.equals(name))
                    found = true;
            }
        }

        if (LOGGER.isDebugEnabled())
            LOGGER.debug("is Attribute " + attrName + " declared in " + type.getName() + ": " + found);

        return found;
    }

    public static boolean doesAttributeComeFromExtension(XSAttributeUse attr, XSComplexTypeDefinition controlType) {
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("doesAttributeComeFromExtension for " + attr.getAttrDeclaration().getName()
                    + " and controlType=" + controlType.getName());
        }

        if (controlType.getDerivationMethod() != XSConstants.DERIVATION_EXTENSION) {
            return false;
        }

        final XSTypeDefinition baseType = controlType.getBaseType();
        if (baseType.getTypeCategory() != XSTypeDefinition.COMPLEX_TYPE) {
            return false;
        }

        final XSComplexTypeDefinition complexType = (XSComplexTypeDefinition) baseType;
        if (SchemaUtil.isAttributeDeclaredIn(attr, complexType, true)) {
            if (LOGGER.isDebugEnabled())
                LOGGER.debug("doesAttributeComeFromExtension: yes");
            return true;
        }

        //recursive call
        if (LOGGER.isDebugEnabled())
            LOGGER.debug("doesAttributeComeFromExtension: recursive call on previous level");
        return SchemaUtil.doesAttributeComeFromExtension(attr, complexType);
    }

    /**
     * finds the minOccurs and maxOccurs of an element declaration
     *
     * @return a table containing minOccurs and MaxOccurs
     */
    public static Occurrence getOccurrence(final XSElementDeclaration elDecl) {
        //get occurance on encosing element declaration
        final XSParticle particle = SchemaUtil.findCorrespondingParticleInComplexType(elDecl);
        final Occurrence result = particle == null ? new Occurrence(1, 1) : new Occurrence(particle);
        if (LOGGER.isDebugEnabled()) {
            LOGGER.debug("getOccurrence for " + elDecl.getName() + ", " + result);
        }
        return result;
    }
}