/*******************************************************************************
* 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.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.MWMappingDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWMethodHandle;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassAttribute;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWMethod;
import org.eclipse.persistence.tools.workbench.utility.iterators.CloneListIterator;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.mappings.DatabaseMapping;
import org.eclipse.persistence.mappings.converters.ObjectTypeConverter;
import org.eclipse.persistence.mappings.foundation.AbstractTransformationMapping;
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;
import org.eclipse.persistence.sessions.Record;
public abstract class MWTransformationMapping
extends MWMapping
implements MWIndirectableMapping, MWTransformer.Parent
{
/** Indicates whether the attribute has a simple value that may be copied rather than built */
private volatile boolean mutable;
public final static String MUTABLE_PROPERTY = "mutable";
/** The AttributeTransformer implementation used to translate from data record to the attribute */
private volatile MWTransformer attributeTransformer;
public final static String ATTRIBUTE_TRANSFORMER_PROPERTY = "attributeTransformer";
/**
* The list of FieldTransformer implementations used to translate from the object to field values
* (Order is stored mainly for XML transformation mappings)
*/
private List fieldTransformerAssociations;
public final static String FIELD_TRANSFORMER_ASSOCIATIONS_LIST = "fieldTransformerAssociations";
/** Used on all subclasses - uses the MWIndirectableMapping INDIRECTION_PROPERTY */
private volatile String indirectionType;
// **************** Constructors ******************************************
protected MWTransformationMapping() {
super();
}
protected MWTransformationMapping(MWMappingDescriptor parent, MWClassAttribute attribute, String name) {
super(parent, attribute, name);
}
// **************** Initialization ****************************************
/**
* initialize persistent state
*/
protected void initialize(Node parent) {
super.initialize(parent);
this.attributeTransformer = new MWNullTransformer(this);
this.fieldTransformerAssociations = new Vector();
this.indirectionType = NO_INDIRECTION;
this.mutable = true;
}
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
children.add(this.attributeTransformer);
synchronized (this.fieldTransformerAssociations) { children.addAll(this.fieldTransformerAssociations); }
}
// **************** Mutable API *******************************************
public boolean isMutable() {
return this.mutable;
}
public void setMutable(boolean newValue) {
boolean oldValue = this.mutable;
this.mutable = newValue;
this.firePropertyChanged(MUTABLE_PROPERTY, oldValue, newValue);
}
// **************** Attribute Transformer *********************************
public MWTransformer getAttributeTransformer() {
return this.attributeTransformer;
}
private void setAttributeTransformer(MWTransformer newAttributeTransformer) {
MWTransformer oldAttributeTransformer = this.attributeTransformer;
this.attributeTransformer = newAttributeTransformer;
this.firePropertyChanged(ATTRIBUTE_TRANSFORMER_PROPERTY, oldAttributeTransformer, newAttributeTransformer);
}
public void clearAttributeTransformer() {
this.setAttributeTransformer(new MWNullTransformer(this));
}
public void setAttributeTransformer(MWMethod newAttributeTransformerMethod) {
this.setAttributeTransformer(new MWMethodBasedTransformer(this, newAttributeTransformerMethod));
}
public void setAttributeTransformer(MWClass newAttributeTransformerClass) {
this.setAttributeTransformer(new MWClassBasedTransformer(this, newAttributeTransformerClass));
}
public Iterator candidateAttributeTransformationMethods() {
// These are more than just the methods that are "valid" attribute transformation methods
return this.getParentDescriptor().getMWClass().allInstanceMethods();
}
// **************** Field Transformer Associations ************************
public ListIterator fieldTransformerAssociations() {
return new CloneListIterator(this.fieldTransformerAssociations);
}
public int fieldTransformerAssociationsSize() {
return this.fieldTransformerAssociations.size();
}
public void addFieldTransformerAssociation(MWFieldTransformerAssociation fieldTransformerAssociation) {
this.addItemToList(fieldTransformerAssociation, this.fieldTransformerAssociations, FIELD_TRANSFORMER_ASSOCIATIONS_LIST);
this.getProject().recalculateAggregatePathsToColumn(this.getParentDescriptor());
}
public void removeFieldTransformerAssociation(MWFieldTransformerAssociation fieldTransformerAssociation) {
int index = this.fieldTransformerAssociations.indexOf(fieldTransformerAssociation);
if (index != -1) {
this.removeNodeFromList(index, this.fieldTransformerAssociations, FIELD_TRANSFORMER_ASSOCIATIONS_LIST);
this.getProject().recalculateAggregatePathsToColumn(this.getParentDescriptor());
}
}
public void clearFieldTransformerAssociations() {
if (this.clearList(this.fieldTransformerAssociations, FIELD_TRANSFORMER_ASSOCIATIONS_LIST)) {
this.getProject().recalculateAggregatePathsToColumn(this.getParentDescriptor());
}
}
public Iterator candidateFieldTransformationMethods() {
// These are more than just the methods that are "valid" field transformation methods
return this.getParentDescriptor().getMWClass().allInstanceMethods();
}
// **************** Indirection *******************************************
public boolean usesValueHolderIndirection() {
return this.indirectionType == VALUE_HOLDER_INDIRECTION;
}
public boolean usesNoIndirection() {
return this.indirectionType == NO_INDIRECTION;
}
public void setUseNoIndirection() {
this.setIndirectionType(NO_INDIRECTION);
}
public void setUseValueHolderIndirection() {
this.setIndirectionType(VALUE_HOLDER_INDIRECTION);
}
private void setIndirectionType(String indirectionType) {
Object oldValue = this.indirectionType;
this.indirectionType = indirectionType;
firePropertyChanged(INDIRECTION_PROPERTY, oldValue, indirectionType);
}
// **************** MWTransformer.Parent **********************************************
public MWTransformationMapping transformationMapping() {
return this;
}
// **************** Morphing **********************************************
protected void initializeOn(MWMapping newMapping) {
newMapping.initializeFromMWTransformationMapping(this);
}
protected void initializeFromMWIndirectableMapping(MWIndirectableMapping oldMapping) {
super.initializeFromMWIndirectableMapping(oldMapping);
if (oldMapping.usesValueHolderIndirection()) {
this.setUseValueHolderIndirection();
}
else if (oldMapping.usesNoIndirection()) {
this.setUseNoIndirection();
}
}
// **************** Problems *********************************************
protected void addProblemsTo(List newProblems) {
super.addProblemsTo(newProblems);
this.attributeTransformer.addAttributeTransformerProblemsForMapping(newProblems, this);
this.addFieldTransformerProblemsTo(newProblems);
this.addIndirectionProblemsTo(newProblems);
}
private void addFieldTransformerProblemsTo(List newProblems) {
// field transformers must be specified unless mapping is read only
if (this.fieldTransformerAssociations.size() == 0 && ! this.isReadOnly()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_FIELD_TRANSFORMER_ASSOCIATIONS_NOT_SPECIFIED));
}
}
private void addIndirectionProblemsTo(List newProblems) {
if (this.getInstanceVariable().isValueHolder()) {
if (! this.usesValueHolderIndirection()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_ATTRIBUTE_WITHOUT_VALUE_HOLDER_INDIRECTION));
}
}
else {
if (!this.getProject().usesWeaving() && this.usesValueHolderIndirection()) {
newProblems.add(this.buildProblem(ProblemConstants.MAPPING_VALUE_HOLDER_INDIRECTION_WITHOUT_VALUE_HOLDER_ATTRIBUTE));
}
}
}
// **************** Runtime conversion ************************************
public DatabaseMapping runtimeMapping() {
AbstractTransformationMapping mapping = (AbstractTransformationMapping) super.runtimeMapping();
// attribute transformer
this.attributeTransformer.setRuntimeAttributeTransformer(mapping);
// field transformers
for (Iterator stream = this.fieldTransformerAssociations(); stream.hasNext(); ) {
((MWFieldTransformerAssociation) stream.next()).addRuntimeFieldTransformer(mapping);
}
// indirection
if (this.usesValueHolderIndirection()) {
mapping.setUsesIndirection(true);
}
// mutability
mapping.setIsMutable(this.mutable);
return mapping;
}
// **************** TopLink Methods ***************************************
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWTransformationMapping.class);
descriptor.getInheritancePolicy().setParentClass(MWMapping.class);
// attribute transformer class
XMLCompositeObjectMapping attributeTransformerMapping = new XMLCompositeObjectMapping();
attributeTransformerMapping.setAttributeName("attributeTransformer");
attributeTransformerMapping.setGetMethodName("getAttributeTransformerForTopLink");
attributeTransformerMapping.setSetMethodName("setAttributeTransformerForTopLink");
attributeTransformerMapping.setReferenceClass(MWTransformer.class);
attributeTransformerMapping.setXPath("attribute-transformer");
descriptor.addMapping(attributeTransformerMapping);
// field transformer associations
XMLCompositeCollectionMapping fieldTransformerAssociationsMapping = new XMLCompositeCollectionMapping();
fieldTransformerAssociationsMapping.setAttributeName("fieldTransformerAssociations");
fieldTransformerAssociationsMapping.setReferenceClass(MWFieldTransformerAssociation.class);
fieldTransformerAssociationsMapping.setXPath("field-transformer-associations");
descriptor.addMapping(fieldTransformerAssociationsMapping);
// indirection
ObjectTypeConverter indirectionTypeConverter = new ObjectTypeConverter();
indirectionTypeConverter.addConversionValue(NO_INDIRECTION, NO_INDIRECTION);
indirectionTypeConverter.addConversionValue(VALUE_HOLDER_INDIRECTION, VALUE_HOLDER_INDIRECTION);
XMLDirectMapping indirectionTypeMapping = new XMLDirectMapping();
indirectionTypeMapping.setAttributeName("indirectionType");
indirectionTypeMapping.setXPath("indirection-type/text()");
indirectionTypeMapping.setNullValue(NO_INDIRECTION);
indirectionTypeMapping.setConverter(indirectionTypeConverter);
descriptor.addMapping(indirectionTypeMapping);
// atomic
descriptor.addDirectMapping("mutable", "mutable/text()");
return descriptor;
}
/**
* check for null transformer
*/
private MWTransformer getAttributeTransformerForTopLink() {
return this.attributeTransformer.valueForTopLink();
}
private void setAttributeTransformerForTopLink(MWTransformer transformer) {
this.attributeTransformer = MWTransformer.buildTransformerForTopLink(transformer);
}
}