Java tutorial
/* * Copyright (c) 2010-2017 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 com.evolveum.midpoint.prism.*; 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.*; import org.apache.commons.lang.StringUtils; import org.jetbrains.annotations.NotNull; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.xml.bind.annotation.XmlEnumValue; import javax.xml.namespace.QName; import java.lang.reflect.Field; import java.util.ArrayList; import java.util.Collection; import java.util.Iterator; import java.util.List; import java.util.function.Consumer; import static com.evolveum.midpoint.prism.PrismConstants.*; import static javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI; /** * 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 DomToSchemaPostProcessor { private static final Trace LOGGER = TraceManager.getTrace(DomToSchemaPostProcessor.class); private final XSSchemaSet xsSchemaSet; private final PrismContext prismContext; private PrismSchemaImpl schema; private String shortDescription; private boolean isRuntime; private boolean allowDelayedItemDefinitions; DomToSchemaPostProcessor(XSSchemaSet xsSchemaSet, PrismContext prismContext) { this.xsSchemaSet = xsSchemaSet; this.prismContext = prismContext; } private SchemaRegistry getSchemaRegistry() { return this.prismContext.getSchemaRegistry(); } private SchemaDefinitionFactory getDefinitionFactory() { return ((PrismContextImpl) prismContext).getDefinitionFactory(); } private String getNamespace() { return schema.getNamespace(); } private boolean isMyNamespace(QName qname) { return getNamespace().equals(qname.getNamespaceURI()); } /** * Main entry point. */ void postprocessSchema(PrismSchemaImpl prismSchema, boolean isRuntime, boolean allowDelayedItemDefinitions, String shortDescription) throws SchemaException { this.schema = prismSchema; this.isRuntime = isRuntime; this.allowDelayedItemDefinitions = allowDelayedItemDefinitions; this.shortDescription = shortDescription; // Create ComplexTypeDefinitions from all top-level complexType // definition in the XSD processComplexTypeDefinitions(xsSchemaSet); // Create SimpleTypeDefinitions from all top-level simpleType // definition in the XSD processSimpleTypeDefinitions(xsSchemaSet); // Create PropertyContainer (and possibly also Property) definition from // the top-level elements in XSD // This also creates ResourceObjectDefinition in some cases createDefinitionsFromElements(xsSchemaSet); } /** * 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())) { LOGGER.trace("### processing CTD {} into {} [{}]", complexType, schema, shortDescription); 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(); ComplexTypeDefinitionImpl ctd = (ComplexTypeDefinitionImpl) 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); } setInstantiationOrder(ctd, complexType.getAnnotation()); if (isObjectDefinition(complexType)) { ctd.setObjectMarker(true); } if (isPropertyContainer(complexType)) { ctd.setContainerMarker(true); } if (isObjectReference(complexType)) { ctd.setReferenceMarker(true); } ctd.setDefaultNamespace(getDefaultNamespace(complexType)); ctd.setIgnoredNamespaces(getIgnoredNamespaces(complexType)); if (isAny(complexType)) { ctd.setXsdAnyMarker(true); } if (isList(complexType)) { ctd.setListMarker(true); } extractDocumentation(ctd, complexType.getAnnotation()); Class<?> compileTimeClass = getSchemaRegistry().determineCompileTimeClass(ctd.getTypeName()); 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 setInstantiationOrder(TypeDefinitionImpl typeDefinition, XSAnnotation annotation) throws SchemaException { Integer order = SchemaProcessorUtil.getAnnotationInteger(annotation, A_INSTANTIATION_ORDER); typeDefinition.setInstantiationOrder(order); } private void processSimpleTypeDefinitions(XSSchemaSet set) throws SchemaException { Iterator<XSSimpleType> iterator = set.iterateSimpleTypes(); while (iterator.hasNext()) { XSSimpleType simpleType = iterator.next(); if (simpleType.getTargetNamespace().equals(schema.getNamespace())) { LOGGER.trace("### processing STD {} into {} [{}]", simpleType, schema, shortDescription); processSimpleTypeDefinition(simpleType); } } } private SimpleTypeDefinition processSimpleTypeDefinition(XSSimpleType simpleType) throws SchemaException { SchemaDefinitionFactory definitionFactory = getDefinitionFactory(); SimpleTypeDefinitionImpl std = (SimpleTypeDefinitionImpl) definitionFactory .createSimpleTypeDefinition(simpleType, prismContext, simpleType.getAnnotation()); SimpleTypeDefinition existingSimpleTypeDefinition = schema .findSimpleTypeDefinitionByType(std.getTypeName()); if (existingSimpleTypeDefinition != null) { // We already have this in schema. So avoid redundant work return existingSimpleTypeDefinition; } markRuntime(std); QName superType = determineSupertype(simpleType); if (superType != null) { std.setSuperType(superType); } setInstantiationOrder(std, simpleType.getAnnotation()); extractDocumentation(std, simpleType.getAnnotation()); if (getSchemaRegistry() != null) { Class<?> compileTimeClass = getSchemaRegistry().determineCompileTimeClass(std.getTypeName()); std.setCompileTimeClass(compileTimeClass); } schema.add(std); return std; } 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); ((DefinitionImpl) 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); ((PrismContainerDefinitionImpl) containerDefinition).setInherited(particleInherited); ((ComplexTypeDefinitionImpl) ctd).add(containerDefinition); } else { PrismPropertyDefinitionImpl propDef = createPropertyDefinition(xsType, elementName, DOMUtil.XSD_ANY, ctd, annotation, p); propDef.setInherited(particleInherited); ((ComplexTypeDefinitionImpl) ctd).add(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; if (typeFromAnnotation != 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 = prismContext.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)) { // ((PrismContainerDefinitionImpl) containerDefinition).setRuntimeSchema(true); // ((PrismContainerDefinitionImpl) containerDefinition).setDynamic(true); // } ((PrismContainerDefinitionImpl) containerDefinition).setInherited(particleInherited); ((ComplexTypeDefinitionImpl) ctd).add(containerDefinition); } else { // Create a property definition (even if this is a XSD // complex type) QName typeName = new QName(xsType.getTargetNamespace(), xsType.getName()); PrismPropertyDefinitionImpl propDef = createPropertyDefinition(xsType, elementName, typeName, ctd, annotation, p); propDef.setInherited(particleInherited); ((ComplexTypeDefinitionImpl) ctd).add(propDef); } } } } private PrismReferenceDefinitionImpl 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); } PrismReferenceDefinitionImpl definition = null; if (containingCtd != null) { definition = (PrismReferenceDefinitionImpl) containingCtd.findItemDefinition(primaryElementName, PrismReferenceDefinition.class); } if (definition == null) { SchemaDefinitionFactory definitionFactory = getDefinitionFactory(); definition = (PrismReferenceDefinitionImpl) definitionFactory.createReferenceDefinition( primaryElementName, typeName, containingCtd, prismContext, annotation, elementParticle); definition.setInherited(inherited); if (containingCtd != null) { ((ComplexTypeDefinitionImpl) 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) { ((ItemDefinitionImpl) itemDef).setMinOccurs(0); Element maxOccursAnnotation = SchemaProcessorUtil.getAnnotationElement(annotation, A_MAX_OCCURS); if (maxOccursAnnotation != null) { String maxOccursString = maxOccursAnnotation.getTextContent(); int maxOccurs = XsdTypeMapper.multiplicityToInteger(maxOccursString); ((ItemDefinitionImpl) itemDef).setMaxOccurs(maxOccurs); } else { ((ItemDefinitionImpl) itemDef).setMaxOccurs(-1); } } else { // itemDef.setMinOccurs(particle.getMinOccurs()); // itemDef.setMaxOccurs(particle.getMaxOccurs()); ((ItemDefinitionImpl) itemDef).setMinOccurs(particle.getMinOccurs().intValue()); ((ItemDefinitionImpl) 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()); LOGGER.trace("### processing item {} into {} [{}]", elementName, schema, shortDescription); 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(); ItemDefinitionImpl definition; if (isPropertyContainer(xsElementDecl) || isObjectDefinition(xsType)) { ComplexTypeDefinition complexTypeDefinition = findComplexTypeDefinition(typeQName); if (complexTypeDefinition == null) { if (!allowDelayedItemDefinitions) { throw new SchemaException("Couldn't parse prism container " + elementName + " of type " + typeQName + " because complex type definition couldn't be found and delayed item definitions are not allowed."); } definition = null; schema.addDelayedItemDefinition(() -> { ComplexTypeDefinition ctd = findComplexTypeDefinition(typeQName); // here we take the risk that ctd is null return createPropertyContainerDefinition(xsType, xsElementDecl, ctd, annotation, null, true); }); } else { definition = createPropertyContainerDefinition(xsType, xsElementDecl, complexTypeDefinition, annotation, null, true); } } else if (isObjectReference(xsElementDecl, xsType)) { definition = processObjectReferenceDefinition(xsType, elementName, annotation, null, null, false); } else { // Create a top-level property definition (even if this is a XSD complex type) definition = createPropertyDefinition(xsType, elementName, typeQName, null, annotation, null); } if (definition != null) { definition.setSubstitutionHead(getSubstitutionHead(xsElementDecl)); schema.add(definition); } } 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()); } } } // We first try to find the definition locally, because in schema registry we don't have the current schema yet. private ComplexTypeDefinition findComplexTypeDefinition(QName typeQName) { ComplexTypeDefinition complexTypeDefinition = schema.findComplexTypeDefinitionByType(typeQName); if (complexTypeDefinition == null) { complexTypeDefinition = getSchemaRegistry().findComplexTypeDefinitionByType(typeQName); } return complexTypeDefinition; } private QName getSubstitutionHead(XSElementDecl element) { XSElementDecl head = element.getSubstAffiliation(); if (head == null) { return null; } else { return new QName(head.getTargetNamespace(), head.getName()); } } 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; } /** * Determine whether the definition contains "list" attribute (directly or indirectly) */ private boolean isList(XSComplexType complexType) { Collection<? extends XSAttributeUse> attributeUses = complexType.getAttributeUses(); return attributeUses != null && attributeUses.stream().anyMatch( au -> au.getDecl() != null && DOMUtil.IS_LIST_ATTRIBUTE_NAME.equals(au.getDecl().getName())); } // not much tested private void applyToDeclarations(XSComponent component, Consumer<XSDeclaration> consumer) { if (component == null) { return; } if (component instanceof XSDeclaration) { consumer.accept((XSDeclaration) component); } // recursion (if needed) if (component instanceof XSParticle) { applyToDeclarations(((XSParticle) component).getTerm(), consumer); } else if (component instanceof XSModelGroup) { for (XSParticle particle : ((XSModelGroup) component).getChildren()) { applyToDeclarations(particle, consumer); } } else if (component instanceof XSModelGroupDecl) { applyToDeclarations(((XSModelGroupDecl) component).getModelGroup(), consumer); } } private QName determineSupertype(XSType type) { XSType baseType = type.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 String getDefaultNamespace(XSType xsType) { Element annoElement = SchemaProcessorUtil.getAnnotationElement(xsType.getAnnotation(), A_DEFAULT_NAMESPACE); if (annoElement != null) { return annoElement.getTextContent(); } if (xsType.getBaseType() != null && !xsType.getBaseType().equals(xsType)) { return getDefaultNamespace(xsType.getBaseType()); } return null; } @NotNull private List<String> getIgnoredNamespaces(XSType xsType) { List<String> rv = new ArrayList<>(); List<Element> annoElements = SchemaProcessorUtil.getAnnotationElements(xsType.getAnnotation(), A_IGNORED_NAMESPACE); for (Element annoElement : annoElements) { rv.add(annoElement.getTextContent()); } if (xsType.getBaseType() != null && !xsType.getBaseType().equals(xsType)) { rv.addAll(getIgnoredNamespaces(xsType.getBaseType())); } return rv; } 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 PrismContainerDefinitionImpl<?> createPropertyContainerDefinition(XSType xsType, XSElementDecl elementDecl, ComplexTypeDefinition complexTypeDefinition, XSAnnotation annotation, XSParticle elementParticle, boolean topLevel) throws SchemaException { QName elementName = new QName(elementDecl.getTargetNamespace(), elementDecl.getName()); PrismContainerDefinitionImpl<?> pcd; SchemaDefinitionFactory definitionFactory = getDefinitionFactory(); Class compileTimeClass = null; if (getSchemaRegistry() != null && complexTypeDefinition != null) { compileTimeClass = getSchemaRegistry().determineCompileTimeClass(complexTypeDefinition.getTypeName()); } if (isObjectDefinition(xsType)) { pcd = definitionFactory.createObjectDefinition(elementName, complexTypeDefinition, prismContext, compileTimeClass); // Multiplicity is fixed to a single-value here pcd.setMinOccurs(1); pcd.setMaxOccurs(1); } else { pcd = definitionFactory.createContainerDefinition(elementName, complexTypeDefinition, prismContext, compileTimeClass); setMultiplicity(pcd, elementParticle, elementDecl.getAnnotation(), topLevel); } markRuntime(pcd); parseItemDefinitionAnnotations(pcd, annotation); 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> PrismPropertyDefinitionImpl<T> createPropertyDefinition(XSType xsType, QName elementName, QName typeName, ComplexTypeDefinition ctd, XSAnnotation annotation, XSParticle elementParticle) throws SchemaException { PrismPropertyDefinitionImpl<T> propDef; SchemaDefinitionFactory definitionFactory = getDefinitionFactory(); Collection<? extends DisplayableValue<T>> allowedValues = parseEnumAllowedValues(typeName, ctd, xsType); Object defaultValue = parseDefaultValue(elementParticle, typeName); propDef = (PrismPropertyDefinitionImpl) 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.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); } Element matchingRuleElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_MATCHING_RULE); if (matchingRuleElement != null) { QName matchingRule = XmlTypeConverter.toJavaValue(matchingRuleElement, QName.class); propDef.setMatchingRuleQName(matchingRule); } Element valueEnumerationRefElement = SchemaProcessorUtil.getAnnotationElement(annotation, A_VALUE_ENUMERATION_REF); if (valueEnumerationRefElement != null) { String oid = valueEnumerationRefElement.getAttribute(PrismConstants.ATTRIBUTE_OID_LOCAL_NAME); if (oid != null) { QName targetType = DOMUtil.getQNameAttribute(valueEnumerationRefElement, PrismConstants.ATTRIBUTE_REF_TYPE_LOCAL_NAME); PrismReferenceValue valueEnumerationRef = new PrismReferenceValue(oid, targetType); propDef.setValueEnumerationRef(valueEnumerationRef); } } 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(QName typeName, ComplexTypeDefinition ctd, 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<>(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 = null; Class compileTimeClass = prismContext.getSchemaRegistry().getCompileTimeClass(typeName); if (ctd != null && !ctd.isRuntimeSchema() && compileTimeClass != null) { String fieldName = null; for (Field field : compileTimeClass.getDeclaredFields()) { XmlEnumValue xmlEnumValue = field.getAnnotation(XmlEnumValue.class); if (xmlEnumValue != null && xmlEnumValue.value() != null && xmlEnumValue.value().equals(value)) { fieldName = field.getName(); } } if (fieldName != null) { T enumValue = (T) Enum.valueOf((Class<Enum>) compileTimeClass, fieldName); edv = new DisplayableValueImpl(enumValue, label, descriptionE != null ? descriptionE.getTextContent() : null); } else { edv = new DisplayableValueImpl(value, label, descriptionE != null ? descriptionE.getTextContent() : null); } } else { 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(ItemDefinitionImpl itemDef, XSAnnotation annotation) throws SchemaException { if (annotation == null || annotation.getAnnotation() == null) { return; } // ignore Boolean ignore = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_IGNORE); if (ignore != null) { itemDef.setProcessing(ItemProcessing.IGNORE); } Element processing = SchemaProcessorUtil.getAnnotationElement(annotation, A_PROCESSING); if (processing != null) { itemDef.setProcessing(ItemProcessing.findByValue(processing.getTextContent())); } // deprecated Boolean deprecated = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_DEPRECATED); if (deprecated != null) { itemDef.setDeprecated(deprecated); } // deprecated since Element deprecatedSince = SchemaProcessorUtil.getAnnotationElement(annotation, A_DEPRECATED_SINCE); if (deprecatedSince != null) { itemDef.setDeprecatedSince(deprecatedSince.getTextContent()); } // experimental Boolean experimental = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_EXPERIMENTAL); if (experimental != null) { itemDef.setExperimental(experimental); } // elaborate Boolean elaborate = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_ELABORATE); if (elaborate != null) { itemDef.setElaborate(elaborate); } // 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()); } // emphasized Boolean emphasized = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_EMPHASIZED); if (emphasized != null) { itemDef.setEmphasized(emphasized); } // documentation extractDocumentation(itemDef, annotation); Boolean heterogeneousListItem = SchemaProcessorUtil.getAnnotationBooleanMarker(annotation, A_HETEROGENEOUS_LIST_ITEM); if (heterogeneousListItem != null) { itemDef.setHeterogeneousListItem(heterogeneousListItem); } } 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) { ((DefinitionImpl) def).setRuntimeSchema(true); } } }