/*******************************************************************************
* 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.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
import java.util.TreeSet;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.ColumnStringHolder;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWColumn;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWTable;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWClassIndicatorPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWClassIndicatorValue;
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.relational.MWInterfaceDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalClassDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalClassIndicatorFieldPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWRelationalDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWTableDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWAbstractReferenceMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWIndirectableContainerMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWIndirectableMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWProxyIndirectionMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.relational.MWRelationalProject;
import org.eclipse.persistence.tools.workbench.utility.CollectionTools;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
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.node.Problem;
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.descriptors.ClassDescriptor;
import org.eclipse.persistence.internal.indirection.ProxyIndirectionPolicy;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.mappings.VariableOneToOneMapping;
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 final class MWVariableOneToOneMapping
extends MWAbstractReferenceMapping
implements MWProxyIndirectionMapping, MWClassIndicatorPolicy.Parent, MWRelationalClassIndicatorFieldPolicy.Parent
{
private Collection columnQueryKeyPairs;
public final static String COLUMN_QUERY_KEY_PAIRS_COLLECTION = "columnQueryKeyPairs";
//TODO this shouldn't be a full classIndicatorFieldPolicy
//we don't support class name is indicator
private volatile MWRelationalClassIndicatorFieldPolicy classIndicatorPolicy;
// ********** static methods **********
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWVariableOneToOneMapping.class);
descriptor.getInheritancePolicy().setParentClass(MWAbstractReferenceMapping.class);
XMLCompositeCollectionMapping columnQueryKeyPairsMapping = new XMLCompositeCollectionMapping();
columnQueryKeyPairsMapping.setAttributeName("columnQueryKeyPairs");
columnQueryKeyPairsMapping.setGetMethodName("getColumnQueryKeyPairsForTopLink");
columnQueryKeyPairsMapping.setSetMethodName("setColumnQueryKeyPairsForTopLink");
columnQueryKeyPairsMapping.setReferenceClass(MWColumnQueryKeyPair.class);
columnQueryKeyPairsMapping.setXPath("column-query-key-pairs/column-query-key-pair");
descriptor.addMapping(columnQueryKeyPairsMapping);
XMLCompositeObjectMapping classIndicatorPolicyMapping = new XMLCompositeObjectMapping();
classIndicatorPolicyMapping.setAttributeName("classIndicatorPolicy");
classIndicatorPolicyMapping.setReferenceClass(MWRelationalClassIndicatorFieldPolicy.class);
classIndicatorPolicyMapping.setXPath("class-indicator-policy");
descriptor.addMapping(classIndicatorPolicyMapping);
return descriptor;
}
private MWVariableOneToOneMapping() {
super();
}
MWVariableOneToOneMapping(MWRelationalClassDescriptor descriptor, MWClassAttribute attribute, String name) {
super(descriptor, attribute, name);
}
// **************** Initialization **************
protected void initialize(Node parent) {
super.initialize(parent);
this.columnQueryKeyPairs = new Vector();
this.classIndicatorPolicy = new MWRelationalClassIndicatorFieldPolicy(this);
}
protected void initialize(MWClassAttribute attribute, String name) {
super.initialize(attribute, name);
if (!getInstanceVariable().isValueHolder() && getInstanceVariable().getType().isInterface()) {
this.indirectionType = PROXY_INDIRECTION;
}
}
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
synchronized (this.columnQueryKeyPairs) { children.addAll(this.columnQueryKeyPairs); }
children.add(this.classIndicatorPolicy);
}
// ************** Accessors **************
public MWRelationalClassIndicatorFieldPolicy getClassIndicatorPolicy() {
return this.classIndicatorPolicy;
}
public void setReferenceDescriptor(MWDescriptor newValue) {
super.setReferenceDescriptor(newValue);
if (newValue != null)
getClassIndicatorPolicy().resetDescriptorAvailableForIndication(((MWRelationalProject) getProject()).descriptorsThatImplement((MWRelationalDescriptor) getReferenceDescriptor()));
else
getClassIndicatorPolicy().resetDescriptorAvailableForIndication(NullIterator.instance());
}
public boolean descriptorIsValidReferenceDescriptor(MWDescriptor descriptor) {
return ((MWRelationalDescriptor) descriptor).isInterfaceDescriptor();
}
public MWColumnQueryKeyPair addColumnQueryKeyPair(MWColumn column, String queryKeyName) {
return addColumnQueryKeyPair(new MWColumnQueryKeyPair(this, column, queryKeyName));
}
private MWColumnQueryKeyPair addColumnQueryKeyPair(MWColumnQueryKeyPair columnQueryKeyPair) {
this.addItemToCollection(columnQueryKeyPair, this.columnQueryKeyPairs, COLUMN_QUERY_KEY_PAIRS_COLLECTION);
this.getProject().recalculateAggregatePathsToColumn(this.getParentDescriptor());
return columnQueryKeyPair;
}
public void removeColumnQueryKeyPair(MWColumnQueryKeyPair columnQueryKeyPair) {
this.removeItemFromCollection(columnQueryKeyPair, this.columnQueryKeyPairs, COLUMN_QUERY_KEY_PAIRS_COLLECTION);
this.getProject().recalculateAggregatePathsToColumn(this.getParentDescriptor());
}
public Iterator columnQueryKeyPairs() {
return new CloneIterator(this.columnQueryKeyPairs);
}
public int columnQueryKeyPairsSize() {
return this.columnQueryKeyPairs.size();
}
/**
* Returns all possible query key names that can be used for this mapping
*/
public Iterator queryKeyNameChoices() {
if (getReferenceDescriptor() == null) {
return NullIterator.instance();
}
return ((MWRelationalDescriptor) getReferenceDescriptor()).allQueryKeyNames();
}
public Collection invalidQueryKeyNames() {
Collection namesOfInvalidQueryKeys = new ArrayList();
MWRelationalDescriptor referenceDescriptor = (MWRelationalDescriptor) getReferenceDescriptor();
if (columnQueryKeyPairsSize() > 0
&& referenceDescriptor != null)
{
for (Iterator associations = columnQueryKeyPairs(); associations.hasNext(); )
{
MWColumnQueryKeyPair pair = (MWColumnQueryKeyPair)associations.next();
if (referenceDescriptor.queryKeyNamed(pair.getQueryKeyName()) == null)
namesOfInvalidQueryKeys.add(pair.getQueryKeyName());
}
}
return namesOfInvalidQueryKeys;
}
// *********** MWProxyIndirectionMapping implementation ***********
public boolean usesProxyIndirection() {
return getIndirectionType() == PROXY_INDIRECTION;
}
public void setUseProxyIndirection() {
setIndirectionType(PROXY_INDIRECTION);
}
// *********** MWQueryable implementation ************
public String iconKey() {
return "mapping.variableOneToOne";
}
// **************** Behavior ***************
//TODO change this to 2 separate calls, implementorAdded and implementorRemoved
public void implementorsChangedFor(MWInterfaceDescriptor descriptor) {
if (getReferenceDescriptor() == descriptor) {
Collection implementors = CollectionTools.collection(((MWRelationalProject) getProject()).descriptorsThatImplement(descriptor));
removeOldIndicators(implementors);
addNewIndicators(implementors);
}
}
private void removeOldIndicators(Collection currentImplementors) {
Iterator currentIndicatorValues = CollectionTools.collection(this.classIndicatorPolicy.classIndicatorValues()).iterator();
while (currentIndicatorValues.hasNext()) {
MWClassIndicatorValue value = (MWClassIndicatorValue) currentIndicatorValues.next();
if (!currentImplementors.contains(value.getDescriptorValue())) {
getClassIndicatorPolicy().removeIndicator(value);
}
}
}
private void addNewIndicators(Collection currentImplementors) {
Iterator implementorsIterator = currentImplementors.iterator();
while (implementorsIterator.hasNext()) {
MWMappingDescriptor classDescriptor = (MWMappingDescriptor) implementorsIterator.next();
if (getClassIndicatorPolicy().getClassIndicatorValueForDescriptor(classDescriptor) == null) {
getClassIndicatorPolicy().addIndicator(null, classDescriptor);
}
}
}
// ************** Aggregate Support ************
public void parentDescriptorMorphedToAggregate() {
super.parentDescriptorMorphedToAggregate();
for (Iterator stream = columnQueryKeyPairs(); stream.hasNext(); ) {
((MWColumnQueryKeyPair) stream.next()).setColumn(null);
}
getClassIndicatorPolicy().setField(null);
}
protected Collection buildAggregateFieldNameGenerators() {
Collection generators = super.buildAggregateFieldNameGenerators();
generators.add(this.classIndicatorPolicy);
for (Iterator stream = columnQueryKeyPairs(); stream.hasNext(); ) {
generators.add(stream.next());
}
return generators;
}
// **************** Morphing ***************
public MWVariableOneToOneMapping asMWVariableOneToOneMapping() {
return this;
}
/**
* IMPORTANT: See MWMapping class comment.
*/
protected void initializeOn(MWMapping newMapping) {
newMapping.initializeFromMWVariableOneToOneMapping(this);
}
protected void initializeFromMWIndirectableContainerMapping(MWIndirectableContainerMapping oldMapping) {
super.initializeFromMWIndirectableContainerMapping(oldMapping);
if (oldMapping.usesValueHolderIndirection()) {
this.setUseValueHolderIndirection();
}
else if (oldMapping.usesNoIndirection()) {
this.setUseNoIndirection();
}
}
protected void initializeFromMWIndirectableMapping(MWIndirectableMapping oldMapping) {
super.initializeFromMWIndirectableMapping(oldMapping);
if (oldMapping.usesValueHolderIndirection()) {
this.setUseValueHolderIndirection();
}
}
public void addWrittenFieldsTo(Collection writtenFields) {
if (this.isReadOnly()) {
return;
}
for (Iterator stream = this.columnQueryKeyPairs(); stream.hasNext(); ) {
MWColumnQueryKeyPair association = (MWColumnQueryKeyPair) stream.next();
if (association.getColumn() != null) {
writtenFields.add(association.getColumn());
}
}
if (this.getClassIndicatorPolicy().getField() != null) {
writtenFields.add(this.getClassIndicatorPolicy().getField());
}
}
// **************** MWRelationalMapping implementation ***************
public boolean parentDescriptorIsAggregate() {
return ((MWRelationalDescriptor) getParentDescriptor()).isAggregateDescriptor();
}
public MWRelationalDescriptor getParentRelationalDescriptor() {
return (MWRelationalDescriptor) getParentDescriptor();
}
// **************** MWClassIndicatorPolicy.Parent implementation **********
public MWMappingDescriptor getContainingDescriptor() {
return this.getParentDescriptor();
}
//*************** Problem Handling ***************
protected void addProblemsTo(List newProblems) {
super.addProblemsTo(newProblems);
this.addNoQueryKeyAssociationsProblemTo(newProblems);
this.addForeignKeyFieldsInvalidProblemTo(newProblems);
this.addForeignKeysNotSpecifiedProblemTo(newProblems);
this.addInvalidQueryKeysProblemsTo(newProblems);
this.addClassIndicatorValueProblemsTo(newProblems);
}
protected String referenceDescriptorInvalidProblemString() {
return ProblemConstants.MAPPING_REFERENCE_DESCRIPTOR_NOT_INTERFACE_DESCRIPTOR;
}
private void addNoQueryKeyAssociationsProblemTo(List newProblems) {
if (this.columnQueryKeyPairsSize() == 0) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_QUERY_KEY_ASSOCIATIONS_NOT_SPECIFIED));
}
}
private void addForeignKeyFieldsInvalidProblemTo(List newProblems) {
ArrayList columns = new ArrayList();
for (Iterator stream = this.getParentRelationalDescriptor().associatedTables(); stream.hasNext(); ) {
CollectionTools.addAll(columns, ((MWTable) stream.next()).columns());
}
for (Iterator stream = this.columnQueryKeyPairs(); stream.hasNext();) {
MWColumnQueryKeyPair pair = (MWColumnQueryKeyPair) stream.next();
MWColumn column = pair.getColumn();
if ((column != null) && ! columns.contains(column)) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_QUERY_KEY_ASSOCIATIONS_FIELD_INVALID,
column.getName(), pair.getQueryKeyName()));
}
}
}
private void addForeignKeysNotSpecifiedProblemTo(List newProblems) {
boolean pass = true;
for (Iterator stream = this.columnQueryKeyPairs(); stream.hasNext();) {
MWColumnQueryKeyPair pair = (MWColumnQueryKeyPair) stream.next();
pass &= pair.getColumn() != null;
}
if( ! this.parentDescriptorIsAggregate() && ! pass) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_QUERY_KEY_ASSOCIATIONS_INCOMPLETE));
}
}
private void addInvalidQueryKeysProblemsTo(List newProblems) {
ArrayList args = new ArrayList();
for (Iterator stream = this.invalidQueryKeyNames().iterator(); stream.hasNext(); ) {
args.add(stream.next());
}
if (! args.isEmpty()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_QUERY_KEY_ASSOCIATIONS_INVALID,
args));
}
}
public void addClassIndicatorFieldNotSpecifiedProblemTo(List newProblems) {
//CR#4067 - An indicator field is not necessary as long as the foreign key field is unique
boolean uniqueForeignKeyFields = true;
if (this.columnQueryKeyPairsSize() == 0) {
uniqueForeignKeyFields = false;
}
for (Iterator stream = this.columnQueryKeyPairs(); stream.hasNext(); ) {
MWColumn column = ((MWColumnQueryKeyPair) stream.next()).getColumn();
if (column != null) {
uniqueForeignKeyFields &= (column.isPrimaryKey() || column.isUnique());
}
}
if (uniqueForeignKeyFields) {
return;
}
if ( ! this.parentDescriptorIsAggregate() && (this.getClassIndicatorPolicy().getField() == null)) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_CLASS_INDICATOR_FIELD_NOT_SPECIFIED));
}
}
private void addClassIndicatorValueProblemsTo(List newProblems) {
MWRelationalDescriptor refDescriptor = (MWRelationalDescriptor) this.getReferenceDescriptor();
if (refDescriptor == null || ! refDescriptor.isInterfaceDescriptor()) {
return;
}
for (Iterator stream = this.getClassIndicatorPolicy().classIndicatorValues(); stream.hasNext();) {
MWClassIndicatorValue value = (MWClassIndicatorValue) stream.next();
if ( ! CollectionTools.collection(refDescriptor.implementors()).contains(value.getDescriptorValue())
&& value.isInclude()) {
newProblems.add(this.buildClassIndicatorValueInvalidProblem(value.getDescriptorValue()));
}
}
}
private Problem buildClassIndicatorValueInvalidProblem(MWDescriptor descriptorValue) {
return this.buildProblem(ProblemConstants.MAPPING_CLASS_INDICATOR_VALUES_INVALID,
descriptorValue.getName(), this.getReferenceDescriptor().getName());
}
// ************* Automap Support ***********
public void automap() {
super.automap();
this.automapClassIndicatorField();
}
private void automapClassIndicatorField() {
if ((this.getClassIndicatorPolicy().getField() != null) || this.parentDescriptorIsAggregate()) {
return;
}
MWTable primaryTable = ((MWTableDescriptor) this.getParentDescriptor()).getPrimaryTable();
if (primaryTable == null) {
return;
}
ColumnStringHolder[] columnStringHolders = ColumnStringHolder.buildHolders(primaryTable.nonPrimaryKeyColumns());
StringHolderScore shs = COLUMN_NAME_MATCHER.match(this.getName().toLowerCase(), columnStringHolders);
if (shs.getScore() > 0.50) { // 0.50 ???
this.getClassIndicatorPolicy().setField(((ColumnStringHolder) shs.getStringHolder()).getColumn());
}
}
private static final PartialStringMatcher COLUMN_NAME_MATCHER = new SimplePartialStringMatcher(PartialStringComparator.DEFAULT_COMPARATOR);
// ************* runtime conversion ***********
protected DatabaseMapping buildRuntimeMapping() {
return new VariableOneToOneMapping();
}
public DatabaseMapping runtimeMapping() {
VariableOneToOneMapping runtimeMapping = (VariableOneToOneMapping) super.runtimeMapping();
if ((getReferenceDescriptor() != null) && (getReferenceDescriptor().getMWClass() != null)) {
runtimeMapping.setReferenceClassName(getReferenceDescriptor().getMWClass().getName());
}
for (Iterator associations = columnQueryKeyPairs(); associations.hasNext(); ) {
((MWColumnQueryKeyPair) associations.next()).adjustRuntimeMapping(runtimeMapping);
}
this.classIndicatorPolicy.adjustRuntimeMapping(runtimeMapping);
if (usesProxyIndirection()) {
runtimeMapping.setIndirectionPolicy(new ProxyIndirectionPolicy());
}
return runtimeMapping;
}
// *************** TopLink methods ***************
/**
* sort the collection for TopLink
*/
private Collection getColumnQueryKeyPairsForTopLink() {
return new TreeSet(this.columnQueryKeyPairs);
}
private void setColumnQueryKeyPairsForTopLink(Collection columnQueryKeyPairs) {
this.columnQueryKeyPairs = columnQueryKeyPairs;
}
public void postProjectBuild() {
super.postProjectBuild();
if (getReferenceDescriptor() != null) {
this.classIndicatorPolicy.setDescriptorsAvailableForIndicatorDictionaryForTopLink(((MWRelationalDescriptor) getReferenceDescriptor()).implementors());
}
else {
this.classIndicatorPolicy.setDescriptorsAvailableForIndicatorDictionary(NullIterator.instance());
}
}
private MWTable findBestMatchingTable(String tableName) {
for (Iterator i = getDatabase().tables(); i.hasNext(); ) {
MWTable table = (MWTable) i.next();
if (table.getShortName().equals(tableName)) {
return table;
}
}
return null;
}
}