/******************************************************************************* * 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 java.util.ListIterator; import java.util.Vector; 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.MWMappingDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.xml.MWEisDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.xml.MWRootEisDescriptor; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWMappingHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle.NodeReferenceScrubber; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWAbstractReferenceMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.relational.MWAbstractTableReferenceMapping; import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute; import org.eclipse.persistence.tools.workbench.mappingsmodel.query.xml.MWEisInteraction; 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.SchemaChange; import org.eclipse.persistence.tools.workbench.utility.iterators.CloneListIterator; import org.eclipse.persistence.tools.workbench.utility.node.Node; import org.eclipse.persistence.tools.workbench.utility.node.Problem; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.ForeignReferenceMapping; 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; public abstract class MWEisReferenceMapping extends MWAbstractReferenceMapping implements MWXmlMapping, MWXmlReferenceMapping { // **************** Variables ********************************************* private List xmlFieldPairs; public final static String XML_FIELD_PAIRS_LIST = "xmlFieldPairs"; private volatile MWEisInteraction selectionInteraction; public final static String SELECTION_INTERACTION_PROPERTY = "selectionInteraction"; private volatile boolean maintainsBidirectionalRelationship; public final static String MAINTAINS_BIDIRECTIONAL_RELATIONSHIP_PROPERTY = "maintainsBidirectionalRelationship"; private MWMappingHandle relationshipPartnerMappingHandle; public final static String RELATIONSHIP_PARTNER_MAPPING_PROPERTY = "relationshipPartnerMapping"; // **************** Constructors ****************************************** /** Default constructor - for TopLink use only */ protected MWEisReferenceMapping() { super(); } protected MWEisReferenceMapping(MWMappingDescriptor descriptor, MWClassAttribute attribute, String attributeName) { super(descriptor, attribute, attributeName); } // **************** Initialization **************************************** /** initialize persistent state */ protected void initialize(Node parent) { super.initialize(parent); this.xmlFieldPairs = new Vector(); this.relationshipPartnerMappingHandle = new MWMappingHandle(this, this.buildRelationshipPartnerMappingScrubber()); if (this.requiresSelectionInteraction()) { this.selectionInteraction = new MWEisInteraction(this); } } /** * return whether the mapping requires a selection interaction * to always be present */ protected abstract boolean requiresSelectionInteraction(); // **************** Reference descriptor ********************************** public boolean descriptorIsValidReferenceDescriptor(MWDescriptor descriptor) { return ((MWEisDescriptor) descriptor).isRootDescriptor(); } // **************** Xml field pairs *************************************** public int xmlFieldPairsSize() { return this.xmlFieldPairs.size(); } public ListIterator xmlFieldPairs() { return new CloneListIterator(this.xmlFieldPairs); } public MWXmlFieldPair xmlFieldPairAt(int index) { return (MWXmlFieldPair) this.xmlFieldPairs.get(index); } public MWXmlFieldPair addFieldPair(String sourceXpath, String targetXpath) { MWXmlFieldPair xmlFieldPair = new MWXmlFieldPair(this, sourceXpath, targetXpath); this.addFieldPair(xmlFieldPair); return xmlFieldPair; } public void addFieldPair(MWXmlFieldPair xmlFieldPair) { this.addItemToList(this.xmlFieldPairsSize(), xmlFieldPair, this.xmlFieldPairs, XML_FIELD_PAIRS_LIST); } /** Builds an empty field pair, but does not add it */ public MWXmlFieldPair buildEmptyFieldPair() { return new MWXmlFieldPair(this); } public void removeXmlFieldPair(MWXmlFieldPair xmlFieldPair) { this.removeNodeFromList(this.xmlFieldPairs.indexOf(xmlFieldPair), this.xmlFieldPairs, XML_FIELD_PAIRS_LIST); } public void clearXmlFieldPairs() { this.clearList(this.xmlFieldPairs, XML_FIELD_PAIRS_LIST); } // **************** Maintain bidirectional relationship ******************* public boolean maintainsBidirectionalRelationship() { return this.maintainsBidirectionalRelationship; } public void setMaintainsBidirectionalRelationship(boolean maintainsBidirectionalRelationship) { boolean old = this.maintainsBidirectionalRelationship; this.maintainsBidirectionalRelationship = maintainsBidirectionalRelationship; firePropertyChanged(MAINTAINS_BIDIRECTIONAL_RELATIONSHIP_PROPERTY, old, maintainsBidirectionalRelationship); if (maintainsBidirectionalRelationship == false) { this.setRelationshipPartnerMapping(null); } } // **************** Relationship partner mapping ************************** public MWMapping getRelationshipPartnerMapping() { return this.relationshipPartnerMappingHandle.getMapping(); } public void setRelationshipPartnerMapping(MWMapping relationshipPartnerMapping) { Object old = this.relationshipPartnerMappingHandle.getMapping(); this.relationshipPartnerMappingHandle.setMapping(relationshipPartnerMapping); firePropertyChanged(RELATIONSHIP_PARTNER_MAPPING_PROPERTY, old, relationshipPartnerMapping); } public void setReferenceDescriptor(MWDescriptor newReferenceDescriptor) { if (getReferenceDescriptor() != newReferenceDescriptor) { setRelationshipPartnerMapping(null); } super.setReferenceDescriptor(newReferenceDescriptor); } public boolean isValidRelationshipPartner() { return true; } // **************** Selection interaction *************** public MWEisInteraction getSelectionInteraction() { return this.selectionInteraction; } protected void setSelectionInteraction(MWEisInteraction selectionInteraction) { if (this.requiresSelectionInteraction() && (selectionInteraction == null)) { throw new NullPointerException(); } Object old = this.selectionInteraction; this.selectionInteraction = selectionInteraction; this.firePropertyChanged(SELECTION_INTERACTION_PROPERTY, old, selectionInteraction); } // **************** MWXmlMapping contract ********************************* public MWSchemaContextComponent schemaContext() { return this.eisDescriptor().getSchemaContext(); } public MWXmlField firstMappedXmlField() { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { MWXmlField xmlField = ((MWXmlFieldPair) stream.next()).getSourceXmlField(); if (xmlField.isSpecified()) { return xmlField; } } return null; } public void addWrittenFieldsTo(Collection writtenXpaths) { if (this.isReadOnly()) { return; } for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { MWXmlField xmlField = ((MWXmlFieldPair) stream.next()).getSourceXmlField(); if (xmlField.isSpecified()) { writtenXpaths.add(xmlField); } } } // **************** MWXpathContext contract (for field pairs) ************* /** Return true if a source field may use a collection xpath */ public abstract boolean sourceFieldMayUseCollectionXpath(); // **************** Convenience ******************************************* protected MWEisDescriptor eisDescriptor() { return (MWEisDescriptor) getParent(); } protected MWEisDescriptor referenceEisDescriptor() { return (this.getReferenceDescriptor() == null) ? null : (MWEisDescriptor) this.getReferenceDescriptor(); } protected MWRootEisDescriptor referenceRootEisDescriptor() { return (this.referenceEisDescriptor() == null || ! this.referenceEisDescriptor().isRootDescriptor()) ? null : (MWRootEisDescriptor) this.referenceEisDescriptor(); } public MWEisDescriptor referenceDescriptor() { return (this.getReferenceDescriptor() == null) ? null : (MWEisDescriptor) this.getReferenceDescriptor(); } // *********** morphing ************ protected void initializeFromMWEisReferenceMapping(MWEisReferenceMapping oldMapping) { super.initializeFromMWEisReferenceMapping(oldMapping); for (Iterator stream = oldMapping.xmlFieldPairs(); stream.hasNext(); ) { MWXmlFieldPair fieldPair = (MWXmlFieldPair) stream.next(); this.addFieldPair(fieldPair.getSourceXmlField().getXpath(), fieldPair.getTargetXmlField().getXpath()); } this.setMaintainsBidirectionalRelationship(oldMapping.maintainsBidirectionalRelationship()); this.setRelationshipPartnerMapping(oldMapping.getRelationshipPartnerMapping()); MWEisInteraction interaction = oldMapping.getSelectionInteraction(); if (this.requiresSelectionInteraction() && (interaction == null)) { this.setSelectionInteraction(new MWEisInteraction(this)); } else { this.setSelectionInteraction(interaction); } } //********* model synchronization support ********* protected void addChildrenTo(List children) { super.addChildrenTo(children); synchronized (this.xmlFieldPairs) { children.addAll(this.xmlFieldPairs); } children.add(this.relationshipPartnerMappingHandle); if (this.requiresSelectionInteraction()) { children.add(this.selectionInteraction); } else { if (this.selectionInteraction != null) { children.add(this.selectionInteraction); } } } private NodeReferenceScrubber buildRelationshipPartnerMappingScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWEisReferenceMapping.this.setRelationshipPartnerMapping(null); } public String toString() { return "MWEisReferenceMapping.buildRelationshipPartnerMappingScrubber()"; } }; } public void mappingReplaced(MWMapping oldMapping, MWMapping newMapping) { super.mappingReplaced(oldMapping, newMapping); if (oldMapping == getRelationshipPartnerMapping()) { setRelationshipPartnerMapping(newMapping); } } /** @see MWXmlNode#resolveXpaths */ public void resolveXpaths() { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { ((MWXmlNode) stream.next()).resolveXpaths(); } } /** @see MWXmlNode#schemaChanged(SchemaChange) */ public void schemaChanged(SchemaChange change) { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { ((MWXmlNode) stream.next()).schemaChanged(change); } } // ************** Problem Handling ************** protected void addProblemsTo(List newProblems) { super.addProblemsTo(newProblems); this.checkDuplicateSourceXpath(newProblems); this.checkDuplicateTargetXpath(newProblems); this.checkRelationshipPartner(newProblems); } protected String referenceDescriptorInvalidProblemString() { return ProblemConstants.MAPPING_REFERENCE_DESCRIPTOR_NOT_ROOT; } private void checkDuplicateSourceXpath(List newProblems) { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { MWXmlFieldPair fieldPair = (MWXmlFieldPair) stream.next(); String sourceXpath = fieldPair.getSourceXmlField().getXpath(); if (fieldPair.duplicateSourceXpath(sourceXpath)) { Problem problem = this.buildProblem(ProblemConstants.MAPPING_SOURCE_XPATH_DUPLICATE, sourceXpath); if (! newProblems.contains(problem)) { newProblems.add(problem); } } } } private void checkDuplicateTargetXpath(List newProblems) { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { MWXmlFieldPair fieldPair = (MWXmlFieldPair) stream.next(); String targetXpath = fieldPair.getTargetXmlField().getXpath(); if (fieldPair.duplicateTargetXpath(targetXpath)) { Problem problem = this.buildProblem(ProblemConstants.MAPPING_TARGET_XPATH_DUPLICATE, targetXpath); if (! newProblems.contains(problem)) { newProblems.add(problem); } } } } private void checkRelationshipPartner(List newProblems) { if (this.maintainsBidirectionalRelationship()) { MWMapping partnerMapping = this.getRelationshipPartnerMapping(); if (partnerMapping == null) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_EIS_RELATIONSHIP_PARTNER_NOT_SPECIFIED)); } else { if ( ! partnerMapping.isValidRelationshipPartner()) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_EIS_RELATIONSHIP_PARTNER_INVALID)); } if ( ! (partnerMapping.maintainsBidirectionalRelationship() && partnerMapping.getRelationshipPartnerMapping() == this)) { newProblems.add(buildProblem(ProblemConstants.MAPPING_EIS_RELATIONSHIP_PARTNER_NOT_MUTUAL)); } } } } /** * Return true if the source xml field has a duplicate xpath among my source xml fields. * (Used in rules and in ui validation) */ public boolean duplicatedSourceXmlField(MWXmlField sourceXmlField) { if (sourceXmlField.isSpecified()) { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { MWXmlField nextSourceXmlField = ((MWXmlFieldPair) stream.next()).getSourceXmlField(); if (sourceXmlField != nextSourceXmlField && sourceXmlField.getXpath().equals(nextSourceXmlField.getXpath())) { return true; } } } return false; } /** * Return true if the target xml field has a duplicate xpath among my target xml fields. * (Used in rules and in ui validation) */ public boolean duplicatedTargetXmlField(MWXmlField targetXmlField) { if (targetXmlField.isSpecified()) { for (Iterator stream = this.xmlFieldPairs(); stream.hasNext(); ) { MWXmlField nextTargetXmlField = ((MWXmlFieldPair) stream.next()).getTargetXmlField(); if (targetXmlField != nextTargetXmlField && targetXmlField.getXpath().equals(nextTargetXmlField.getXpath())) { return true; } } } return false; } // **************** Runtime Conversion *************** public DatabaseMapping runtimeMapping() { ForeignReferenceMapping mapping = (ForeignReferenceMapping) super.runtimeMapping(); if (this.maintainsBidirectionalRelationship() && this.getRelationshipPartnerMapping() != null) { mapping.setRelationshipPartnerAttributeName(this.getRelationshipPartnerMapping().getName()); } MWEisInteraction interaction = this.getSelectionInteraction(); if (this.requiresSelectionInteraction()) { mapping.setSelectionCall(interaction.runtimeInteraction()); } else { if ((interaction != null) && interaction.isSpecified()) { mapping.setSelectionCall(interaction.runtimeInteraction()); } } return mapping; } //***************** TopLink methods *************************************** public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(MWEisReferenceMapping.class); descriptor.getInheritancePolicy().setParentClass(MWAbstractReferenceMapping.class); XMLCompositeCollectionMapping xpathFieldPairsListMapping = new XMLCompositeCollectionMapping(); xpathFieldPairsListMapping.setAttributeName("xmlFieldPairs"); xpathFieldPairsListMapping.setReferenceClass(MWXmlFieldPair.class); xpathFieldPairsListMapping.setXPath("xml-field-pairs/field-pair"); descriptor.addMapping(xpathFieldPairsListMapping); XMLDirectMapping mbdrMapping = (XMLDirectMapping) descriptor.addDirectMapping("maintainsBidirectionalRelationship", "maintains-bidirectional-relationship/text()"); mbdrMapping.setNullValue(Boolean.FALSE); XMLCompositeObjectMapping relationshipPartnerMapping = new XMLCompositeObjectMapping(); relationshipPartnerMapping.setAttributeName("relationshipPartnerMappingHandle"); relationshipPartnerMapping.setGetMethodName("getRelationshipPartnerMappingHandleForTopLink"); relationshipPartnerMapping.setSetMethodName("setRelationshipPartnerMappingHandleForTopLink"); relationshipPartnerMapping.setReferenceClass(MWMappingHandle.class); relationshipPartnerMapping.setXPath("relationship-partner-mapping-handle"); descriptor.addMapping(relationshipPartnerMapping); XMLCompositeObjectMapping selectionInteractionMapping = new XMLCompositeObjectMapping(); selectionInteractionMapping.setAttributeName("selectionInteraction"); selectionInteractionMapping.setReferenceClass(MWEisInteraction.class); selectionInteractionMapping.setXPath("selection-interaction"); descriptor.addMapping(selectionInteractionMapping); return descriptor; } /** * check for null */ private MWMappingHandle getRelationshipPartnerMappingHandleForTopLink() { return (this.relationshipPartnerMappingHandle.getMapping() == null) ? null : this.relationshipPartnerMappingHandle; } private void setRelationshipPartnerMappingHandleForTopLink(MWMappingHandle handle) { NodeReferenceScrubber scrubber = this.buildRelationshipPartnerMappingScrubber(); this.relationshipPartnerMappingHandle = ((handle == null) ? new MWMappingHandle(this, scrubber) : handle.setScrubber(scrubber)); } }