/*******************************************************************************
* 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.io.PrintWriter;
import java.io.StringWriter;
import java.text.Collator;
import java.util.Iterator;
import java.util.List;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
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.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWMappingDescriptor;
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.handles.MWColumnHandle;
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.MWMapping;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneIterator;
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.descriptors.DescriptorEvent;
import org.eclipse.persistence.mappings.AggregateObjectMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
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;
/**
* This class represents a mapping path from an aggregate mapping to
* one of its submappings. Because an aggregate's submappings can be nested
* several layers deep, this class maintains a reference to a List of mappings
* which is essentially a tree path to a database column.
*/
public final class MWAggregatePathToColumn extends MWModel {
/**
* List of MWMappings that lead to the column.
*/
private List mappingHandles;
private volatile AggregateRuntimeFieldNameGenerator aggregateRuntimeFieldNameGenerator; //transient
public final static String AGGREGATE_RUNTIME_FIELD_NAME_GENERATOR_PROPERTY = "aggregateRuntimeFieldNameGenerator";
private volatile String fieldDescription; //persistent
/**
* The column that the above path maps to.
*/
private MWColumnHandle columnHandle;
public final static String COLUMN_PROPERTY = "column";
// ************** Constructors ***************
/** Default constructor - for TopLink use only */
private MWAggregatePathToColumn() {
super();
}
MWAggregatePathToColumn(MWAggregateMapping mapping) {
super(mapping);
}
protected void initialize(Node parent) {
super.initialize(parent);
this.mappingHandles = new Vector();
this.columnHandle = new MWColumnHandle(this, this.buildColumnScrubber());
}
// ************** Accessors ***************
public MWColumn getColumn() {
return this.columnHandle.getColumn();
}
public void setColumn(MWColumn column) {
MWColumn old = this.getColumn();
this.columnHandle.setColumn(column);
firePropertyChanged(COLUMN_PROPERTY, old, column);
}
public AggregateRuntimeFieldNameGenerator getAggregateRuntimeFieldNameGenerator() {
return this.aggregateRuntimeFieldNameGenerator;
}
public void setAggregateRuntimeFieldNameGenerator(AggregateRuntimeFieldNameGenerator aggregateRuntimeFieldNameGenerator) {
this.aggregateRuntimeFieldNameGenerator = aggregateRuntimeFieldNameGenerator;
}
void addMappingNode(MWMapping node) {
if (getMappingNodes().contains(node)) {
return;
}
this.mappingHandles.add(new MWMappingHandle(this, node, NodeReferenceScrubber.NULL_INSTANCE));
}
private void addMappingAt(int index, MWMapping mapping) {
this.mappingHandles.add(index, new MWMappingHandle(this, mapping, NodeReferenceScrubber.NULL_INSTANCE));
}
/**
* Creates a copy of the pathToField:
* points to the same parent mapping
* points to the same field description
* points to the same field
*/
MWAggregatePathToColumn copy(MWAggregateMapping parent) {
MWAggregatePathToColumn copy = new MWAggregatePathToColumn(parent);
for (Iterator stream = this.getMappingNodes().iterator(); stream.hasNext(); ) {
copy.addMappingNode((MWMapping) stream.next());
}
copy.setAggregateRuntimeFieldNameGenerator(this.getAggregateRuntimeFieldNameGenerator());
copy.setColumn(this.columnHandle.getColumn());
return copy;
}
MWMapping mappingAt(int mappingIndex) {
return (MWMapping) getMappingNodes().get(mappingIndex);
}
List getMappingNodes() {
List result = new Vector(this.mappingHandles.size());
for (Iterator stream = new CloneIterator(this.mappingHandles); stream.hasNext(); ) {
result.add(((MWMappingHandle) stream.next()).getMapping());
}
return result;
}
/**
* Inserts a root mapping node into the collection of
* mapping nodes.
**/
void insertRootMappingNode(MWMapping node) {
addMappingAt(0, node);
}
// ************** Containment hierarchy ***************
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
synchronized (this.mappingHandles) { children.addAll(this.mappingHandles); }
children.add(this.columnHandle);
}
private NodeReferenceScrubber buildColumnScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWAggregatePathToColumn.this.setColumn(null);
}
public String toString() {
return "MWAggregatePathToColumn.buildColumnScrubber()";
}
};
}
// we don't worry about 'mappings' here because they only ever
// reference mappings in a MWAggregateDescriptor; and the 'mappings'
// will be rebuilt whenever a mapping is removed from a MWAggregateDescriptor;
// see MWAggregateDescriptor.removeMapping(MWMapping) and
// MWProject.recalculateAggregatePathsToColumn(MWMappingDescriptor)
// ~bjv (per kfm)
public void mappingReplaced(MWMapping oldMapping, MWMapping newMapping) {
super.mappingReplaced(oldMapping, newMapping);
// like #nodeRemoved(Node), above, we don't worry about 'mappingHandles' here;
// they will be rebuilt when the new mapping is added to the aggregate descriptor;
// see MWAggregateDescriptor.addMapping(MWMapping) and
// MWProject.recalculateAggregatePathsToColumn(MWMappingDescriptor)
// ~bjv (per kfm)
}
// ************** Problem Handling ***************
boolean isPathReadOnly(){
Iterator mappingNodes = getMappingNodes().iterator();
while (mappingNodes.hasNext()){
MWMapping mapping = (MWMapping) mappingNodes.next();
// short circuit
if (mapping.isReadOnly())
return true;
}
return false;
}
boolean fieldIsWritten() {
if (isPathReadOnly()) {
return false;
}
return this.aggregateRuntimeFieldNameGenerator.fieldIsWritten();
}
// ************** display methods ***************
public String getPathDescription() {
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(sw);
Iterator i = getMappingNodes().iterator();
if (i.hasNext()) {
pw.print(mappingAt(0).getParentDescriptor().getMWClass().shortName());
} else {
pw.print(this.aggregateRuntimeFieldNameGenerator.owningDescriptor().getMWClass().shortName());
}
while (i.hasNext()) {
pw.print(".");
pw.print(((MWMapping) i.next()).getName());
}
return sw.getBuffer().toString();
}
public void toString(StringBuffer sb) {
sb.append(this.getPathDescription());
sb.append(" -> ");
if (this.getColumn() == null) {
sb.append("null");
} else {
sb.append(this.getColumn().qualifiedName());
}
}
public String description() {
StringBuffer sb = new StringBuffer();
this.toString(sb);
return sb.toString();
}
public int compareTo(Object o) {
MWAggregatePathToColumn otherPathFieldAssociation = (MWAggregatePathToColumn) o;
return Collator.getInstance().compare(getPathDescription() + getAggregateRuntimeFieldNameGenerator().fieldNameForRuntime(),
otherPathFieldAssociation.getPathDescription() + otherPathFieldAssociation.getAggregateRuntimeFieldNameGenerator().fieldNameForRuntime());
}
// **************** Containment Hierarchy ************
private MWAggregateMapping getAggregateMapping() {
return (MWAggregateMapping) getParent();
}
private MWRelationalDescriptor getParentDescriptor() {
return (MWRelationalDescriptor) getAggregateMapping().getParentDescriptor();
}
//**************** Runtime Conversion ************
void adjustRuntimeMapping(AggregateObjectMapping runtimeMapping) {
if (!getParentDescriptor().isAggregateDescriptor()) {
if (getColumn() != null) {
runtimeMapping.addFieldNameTranslation(getColumn().qualifiedName(), calculateAggregateFieldName());
}
}
else {
runtimeMapping.addFieldNameTranslation(calculateSourceFieldName(), calculateAggregateFieldName());
}
}
private String calculateAggregateFieldName() {
String fieldName = "";
for (Iterator mappingNodes = getMappingNodes().listIterator(); mappingNodes.hasNext(); ) {
MWMapping mapping = (MWMapping) mappingNodes.next();
if (fieldName.equals("")) {
fieldName += mapping.getName();
}
else {
fieldName += "_" + mapping.getName();
}
}
if (!fieldName.equals("")) {
fieldName += "->";
}
fieldName += getAggregateRuntimeFieldNameGenerator().fieldNameForRuntime();
return fieldName;
}
private String calculateSourceFieldName() {
return ((MWAggregateMapping) getParent()).getName() + "_" + calculateAggregateFieldName();
}
// ********** TopLink methods **********
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWAggregatePathToColumn.class);
XMLDirectMapping fieldDescriptionMapping = new XMLDirectMapping();
fieldDescriptionMapping.setAttributeName("aggregateRuntimeFieldNameGenerator");
fieldDescriptionMapping.setGetMethodName("getFieldDescriptionForTopLink");
fieldDescriptionMapping.setSetMethodName("setFieldDescriptionForTopLink");
fieldDescriptionMapping.setXPath("field-description/text()");
descriptor.addMapping(fieldDescriptionMapping);
XMLCompositeObjectMapping columnHandleMapping = new XMLCompositeObjectMapping();
columnHandleMapping.setAttributeName("columnHandle");
columnHandleMapping.setGetMethodName("getColumnHandleForTopLink");
columnHandleMapping.setSetMethodName("setColumnHandleForTopLink");
columnHandleMapping.setReferenceClass(MWColumnHandle.class);
columnHandleMapping.setXPath("column-handle");
descriptor.addMapping(columnHandleMapping);
XMLCompositeCollectionMapping mappingHandlesMapping = new XMLCompositeCollectionMapping();
mappingHandlesMapping.setAttributeName("mappingHandles");
mappingHandlesMapping.setGetMethodName("getMappingHandlesForTopLink");
mappingHandlesMapping.setSetMethodName("setMappingHandlesForTopLink");
mappingHandlesMapping.setReferenceClass(MWMappingHandle.class);
mappingHandlesMapping.setXPath("mapping-handles/mapping-handle");
descriptor.addMapping(mappingHandlesMapping);
return descriptor;
}
private String getFieldDescriptionForTopLink() {
return getAggregateRuntimeFieldNameGenerator().fieldNameForRuntime();
}
private void setFieldDescriptionForTopLink(String fieldDescription) {
this.fieldDescription = fieldDescription;
}
private List getMappingHandlesForTopLink() {
return this.mappingHandles;
}
private void setMappingHandlesForTopLink(List handles) {
for (Iterator stream = handles.iterator(); stream.hasNext(); ) {
((MWMappingHandle) stream.next()).setScrubber(NodeReferenceScrubber.NULL_INSTANCE);
}
this.mappingHandles = handles;
}
/**
* check for null
*/
private MWColumnHandle getColumnHandleForTopLink() {
return (this.columnHandle.getColumn() == null) ? null : this.columnHandle;
}
private void setColumnHandleForTopLink(MWColumnHandle columnHandle) {
NodeReferenceScrubber scrubber = this.buildColumnScrubber();
this.columnHandle = ((columnHandle == null) ? new MWColumnHandle(this, scrubber) : columnHandle.setScrubber(scrubber));
}
public void postProjectBuild() {
super.postProjectBuild();
if (this.fieldDescription == null) {
return; //legacy cases are handled in the legacyXXPostPostProjectBuild methods
}
Iterator aggregateFieldNameGenerators;
if (this.mappingHandles.size() == 0) {
aggregateFieldNameGenerators = ((MWRelationalDescriptor) getAggregateMapping().getReferenceDescriptor()).buildAggregateFieldNameGenerators().iterator();
}
else {
MWMappingHandle mappingHandle = (MWMappingHandle) this.mappingHandles.get(this.mappingHandles.size() - 1);
aggregateFieldNameGenerators = mappingHandle.getMapping().aggregateFieldNameGenerators();
if (!aggregateFieldNameGenerators.hasNext()) {
aggregateFieldNameGenerators = ((MWRelationalDescriptor) ((MWAggregateMapping) mappingHandle.getMapping()).getReferenceDescriptor()).buildAggregateFieldNameGenerators().iterator();
}
}
while (aggregateFieldNameGenerators.hasNext()) {
AggregateRuntimeFieldNameGenerator aggregateFieldNameGenerator = (AggregateRuntimeFieldNameGenerator) aggregateFieldNameGenerators.next();
if (this.fieldDescription.equals(aggregateFieldNameGenerator.fieldNameForRuntime())) {
setAggregateRuntimeFieldNameGenerator(aggregateFieldNameGenerator);
break;
}
}
this.fieldDescription = null;
}
}