/*******************************************************************************
* 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.relational;
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.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWQueryKey;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWColumnPair;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalClassDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWCollectionContainerPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWContainerPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWIndirectableContainerMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWListContainerPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapContainerMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapContainerPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWSetContainerPolicy;
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.filters.Filter;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneListIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.FilteringIterator;
import org.eclipse.persistence.tools.workbench.utility.iterators.NullIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.tools.workbench.utility.string.StringTools;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.indirection.TransparentIndirectionPolicy;
import org.eclipse.persistence.mappings.CollectionMapping;
import org.eclipse.persistence.mappings.DatabaseMapping;
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.XMLTransformationMapping;
import org.eclipse.persistence.sessions.Record;
public abstract class MWCollectionMapping
extends MWAbstractTableReferenceMapping
implements MWIndirectableContainerMapping, MWMapContainerMapping
{
private volatile MWContainerPolicy containerPolicy;
private List orderings;
public static final String ORDERINGS_LIST = "orderings";
// **************** static variables **************************************
public final static String EJB_INDIRECT_LIST_CLASS_NAME = "org.eclipse.persistence.indirection.EJBIndirectList";
public final static String EJB_INDIRECT_SET_CLASS_NAME = "org.eclipse.persistence.indirection.EJBIndirectSet";
public final static String INDIRECT_LIST_CLASS_NAME = "org.eclipse.persistence.indirection.IndirectList";
public final static String INDIRECT_MAP_CLASS_NAME = "org.eclipse.persistence.indirection.IndirectMap";
public final static String INDIRECT_SET_CLASS_NAME = "org.eclipse.persistence.indirection.IndirectSet";
// **************** Constructors ****************************************
/** Default constructor - for TopLink use only */
protected MWCollectionMapping() {
super();
}
protected MWCollectionMapping(MWRelationalClassDescriptor descriptor, MWClassAttribute attribute, String name) {
super(descriptor, attribute, name);
}
// **************** Building and Initializing *************
protected void initialize(Node parent) {
super.initialize(parent);
this.orderings = new Vector();
}
protected void initialize(MWClassAttribute attribute, String name) {
super.initialize(attribute, name);
//TODO wow this is not pretty! and it's copied in other collection mappings
if (attribute.getDimensionality() > 0) {
this.containerPolicy = new MWListContainerPolicy(this);
this.setUseTransparentIndirection();
} else {
MWClass type = null;
if (attribute.isValueHolder()) {
this.setUseValueHolderIndirection();
type = attribute.getValueType();
} else {
this.setUseTransparentIndirection();
type = attribute.getType();
}
if (type.isAssignableToMap()) {
this.containerPolicy = new MWMapContainerPolicy(this);
}
else if (type.isAssignableToList()) {
this.containerPolicy = new MWListContainerPolicy(this);
}
else if (type.isAssignableToSet()) {
this.containerPolicy = new MWSetContainerPolicy(this);
}
else if (type.isAssignableToCollection()){
this.containerPolicy = new MWCollectionContainerPolicy(this);
}
else { // this is the default in the runtime
this.containerPolicy = new MWListContainerPolicy(this);
}
}
}
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
children.add(this.containerPolicy);
synchronized (this.orderings) { children.addAll(this.orderings); }
}
// **************** Container policy **************************************
public MWContainerPolicy getContainerPolicy() {
return this.containerPolicy;
}
private void setContainerPolicy(MWContainerPolicy containerPolicy) {
Object oldValue = this.containerPolicy;
this.containerPolicy = containerPolicy;
firePropertyChanged(CONTAINER_POLICY_PROPERTY, oldValue, containerPolicy);
}
public MWMapContainerPolicy setMapContainerPolicy() {
if (this.containerPolicy instanceof MWMapContainerPolicy) {
return (MWMapContainerPolicy) this.containerPolicy;
}
MWMapContainerPolicy cp = new MWMapContainerPolicy(this);
this.setContainerPolicy(cp);
return cp;
}
public MWCollectionContainerPolicy setCollectionContainerPolicy() {
if (this.containerPolicy instanceof MWCollectionContainerPolicy) {
return (MWCollectionContainerPolicy) this.containerPolicy;
}
MWCollectionContainerPolicy cp = new MWCollectionContainerPolicy(this);
this.setContainerPolicy(cp);
return cp;
}
public MWListContainerPolicy setListContainerPolicy() {
if (this.containerPolicy instanceof MWListContainerPolicy) {
return (MWListContainerPolicy) this.containerPolicy;
}
MWListContainerPolicy cp = new MWListContainerPolicy(this);
this.setContainerPolicy(cp);
return cp;
}
public MWSetContainerPolicy setSetContainerPolicy() {
if (this.containerPolicy instanceof MWSetContainerPolicy) {
return (MWSetContainerPolicy) this.containerPolicy;
}
MWSetContainerPolicy cp = new MWSetContainerPolicy(this);
this.setContainerPolicy(cp);
return cp;
}
public Iterator candidateKeyMethods(Filter keyMethodFilter) {
if (this.getReferenceDescriptor() != null) {
return new FilteringIterator(this.getReferenceDescriptor().getMWClass().allInstanceMethods(), keyMethodFilter);
}
return NullIterator.instance();
}
// **************** Morphing **********************
protected void initializeFromMWCollectionMapping(MWCollectionMapping oldMapping) {
super.initializeFromMWCollectionMapping(oldMapping);
for (ListIterator i = orderings(); i.hasNext(); ) {
MWCollectionOrdering ordering = (MWCollectionOrdering) i.next();
MWCollectionOrdering newOrdering = oldMapping.addOrdering(ordering.getQueryKey());
newOrdering.setAscending(ordering.isAscending());
}
this.getContainerPolicy().getDefaultingContainerClass().setContainerClass(oldMapping.getContainerPolicy().getDefaultingContainerClass().getContainerClass());
}
protected void initializeFromMWIndirectableContainerMapping(MWIndirectableContainerMapping oldMapping) {
super.initializeFromMWIndirectableContainerMapping(oldMapping);
if (oldMapping.usesTransparentIndirection()) {
this.setUseTransparentIndirection();
}
}
// **************** Accessors ***************
public ListIterator orderings() {
return new CloneListIterator(this.orderings);
}
public int orderingsSize() {
return this.orderings.size();
}
public int indexOfOrdering(MWCollectionOrdering ordering) {
return this.orderings.indexOf(ordering);
}
public void moveOrderingUp(MWCollectionOrdering ordering) {
int index = indexOfOrdering(ordering);
removeOrdering(index);
addOrdering(index - 1, ordering);
}
public void moveOrderingDown(MWCollectionOrdering ordering) {
int index = indexOfOrdering(ordering);
removeOrdering(ordering);
addOrdering(index + 1, ordering);
}
public MWCollectionOrdering addOrdering(MWQueryKey queryKey) {
MWCollectionOrdering ordering = new MWCollectionOrdering(this);
ordering.setQueryKey(queryKey);
addOrdering(orderingsSize(), ordering);
return ordering;
}
private void addOrdering(int index, MWCollectionOrdering ordering) {
addItemToList(index, ordering, this.orderings, ORDERINGS_LIST);
}
public void removeOrdering(MWCollectionOrdering ordering) {
removeOrdering(this.orderings.indexOf(ordering));
}
public void removeOrdering(int index) {
removeItemFromList(index, this.orderings, ORDERINGS_LIST);
}
public void setUseTransparentIndirection() {
setIndirectionType(TRANSPARENT_INDIRECTION);
}
public void setReferenceDescriptor(MWDescriptor descriptor) {
super.setReferenceDescriptor(descriptor);
getContainerPolicy().referenceDescriptorChanged(descriptor);
}
// ************** Queryable interface *************
public boolean usesAnyOf() {
return true;
}
public boolean isTraversableForReadAllQueryOrderable() {
return false;
}
// **************** Queries ***************
public boolean usesTransparentIndirection() {
return getIndirectionType() == TRANSPARENT_INDIRECTION;
}
public boolean isCollectionMapping(){
return true;
}
// **************** Behavior **********************************************
protected void forceEjb20Indirection() {
super.forceEjb20Indirection();
setUseTransparentIndirection();
}
// **************** Aggregate Support *****************
protected Collection buildAggregateFieldNameGenerators() {
Collection generators = super.buildAggregateFieldNameGenerators();
if (getReference() != null) {
Iterator i = getReference().columnPairs();
while (i.hasNext()) {
MWColumnPair association = (MWColumnPair) i.next();
generators.add(new ColumnPairAggregateRuntimeFieldNameGenerator(this, association, false));
}
}
return generators;
}
protected boolean fieldIsWritten(MWColumnPair association) {
return false;
}
//********* runtime conversion *********
public DatabaseMapping runtimeMapping() {
CollectionMapping runtimeMapping = (CollectionMapping) super.runtimeMapping();
// *** Indirection ***
if (usesTransparentIndirection())
runtimeMapping.setIndirectionPolicy(new TransparentIndirectionPolicy());
else if (usesValueHolderIndirection())
runtimeMapping.useBasicIndirection();
else
runtimeMapping.dontUseIndirection();
runtimeMapping.setContainerPolicy(this.containerPolicy.runtimeContainerPolicy());
if (getContainerPolicy().usesSorting() && getContainerPolicy().getComparatorClass() != null) {
runtimeMapping.useSortedSetClassName(getContainerPolicy().getDefaultingContainerClass().getContainerClass().getName(), getContainerPolicy().getComparatorClass().getName());
}
for (ListIterator i = orderings(); i.hasNext(); ) {
((MWCollectionOrdering) i.next()).adjustRuntimeMapping(runtimeMapping);
}
return runtimeMapping;
}
// ************** Problem Handling ****************
protected void addProblemsTo(List newProblems) {
super.addProblemsTo(newProblems);
this.checkContainerClassIsValidForTransparentIndirection(newProblems);
this.addUsesTransparentIndirectionWhileMaintainsBiDirectionalRelationship(newProblems);
}
public void addWrittenFieldsTo(Collection writtenFields) {
//m-m and 1-m mappings do not directly write their fields
}
// overridden from MWTableReferenceMapping
protected void mappingAndVariableDontUseIndirectionTest(List newProblems) {
if (! this.usesValueHolderIndirection() && this.getInstanceVariable().isValueHolder()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_ATTRIBUTE_WITHOUT_VALUE_HOLDER_INDIRECTION));
}
}
// overridden from MWTableReferenceMapping
protected void mappingAndVariableUseIndirectionTest(List newProblems) {
if ( ! this.getProject().usesWeaving() && this.usesValueHolderIndirection() && ! getInstanceVariable().isValueHolder()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_INDIRECTION_WITHOUT_VALUE_HOLDER_ATTRIBUTE));
}
}
private void checkContainerClassIsValidForTransparentIndirection(List newProblems) {
if ( ! this.usesTransparentIndirection()) {
return;
}
MWClass collectionType = this.getContainerPolicy().getDefaultingContainerClass().getContainerClass();
if (collectionType != null && ! collectionType.mightBeAssignableToIndirectContainer()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_CONTAINER_CLASS_INVALID_FOR_TRANSPARENT_INDIRECTION));
}
}
private void addUsesTransparentIndirectionWhileMaintainsBiDirectionalRelationship(List newProblems) {
if (this.maintainsBidirectionalRelationship() && (this.usesNoIndirection() || this.usesValueHolderIndirection())) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_COLLECTION_MAINTTAINSBIDIRECTIONAL_NO_TRANSPARENT_INDIRECTION));
}
}
// **************** TopLink methods ****************
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWCollectionMapping.class);
descriptor.getInheritancePolicy().setParentClass(MWAbstractTableReferenceMapping.class);
XMLCompositeObjectMapping containerPolicyMapping = new XMLCompositeObjectMapping();
containerPolicyMapping.setAttributeName("containerPolicy");
containerPolicyMapping.setReferenceClass(MWContainerPolicy.MWContainerPolicyRoot.class);
containerPolicyMapping.setXPath("container-policy");
descriptor.addMapping(containerPolicyMapping);
XMLCompositeCollectionMapping orderingsMapping = new XMLCompositeCollectionMapping();
orderingsMapping.setAttributeName("orderings");
orderingsMapping.setReferenceClass(MWCollectionOrdering.class);
orderingsMapping.setXPath("orderings/ordering");
descriptor.addMapping(orderingsMapping);
return descriptor;
}
}