/*******************************************************************************
* 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;
import java.text.Collator;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.SortedSet;
import java.util.TreeSet;
import org.eclipse.persistence.tools.workbench.mappingsmodel.db.MWDatabase;
import org.eclipse.persistence.tools.workbench.mappingsmodel.descriptor.MWDescriptor;
import org.eclipse.persistence.tools.workbench.mappingsmodel.mapping.MWMapping;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClass;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWClassRepository;
import org.eclipse.persistence.tools.workbench.mappingsmodel.meta.MWTypeDeclaration;
import org.eclipse.persistence.tools.workbench.mappingsmodel.project.MWProject;
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.node.AbstractNodeModel;
import org.eclipse.persistence.tools.workbench.utility.node.Node;
import org.eclipse.persistence.descriptors.ClassDescriptor;
import org.eclipse.persistence.descriptors.DescriptorEvent;
import org.eclipse.persistence.oxm.XMLDescriptor;
import org.eclipse.persistence.queries.ReadAllQuery;
public abstract class MWModel
extends AbstractNodeModel
implements MWNode
{
// ********** constructors **********
/**
* Default constructor - for TopLink use only.
*/
protected MWModel() {
super();
}
/**
* Every object (except MWProject) must have a parent.
* Use this constructor to create a new object - the default
* constructor should only be used by TopLink.
* @see #initialize(Node)
*/
protected MWModel(MWNode parent) {
super(parent);
}
// ********** convenience methods **********
/**
* Return the object's parent in the containment hierarchy.
* Every object except the MWProject will have a parent.
*/
// TODO this can be renamed to getParent() when we move to jdk 1.5
public final MWNode getMWParent() {
return (MWNode) getParent();
}
/**
* Do NOT override this method.
* Every model object should be able to return the project.
*/
public final MWProject getProject() {
if (this.getParent() == null) {
try {
return (MWProject) this;
} catch (ClassCastException ex) {
throw new IllegalStateException("This object is missing a parent: " + this);
}
}
return (this.getMWParent()).getProject();
}
/**
* Do NOT override this method.
* Every model object in a relational project should be able to return the database.
*/
public final MWDatabase getDatabase() {
MWRelationalProject project;
try {
project = (MWRelationalProject) this.getProject();
} catch (ClassCastException cce) {
throw new UnsupportedOperationException("This object is not in a relational project: " + this);
}
return project.getTableRepository();
}
/**
* Do NOT override this method.
* Every model object should be able to return the class repository.
*/
public final MWClassRepository getRepository() {
return this.getProject().getClassRepository();
}
/**
* Do NOT override this method.
* Every model object should be able to fetch types.
* This returns a type that may be only a stub
* (i.e. it does not have any attributes, methods, etc. populated).
*
* Typically, this is the best method to call when you just need
* a class (as opposed to an attribute or method). If you need
* a fully-populated type, call this method to get the type,
* check whether the type is a stub, and, if it is a stub, refresh it.
* This may be require some interaction with the user.
*
* You may not *always* want to refresh a non-stub type,
* since that would probably drop any changes entered manually
* by the user since the last refresh.
*/
public final MWClass typeNamed(String typeName) {
return this.getRepository().typeNamedInternal(typeName);
}
/**
* Do NOT override this method.
* Every model object should be able to fetch types.
* This returns a type that may be only a stub
* (i.e. it does not have any attributes, methods, etc. populated).
*
* Typically, this is the best method to call when you just need
* a class (as opposed to an attribute or method). If you need
* a fully-populated type, call this method to get the type,
* check whether the type is a stub, and, if it is a stub, refresh it.
* This may be require some interaction with the user.
*
* You may not *always* want to refresh a non-stub type,
* since that would probably drop any changes entered manually
* by the user since the last refresh.
*/
public final MWClass typeFor(Class javaClass) {
return this.typeNamed(javaClass.getName());
}
// ********** model synchronization support **********
/**
* Convenience method.
* Remove the specified node from the specified bound collection,
* firing the appropriate change event if necessary and
* launching the cascading #nodeRemoved() mechanism.
* Return true if the collection changed - @see java.util.Collection.remove(Object).
*/
protected boolean removeNodeFromCollection(Node node, Collection collection, String collectionName) {
boolean changed = this.removeItemFromCollection(node, collection, collectionName);
if (changed) {
this.getProject().nodeRemoved(node);
}
return changed;
}
/**
* Convenience method.
* Remove the specified node from the specified collection
* and launch the cascading #nodeRemoved() mechanism.
* No change event is fired.
* Return true if the collection changed - @see java.util.Collection.remove(Object).
*/
protected boolean removeNodeFromCollection(Node node, Collection collection) {
boolean changed = collection.remove(node);
if (changed) {
this.getProject().nodeRemoved(node);
}
return changed;
}
/**
* Convenience method.
* Launch the cascading #nodeRemoved() mechanism for the specified nodes.
*/
protected void nodesRemoved(Collection removedNodes) {
this.nodesRemoved(removedNodes.iterator());
}
/**
* Convenience method.
* Launch the cascading #nodeRemoved() mechanism for the specified nodes.
*/
protected void nodesRemoved(Iterator removedNodes) {
while (removedNodes.hasNext()) {
this.getProject().nodeRemoved((Node) removedNodes.next());
}
}
/**
* Convenience method.
* Remove the specified nodes from the specified bound collection,
* firing the appropriate change event if necessary and
* launching the cascading #nodeRemoved() mechanism.
* Return true if the collection changed - @see java.util.Collection.remove(Object).
*/
protected boolean removeNodesFromCollection(Collection nodes, Collection collection, String collectionName) {
return this.removeNodesFromCollection(nodes.iterator(), collection, collectionName);
}
/**
* Convenience method.
* Remove the specified nodes from the specified bound collection,
* firing the appropriate change event if necessary,
* and launch the cascading #nodeRemoved() mechanism.
* Return true if the collection changed - @see java.util.Collection.remove(Object).
*/
protected boolean removeNodesFromCollection(Iterator nodes, Collection collection, String collectionName) {
Collection removedNodes = CollectionTools.collection(nodes);
removedNodes.retainAll(collection);
boolean changed = this.removeItemsFromCollection(nodes, collection, collectionName);
this.nodesRemoved(removedNodes);
return changed;
}
/**
* Convenience method.
* Remove the specified nodes from the specified collection
* and launch the cascading #nodeRemoved() mechanism.
* No change event is fired.
* Return true if the collection changed - @see java.util.Collection.remove(Object).
*/
protected boolean removeNodesFromCollection(Collection nodes, Collection collection) {
return this.removeNodesFromCollection(nodes.iterator(), collection);
}
/**
* Convenience method.
* Remove the specified nodes from the specified collection
* and launch the cascading #nodeRemoved() mechanism.
* No change event is fired.
* Return true if the collection changed - @see java.util.Collection.remove(Object).
*/
protected boolean removeNodesFromCollection(Iterator nodes, Collection collection) {
Collection removedNodes = CollectionTools.collection(nodes);
removedNodes.retainAll(collection);
boolean changed = collection.removeAll(removedNodes);
this.nodesRemoved(removedNodes);
return changed;
}
/**
* Convenience method.
* Clear the specified collection, firing the appropriate change event
* if necessary and launching the cascading #nodeRemoved() mechanism.
*/
protected void clearNodeCollection(Collection nodeCollection, String collectionName) {
Collection removedNodes = new ArrayList(nodeCollection);
this.clearCollection(nodeCollection, collectionName);
this.nodesRemoved(removedNodes);
}
/**
* Convenience method.
* Clear the specified collection and launch the cascading #nodeRemoved()
* mechanism.
* No change event is fired.
*/
protected void clearNodeCollection(Collection nodeCollection) {
Collection removedNodes = new ArrayList(nodeCollection);
nodeCollection.clear();
this.nodesRemoved(removedNodes);
}
/**
* Convenience method.
* Remove the node specified by index from the specified bound list,
* firing the appropriate change event if necessary and
* launching the cascading #nodeRemoved() mechanism.
* Return the removed object - @see java.util.List.remove(int).
*/
protected Object removeNodeFromList(int index, List nodeList, String listName) {
Node removedNode = (Node) this.removeItemFromList(index, nodeList, listName);
this.getProject().nodeRemoved(removedNode);
return removedNode;
}
/**
* Convenience method.
* Remove the node specified by index from the specified list
* and launch the cascading #nodeRemoved() mechanism.
* No change event is fired.
* Return the removed object - @see java.util.List.remove(int).
*/
protected Object removeNodeFromList(int index, List list) {
Node removedNode = (Node) list.remove(index);
this.getProject().nodeRemoved(removedNode);
return removedNode;
}
/**
* Convenience method.
* Clear the specified list, firing the appropriate change event if necessary
* and launching the cascading #nodeRemoved() mechanism.
*/
protected void clearNodeList(List nodeList, String listName) {
Collection removedNodes = new ArrayList(nodeList);
this.clearList(nodeList, listName);
this.nodesRemoved(removedNodes);
}
/**
* Convenience method.
* Clear the specified list and launch the cascading #nodeRemoved() mechanism.
* No change event is fired.
*/
protected void clearNodeList(List nodeList) {
// since no events thrown, same as for collection
this.clearNodeCollection(nodeList);
}
/**
* This is called when a mapping is morphed
*/
public void mappingReplaced(MWMapping oldMapping, MWMapping newMapping) {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).mappingReplaced(oldMapping, newMapping);
}
// when you override this method, don't forget to include:
// super.mappingReplaced(oldMapping, newMapping);
}
/**
* This called when a descriptor is morphed.
*/
public void descriptorReplaced(MWDescriptor oldDescriptor, MWDescriptor newDescriptor) {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).descriptorReplaced(oldDescriptor, newDescriptor);
}
// when you override this method, don't forget to include:
// super.descriptorReplaced(oldDescriptor, newDescriptor);
}
/**
* Performance tweak:
* This is called when a descriptor is unmapped in lieu of
* calling #nodeRemoved(Node) for all the mappings
*/
public void descriptorUnmapped(Collection mappings) {
for (Iterator stream = this.children(); stream.hasNext(); ) {
MWNode child = (MWNode) stream.next();
child.descriptorUnmapped(mappings);
}
// when you override this method, don't forget to include:
// super.descriptorUnmapped(mappings);
}
// ********** post-read methods **********
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink. Now we can cascade through
* the model and resolve all the class handles and class sub-object
* handles. This happens BEFORE the call to #postProjectBuild() below.
* @see MWProject#postProjectBuild()
*/
public final void resolveClassHandles() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).resolveClassHandles();
}
}
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink. Now we can cascade through
* the model and resolve all the meta data object (table, schema,
* field, etc.) handles. This happens BEFORE the call to
* #postProjectBuild() below.
* @see MWProject#postProjectBuild()
*/
public final void resolveMetadataHandles() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).resolveMetadataHandles();
}
}
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink. Now we can cascade through
* the model and resolve all the meta data object (table, schema,
* column, etc.) handles. This happens BEFORE the call to
* #postProjectBuild() below.
* @see MWProject#postProjectBuild()
*/
public final void resolveColumnHandles() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).resolveColumnHandles();
}
}
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink. Now we can cascade through
* the model and resolve all the meta data object (table, schema,
* field, etc.) handles. This happens BEFORE the call to
* #postProjectBuild() below.
* @see MWProject#postProjectBuild()
*/
public final void resolveReferenceHandles() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).resolveReferenceHandles();
}
}
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink. Now we can cascade through
* the model and resolve all the descriptor handles and descriptor
* sub-object handles. This happens BEFORE the call to
* #postProjectBuild() below.
* @see MWProject#postProjectBuild()
*/
public final void resolveDescriptorHandles() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).resolveDescriptorHandles();
}
}
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink. Now we can cascade through
* the model and resolve all the method handles.
* This happens BEFORE the call to #postProjectBuild() below.
* @see MWProject#postProjectBuild()
*/
public final void resolveMethodHandles() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).resolveMethodHandles();
}
}
/**
* When this method is called, everything in the project has been
* read in and initialized by TopLink, and all the class, table, and
* descriptor handles have been resolved.
* Now we can cascade through the model, tying everything together.
*/
public void postProjectBuild() {
for (Iterator stream = this.children(); stream.hasNext(); ) {
((MWNode) stream.next()).postProjectBuild();
}
// when you override this method, don't forget to include:
// super.postProjectBuild();
}
// ********** display methods **********
/**
* Return something useful for the UI.
*/
public String displayString() {
// the default is to use the developer-friendly string...
return this.toString();
}
// ********** legacy methods **********
/**
* Our "legacy":
* These methods are here so we have the ability to perform
* version-specific initialization (e.g. initializing handles,
* which did not exist before 4.0).
*/
// ***** Version 6.0 events *****
protected void legacy60PostBuild(DescriptorEvent event) {
// when you override this method, don't forget to include:
// super.legacy50PostBuild(event);
}
// ********** legacy TopLink support **********
/**
* Build and return a descriptor with the appropriate descriptor events configured.
*/
protected static XMLDescriptor legacy60BuildStandardDescriptor() {
org.eclipse.persistence.oxm.XMLDescriptor descriptor = new org.eclipse.persistence.oxm.XMLDescriptor();
descriptor.getEventManager().setPostBuildSelector("legacy60PostBuild");
return descriptor;
}
// ********** hacking **********
// TODO: remove this when there is a better way to build this list
public SortedSet buildBasicTypes() {
SortedSet result = new TreeSet(this.buildBasicTypesComparator());
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Boolean.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Byte.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Character.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Double.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Float.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Integer.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Long.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Short.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.String.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.math.BigDecimal.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.math.BigInteger.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.sql.Date.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.sql.Time.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.sql.Timestamp.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.util.Calendar.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.util.Date.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.sql.Blob.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(java.sql.Clob.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(Number.class)));
result.add(new MWTypeDeclaration(this, this.typeFor(byte.class), 1));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Byte.class), 1));
result.add(new MWTypeDeclaration(this, this.typeFor(char.class), 1));
result.add(new MWTypeDeclaration(this, this.typeFor(java.lang.Character.class), 1));
return result;
}
/**
* sort on short class name; if the short name is the same,
* sort on the fully-qualified name
*/
private Comparator buildBasicTypesComparator() {
return new Comparator() {
public int compare(Object o1, Object o2) {
MWTypeDeclaration c1 = (MWTypeDeclaration) o1;
MWTypeDeclaration c2 = (MWTypeDeclaration) o2;
int result = Collator.getInstance().compare(c1.shortTypeName(), c2.shortTypeName());
if (result != 0) {
return result;
}
result = Collator.getInstance().compare(c1.typeName(), c2.typeName());
if (result != 0) {
return result;
}
return c1.getDimensionality() - c2.getDimensionality();
}
};
}
/**
* Some toplink library classes have been replaced with other classes,
* on legacy project read the class references should be changed.
*/
public static String legacyReplaceToplinkDeprecatedClassReferences(String legacyClassName) {
if ("org.eclipse.persistence.publicinterface.Descriptor".equals(legacyClassName)) {
return "org.eclipse.persistence.descriptors.ClassDescriptor";
} else if ("org.eclipse.persistence.publicinterface.DescriptorEvent".equals(legacyClassName)) {
return "org.eclipse.persistence.descriptors.DescriptorEvent";
} else {
return legacyClassName;
}
}
public static String legacyReplaceToplinkDepracatedClassReferencesFromSignature(String legacyMethodSignature) {
if (legacyMethodSignature != null) {
if (legacyMethodSignature.contains("org.eclipse.persistence.publicinterface.Descriptor")) {
if (legacyMethodSignature.contains("DescriptorEvent")) {
legacyMethodSignature = legacyMethodSignature.replace("org.eclipse.persistence.publicinterface.DescriptorEvent", "org.eclipse.persistence.descriptors.DescriptorEvent");
} else {
legacyMethodSignature = legacyMethodSignature.replace("org.eclipse.persistence.publicinterface.Descriptor", "org.eclipse.persistence.descriptors.ClassDescriptor");
}
}
}
return legacyMethodSignature;
}
}