com.evolveum.midpoint.prism.schema.DomToSchemaProcessor.java Source code

Java tutorial

Introduction

Here is the source code for com.evolveum.midpoint.prism.schema.DomToSchemaProcessor.java

Source

/*
 * Copyright (c) 2010-2015 Evolveum
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.evolveum.midpoint.prism.schema;

import static com.evolveum.midpoint.prism.PrismConstants.A_ACCESS;
import static com.evolveum.midpoint.prism.PrismConstants.A_ACCESS_CREATE;
import static com.evolveum.midpoint.prism.PrismConstants.A_ACCESS_READ;
import static com.evolveum.midpoint.prism.PrismConstants.A_ACCESS_UPDATE;
import static com.evolveum.midpoint.prism.PrismConstants.A_COMPOSITE;
import static com.evolveum.midpoint.prism.PrismConstants.A_DEPRECATED;
import static com.evolveum.midpoint.prism.PrismConstants.A_DISPLAY_NAME;
import static com.evolveum.midpoint.prism.PrismConstants.A_DISPLAY_ORDER;
import static com.evolveum.midpoint.prism.PrismConstants.A_EXTENSION;
import static com.evolveum.midpoint.prism.PrismConstants.A_EXTENSION_REF;
import static com.evolveum.midpoint.prism.PrismConstants.A_HELP;
import static com.evolveum.midpoint.prism.PrismConstants.A_IGNORE;
import static com.evolveum.midpoint.prism.PrismConstants.A_INDEXED;
import static com.evolveum.midpoint.prism.PrismConstants.A_MAX_OCCURS;
import static com.evolveum.midpoint.prism.PrismConstants.A_OBJECT;
import static com.evolveum.midpoint.prism.PrismConstants.A_OBJECT_REFERENCE;
import static com.evolveum.midpoint.prism.PrismConstants.A_OBJECT_REFERENCE_TARGET_TYPE;
import static com.evolveum.midpoint.prism.PrismConstants.A_OPERATIONAL;
import static com.evolveum.midpoint.prism.PrismConstants.A_PROPERTY_CONTAINER;
import static com.evolveum.midpoint.prism.PrismConstants.A_TYPE;
import static com.evolveum.midpoint.prism.PrismConstants.SCHEMA_DOCUMENTATION;
import static com.evolveum.midpoint.prism.PrismConstants.SCHEMA_APP_INFO;
import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI;

import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;

import javax.xml.XMLConstants;
import javax.xml.bind.annotation.XmlEnumValue;
import javax.xml.namespace.QName;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.apache.cxf.common.util.ReflectionInvokationHandler.Optional;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.EntityResolver;
import org.xml.sax.InputSource;
import org.xml.sax.SAXException;

import com.evolveum.midpoint.prism.ComplexTypeDefinition;
import com.evolveum.midpoint.prism.Definition;
import com.evolveum.midpoint.prism.DisplayableValueImpl;
import com.evolveum.midpoint.prism.ItemDefinition;
import com.evolveum.midpoint.prism.PrismConstants;
import com.evolveum.midpoint.prism.PrismContainerDefinition;
import com.evolveum.midpoint.prism.PrismContext;
import com.evolveum.midpoint.prism.PrismPropertyDefinition;
import com.evolveum.midpoint.prism.PrismReferenceDefinition;
import com.evolveum.midpoint.prism.xml.XmlTypeConverter;
import com.evolveum.midpoint.prism.xml.XsdTypeMapper;
import com.evolveum.midpoint.util.DOMUtil;
import com.evolveum.midpoint.util.DisplayableValue;
import com.evolveum.midpoint.util.exception.SchemaException;
import com.evolveum.midpoint.util.logging.Trace;
import com.evolveum.midpoint.util.logging.TraceManager;
import com.sun.xml.xsom.XSAnnotation;
import com.sun.xml.xsom.XSComplexType;
import com.sun.xml.xsom.XSContentType;
import com.sun.xml.xsom.XSElementDecl;
import com.sun.xml.xsom.XSFacet;
import com.sun.xml.xsom.XSModelGroup;
import com.sun.xml.xsom.XSParticle;
import com.sun.xml.xsom.XSRestrictionSimpleType;
import com.sun.xml.xsom.XSSchema;
import com.sun.xml.xsom.XSSchemaSet;
import com.sun.xml.xsom.XSTerm;
import com.sun.xml.xsom.XSType;
import com.sun.xml.xsom.impl.ElementDecl;
import com.sun.xml.xsom.parser.XSOMParser;
import com.sun.xml.xsom.util.DomAnnotationParserFactory;

/**
 * Parser for DOM-represented XSD, creates midPoint Schema representation.
 * 
 * It will parse schema in several passes. It has special handling if the schema is "resource schema", which will
 * create ResourceObjectDefinition and ResourceObjectAttributeDefinition instead of PropertyContainer and Property. 
 * 
 * @author lazyman
 * @author Radovan Semancik
 */
class DomToSchemaProcessor {

    private static final Trace LOGGER = TraceManager.getTrace(DomToSchemaProcessor.class);

    private PrismSchema schema;
    private EntityResolver entityResolver;
    private PrismContext prismContext;
    private XSSchemaSet xsSchemaSet;
    private String shortDescription;
    private boolean isRuntime = false;

    public EntityResolver getEntityResolver() {
        return entityResolver;
    }

    public void setEntityResolver(EntityResolver entityResolver) {
        this.entityResolver = entityResolver;
    }

    public PrismContext getPrismContext() {
        return prismContext;
    }

    public void setPrismContext(PrismContext prismContext) {
        this.prismContext = prismContext;
    }

    public SchemaRegistry getSchemaRegistry() {
        return this.prismContext.getSchemaRegistry();
    }

    private SchemaDefinitionFactory getDefinitionFactory() {
        return prismContext.getDefinitionFactory();
    }

    private String getNamespace() {
        return schema.getNamespace();
    }

    private boolean isMyNamespace(QName qname) {
        return getNamespace().equals(qname.getNamespaceURI());
    }

    public String getShortDescription() {
        return shortDescription;
    }

    public void setShortDescription(String shortDescription) {
        this.shortDescription = shortDescription;
    }

    public boolean isRuntime() {
        return isRuntime;
    }

    public void setRuntime(boolean isRuntime) {
        this.isRuntime = isRuntime;
    }

    /**
     * Main entry point.
     * 
     * 
     * @param xsdSchema DOM-represented XSD schema
     * @return parsed midPoint schema
     * @throws SchemaException in case of any error
     */
    PrismSchema parseDom(PrismSchema prismSchema, Element xsdSchema) throws SchemaException {
        Validate.notNull(xsdSchema, "XSD schema element must not be null.");

        schema = prismSchema;

        initSchema(xsdSchema);

        xsSchemaSet = parseSchema(xsdSchema);
        if (xsSchemaSet == null) {
            return schema;
        }

        // Create ComplexTypeDefinitions from all top-level complexType definition in the XSD
        processComplexTypeDefinitions(xsSchemaSet);

        // Create PropertyContainer (and possibly also Property) definition from the top-level elements in XSD
        // This also creates ResourceObjectDefinition in some cases
        createDefinitionsFromElements(xsSchemaSet);

        return schema;
    }

    private void initSchema(Element xsdSchema) throws SchemaException {
        String targetNamespace = DOMUtil.getAttribute(xsdSchema, DOMUtil.XSD_ATTR_TARGET_NAMESPACE);
        if (StringUtils.isEmpty(targetNamespace)) {
            throw new SchemaException("Schema does not have targetNamespace specification");
        }
        schema.setNamespace(targetNamespace);
    }

    private XSSchemaSet parseSchema(Element schema) throws SchemaException {
        // Make sure that the schema parser sees all the namespace declarations
        DOMUtil.fixNamespaceDeclarations(schema);
        XSSchemaSet xss = null;
        try {
            TransformerFactory transfac = TransformerFactory.newInstance();
            Transformer trans = transfac.newTransformer();
            trans.setOutputProperty(OutputKeys.OMIT_XML_DECLARATION, "no");
            trans.setOutputProperty(OutputKeys.INDENT, "yes");

            DOMSource source = new DOMSource(schema);
            ByteArrayOutputStream out = new ByteArrayOutputStream();
            StreamResult result = new StreamResult(out);

            trans.transform(source, result);

            XSOMParser parser = createSchemaParser();
            InputSource inSource = new InputSource(new ByteArrayInputStream(out.toByteArray()));
            // XXX: hack: it's here to make entity resolver work...
            inSource.setSystemId("SystemId");
            // XXX: end hack
            inSource.setEncoding("utf-8");

            parser.parse(inSource);

            xss = parser.getResult();

        } catch (SAXException e) {
            throw new SchemaException("XML error during XSD schema parsing: " + e.getMessage()
                    + "(embedded exception " + e.getException() + ") in " + shortDescription, e);
        } catch (TransformerException e) {
            throw new SchemaException("XML transformer error during XSD schema parsing: " + e.getMessage()
                    + "(locator: " + e.getLocator() + ", embedded exception:" + e.getException() + ") in "
                    + shortDescription, e);
        } catch (RuntimeException e) {
            // This sometimes happens, e.g. NPEs in Saxon
            if (LOGGER.isErrorEnabled()) {
                LOGGER.error("Unexpected error {} during parsing of schema:\n{}", e.getMessage(),
                        DOMUtil.serializeDOMToString(schema));
            }
            throw new SchemaException(
                    "XML error during XSD schema parsing: " + e.getMessage() + " in " + shortDescription, e);
        }

        return xss;
    }

    private XSOMParser createSchemaParser() {
        XSOMParser parser = new XSOMParser();
        if (entityResolver == null) {
            entityResolver = prismContext.getSchemaRegistry();
            if (entityResolver == null) {
                throw new IllegalStateException(
                        "Entity resolver is not set (even tried to pull it from prism context)");
            }
        }
        SchemaHandler errorHandler = new SchemaHandler(entityResolver);
        parser.setErrorHandler(errorHandler);
        parser.setAnnotationParser(new DomAnnotationParserFactory());
        parser.setEntityResolver(errorHandler);

        return parser;
    }

    /**
     * Create ComplexTypeDefinitions from all top-level complexType definitions in the XSD.
     * 
     * These definitions are the reused later to fit inside PropertyContainer definitions.
     * 
     * @param set XS Schema Set
     */
    private void processComplexTypeDefinitions(XSSchemaSet set) throws SchemaException {
        Iterator<XSComplexType> iterator = set.iterateComplexTypes();
        while (iterator.hasNext()) {
            XSComplexType complexType = iterator.next();
            if (complexType.getTargetNamespace().equals(schema.getNamespace())) {
                processComplexTypeDefinition(complexType);
            }
        }
    }

    private ComplexTypeDefinition getOrProcessComplexType(QName typeName) throws SchemaException {
        ComplexTypeDefinition complexTypeDefinition = schema.findComplexTypeDefinition(typeName);
        if (complexTypeDefinition != null) {
            return complexTypeDefinition;
        }
        // The definition is not yet processed (or does not exist). Let's try to process it.
        XSComplexType complexType = xsSchemaSet.getComplexType(typeName.getNamespaceURI(), typeName.getLocalPart());
        return processComplexTypeDefinition(complexType);
    }

    /**
     * Creates ComplexTypeDefinition object from a single XSD complexType definition.
     * @param complexType XS complex type definition
     */
    private ComplexTypeDefinition processComplexTypeDefinition(XSComplexType complexType) throws SchemaException {

        SchemaDefinitionFactory definitionFactory = getDefinitionFactory();
        ComplexTypeDefinition ctd = definitionFactory.createComplexTypeDefinition(complexType, prismContext,
                complexType.getAnnotation());

        ComplexTypeDefinition existingComplexTypeDefinition = schema.findComplexTypeDefinition(ctd.getTypeName());
        if (existingComplexTypeDefinition != null) {
            // We already have this in schema. So avoid redundant work and infinite loops;
            return existingComplexTypeDefinition;
        }
        // Add to the schema right now to avoid loops - even if it is not complete yet
        // The definition may reference itself
        schema.add(ctd);

        XSContentType content = complexType.getContentType();
        XSContentType explicitContent = complexType.getExplicitContent();
        if (content != null) {
            XSParticle particle = content.asParticle();
            if (particle != null) {
                XSTerm term = particle.getTerm();

                if (term.isModelGroup()) {
                    Boolean inherited = null;
                    if (explicitContent == null || content == explicitContent) {
                        inherited = false;
                    }
                    addPropertyDefinitionListFromGroup(term.asModelGroup(), ctd, inherited, explicitContent);
                }
            }

            XSAnnotation annotation = complexType.getAnnotation();
            Element extensionAnnotationElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_EXTENSION);
            if (extensionAnnotationElement != null) {
                QName extensionType = DOMUtil.getQNameAttribute(extensionAnnotationElement,
                        A_EXTENSION_REF.getLocalPart());
                if (extensionType == null) {
                    throw new SchemaException("The " + A_EXTENSION + "annontation on " + ctd.getTypeName()
                            + " complex type does not have " + A_EXTENSION_REF.getLocalPart() + " attribute",
                            A_EXTENSION_REF);
                }
                ctd.setExtensionForType(extensionType);
            }
        }

        markRuntime(ctd);

        if (complexType.isAbstract()) {
            ctd.setAbstract(true);
        }

        QName superType = determineSupertype(complexType);
        if (superType != null) {
            ctd.setSuperType(superType);
        }

        if (isObjectDefinition(complexType)) {
            ctd.setObjectMarker(true);
        }
        if (isPropertyContainer(complexType)) {
            ctd.setContainerMarker(true);
        }

        if (isAny(complexType)) {
            ctd.setXsdAnyMarker(true);
        }

        extractDocumentation(ctd, complexType.getAnnotation());

        if (getSchemaRegistry() != null) {
            Class<?> compileTimeClass = getSchemaRegistry().determineCompileTimeClass(ctd);
            ctd.setCompileTimeClass(compileTimeClass);
        }

        definitionFactory.finishComplexTypeDefinition(ctd, complexType, prismContext, complexType.getAnnotation());

        // Attempt to create object or container definition from this complex type

        PrismContainerDefinition<?> defFromComplexType = getDefinitionFactory()
                .createExtraDefinitionFromComplexType(complexType, ctd, prismContext, complexType.getAnnotation());

        if (defFromComplexType != null) {
            markRuntime(defFromComplexType);
            schema.add(defFromComplexType);
        }

        return ctd;

    }

    private void extractDocumentation(Definition definition, XSAnnotation annotation) {
        if (annotation == null) {
            return;
        }
        Element documentationElement = SchemaProcessorUtil.getAnnotationElement(annotation,
                DOMUtil.XSD_DOCUMENTATION_ELEMENT);
        if (documentationElement != null) {
            // The documentation may be HTML-formatted. Therefore we want to keep the formatting and tag names
            String documentationText = DOMUtil.serializeElementContent(documentationElement);
            definition.setDocumentation(documentationText);
        }
    }

    /**
    * Creates ComplexTypeDefinition object from a XSModelGroup inside XSD complexType definition.
    * This is a recursive method. It can create "anonymous" internal PropertyContainerDefinitions.
    * The definitions will be added to the ComplexTypeDefinition provided as parameter.
     * @param group XSD XSModelGroup
     * @param ctd ComplexTypeDefinition that will hold the definitions
     * @param inherited Are these properties inherited? (null means we don't know and we'll determine that from explicitContent)
     * @param explicitContent Explicit (i.e. non-inherited) content of the type being parsed - filled-in only for subtypes!
     */
    private void addPropertyDefinitionListFromGroup(XSModelGroup group, ComplexTypeDefinition ctd,
            Boolean inherited, XSContentType explicitContent) throws SchemaException {

        XSParticle[] particles = group.getChildren();
        for (XSParticle p : particles) {
            boolean particleInherited = inherited != null ? inherited : (p != explicitContent);
            XSTerm pterm = p.getTerm();
            if (pterm.isModelGroup()) {
                addPropertyDefinitionListFromGroup(pterm.asModelGroup(), ctd, particleInherited, explicitContent);
            }

            // xs:element inside complex type
            if (pterm.isElementDecl()) {
                XSAnnotation annotation = selectAnnotationToUse(p.getAnnotation(), pterm.getAnnotation());

                XSElementDecl elementDecl = pterm.asElementDecl();
                QName elementName = new QName(elementDecl.getTargetNamespace(), elementDecl.getName());
                QName typeFromAnnotation = getTypeAnnotation(p.getAnnotation());

                XSType xsType = elementDecl.getType();

                if (isObjectReference(xsType, annotation)) {

                    processObjectReferenceDefinition(xsType, elementName, annotation, ctd, p, particleInherited);

                } else if (isObjectDefinition(xsType)) {
                    // This is object reference. It also has its *Ref equivalent which will get parsed.
                    // therefore it is safe to ignore

                } else if (xsType.getName() == null && typeFromAnnotation == null) {

                    if (isAny(xsType)) {
                        if (isPropertyContainer(elementDecl)) {
                            XSAnnotation containerAnnotation = xsType.getAnnotation();
                            PrismContainerDefinition<?> containerDefinition = createPropertyContainerDefinition(
                                    xsType, p, null, containerAnnotation, false);
                            containerDefinition.setInherited(particleInherited);
                            ctd.addDefinition(containerDefinition);
                        } else {
                            PrismPropertyDefinition propDef = createPropertyDefinition(xsType, elementName,
                                    DOMUtil.XSD_ANY, ctd, annotation, p);
                            propDef.setInherited(particleInherited);
                            ctd.addDefinition(propDef);
                        }
                    }

                } else if (isPropertyContainer(elementDecl)) {

                    // Create an inner PropertyContainer. It is assumed that this is a XSD complex type
                    XSComplexType complexType = (XSComplexType) xsType;
                    ComplexTypeDefinition complexTypeDefinition = null;
                    if (typeFromAnnotation != null && complexType != null
                            && !typeFromAnnotation.equals(getType(xsType))) {
                        // There is a type override annotation. The type that the schema parser determined is useless
                        // We need to locate our own complex type definition
                        if (isMyNamespace(typeFromAnnotation)) {
                            complexTypeDefinition = getOrProcessComplexType(typeFromAnnotation);
                        } else {
                            complexTypeDefinition = getPrismContext().getSchemaRegistry()
                                    .findComplexTypeDefinition(typeFromAnnotation);
                        }
                        if (complexTypeDefinition == null) {
                            throw new SchemaException("Cannot find definition of complex type " + typeFromAnnotation
                                    + " as specified in type override annotation at " + elementName);
                        }
                    } else {
                        complexTypeDefinition = processComplexTypeDefinition(complexType);
                    }
                    XSAnnotation containerAnnotation = complexType.getAnnotation();
                    PrismContainerDefinition<?> containerDefinition = createPropertyContainerDefinition(xsType, p,
                            complexTypeDefinition, containerAnnotation, false);
                    if (isAny(xsType)) {
                        containerDefinition.setRuntimeSchema(true);
                        containerDefinition.setDynamic(true);
                    }
                    containerDefinition.setInherited(particleInherited);
                    ctd.addDefinition(containerDefinition);

                } else {

                    // Create a property definition (even if this is a XSD complex type)
                    QName typeName = new QName(xsType.getTargetNamespace(), xsType.getName());

                    PrismPropertyDefinition propDef = createPropertyDefinition(xsType, elementName, typeName, ctd,
                            annotation, p);
                    propDef.setInherited(particleInherited);
                    ctd.add(propDef);
                }
            }
        }
    }

    private PrismReferenceDefinition processObjectReferenceDefinition(XSType xsType, QName elementName,
            XSAnnotation annotation, ComplexTypeDefinition containingCtd, XSParticle elementParticle,
            boolean inherited) throws SchemaException {
        QName typeName = new QName(xsType.getTargetNamespace(), xsType.getName());
        QName primaryElementName = elementName;
        Element objRefAnnotationElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_OBJECT_REFERENCE);
        boolean hasExplicitPrimaryElementName = (objRefAnnotationElement != null
                && !StringUtils.isEmpty(objRefAnnotationElement.getTextContent()));
        if (hasExplicitPrimaryElementName) {
            primaryElementName = DOMUtil.getQNameValue(objRefAnnotationElement);
        }
        PrismReferenceDefinition definition = null;
        if (containingCtd != null) {
            definition = containingCtd.findItemDefinition(primaryElementName, PrismReferenceDefinition.class);
        }
        if (definition == null) {
            SchemaDefinitionFactory definitionFactory = getDefinitionFactory();
            definition = definitionFactory.createReferenceDefinition(primaryElementName, typeName, containingCtd,
                    prismContext, annotation, elementParticle);
            definition.setInherited(inherited);
            if (containingCtd != null) {
                containingCtd.add(definition);
            }
        }
        if (hasExplicitPrimaryElementName) {
            // The elements that have explicit type name determine the target type name (if not yet set)
            if (definition.getTargetTypeName() == null) {
                definition.setTargetTypeName(typeName);
            }
            if (definition.getCompositeObjectElementName() == null) {
                definition.setCompositeObjectElementName(elementName);
            }
        } else {
            // The elements that use default element names override type definition
            // as there can be only one such definition, therefore the behavior is deterministic
            definition.setTypeName(typeName);
        }
        Element targetTypeAnnotationElement = SchemaProcessorUtil.getAnnotationElement(annotation,
                A_OBJECT_REFERENCE_TARGET_TYPE);
        if (targetTypeAnnotationElement != null
                && !StringUtils.isEmpty(targetTypeAnnotationElement.getTextContent())) {
            // Explicit definition of target type overrides previous logic
            QName targetType = DOMUtil.getQNameValue(targetTypeAnnotationElement);
            definition.setTargetTypeName(targetType);
        }
        setMultiplicity(definition, elementParticle, annotation, false);
        Boolean composite = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_COMPOSITE);
        if (composite != null) {
            definition.setComposite(composite);
        }

        parseItemDefinitionAnnotations(definition, annotation);
        //        extractDocumentation(definition, annotation);
        return definition;
    }

    private void setMultiplicity(ItemDefinition itemDef, XSParticle particle, XSAnnotation annotation,
            boolean topLevel) {
        if (topLevel || particle == null) {
            itemDef.setMinOccurs(0);
            Element maxOccursAnnotation = SchemaProcessorUtil.getAnnotationElement(annotation, A_MAX_OCCURS);
            if (maxOccursAnnotation != null) {
                String maxOccursString = maxOccursAnnotation.getTextContent();
                int maxOccurs = XsdTypeMapper.multiplicityToInteger(maxOccursString);
                itemDef.setMaxOccurs(maxOccurs);
            } else {
                itemDef.setMaxOccurs(-1);
            }
        } else {
            //itemDef.setMinOccurs(particle.getMinOccurs());
            //itemDef.setMaxOccurs(particle.getMaxOccurs());
            itemDef.setMinOccurs(particle.getMinOccurs().intValue());
            itemDef.setMaxOccurs(particle.getMaxOccurs().intValue());
        }
    }

    /**
     * Create PropertyContainer (and possibly also Property) definition from the top-level elements in XSD.
     * Each top-level element will be interpreted as a potential PropertyContainer. The element name will be set as
     * name of the PropertyContainer, element type will become type (indirectly through ComplexTypeDefinition).
     * 
     * No need to recurse here. All the work was already done while creating ComplexTypeDefinitions.
     * 
     * @param set XS Schema Set
     * @throws SchemaException
     */
    private void createDefinitionsFromElements(XSSchemaSet set) throws SchemaException {
        Iterator<XSElementDecl> iterator = set.iterateElementDecls();
        while (iterator.hasNext()) {
            XSElementDecl xsElementDecl = iterator.next();
            if (isDeprecated(xsElementDecl)) {
                // Safe to ignore. We want it in the XSD schema only. The real definition will be
                // parsed from the non-deprecated variant
            }

            if (xsElementDecl.getTargetNamespace().equals(schema.getNamespace())) {

                QName elementName = new QName(xsElementDecl.getTargetNamespace(), xsElementDecl.getName());
                XSType xsType = xsElementDecl.getType();
                if (xsType == null) {
                    throw new SchemaException("Found element " + elementName + " without type definition");
                }
                QName typeQName = determineType(xsElementDecl);
                if (typeQName == null) {
                    // No type defined, safe to skip
                    continue;
                    //throw new SchemaException("Found element "+elementName+" with incomplete type name: {"+xsType.getTargetNamespace()+"}"+xsType.getName());
                }
                XSAnnotation annotation = xsElementDecl.getAnnotation();

                if (isPropertyContainer(xsElementDecl) || isObjectDefinition(xsType)) {

                    ComplexTypeDefinition complexTypeDefinition = schema.findComplexTypeDefinition(typeQName);
                    PrismContainerDefinition<?> propertyContainerDefinition = createPropertyContainerDefinition(
                            xsType, xsElementDecl, complexTypeDefinition, annotation, null, true);
                    schema.getDefinitions().add(propertyContainerDefinition);

                } else if (isObjectReference(xsElementDecl, xsType)) {

                    PrismReferenceDefinition refDef = processObjectReferenceDefinition(xsType, elementName,
                            annotation, null, null, false);

                    schema.getDefinitions().add(refDef);

                } else {

                    // Create a top-level property definition (even if this is a XSD complex type)
                    PrismPropertyDefinition propDef = createPropertyDefinition(xsType, elementName, typeQName, null,
                            xsElementDecl.getAnnotation(), null);
                    schema.getDefinitions().add(propDef);
                }

            } else if (xsElementDecl.getTargetNamespace().equals(XMLConstants.W3C_XML_SCHEMA_NS_URI)) {
                // This is OK to ignore. These are imported elements from other schemas
                //            } else {
                //               throw new SchemaException("Found element "+xsElementDecl.getName()+" with wrong namespace "+xsElementDecl.getTargetNamespace()+" while expecting "+schema.getNamespace());
            }
        }
    }

    private QName determineType(XSElementDecl xsElementDecl) {
        // Check for a:type annotation. If present, this overrides the type
        QName type = getTypeAnnotation(xsElementDecl);
        if (type != null) {
            return type;
        }
        XSType xsType = xsElementDecl.getType();
        if (xsType == null) {
            return null;
        }
        return getType(xsType);
    }

    private QName getType(XSType xsType) {
        if (xsType.getName() == null) {
            return null;
        }
        return new QName(xsType.getTargetNamespace(), xsType.getName());
    }

    private QName getTypeAnnotation(XSElementDecl xsElementDecl) {
        XSAnnotation annotation = xsElementDecl.getAnnotation();
        return getTypeAnnotation(annotation);
    }

    private QName getTypeAnnotation(XSAnnotation annotation) {
        return SchemaProcessorUtil.getAnnotationQName(annotation, A_TYPE);
    }

    /**
     * Determine whether the definition contains xsd:any (directly or indirectly)
     */
    private boolean isAny(XSType xsType) {
        if (xsType instanceof XSComplexType) {
            XSComplexType complexType = (XSComplexType) xsType;
            XSContentType contentType = complexType.getContentType();
            if (contentType != null) {
                XSParticle particle = contentType.asParticle();
                if (particle != null) {
                    XSTerm term = particle.getTerm();
                    if (term != null) {
                        return isAny(term);
                    }
                }
            }
        }
        return false;
    }

    private QName determineSupertype(XSComplexType complexType) {
        XSType baseType = complexType.getBaseType();
        if (baseType == null) {
            return null;
        }
        if (baseType.getName().equals("anyType")) {
            return null;
        }
        return new QName(baseType.getTargetNamespace(), baseType.getName());
    }

    /**
     * Determine whether the definition contains xsd:any (directly or indirectly)
     */
    private boolean isAny(XSTerm term) {
        if (term.isWildcard()) {
            return true;
        }
        if (term.isModelGroup()) {
            XSParticle[] children = term.asModelGroup().getChildren();
            if (children != null) {
                for (XSParticle childParticle : children) {
                    XSTerm childTerm = childParticle.getTerm();
                    if (childTerm != null) {
                        if (isAny(childTerm)) {
                            return true;
                        }
                    }
                }
            }
        }
        return false;
    }

    private boolean isPropertyContainer(XSElementDecl xsElementDecl) {
        Element annoElement = SchemaProcessorUtil.getAnnotationElement(xsElementDecl.getAnnotation(),
                A_PROPERTY_CONTAINER);
        if (annoElement != null) {
            return true;
        }
        return isPropertyContainer(xsElementDecl.getType());
    }

    /**
     * Returns true if provides XSD type is a property container. It looks for annotations.
     */
    private boolean isPropertyContainer(XSType xsType) {
        Element annoElement = SchemaProcessorUtil.getAnnotationElement(xsType.getAnnotation(),
                A_PROPERTY_CONTAINER);
        if (annoElement != null) {
            return true;
        }
        if (xsType.getBaseType() != null && !xsType.getBaseType().equals(xsType)) {
            return isPropertyContainer(xsType.getBaseType());
        }
        return false;
    }

    private boolean isObjectReference(XSElementDecl xsElementDecl, XSType xsType) {
        XSAnnotation annotation = xsType.getAnnotation();
        return isObjectReference(xsType, annotation);
    }

    private boolean isObjectReference(XSType xsType, XSAnnotation annotation) {
        if (isObjectReference(annotation)) {
            return true;
        }
        return isObjectReference(xsType);
    }

    private boolean isObjectReference(XSAnnotation annotation) {
        Element objRefAnnotationElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_OBJECT_REFERENCE);
        return (objRefAnnotationElement != null);
    }

    private boolean isObjectReference(XSType xsType) {
        return SchemaProcessorUtil.hasAnnotation(xsType, A_OBJECT_REFERENCE);
    }

    /**
     * Returns true if provides XSD type is an object definition. It looks for a ObjectType supertype.
     */
    private boolean isObjectDefinition(XSType xsType) {
        return SchemaProcessorUtil.hasAnnotation(xsType, A_OBJECT);
    }

    /**
     * Creates appropriate instance of PropertyContainerDefinition.
     * It may be PropertyContainerDefinition itself or one of its subclasses (ResourceObjectDefinition). This method also takes care of parsing
     * all the annotations and similar fancy stuff.
     * 
     * We need to pass createResourceObject flag explicitly here. Because even if we are in resource schema, we want PropertyContainers inside
     * ResourceObjects, not ResourceObjects inside ResouceObjects.
     */
    private PrismContainerDefinition<?> createPropertyContainerDefinition(XSType xsType, XSParticle elementParticle,
            ComplexTypeDefinition complexTypeDefinition, XSAnnotation annotation, boolean topLevel)
            throws SchemaException {
        XSTerm elementTerm = elementParticle.getTerm();
        XSElementDecl elementDecl = elementTerm.asElementDecl();

        PrismContainerDefinition<?> pcd = createPropertyContainerDefinition(xsType, elementDecl,
                complexTypeDefinition, annotation, elementParticle, topLevel);
        return pcd;
    }

    @SuppressWarnings({ "rawtypes", "unchecked" })
    private PrismContainerDefinition<?> createPropertyContainerDefinition(XSType xsType, XSElementDecl elementDecl,
            ComplexTypeDefinition complexTypeDefinition, XSAnnotation complexTypeAnnotation,
            XSParticle elementParticle, boolean topLevel) throws SchemaException {

        QName elementName = new QName(elementDecl.getTargetNamespace(), elementDecl.getName());
        PrismContainerDefinition<?> pcd = null;

        SchemaDefinitionFactory definitionFactory = getDefinitionFactory();

        if (isObjectDefinition(xsType)) {
            Class compileTimeClass = null;
            if (getSchemaRegistry() != null) {
                compileTimeClass = getSchemaRegistry().determineCompileTimeClass(elementName,
                        complexTypeDefinition);
            }
            pcd = definitionFactory.createObjectDefinition(elementName, complexTypeDefinition, prismContext,
                    compileTimeClass, complexTypeAnnotation, elementParticle);
            // Multiplicity is fixed to a single-value here
            pcd.setMinOccurs(1);
            pcd.setMaxOccurs(1);
        } else {
            pcd = definitionFactory.createContainerDefinition(elementName, complexTypeDefinition, prismContext,
                    complexTypeAnnotation, elementParticle);
            setMultiplicity(pcd, elementParticle, elementDecl.getAnnotation(), topLevel);
        }

        markRuntime(pcd);

        parseItemDefinitionAnnotations(pcd, complexTypeAnnotation);
        parseItemDefinitionAnnotations(pcd, elementDecl.getAnnotation());
        if (elementParticle != null) {
            parseItemDefinitionAnnotations(pcd, elementParticle.getAnnotation());
        }

        return pcd;
    }

    /**
     * Creates appropriate instance of PropertyDefinition.
     * It creates either PropertyDefinition itself or one of its subclasses (ResourceObjectAttributeDefinition). The behavior
     * depends of the "mode" of the schema. This method is also processing annotations and other fancy property-relates stuff.
     */
    private <T> PrismPropertyDefinition<T> createPropertyDefinition(XSType xsType, QName elementName,
            QName typeName, ComplexTypeDefinition ctd, XSAnnotation annotation, XSParticle elementParticle)
            throws SchemaException {
        PrismPropertyDefinition<T> propDef;

        SchemaDefinitionFactory definitionFactory = getDefinitionFactory();

        Collection<? extends DisplayableValue<T>> allowedValues = parseEnumAllowedValues(xsType);

        Object defaultValue = parseDefaultValue(elementParticle, typeName);

        propDef = definitionFactory.createPropertyDefinition(elementName, typeName, ctd, prismContext, annotation,
                elementParticle, allowedValues, null);
        setMultiplicity(propDef, elementParticle, annotation, ctd == null);

        // Process generic annotations
        parseItemDefinitionAnnotations(propDef, annotation);

        List<Element> accessElements = SchemaProcessorUtil.getAnnotationElements(annotation, A_ACCESS);
        if (accessElements == null || accessElements.isEmpty()) {
            // Default access is read-write-create
            propDef.setCanAdd(true);
            propDef.setCanModify(true);
            propDef.setCanRead(true);
        } else {
            propDef.setCanAdd(false);
            propDef.setCanModify(false);
            propDef.setCanRead(false);
            for (Element e : accessElements) {
                String access = e.getTextContent();
                if (access.equals(A_ACCESS_CREATE)) {
                    propDef.setCanAdd(true);
                }
                if (access.equals(A_ACCESS_UPDATE)) {
                    propDef.setCanModify(true);
                }
                if (access.equals(A_ACCESS_READ)) {
                    propDef.setCanRead(true);
                }
            }
        }

        markRuntime(propDef);

        Element indexableElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_INDEXED);
        if (indexableElement != null) {
            Boolean indexable = XmlTypeConverter.toJavaValue(indexableElement, Boolean.class);
            propDef.setIndexed(indexable);
        }

        return propDef;
    }

    private Object parseDefaultValue(XSParticle elementParticle, QName typeName) {
        if (elementParticle == null) {
            return null;
        }
        XSTerm term = elementParticle.getTerm();
        if (term == null) {
            return null;
        }

        XSElementDecl elementDecl = term.asElementDecl();
        if (elementDecl == null) {
            return null;
        }
        if (elementDecl.getDefaultValue() != null) {
            if (XmlTypeConverter.canConvert(typeName)) {
                return XmlTypeConverter.toJavaValue(elementDecl.getDefaultValue().value, typeName);
            }
            return elementDecl.getDefaultValue().value;
        }
        return null;
    }

    private <T> Collection<? extends DisplayableValue<T>> parseEnumAllowedValues(XSType xsType) {
        if (xsType.isSimpleType()) {
            if (xsType.asSimpleType().isRestriction()) {
                XSRestrictionSimpleType restriction = xsType.asSimpleType().asRestriction();
                List<XSFacet> enumerations = restriction.getDeclaredFacets(XSFacet.FACET_ENUMERATION);
                List<DisplayableValueImpl<T>> enumValues = new ArrayList<DisplayableValueImpl<T>>(
                        enumerations.size());
                for (XSFacet facet : enumerations) {
                    String value = facet.getValue().value;
                    Element descriptionE = SchemaProcessorUtil.getAnnotationElement(facet.getAnnotation(),
                            SCHEMA_DOCUMENTATION);
                    Element appInfo = SchemaProcessorUtil.getAnnotationElement(facet.getAnnotation(),
                            SCHEMA_APP_INFO);
                    Element valueE = null;
                    if (appInfo != null) {
                        NodeList list = appInfo.getElementsByTagNameNS(PrismConstants.A_LABEL.getNamespaceURI(),
                                PrismConstants.A_LABEL.getLocalPart());
                        if (list.getLength() != 0) {
                            valueE = (Element) list.item(0);
                        }
                    }
                    String label = null;
                    if (valueE != null) {
                        label = valueE.getTextContent();
                    } else {
                        label = value;
                    }

                    DisplayableValueImpl<T> edv = new DisplayableValueImpl(value, label,
                            descriptionE != null ? descriptionE.getTextContent() : null);

                    enumValues.add(edv);

                }
                if (enumValues != null && !enumValues.isEmpty()) {
                    return enumValues;
                }

            }
        }
        return null;
    }

    private void parseItemDefinitionAnnotations(ItemDefinition itemDef, XSAnnotation annotation)
            throws SchemaException {
        if (annotation == null || annotation.getAnnotation() == null) {
            return;
        }

        QName elementName = itemDef.getName();

        // ignore
        Boolean ignore = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_IGNORE);
        if (ignore != null) {
            itemDef.setIgnored(ignore);
        }

        // deprecated
        Boolean deprecated = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_DEPRECATED);
        if (deprecated != null) {
            itemDef.setDeprecated(deprecated);
        }

        // operational
        Boolean operational = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_OPERATIONAL);
        if (operational != null) {
            itemDef.setOperational(operational);
        }

        // displayName
        Element attributeDisplayName = SchemaProcessorUtil.getAnnotationElement(annotation, A_DISPLAY_NAME);
        if (attributeDisplayName != null) {
            itemDef.setDisplayName(attributeDisplayName.getTextContent());
        }

        // displayOrder
        Element displayOrderElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_DISPLAY_ORDER);
        if (displayOrderElement != null) {
            Integer displayOrder = DOMUtil.getIntegerValue(displayOrderElement);
            itemDef.setDisplayOrder(displayOrder);
        }

        // help
        Element help = SchemaProcessorUtil.getAnnotationElement(annotation, A_HELP);
        if (help != null) {
            itemDef.setHelp(help.getTextContent());
        }

        // documentation
        extractDocumentation(itemDef, annotation);
    }

    private boolean isDeprecated(XSElementDecl xsElementDecl) throws SchemaException {
        XSAnnotation annotation = xsElementDecl.getAnnotation();
        Boolean deprecated = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_DEPRECATED);
        return (deprecated != null && deprecated);
    }

    private boolean containsAccessFlag(String flag, List<Element> accessList) {
        for (Element element : accessList) {
            if (flag.equals(element.getTextContent())) {
                return true;
            }
        }

        return false;
    }

    private XSAnnotation selectAnnotationToUse(XSAnnotation particleAnnotation, XSAnnotation termAnnotation) {
        boolean useParticleAnnotation = false;
        if (particleAnnotation != null && particleAnnotation.getAnnotation() != null) {
            if (testAnnotationAppinfo(particleAnnotation)) {
                useParticleAnnotation = true;
            }
        }

        boolean useTermAnnotation = false;
        if (termAnnotation != null && termAnnotation.getAnnotation() != null) {
            if (testAnnotationAppinfo(termAnnotation)) {
                useTermAnnotation = true;
            }
        }

        if (useParticleAnnotation) {
            return particleAnnotation;
        }

        if (useTermAnnotation) {
            return termAnnotation;
        }

        return null;
    }

    private boolean testAnnotationAppinfo(XSAnnotation annotation) {
        Element appinfo = SchemaProcessorUtil.getAnnotationElement(annotation,
                new QName(W3C_XML_SCHEMA_NS_URI, "appinfo"));
        if (appinfo != null) {
            return true;
        }

        return false;
    }

    private void markRuntime(Definition def) {
        if (isRuntime) {
            def.setRuntimeSchema(true);
        }
    }

}