/******************************************************************************* * 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.mapping.xml; import java.util.Collection; import java.util.Iterator; import java.util.List; import javax.xml.namespace.QName; import org.eclipse.persistence.mappings.AggregateMapping; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.XMLField; 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.ProblemConstants; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.xml.MWXmlDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWDescriptorHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWNamedSchemaComponentHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle.NodeReferenceScrubber; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWReferenceObjectMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute; import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWMethod; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWComplexTypeDefinition; import org.eclipse.persistence.tools.workbench.mappingsmodel.schema.MWSchemaContextComponent; import org.eclipse.persistence.tools.workbench.mappingsmodel.xml.MWXmlField; import org.eclipse.persistence.tools.workbench.mappingsmodel.xml.MWXmlNode; import org.eclipse.persistence.tools.workbench.mappingsmodel.xml.MWXpathContext; import org.eclipse.persistence.tools.workbench.mappingsmodel.xml.MWXpathSpec; import org.eclipse.persistence.tools.workbench.mappingsmodel.xml.SchemaChange; import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator; import org.eclipse.persistence.tools.workbench.utility.node.Node; public abstract class MWAbstractCompositeMapping extends MWMapping implements MWReferenceObjectMapping, MWXmlMapping, MWXpathedMapping, MWXpathContext, MWXmlElementTypeableMapping { // **************** Variables ********************************************* private MWDescriptorHandle referenceDescriptorHandle; public final static String REFERENCE_DESCRIPTOR_PROPERTY = "referenceDescriptor"; private MWXmlField xmlField; private MWNamedSchemaComponentHandle elementTypeHandle; public final static String ELEMENT_TYPE_PROPERTY = "elementType"; private MWContainerAccessor containerAccessor; public final static String CONTAINER_ACCESSOR_PROPERTY = "containerAccessor"; private Boolean usesContainerAccessor; public final static String USES_CONTAINER_ACCESSOR_PROPERTY = "usesContainerAccessor"; // **************** Constructors ****************************************** /** Default constructor - for TopLink use only */ protected MWAbstractCompositeMapping() { super(); } protected MWAbstractCompositeMapping(MWXmlDescriptor descriptor, MWClassAttribute attribute, String name) { super(descriptor, attribute, name); } // **************** Initialization **************************************** protected void initialize(Node parent) { super.initialize(parent); this.referenceDescriptorHandle = new MWDescriptorHandle(this, this.buildReferenceDescriptorScrubber()); this.xmlField = new MWXmlField(this); this.elementTypeHandle = new MWNamedSchemaComponentHandle(this, this.buildElementTypeScrubber()); this.containerAccessor = new MWNullContainerAccessor(this); this.usesContainerAccessor = new Boolean(false); } private NodeReferenceScrubber buildElementTypeScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWAbstractCompositeMapping.this.setElementType(null); } public String toString() { return "MWAbstractCompositeMapping.buildElementTypeScrubber()"; } }; } // **************** Reference descriptor ********************************** public MWDescriptor getReferenceDescriptor() { return this.referenceDescriptorHandle.getDescriptor(); } public void setReferenceDescriptor(MWDescriptor newReferenceDescriptor) { MWDescriptor oldReferenceDescriptor = this.referenceDescriptorHandle.getDescriptor(); this.referenceDescriptorHandle.setDescriptor(newReferenceDescriptor); this.firePropertyChanged(REFERENCE_DESCRIPTOR_PROPERTY, oldReferenceDescriptor, newReferenceDescriptor); } public boolean descriptorIsValidReferenceDescriptor(MWDescriptor descriptor) { // can have pretty much any reference descriptor return true; } // **************** Element type ****************************************** public MWComplexTypeDefinition getElementType() { return (MWComplexTypeDefinition) this.elementTypeHandle.getComponent(); } public void setElementType(MWComplexTypeDefinition newElementType) { MWComplexTypeDefinition oldElementType = this.getElementType(); this.elementTypeHandle.setComponent(newElementType); this.firePropertyChanged(ELEMENT_TYPE_PROPERTY, oldElementType, newElementType); } // **************** container accessor ************************************ public Boolean usesContainerAccessor() { return this.usesContainerAccessor; } public void setUsesContainerAccessor(Boolean use) { boolean oldValue = this.usesContainerAccessor().booleanValue(); this.usesContainerAccessor = use; this.firePropertyChanged(USES_CONTAINER_ACCESSOR_PROPERTY, oldValue, use.booleanValue()); } public MWContainerAccessor getContainerAccessor() { return this.containerAccessor; } public void setContainerAccessor(MWContainerAccessor newAccessor) { MWContainerAccessor oldValue = this.getContainerAccessor(); this.containerAccessor = newAccessor; this.firePropertyChanged(CONTAINER_ACCESSOR_PROPERTY, oldValue, newAccessor); } public void setContainerAccessorGetMethod(MWMethod getMethod) { if(getContainerAccessor().isNull() || getContainerAccessor().isAttribute()) { this.setContainerAccessor(new MWMethodContainerAccessor(this, getMethod, null)); } else { ((MWMethodContainerAccessor)this.getContainerAccessor()).setAccessorGetMethod(getMethod); } } public void setContainerAccessorSetMethod(MWMethod setMethod) { if(getContainerAccessor().isNull() || getContainerAccessor().isAttribute()) { this.setContainerAccessor(new MWMethodContainerAccessor(this, null, setMethod)); } else { ((MWMethodContainerAccessor)this.getContainerAccessor()).setAccessorSetMethod(setMethod); } } public void setContainerAccessorAttribute(MWClassAttribute attribute) { this.setContainerAccessor(new MWAttributeContainerAccessor(this, attribute)); } public Iterator candidateAccessorMethods() { if (this.getReferenceDescriptor() != null) { return this.getReferenceDescriptor().getMWClass().allInstanceMethods(); } return NullIterator.instance(); } public Iterator candidateAccessorAttributes() { if (this.getReferenceDescriptor() != null) { return this.getReferenceDescriptor().getMWClass().allInstanceVariables(); } return NullIterator.instance(); } // **************** MWXpathedMapping implementation ********************** public MWXmlField getXmlField() { return this.xmlField; } // **************** MWXmlMapping contract ********************************* public MWSchemaContextComponent schemaContext() { return this.xmlDescriptor().getSchemaContext(); } public MWXmlField firstMappedXmlField() { return this.getXmlField().isResolved() ? this.getXmlField() : null; } public void addWrittenFieldsTo(Collection writtenFields) { if (! this.isReadOnly() && ! this.getXmlField().getXpath().equals("")) { writtenFields.add(this.getXmlField()); } } // **************** MWXpathContext implementation ************************ public MWSchemaContextComponent schemaContext(MWXmlField xmlField) { return this.xmlDescriptor().getSchemaContext(); } public MWXpathSpec xpathSpec(MWXmlField xmlField) { return this.buildXpathSpec(); } protected MWXpathSpec buildXpathSpec() { return new MWXpathSpec() { public boolean mayUseCollectionData() { return MWAbstractCompositeMapping.this.mayUseCollectionData(); } public boolean mayUseComplexData() { return true; } public boolean mayUseSimpleData() { return false; } }; } protected abstract boolean mayUseCollectionData(); // **************** Convenience ******************************************* protected MWXmlDescriptor xmlDescriptor() { return (MWXmlDescriptor) this.getParent(); } // **************** Morphing ********************************************** protected void initializeFromMWXpathedMapping(MWXpathedMapping oldMapping) { super.initializeFromMWXpathedMapping(oldMapping); this.getXmlField().setXpath(oldMapping.getXmlField().getXpath()); // can't use typed, so don't set that } protected void initializeFromMWReferenceObjectMapping(MWReferenceObjectMapping oldMapping) { super.initializeFromMWReferenceObjectMapping(oldMapping); this.setReferenceDescriptor(oldMapping.getReferenceDescriptor()); } // **************** Problem handling ************************************** protected void addProblemsTo(List newProblems) { // would like to add xpath and reference descriptor problems first this.addXmlFieldProblemsTo(newProblems); this.addReferenceDescriptorNotSpecifiedProblemTo(newProblems); // TODO: reference descriptor validation ?? this.addReferenceDescriptorInactiveProblemTo(newProblems); this.addContainerAccessorNotSpecifiedProblemTo(newProblems); super.addProblemsTo(newProblems); } protected void addContainerAccessorNotSpecifiedProblemTo(List newProblems) { if (this.usesContainerAccessor() && this.containerAccessor.isNull()) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_CONTAINER_ACCESSOR_NOT_CONFIGURED)); } } protected void addXmlFieldProblemsTo(List newProblems) { this.addXpathNotSpecifiedProblemTo(newProblems); } private void addXpathNotSpecifiedProblemTo(List newProblems) { if (! this.getXmlField().isSpecified()) { newProblems.add(this.buildProblem(ProblemConstants.XPATH_NOT_SPECIFIED)); } } private void addReferenceDescriptorNotSpecifiedProblemTo(List newProblems) { if (this.xmlDescriptor().isEisDescriptor() && this.getReferenceDescriptor() == null) { newProblems.add(buildProblem(ProblemConstants.MAPPING_REFERENCE_DESCRIPTOR_NOT_SPECIFIED)); } } private void addReferenceDescriptorInactiveProblemTo(List newProblems) { if (this.getReferenceDescriptor() != null && ! this.getReferenceDescriptor().isActive()) { newProblems.add( this.buildProblem( ProblemConstants.MAPPING_REFERENCE_DESCRIPTOR_IS_INACTIVE, this.getInstanceVariable().getName(), this.getReferenceDescriptor().getMWClass().shortName() ) ); } } // **************** containment hierarchy ********************************* protected void addChildrenTo(List children) { super.addChildrenTo(children); children.add(this.referenceDescriptorHandle); children.add(this.xmlField); children.add(this.elementTypeHandle); children.add(this.containerAccessor); } private NodeReferenceScrubber buildReferenceDescriptorScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWAbstractCompositeMapping.this.setReferenceDescriptor(null); } public String toString() { return "MWAbstractCompositeMapping.buildReferenceDescriptorScrubber()"; } }; } public void descriptorReplaced(MWDescriptor oldDescriptor, MWDescriptor newDescriptor) { super.descriptorReplaced(oldDescriptor, newDescriptor); // we don't need this until we support multiple descriptor types in XML/EIS projects // if (this.getReferenceDescriptor() == oldDescriptor) { // this.setReferenceDescriptor(newDescriptor); // } } /** @see MWXmlNode#resolveXpaths */ public void resolveXpaths() { this.xmlField.resolveXpaths(); } /** @see MWXmlNode#schemaChanged(SchemaChange) */ public void schemaChanged(SchemaChange change) { this.xmlField.schemaChanged(change); } // **************** Runtime conversion ************************************ public DatabaseMapping runtimeMapping() { AggregateMapping runtimeMapping = (AggregateMapping) super.runtimeMapping(); if (this.getReferenceDescriptor() != null) { runtimeMapping.setReferenceClassName(this.getReferenceDescriptor().getMWClass().getName()); } if (this.getElementType() != null && runtimeMapping.getField() != null) { ((XMLField)runtimeMapping.getField()).setLeafElementType(new QName(getElementType().qName())); } return runtimeMapping; } // **************** TopLink methods *************************************** public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(MWAbstractCompositeMapping.class); descriptor.descriptorIsAggregate(); descriptor.getInheritancePolicy().setParentClass(MWMapping.class); descriptor.addDirectMapping("usesContainerAccessor", "getUsesContainerAccessorForTopLink", "setUsesContainerAccessorForTopLink", "uses-container-accessor"); XMLCompositeObjectMapping referenceDescriptorMapping = new XMLCompositeObjectMapping(); referenceDescriptorMapping.setAttributeName("referenceDescriptorHandle"); referenceDescriptorMapping.setGetMethodName("getReferenceDescriptorHandleForTopLink"); referenceDescriptorMapping.setSetMethodName("setReferenceDescriptorHandleForTopLink"); referenceDescriptorMapping.setXPath("reference-descriptor-handle"); referenceDescriptorMapping.setReferenceClass(MWDescriptorHandle.class); descriptor.addMapping(referenceDescriptorMapping); XMLCompositeObjectMapping xmlFieldMapping = new XMLCompositeObjectMapping(); xmlFieldMapping.setReferenceClass(MWXmlField.class); xmlFieldMapping.setAttributeName("xmlField"); xmlFieldMapping.setGetMethodName("getXmlFieldForTopLink"); xmlFieldMapping.setSetMethodName("setXmlFieldForTopLink"); xmlFieldMapping.setXPath("xpath"); descriptor.addMapping(xmlFieldMapping); XMLCompositeObjectMapping elementTypeHandleMapping = new XMLCompositeObjectMapping(); elementTypeHandleMapping.setAttributeName("elementTypeHandle"); elementTypeHandleMapping.setGetMethodName("getElementTypeHandleForTopLink"); elementTypeHandleMapping.setSetMethodName("setElementTypeHandleForTopLink"); elementTypeHandleMapping.setReferenceClass(MWNamedSchemaComponentHandle.class); elementTypeHandleMapping.setXPath("element-type-handle"); descriptor.addMapping(elementTypeHandleMapping); XMLCompositeObjectMapping containerAccessorMapping = new XMLCompositeObjectMapping(); containerAccessorMapping.setAttributeName("containerAccessor"); containerAccessorMapping.setGetMethodName("getContainerAccessorForTopLink"); containerAccessorMapping.setSetMethodName("setContainerAccessorForTopLink"); containerAccessorMapping.setReferenceClass(MWContainerAccessor.class); containerAccessorMapping.setXPath("container-accessor"); descriptor.addMapping(containerAccessorMapping); return descriptor; } public static XMLDescriptor legacy60BuildDescriptor() { XMLDescriptor descriptor = MWModel.legacy60BuildStandardDescriptor(); descriptor.setJavaClass(MWAbstractCompositeMapping.class); descriptor.descriptorIsAggregate(); descriptor.getInheritancePolicy().setParentClass(MWMapping.class); XMLCompositeObjectMapping referenceDescriptorMapping = new XMLCompositeObjectMapping(); referenceDescriptorMapping.setAttributeName("referenceDescriptorHandle"); referenceDescriptorMapping.setGetMethodName("getReferenceDescriptorHandleForTopLink"); referenceDescriptorMapping.setSetMethodName("setReferenceDescriptorHandleForTopLink"); referenceDescriptorMapping.setXPath("reference-descriptor-handle"); referenceDescriptorMapping.setReferenceClass(MWDescriptorHandle.class); descriptor.addMapping(referenceDescriptorMapping); XMLCompositeObjectMapping xmlFieldMapping = new XMLCompositeObjectMapping(); xmlFieldMapping.setReferenceClass(MWXmlField.class); xmlFieldMapping.setAttributeName("xmlField"); xmlFieldMapping.setGetMethodName("getXmlFieldForTopLink"); xmlFieldMapping.setSetMethodName("setXmlFieldForTopLink"); xmlFieldMapping.setXPath("xpath"); descriptor.addMapping(xmlFieldMapping); XMLCompositeObjectMapping elementTypeHandleMapping = new XMLCompositeObjectMapping(); elementTypeHandleMapping.setAttributeName("elementTypeHandle"); elementTypeHandleMapping.setGetMethodName("getElementTypeHandleForTopLink"); elementTypeHandleMapping.setSetMethodName("setElementTypeHandleForTopLink"); elementTypeHandleMapping.setReferenceClass(MWNamedSchemaComponentHandle.class); elementTypeHandleMapping.setXPath("element-type-handle"); descriptor.addMapping(elementTypeHandleMapping); return descriptor; } private Boolean getUsesContainerAccessorForTopLink() { return this.usesContainerAccessor; } private void setUsesContainerAccessorForTopLink(Boolean value) { if (value == null) { this.usesContainerAccessor = Boolean.FALSE; } else { this.usesContainerAccessor = value; } } private MWContainerAccessor getContainerAccessorForTopLink() { return this.containerAccessor.valueForTopLink(); } private void setContainerAccessorForTopLink(MWContainerAccessor newAccessor) { this.containerAccessor = MWContainerAccessor.buildAccessorForTopLink(newAccessor); } /** * check for null */ private MWDescriptorHandle getReferenceDescriptorHandleForTopLink() { return (this.referenceDescriptorHandle.getDescriptor() == null) ? null : this.referenceDescriptorHandle; } private void setReferenceDescriptorHandleForTopLink(MWDescriptorHandle referenceDescriptorHandle) { NodeReferenceScrubber scrubber = this.buildReferenceDescriptorScrubber(); this.referenceDescriptorHandle = ((referenceDescriptorHandle == null) ? new MWDescriptorHandle(this, scrubber) : referenceDescriptorHandle.setScrubber(scrubber)); } private MWXmlField getXmlFieldForTopLink() { return (this.xmlField.isSpecified()) ? this.xmlField: null; } private void setXmlFieldForTopLink(MWXmlField xmlField) { this.xmlField = ((xmlField == null) ? new MWXmlField(this) : xmlField); } /** * check for null */ private MWNamedSchemaComponentHandle getElementTypeHandleForTopLink() { return (this.elementTypeHandle.getComponent() == null) ? null : this.elementTypeHandle; } private void setElementTypeHandleForTopLink(MWNamedSchemaComponentHandle handle) { NodeReferenceScrubber scrubber = this.buildElementTypeScrubber(); this.elementTypeHandle = ((handle == null) ? new MWNamedSchemaComponentHandle(this, scrubber) : handle.setScrubber(scrubber)); } }