/*******************************************************************************
* 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.descriptor;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Vector;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWDataField;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWModel;
import org.eclipse.persistence.tools.workbench.mappingsmodel.MWNominative;
import org.eclipse.persistence.tools.workbench.mappingsmodel.ProblemConstants;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWAggregateDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWInterfaceDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.relational.MWTableDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.xml.MWCompositeEisDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.xml.MWOXDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.xml.MWRootEisDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.handles.MWClassHandle;
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.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.DefaultMWClassRefreshPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.ExternalClassLoadFailureContainer;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.ExternalClassLoadFailureEvent;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassRefreshPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.MWProject;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.MWProjectDefaultsPolicy;
import org.eclipse.persistence.tools.workbench.mappingsmodel.spi.meta.ExternalClassNotFoundException;
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.descriptors.DescriptorEvent;
import org.eclipse.persistence.descriptors.InheritancePolicy;
import org.eclipse.persistence.mappings.DirectToFieldMapping;
import org.eclipse.persistence.mappings.OneToOneMapping;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.oxm.mappings.XMLCompositeObjectMapping;
import org.eclipse.persistence.oxm.mappings.XMLDirectMapping;
public abstract class MWDescriptor extends MWModel
implements MWNominative {
private MWClassHandle mwClassHandle;
public final static String MW_CLASS_PROPERTY = "mwClass";
private volatile String name; //fullClassName
public static final String NAME_PROPERTY = "name";
/** defaults to true **/
private volatile boolean active;
public final static String ACTIVE_PROPERTY = "active";
protected MWTransactionalPolicy transactionalPolicy;
/** used to store legacy values until they can be used in a postbuild **/
protected Map legacyValuesMap;
// ********** constructors **********
/**
* Default constructor - for TopLink use only.
*/
protected MWDescriptor() {
super();
}
protected MWDescriptor(MWProject parent, MWClass type, String name) {
super(parent);
initialize(type, name);
}
// ********** initialization **********
/**
* initialize transient state
*/
protected void initialize() {
super.initialize();
this.legacyValuesMap = new HashMap();
}
/**
* initialize persistent state
*/
protected void initialize(Node parent) {
super.initialize(parent);
this.mwClassHandle = new MWClassHandle(this, this.buildMWClassScrubber());
this.active = true;
}
protected void initialize(MWClass mwClass, String descriptorName) {
this.mwClassHandle.setType(mwClass);
this.name = descriptorName;
this.transactionalPolicy = buildDefaultTransactionalPolicy();
}
// ********** containment hierarchy **********
protected void addChildrenTo(List children) {
super.addChildrenTo(children);
children.add(this.mwClassHandle);
children.add(this.transactionalPolicy);
}
private NodeReferenceScrubber buildMWClassScrubber() {
return new NodeReferenceScrubber() {
public void nodeReferenceRemoved(Node node, MWHandle handle) {
MWDescriptor.this.setMWClass(null);
}
public String toString() {
return "MWDescriptor.buildMWClassScrubber()";
}
};
}
// ********** TransactionalPolicy API**********
/** default implementation */
protected MWTransactionalPolicy buildDefaultTransactionalPolicy() {
return new MWNullTransactionalPolicy(this);
}
public MWTransactionalPolicy getTransactionalPolicy() {
return this.transactionalPolicy;
}
public abstract void applyAdvancedPolicyDefaults(MWProjectDefaultsPolicy defaultsPolicy);
// *********** Accessors ************
public MWClass getMWClass() {
return this.mwClassHandle.getType();
}
public void setMWClass(MWClass newValue) {
if (newValue == null) {
throw new NullPointerException();
}
Object oldValue = this.mwClassHandle.getType();
this.mwClassHandle.setType(newValue);
this.firePropertyChanged(MW_CLASS_PROPERTY, oldValue, newValue);
}
public String getName() {
return this.name;
}
public void setName(String name) {
Object old = this.name;
this.name = name;
this.firePropertyChanged(NAME_PROPERTY, old, name);
if (this.attributeValueHasChanged(old, name)) {
this.getProject().nodeRenamed(this);
}
}
/**
* Indicates whether this descriptor is deamed a root descriptor. All mapping descriptors
* have a notion of being root versus aggregate.
*/
public boolean isRootDescriptor() {
return false;
}
public boolean isActive() {
return this.active;
}
public boolean isInactive() {
return ! this.active;
}
public void setActive(boolean newValue) {
boolean oldValue = this.active;
this.active = newValue;
this.firePropertyChanged(ACTIVE_PROPERTY, oldValue, newValue);
}
public boolean supportsCachingPolicy() {
return false;
}
// **************** queries *******************
public String packageName() {
return getMWClass().packageName();
}
public String shortName() {
return getMWClass().shortName();
}
public Iterator mappings() {
return NullIterator.instance();
}
public int inheritedAttributesSize() {
return 0;
}
public int mappingsSize() {
return 0;
}
public MWMapping mappingNamed(String mappingName) {
return null;
}
public Iterator mappingsIncludingInherited() {
return NullIterator.instance();
}
protected List getMappingsIncludingInherited() {
return new Vector();
}
public Collection getAllQueryKeysIncludingInherited() {
return new ArrayList();
}
public Collection getAutoGeneratedQueryKeysIncludingInherited() {
return new ArrayList();
}
public Collection writableMappingsForField( MWDataField field ) {
return Collections.EMPTY_LIST;
}
/** Use this method if other descriptor settings or policies depend upon inheritance */
void inheritanceChanged() {
this.transactionalPolicy.descriptorInheritanceChanged();
}
// **************** behavior *******************
public void unmap() {
//call super.unmap() when overriding this method
}
/** Return true if this descriptor can be part of a descriptor inheritance tree */
public abstract boolean canHaveInheritance();
public abstract MWInheritancePolicy getInheritancePolicy();
/** Return true if this descriptor is part of a descriptor inheritance tree
* (simply having an inheritance policy is not enough) */
public abstract boolean hasDefinedInheritance();
/**
* Return true if this descriptor has a defined instantiation policy
*/
public abstract boolean hasActiveInstantiationPolicy();
/** Return an iterator of descriptors, each which inherits from the one before,
* and terminates at the root descriptor (or at the point of cyclicity). */
public Iterator inheritanceHierarchy() {
return this.getInheritancePolicy().descriptorLineage();
}
/**
* Return true if this descriptor, or any descriptor in the
* inheritance hierarchy uses sequencing, as descriptors
* inherit sequencing information
* (overridden in MWTableDescriptor)
*/
public boolean usesSequencingInDescriptorHierarchy() {
return false;
}
// **************** morphing *******************
/**
* Subclasses should override this method to call the
* appropriate initializeFromMW___Descriptor() method.
*/
public abstract void initializeOn(MWDescriptor newDescriptor);
protected void initializeDescriptorAfterMorphing(MWDescriptor newDescriptor) {
this.initializeOn(newDescriptor);
newDescriptor.setChildBackpointers();
getProject().replaceDescriptor(this, newDescriptor);
}
protected void initializeFromMWDescriptor(MWDescriptor oldDescriptor)
{
this.setActive(oldDescriptor.isActive());
this.setName(oldDescriptor.getName());
}
protected void initializeFromMWMappingDescriptor(MWMappingDescriptor oldDescriptor) {
this.initializeFromMWDescriptor(oldDescriptor);
}
// **************** class refreshing *******************
public void refreshClass(ExternalClassLoadFailureContainer failures, DescriptorCreationFailureListener descCreationFailureListener) {
this.refreshClass(failures, this.buildMWClassRefreshPolicy(), descCreationFailureListener);
}
public void refreshClass()
throws ExternalClassNotFoundException, InterfaceDescriptorCreationException {
this.refreshClass(this.buildMWClassRefreshPolicy());
}
protected MWClassRefreshPolicy buildMWClassRefreshPolicy() {
return DefaultMWClassRefreshPolicy.instance();
}
protected void refreshClass(ExternalClassLoadFailureContainer failures, MWClassRefreshPolicy refreshPolicy, DescriptorCreationFailureListener descCreationFailureListener) {
try {
this.refreshClass(refreshPolicy);
} catch (ExternalClassNotFoundException e) {
failures.externalClassLoadFailure(new ExternalClassLoadFailureEvent(this, this.getName(), e));
} catch (InterfaceDescriptorCreationException e) {
descCreationFailureListener.descriptorCreationFailure(new DescriptorCreationFailureEvent(this, this.getName(), "DESCRIPTOR_REFRESH_ERROR_MESSAGE"));
}
}
// subclasses will throw InterfaceDescriptorCreationException
protected void refreshClass(MWClassRefreshPolicy refreshPolicy)
throws ExternalClassNotFoundException, InterfaceDescriptorCreationException {
getMWClass().refresh(refreshPolicy);
}
// *************** Problem Handling **************
/**
* This is overwritten so that *ONLY* active descriptors validate
* themselves and their branches.
* @see AbstractNodeModel.validateBranch()
*/
public boolean validateBranchInternal() {
return this.isActive() ?
super.validateBranchInternal()
:
this.clearAllBranchProblemsInternal();
}
protected void addProblemsTo(List currentProblems) {
super.addProblemsTo(currentProblems);
this.checkForFinalSuperClass(currentProblems);
this.checkForPublicClass(currentProblems);
// TODO this is a hack until we get a visible class repository
this.getMWClass().addDescriptorProblemsTo(currentProblems);
}
private void checkForPublicClass(List currentProblems){
if ( ! this.getMWClass().getModifier().isPublic()) {
currentProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_CLASS_NOT_PUBLIC));
}
}
private void checkForFinalSuperClass(List currentProblems) {
MWClass superclass = this.getMWClass().getSuperclass();
if ((superclass != null) && superclass.getModifier().isFinal()) {
currentProblems.add(this.buildProblem(ProblemConstants.DESCRIPTOR_CLASS_SUBCLASSES_FINAL_CLASS));
}
}
// ************* Automap Support ****************
public final void automapInheritanceHierarchy(Collection automapDescriptors) {
if (this.isActive() && this.canHaveInheritance() && ! this.hasDefinedInheritance()) {
this.automapInheritanceHierarchyInternal(automapDescriptors);
}
}
protected void automapInheritanceHierarchyInternal(Collection automapDescriptors) {
// do nothing by default
}
public final boolean autoMapRequiresMetaData() {
return this.isActive() && this.autoMapRequiresMetaDataInternal();
}
protected boolean autoMapRequiresMetaDataInternal() {
return false; // the default is false;
}
/**
* make sure descriptor is active
*/
public final void automap() {
if (this.isActive()) {
this.automapInternal();
}
}
protected void automapInternal() {
// do nothing by default
}
// **************** Runtime Conversion *******************
public ClassDescriptor buildRuntimeDescriptor() {
ClassDescriptor runtimeDescriptor = this.buildBasicRuntimeDescriptor();
this.transactionalPolicy.adjustRuntimeDescriptor(runtimeDescriptor);
return runtimeDescriptor;
}
/**
* Build the runtime descriptor, set the type (interface, aggregate, etc),
* and set the class name
*/
protected abstract ClassDescriptor buildBasicRuntimeDescriptor();
//*************** Display Methods **************
public String displayString() {
return getMWClass().shortName();
}
public String displayStringWithPackage() {
String packageName = this.packageName();
if (StringTools.stringIsEmpty(packageName)) {
packageName = "default package";
}
return this.getMWClass().shortName() + " (" + packageName + ")";
}
public void toString(StringBuffer sb) {
sb.append(getName());
}
//*************** TopLink Methods **************
public static XMLDescriptor buildDescriptor() {
XMLDescriptor descriptor = new XMLDescriptor();
descriptor.setJavaClass(MWDescriptor.class);
descriptor.setDefaultRootElement("descriptor");
org.eclipse.persistence.descriptors.InheritancePolicy ip = (org.eclipse.persistence.descriptors.InheritancePolicy)descriptor.getInheritancePolicy();
ip.setClassIndicatorFieldName("@type");
ip.addClassIndicator(MWTableDescriptor.class, "relational");
ip.addClassIndicator(MWAggregateDescriptor.class, "aggregate");
ip.addClassIndicator(MWInterfaceDescriptor.class, "interface");
ip.addClassIndicator(MWRootEisDescriptor.class, "eis-root");
ip.addClassIndicator(MWCompositeEisDescriptor.class, "eis-composite");
ip.addClassIndicator(MWOXDescriptor.class, "o-x");
descriptor.addDirectMapping("name", "name/text()");
((XMLDirectMapping) descriptor.addDirectMapping("comment", "comment/text()")).setNullValue("");
//TODO could change to inactive - then False would be the null value.
//most descriptors are active
((XMLDirectMapping) descriptor.addDirectMapping("active", "active/text()")).setNullValue(Boolean.TRUE);
XMLCompositeObjectMapping mwClassHandleMapping = new XMLCompositeObjectMapping();
mwClassHandleMapping.setAttributeName("mwClassHandle");
mwClassHandleMapping.setGetMethodName("getMWClassHandleForTopLink");
mwClassHandleMapping.setSetMethodName("setMWClassHandleForTopLink");
mwClassHandleMapping.setReferenceClass(MWClassHandle.class);
mwClassHandleMapping.setXPath("class-handle");
descriptor.addMapping(mwClassHandleMapping);
XMLCompositeObjectMapping transactionalPolicyMapping = new XMLCompositeObjectMapping();
transactionalPolicyMapping.setAttributeName("transactionalPolicy");
transactionalPolicyMapping.setGetMethodName("getTransactionalPolicyForTopLink");
transactionalPolicyMapping.setSetMethodName("setTransactionalPolicyForTopLink");
transactionalPolicyMapping.setReferenceClass(MWAbstractTransactionalPolicy.class);
transactionalPolicyMapping.setXPath("transactional-policy");
descriptor.addMapping(transactionalPolicyMapping);
return descriptor;
}
/**
* check for null
*/
private MWClassHandle getMWClassHandleForTopLink() {
return (this.mwClassHandle.getType() == null) ? null : this.mwClassHandle;
}
private void setMWClassHandleForTopLink(MWClassHandle mwClassHandle) {
NodeReferenceScrubber scrubber = this.buildMWClassScrubber();
this.mwClassHandle = ((mwClassHandle == null) ? new MWClassHandle(this, scrubber) : mwClassHandle.setScrubber(scrubber));
}
private MWAbstractTransactionalPolicy getTransactionalPolicyForTopLink() {
return this.transactionalPolicy.getValueForTopLink();
}
private void setTransactionalPolicyForTopLink(MWAbstractTransactionalPolicy transactionalPolicy) {
if (transactionalPolicy == null) {
this.transactionalPolicy = new MWNullTransactionalPolicy(this);
} else {
this.transactionalPolicy = transactionalPolicy;
}
}
}