/******************************************************************************* * Copyright (c) 1998, 2015 Oracle and/or its affiliates. All rights reserved. * This program and the accompanying materials are made available under the * terms of the Eclipse Public License v1.0 and Eclipse Distribution License v. 1.0 * which accompanies this distribution. * The Eclipse Public License is available at http://www.eclipse.org/legal/epl-v10.html * and the Eclipse Distribution License is available at * http://www.eclipse.org/org/documents/edl-v10.php. * * Contributors: * Oracle - initial API and implementation from Oracle TopLink ******************************************************************************/ package org.eclipse.persistence.tools.workbench.mappingsmodel.schema; import java.util.Collection; import java.util.Hashtable; import java.util.Iterator; import java.util.List; import java.util.Map; import java.util.TreeMap; import javax.xml.XMLConstants; import org.apache.xerces.impl.xs.XSComplexTypeDecl; import org.apache.xerces.impl.xs.XSElementDecl; import org.apache.xerces.impl.xs.XSParticleDecl; import org.apache.xerces.xs.XSConstants; import org.apache.xerces.xs.XSObject; import org.apache.xerces.xs.XSTypeDefinition; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.mappings.XMLCompositeCollectionMapping; import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; import org.eclipse.persistence.oxm.mappings.XMLDirectMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.QName; import org.eclipse.persistence.tools.workbench.utility.iterators.CompositeIterator; import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator; import org.eclipse.persistence.tools.workbench.utility.node.Node; public final class ExplicitElementDeclaration extends AbstractNamedSchemaComponent implements MWElementDeclaration { // **************** Variables ********************************************* private volatile MWSchemaTypeDefinition type; private volatile boolean nillable; /** * This is non-null only when nillable=true. * Also, not persisted. Built solely based on state of nillable. */ private transient volatile MWAttributeDeclaration nilAttribute; private volatile String defaultValue; private volatile String fixedValue; private Map identityConstraints; private volatile ReferencedElementDeclaration substitutionGroup; private volatile boolean abstractFlag; // // These two variables are only used when the element is used as a particle // /** The minimum number of times the element occurs in a document */ private volatile int minOccurs; /** The maximum number of times the element occurs in a document */ private volatile int maxOccurs; // **************** Constructors ****************************************** /** Toplink Use Only */ private ExplicitElementDeclaration() { super(); } ExplicitElementDeclaration(MWModel parent, String name) { super(parent, name); } // **************** Initialization **************************************** protected void initialize(Node parent) { super.initialize(parent); this.identityConstraints = new Hashtable(); } protected void initialize() { super.initialize(); this.minOccurs = 1; this.maxOccurs = 1; } protected void addChildrenTo(List children) { super.addChildrenTo(children); if (this.type != null) { children.add(this.type); } synchronized (this.identityConstraints) { children.addAll(this.identityConstraints.values()); } if (this.substitutionGroup != null) { children.add(this.substitutionGroup); } } private MWAttributeDeclaration buildNilAttribute() { return new ReferencedAttributeDeclaration(this, "nil", XMLConstants.W3C_XML_SCHEMA_NS_URI); } // **************** MWElementDeclaration contract ************************* public MWSchemaTypeDefinition getType() { return this.type; } public MWElementDeclaration getSubstitutionGroup() { return this.substitutionGroup; } public boolean isAbstract() { return this.abstractFlag; } public String getDefaultValue() { return this.defaultValue; } public String getFixedValue() { return this.fixedValue; } public boolean isNillable() { return this.nillable; } // **************** MWParticle contract *********************************** public int getMinOccurs() { return this.minOccurs; } public int getMaxOccurs() { return this.maxOccurs; } public boolean isDescriptorContextComponent() { return ! this.type.isReference(); } public boolean isEquivalentTo(XSParticleDecl xsParticle) { if (xsParticle.getTerm() instanceof XSElementDecl) { XSElementDecl elementNode = (XSElementDecl) xsParticle.getTerm(); return this.getName().equals(elementNode.getName()); } else { return false; } } // **************** MWXpathableSchemaComponent contract ******************* public Iterator baseBuiltInTypes() { return this.type.baseBuiltInTypes(); } // **************** MWSchemaContextComponent contract ********************* public boolean hasType() { return true; } public String contextTypeQname() { return this.type.contextTypeQname(); } public boolean containsText() { return this.type.containsText(); } public boolean containsWildcard() { return this.type.containsWildcard(); } public int compareSchemaOrder(MWElementDeclaration element1, MWElementDeclaration element2) { return this.type.compareSchemaOrder(element1, element2); } // **************** MWNamedSchemaComponent contract *********************** public String componentTypeName() { return "element"; } public void addDirectlyOwnedComponentsTo(Collection directlyOwnedComponents) { this.type.addDirectlyOwnedComponentsTo(directlyOwnedComponents); } // **************** MWSchemaModel contract ******************************** public Iterator structuralComponents() { return this.type.structuralComponents(); } public Iterator descriptorContextComponents() { if (! this.type.isReference()) { return this.type.descriptorContextComponents(); } else { return NullIterator.instance(); } } public Iterator xpathComponents() { Iterator typeXpathComponents = this.type.xpathComponents(); if (this.nilAttribute == null) { return typeXpathComponents; } else { return new CompositeIterator(this.nilAttribute, this.type.xpathComponents()); } } public MWNamedSchemaComponent nestedNamedComponent(QName qName) { return this.type.nestedNamedComponent(qName); } public MWAttributeDeclaration nestedAttribute(String namespaceUrl, String attributeName) { if (this.nilAttribute != null && namespaceUrl.equals(this.nilAttribute.getNamespaceUrl()) && attributeName.equals(this.nilAttribute.getName()) ) { return this.nilAttribute; } else { return this.type.nestedAttribute(namespaceUrl, attributeName); } } public MWElementDeclaration nestedElement(String namespaceUrl, String elementName) { return this.type.nestedElement(namespaceUrl, elementName); } public int totalElementCount() { return 1; } // **************** SchemaModel contract ********************************* protected void reloadInternal(XSObject xsObject) { XSElementDecl elemenDecl = null; if (xsObject instanceof XSParticleDecl) { elemenDecl = (XSElementDecl)((XSParticleDecl)xsObject).getTerm(); super.reloadInternal(elemenDecl); } else { elemenDecl = (XSElementDecl)xsObject; super.reloadInternal(xsObject); } this.reloadType(elemenDecl); this.reloadNillable(elemenDecl); if (elemenDecl.getConstraintType() != XSConstants.VC_NONE) { if (elemenDecl.getConstraintType() == XSConstants.VC_DEFAULT) { this.defaultValue = elemenDecl.getConstraintValue(); } else if (elemenDecl.getConstraintType() == XSConstants.VC_FIXED) { this.fixedValue = elemenDecl.getConstraintValue(); } } this.abstractFlag = elemenDecl.getAbstract(); if (xsObject instanceof XSParticleDecl) { this.minOccurs = ((XSParticleDecl)xsObject).getMinOccurs(); this.maxOccurs = ((XSParticleDecl)xsObject).getMaxOccurs(); if (((XSParticleDecl)xsObject).getMaxOccursUnbounded()) { this.maxOccurs = MWXmlSchema.INFINITY; } } this.reloadSubstitutionGroup(elemenDecl); this.reloadIdentityConstraints(elemenDecl); } private void reloadType(XSElementDecl elementDecl) { MWSchemaTypeDefinition oldType = this.type; XSTypeDefinition typeDef = elementDecl.getTypeDefinition(); String typeName = typeDef.getName(); String typeNamespace = typeDef.getNamespace(); // This is a short circuit to avoid infinitely nested elements. // Basically, if an element contains itself, we snip the chain at three nested elements. if (! "".equals(this.getName()) && this.parentNamedComponent() instanceof ExplicitElementDeclaration && this.getName().equals(this.parentNamedComponent().getName()) && this.parentNamedComponent().parentNamedComponent() instanceof ExplicitElementDeclaration && this.getName().equals(this.parentNamedComponent().parentNamedComponent().getName()) ) { this.type = ReferencedSchemaTypeDefinition.urType(this); } // And from here, we reload the type normally ... else if (typeName == null && ! (typeDef.getTypeCategory() == XSTypeDefinition.COMPLEX_TYPE && ((XSComplexTypeDecl) typeDef).getBaseType() == null)) { // *** Type is unnamed and based off of another type - create an explicit type *** this.type = ExplicitSchemaTypeDefinition.reloadedExplicitType(this, this.type, elementDecl); } else { // *** Type is named or unnamed with no base type (ur type) - create a referenced type *** this.type = ReferencedSchemaTypeDefinition.reloadedReferencedType(this, oldType, typeName, typeNamespace, typeDef); } if (oldType != this.type && oldType != null) { this.getProject().nodeRemoved(oldType); } } private void reloadNillable(XSElementDecl elemenDecl) { this.nillable = elemenDecl.getNillable(); if (this.nillable) { this.nilAttribute = this.buildNilAttribute(); } else { this.nilAttribute = null; } } private void reloadSubstitutionGroup(XSElementDecl elementDecl) { if (elementDecl.getSubstitutionGroupAffiliation() != null) { String substitutionGroupName = elementDecl.getSubstitutionGroupAffiliation().getName(); String substitutionGroupNamespace = elementDecl.getSubstitutionGroupAffiliation().getNamespace(); this.substitutionGroup = new ReferencedElementDeclaration(this, substitutionGroupName, substitutionGroupNamespace); } else { this.substitutionGroup = null; } } private void reloadIdentityConstraints(XSElementDecl elementDecl) { org.apache.xerces.impl.xs.identity.IdentityConstraint[] identityNodes = elementDecl.getIDConstraints(); for (int i = 0; identityNodes != null && i < identityNodes.length; i ++ ) { org.apache.xerces.impl.xs.identity.IdentityConstraint identityNode = identityNodes[i]; String identityName = identityNode.getName(); IdentityConstraintDefinition identityConstraint = (IdentityConstraintDefinition) this.identityConstraints.get(identityName); if (identityConstraint == null) { identityConstraint = new IdentityConstraintDefinition(this, identityName); this.identityConstraints.put(identityName, identityConstraint); } identityConstraint.reload(identityNode); } } public void resolveReferences() { super.resolveReferences(); this.type.resolveReferences(); if (this.nilAttribute != null) { this.nilAttribute.resolveReferences(); } if (this.substitutionGroup != null) { this.substitutionGroup.resolveReferences(); } } //*********************************** Toplink persistence use **************** private Map getIdentityConstraintsForToplink() { return new TreeMap(this.identityConstraints); } private void setIdentityConstraintsForToplink(Map map) { Iterator iter = map.values().iterator(); Map elementMap = new Hashtable(); while(iter.hasNext()) { IdentityConstraintDefinition icd = (IdentityConstraintDefinition)iter.next(); elementMap.put(icd.getName(), icd); } this.identityConstraints = elementMap; } // **************** TopLink methods *************************************** public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(ExplicitElementDeclaration.class); descriptor.getInheritancePolicy().setParentClass(AbstractNamedSchemaComponent.class); XMLCompositeObjectMapping typeMapping = new XMLCompositeObjectMapping(); typeMapping.setAttributeName("type"); typeMapping.setReferenceClass(AbstractSchemaComponent.class); typeMapping.setXPath("type"); descriptor.addMapping(typeMapping); XMLDirectMapping nillableMapping = (XMLDirectMapping) descriptor.addDirectMapping("nillable", "nillable/text()"); nillableMapping.setGetMethodName("getNillableForTopLink"); nillableMapping.setSetMethodName("setNillableForTopLink"); nillableMapping.setNullValue(Boolean.FALSE); descriptor.addDirectMapping("defaultValue", "default-value/text()"); descriptor.addDirectMapping("fixedValue", "fixed-value/text()"); XMLDirectMapping abstractMapping = (XMLDirectMapping) descriptor.addDirectMapping("abstractFlag", "abstract/text()"); abstractMapping.setNullValue(Boolean.FALSE); ((XMLDirectMapping) descriptor.addDirectMapping("minOccurs", "min-occurs/text()")).setNullValue(new Integer(1)); ((XMLDirectMapping) descriptor.addDirectMapping("maxOccurs", "max-occurs/text()")).setNullValue(new Integer(1)); XMLCompositeObjectMapping substitutionGroupMapping = new XMLCompositeObjectMapping(); substitutionGroupMapping.setAttributeName("substitutionGroup"); substitutionGroupMapping.setReferenceClass(ReferencedElementDeclaration.class); substitutionGroupMapping.setXPath("substitution-group"); descriptor.addMapping(substitutionGroupMapping); XMLCompositeCollectionMapping indentityConstraintsMapping = new XMLCompositeCollectionMapping(); indentityConstraintsMapping.setAttributeName("identityConstraints"); indentityConstraintsMapping.setGetMethodName("getIdentityConstraintsForToplink"); indentityConstraintsMapping.setSetMethodName("setIdentityConstraintsForToplink"); indentityConstraintsMapping.setXPath("identity-constraints/identity-constraint"); indentityConstraintsMapping.setReferenceClass(IdentityConstraintDefinition.class); indentityConstraintsMapping.useMapClass(TreeMap.class, "getName"); descriptor.addMapping(indentityConstraintsMapping); return descriptor; } private boolean getNillableForTopLink() { return this.nillable; } private void setNillableForTopLink(boolean newValue) { this.nillable = newValue; if (newValue) { this.nilAttribute = this.buildNilAttribute(); } } }