/******************************************************************************* * 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; import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants; import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWCachingPolicy; 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.handles.MWDescriptorHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle; import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWHandle.NodeReferenceScrubber; import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass; import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute; import org.eclipse.persistence.tools.workbench.utility.node.Node; import org.eclipse.persistence.tools.workbench.utility.string.CollectionStringHolder; import org.eclipse.persistence.tools.workbench.utility.string.PartialStringComparator; import org.eclipse.persistence.tools.workbench.utility.string.PartialStringMatcher; import org.eclipse.persistence.tools.workbench.utility.string.SimplePartialStringMatcher; import org.eclipse.persistence.tools.workbench.utility.string.PartialStringMatcher.StringHolderScore; import org.eclipse.persistence.mappings.DatabaseMapping; import org.eclipse.persistence.mappings.ForeignReferenceMapping; import org.eclipse.persistence.mappings.converters.ObjectTypeConverter; import org.eclipse.persistence.oxm.XMLDescriptor; import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping; import org.eclipse.persistence.oxm.mappings.XMLDirectMapping; public abstract class MWAbstractReferenceMapping extends MWMapping implements MWReferenceMapping { private MWDescriptorHandle referenceDescriptorHandle; protected volatile String indirectionType; private volatile boolean privateOwned; // **************** Constructors *************** /** Default constructor - for TopLink use only */ protected MWAbstractReferenceMapping() { super(); } protected MWAbstractReferenceMapping(MWMappingDescriptor descriptor, MWClassAttribute attribute, String attributeName) { super(descriptor, attribute, attributeName); } // **************** Initialization *************** /** * initialize persistent state */ protected void initialize(Node parent) { super.initialize(parent); this.referenceDescriptorHandle = new MWDescriptorHandle(this, this.buildReferenceDescriptorScrubber()); this.privateOwned = false; } protected void initialize(MWClassAttribute attribute, String name) { super.initialize(attribute, name); if (this.getInstanceVariable().isEjb20Attribute()) { this.forceEjb20Indirection(); } else { if (this.getInstanceVariable().isValueHolder()) { this.indirectionType = VALUE_HOLDER_INDIRECTION; } else { this.indirectionType = NO_INDIRECTION; } } } protected void forceEjb20Indirection() { setUseValueHolderIndirection(); } // **************** Accessors *************** public MWDescriptor getReferenceDescriptor() { return this.referenceDescriptorHandle.getDescriptor(); } public void setReferenceDescriptor(MWDescriptor newValue) { Object oldValue = this.referenceDescriptorHandle.getDescriptor(); this.referenceDescriptorHandle.setDescriptor(newValue); this.firePropertyChanged(REFERENCE_DESCRIPTOR_PROPERTY, oldValue, newValue); //newValue will not be null if the method resetReferenceDescriptor called this method. //resetReferenceDescriptor is called when morphing mappings. The mapping's parent would //not be set at this point, so we must get the project from a different object. if (newValue != null) newValue.getProject().notifyExpressionsToRecalculateQueryables(); else getProject().notifyExpressionsToRecalculateQueryables(); } public boolean isReferenceMapping(){ return true; } public boolean usesNoIndirection() { return this.indirectionType == NO_INDIRECTION; } public boolean usesValueHolderIndirection() { return this.indirectionType == VALUE_HOLDER_INDIRECTION; } public void setUseValueHolderIndirection() { setIndirectionType(VALUE_HOLDER_INDIRECTION); } public void setUseNoIndirection() { setIndirectionType(NO_INDIRECTION); } protected void setIndirectionType(String indirectionType) { Object oldValue = this.indirectionType; this.indirectionType = indirectionType; firePropertyChanged(INDIRECTION_PROPERTY, oldValue, indirectionType); } protected String getIndirectionType() { return this.indirectionType; } // **************** private owned *************** public boolean isPrivateOwned() { return this.privateOwned; } public void setPrivateOwned(boolean newValue) { boolean oldValue = this.privateOwned; this.privateOwned = newValue; this.firePropertyChanged(PRIVATE_OWNED_PROPERTY, oldValue, newValue); } //********* mapping morphing support ********* protected void initializeFromMWReferenceObjectMapping(MWReferenceObjectMapping oldMapping) { super.initializeFromMWReferenceObjectMapping(oldMapping); this.setReferenceDescriptor(oldMapping.getReferenceDescriptor()); } protected void initializeFromMWReferenceMapping(MWReferenceMapping oldMapping) { super.initializeFromMWReferenceMapping(oldMapping); this.setPrivateOwned(oldMapping.isPrivateOwned()); } protected void initializeFromMWIndirectableMapping(MWIndirectableMapping oldMapping) { super.initializeFromMWIndirectableMapping(oldMapping); if (oldMapping.usesValueHolderIndirection()) { this.setUseValueHolderIndirection(); } else if (oldMapping.usesNoIndirection()) { this.setUseNoIndirection(); } } // ************** Automap Support ***************************************** public void automap() { super.automap(); this.automapReferenceDescriptor(); } /** * try to find a reasonable reference descriptor */ private void automapReferenceDescriptor() { if (this.getReferenceDescriptor() != null) { return; } MWDescriptor referenceDescriptor = this.findReferenceDescriptor(); if (referenceDescriptor != null) { this.setReferenceDescriptor(referenceDescriptor); } } private MWDescriptor findReferenceDescriptor() { MWClass type = this.getInstanceVariable().getType(); // calculate a name to use for finding a reference descriptor; // if the type is "non-descript", use the attribute name; // otherwise, use the "short" name of the attribute's declared type String name; if (type.isPrimitive() || type.isValueHolder() || type.isAssignableToMap() || type.isAssignableToCollection() || type.isAssignableTo(this.typeFor(Number.class)) || type == this.typeFor(String.class)) { name = this.getName(); } else { name = type.shortName(); } CollectionStringHolder[] holders = this.buildMultiDescriptorStringHolders(); StringHolderScore shs = this.match(name.toLowerCase(), holders); if (shs.getScore() < 0.80) { // ??? return null; } // look for a descriptor in the same package as this descriptor String packageName = this.getParentDescriptor().packageName(); MWDescriptor descriptor = null; for (Iterator stream = ((CollectionStringHolder) shs.getStringHolder()).iterator(); stream.hasNext(); ) { descriptor = (MWDescriptor) stream.next(); if (descriptor.packageName().equals(packageName)) { return descriptor; } } // if none of the descriptors is in the same package, take the last one return descriptor; } /** * gather together all the "candidate" descriptors that have * the same "short" name but different packages */ private CollectionStringHolder[] buildMultiDescriptorStringHolders() { Collection descriptors = this.candidateReferenceDescriptors(); Map holders = new HashMap(descriptors.size()); for (Iterator stream = descriptors.iterator(); stream.hasNext(); ) { MWDescriptor descriptor = (MWDescriptor) stream.next(); String shortName = descriptor.shortName().toLowerCase(); CollectionStringHolder holder = (CollectionStringHolder) holders.get(shortName); if (holder == null) { holder = new CollectionStringHolder(shortName); holders.put(shortName, holder); } holder.add(descriptor); } return (CollectionStringHolder[]) holders.values().toArray(new CollectionStringHolder[holders.size()]); } private Collection candidateReferenceDescriptors() { Collection descriptors = new ArrayList(); for (Iterator stream = this.getProject().descriptors(); stream.hasNext(); ) { MWDescriptor descriptor = (MWDescriptor) stream.next(); if (this.descriptorIsValidReferenceDescriptor(descriptor)) { descriptors.add(descriptor); } } return descriptors; } private StringHolderScore match(String string, CollectionStringHolder[] multiDescriptorStringHolders) { return PARTIAL_STRING_MATCHER.match(string, multiDescriptorStringHolders); } private static final PartialStringMatcher PARTIAL_STRING_MATCHER = new SimplePartialStringMatcher(PartialStringComparator.DEFAULT_COMPARATOR); //********* containment hierarchy ********* protected void addChildrenTo(List children) { super.addChildrenTo(children); children.add(this.referenceDescriptorHandle); } private NodeReferenceScrubber buildReferenceDescriptorScrubber() { return new NodeReferenceScrubber() { public void nodeReferenceRemoved(Node node, MWHandle handle) { MWAbstractReferenceMapping.this.setReferenceDescriptor(null); } public String toString() { return "MWAbstractReferenceMapping.buildReferenceDescriptorScrubber()"; } }; } public void descriptorReplaced(MWDescriptor oldDescriptor, MWDescriptor newDescriptor) { super.descriptorReplaced(oldDescriptor, newDescriptor); if (this.getReferenceDescriptor() == oldDescriptor) { this.setReferenceDescriptor(newDescriptor); } } // ************** Problem Handling ************* protected void addProblemsTo(List newProblems) { super.addProblemsTo(newProblems); this.checkReferenceDescriptor(newProblems); this.checkIndirection(newProblems); } protected void checkReferenceDescriptor(List newProblems) { MWDescriptor refDescriptor = this.getReferenceDescriptor(); if (refDescriptor == null) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_REFERENCE_DESCRIPTOR_NOT_SPECIFIED)); } else { if ( ! this.descriptorIsValidReferenceDescriptor(refDescriptor)) { newProblems.add(this.buildProblem(this.referenceDescriptorInvalidProblemString())); } if ( ! refDescriptor.isActive()) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_REFERENCE_DESCRIPTOR_IS_INACTIVE, this.getInstanceVariable().getName(), this.getReferenceDescriptor().getMWClass().shortName())); } this.checkReferenceDescriptorCachIsolation(newProblems); } } protected abstract String referenceDescriptorInvalidProblemString(); protected void checkReferenceDescriptorCachIsolation(List newProblems) { MWCachingPolicy policy = this.getParentDescriptor().getTransactionalPolicy().getCachingPolicy(); String cacheIsolation = policy.getCacheIsolation().getMWModelOption(); if (cacheIsolation == MWCachingPolicy.CACHE_ISOLATION_PROJECT_DEFAULT) { policy = policy.getProject().getDefaultsPolicy().getCachingPolicy(); cacheIsolation = policy.getCacheIsolation().getMWModelOption(); } // If descriptor is shared then this reference mapping // can't access an isolated descriptor if (cacheIsolation == MWCachingPolicy.CACHE_ISOLATION_SHARED) { policy = this.getReferenceDescriptor().getTransactionalPolicy().getCachingPolicy(); String referenceCacheIsolation = policy.getCacheIsolation().getMWModelOption(); if (referenceCacheIsolation == MWCachingPolicy.CACHE_ISOLATION_PROJECT_DEFAULT) { policy = policy.getProject().getDefaultsPolicy().getCachingPolicy(); referenceCacheIsolation = policy.getCacheIsolation().getMWModelOption(); } if (referenceCacheIsolation == MWCachingPolicy.CACHE_ISOLATION_ISOLATED) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_CANNOT_ACCESS_ISOLATED_DESCRIPTOR, this.getReferenceDescriptor().getName(), this.getName())); } } } //TODO refactor this along with other mappings that test 022 and 044. protected void checkIndirection(List newProblems) { if (this.usesValueHolderIndirection()) { if (this.getInstanceVariable().isTLValueHolder()) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_INDIRECTION_WITH_TL_VALUE_HOLDER_ATTRIBUTE)); } else if ( ! getProject().usesWeaving() && ! this.getInstanceVariable().isValueHolder()) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_INDIRECTION_WITHOUT_VALUE_HOLDER_ATTRIBUTE)); } } else { if (this.getInstanceVariable().isValueHolder()) { newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_ATTRIBUTE_WITHOUT_VALUE_HOLDER_INDIRECTION)); } } } //********* Runtime conversion ********* public DatabaseMapping runtimeMapping() { ForeignReferenceMapping runtimeMapping = (ForeignReferenceMapping) super.runtimeMapping(); if (getReferenceDescriptor() != null) { runtimeMapping.setReferenceClassName(getReferenceDescriptor().getMWClass().getName()); } if (usesValueHolderIndirection()) { runtimeMapping.useBasicIndirection(); } else { runtimeMapping.dontUseIndirection(); } runtimeMapping.setIsPrivateOwned(isPrivateOwned()); return runtimeMapping; } //********* Displaying ********* public void toString(StringBuffer sb) { if (getInstanceVariable() == null) { sb.append("<no instance variable>"); } else { sb.append(getInstanceVariable().getName()); } sb.append(" -> "); if (getReferenceDescriptor() == null) { sb.append("<no reference descriptor selected>"); } else { sb.append(getReferenceDescriptor().getMWClass().shortName()); } } //********* TopLink only methods ********* public static XMLDescriptor buildDescriptor() { XMLDescriptor descriptor = new XMLDescriptor(); descriptor.setJavaClass(MWAbstractReferenceMapping.class); descriptor.getInheritancePolicy().setParentClass(MWMapping.class); XMLCompositeObjectMapping referenceDescriptorMapping = new XMLCompositeObjectMapping(); referenceDescriptorMapping.setAttributeName("referenceDescriptorHandle"); referenceDescriptorMapping.setGetMethodName("getReferenceDescriptorHandleForTopLink"); referenceDescriptorMapping.setSetMethodName("setReferenceDescriptorHandleForTopLink"); referenceDescriptorMapping.setReferenceClass(MWDescriptorHandle.class); referenceDescriptorMapping.setXPath("reference-descriptor-handle"); descriptor.addMapping(referenceDescriptorMapping); ObjectTypeConverter indirectionTypeConverter = new ObjectTypeConverter(); indirectionTypeConverter.addConversionValue(NO_INDIRECTION, NO_INDIRECTION); indirectionTypeConverter.addConversionValue(VALUE_HOLDER_INDIRECTION, VALUE_HOLDER_INDIRECTION); //TODO this is not valid for refereceMapping, just for collectionMapping, should we not persist it here? indirectionTypeConverter.addConversionValue(MWIndirectableContainerMapping.TRANSPARENT_INDIRECTION, MWIndirectableContainerMapping.TRANSPARENT_INDIRECTION); indirectionTypeConverter.addConversionValue(MWProxyIndirectionMapping.PROXY_INDIRECTION, MWProxyIndirectionMapping.PROXY_INDIRECTION); XMLDirectMapping indirectionTypeMapping = new XMLDirectMapping(); indirectionTypeMapping.setAttributeName("indirectionType"); indirectionTypeMapping.setXPath("indirection-type/text()"); indirectionTypeMapping.setNullValue(NO_INDIRECTION); indirectionTypeMapping.setConverter(indirectionTypeConverter); descriptor.addMapping(indirectionTypeMapping); XMLDirectMapping privateOwnedMapping = (XMLDirectMapping) descriptor.addDirectMapping("privateOwned", "private-owned/text()"); privateOwnedMapping.setNullValue(Boolean.FALSE); return descriptor; } /** * 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)); } }