| /******************************************************************************* |
| * Copyright (c) 1998, 2012 Oracle. 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.db.relational.handles; |
| |
| import java.util.Collection; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.ListIterator; |
| import org.eclipse.persistence.tools.db.relational.ELNode; |
| import org.eclipse.persistence.tools.utility.iterator.EmptyIterator; |
| import org.eclipse.persistence.tools.utility.model.listener.ChangeListener; |
| import org.eclipse.persistence.tools.utility.model.listener.CollectionChangeListener; |
| import org.eclipse.persistence.tools.utility.model.listener.ListChangeListener; |
| import org.eclipse.persistence.tools.utility.model.listener.PropertyChangeListener; |
| import org.eclipse.persistence.tools.utility.model.listener.StateChangeListener; |
| import org.eclipse.persistence.tools.utility.node.Node; |
| import org.eclipse.persistence.tools.utility.node.Problem; |
| |
| /** |
| * Handles are used to isolate the painful bits of code |
| * necessary to correctly handle references to model objects. |
| * |
| * All handles should subclass this abstract class. |
| * |
| * @version 2.6 |
| */ |
| @SuppressWarnings("nls") |
| public abstract class MWHandle implements ELNode { |
| |
| |
| /** Containment hierarchy. */ |
| private ELNode parent; // pseudo-final |
| |
| /** This is used to synchronize the model when a node is removed. */ |
| private NodeReferenceScrubber scrubber; // pseudo-final |
| |
| /** A handle is dirty when the path to its node changes. */ |
| private volatile boolean dirty; |
| |
| // ********** constructors ********** |
| |
| /** |
| * default constructor - for TopLink use only |
| */ |
| protected MWHandle() { |
| super(); |
| // a new object is dirty, by definition |
| this.dirty = true; |
| } |
| |
| protected MWHandle(ELNode parent, NodeReferenceScrubber scrubber) { |
| this(); |
| this.setParent(parent); |
| this.setScrubberInternal(scrubber); |
| } |
| |
| |
| // ********** containment hierarchy (parent/children) ********** |
| |
| @Override |
| public final Node getParent() { |
| return this.parent; |
| } |
| |
| /** |
| * Set the object's parent in the containment hierarchy. |
| * Most objects must have a parent. |
| */ |
| public final void setParent(Node parent) { |
| if (parent == null) { |
| throw new NullPointerException(); |
| } |
| this.parent = (ELNode) parent; |
| } |
| |
| // handles do not have children |
| @Override |
| public final Iterator<Node> children() { |
| return EmptyIterator.instance(); |
| } |
| |
| // handles do not have children |
| public final void setChildBackpointers() { |
| // do nothing |
| } |
| |
| @Override |
| public final boolean isDescendantOf(Node node) { |
| return (this == node) || this.parent.isDescendantOf(node); |
| } |
| |
| @Override |
| public final void addBranchReferencesTo(Collection branchReferences) { |
| Node node = this.node(); |
| if (node != null) { |
| branchReferences.add(new SimpleReference(this, node)); |
| } |
| } |
| |
| // handles do not have children |
| @Override |
| public final void addAllNodesTo(Collection nodes) { |
| nodes.add(this); |
| } |
| |
| |
| // ********** dirty flag support ********** |
| |
| @Override |
| public final boolean isDirtyBranch() { |
| return this.dirty; |
| } |
| |
| @Override |
| public final void markBranchDirty() { |
| throw new IllegalStateException("handles shouldn't have children"); |
| } |
| |
| @Override |
| public final void markEntireBranchDirty() { |
| this.markDirty(); |
| } |
| |
| @Override |
| public final void cascadeMarkEntireBranchClean() { |
| this.dirty = false; |
| } |
| |
| @Override |
| public final void markBranchCleanIfPossible() { |
| throw new IllegalStateException("handles shouldn't have children"); |
| } |
| |
| private void markDirty() { |
| this.dirty = true; |
| this.parent.markBranchDirty(); |
| } |
| |
| |
| // ********** convenience methods ********** |
| |
| @Override |
| public final ELNode getMWParent() { |
| return this.parent; |
| } |
| |
| // ********** model synchronization support ********** |
| |
| /** |
| * Returns the node referenced by the handle. |
| */ |
| protected abstract Node node(); |
| |
| /** |
| * If the handle's node has been renamed, or it is a descendant of |
| * a node that has been renamed, the handle must mark its branch |
| * dirty so that the handle is saved with the new name. |
| */ |
| @Override |
| public void nodeRenamed(Node node) { |
| if ((this.node() != null) && this.node().isDescendantOf(node)) { |
| this.markDirty(); |
| } |
| } |
| |
| /** |
| * If the handle's node has been removed, or it is a descendant of |
| * a node that has been removed, notify the scrubber. |
| */ |
| @Override |
| public final void nodeRemoved(Node removedNode) { |
| if ((this.node() != null) && this.node().isDescendantOf(removedNode)) { |
| this.scrubber.nodeReferenceRemoved(this.node(), this); |
| } |
| } |
| |
| /** |
| * Subclasses will probably implement something like |
| * #setScrubber(NodeReferenceScrubber) that returns 'this' |
| */ |
| protected final void setScrubberInternal(NodeReferenceScrubber scrubber) { |
| if (scrubber == null) { |
| throw new NullPointerException(); |
| } |
| this.scrubber = scrubber; |
| } |
| |
| |
| // ********** post-read methods ********** |
| |
| /** |
| * Override this method if there are objects in the hierarchy |
| * that depend on this handle being resolved before postProjectBuild(). |
| * Do not override unless the handle is for a class or class sub-object |
| * (attribute, method, etc.) |
| */ |
| public void resolveClassHandles() { |
| // do nothing |
| } |
| |
| /** |
| * Override this method if there are objects in the hierarchy |
| * that depend on this handle being resolved before postProjectBuild(). |
| * Do not override unless the handle is for a descriptor or descriptor sub-object |
| * (mapping, xml data field, etc.) |
| */ |
| public void resolveDescriptorHandles() { |
| // do nothing |
| } |
| |
| /** |
| * Override this method if there are objects in the hierarchy |
| * that depend on this handle being resolved before postProjectBuild(). |
| * Do not override unless the handle is for a meta data object or sub-object |
| * (field, schema component, etc.) |
| */ |
| public void resolveMetadataHandles() { |
| // do nothing |
| } |
| |
| /** |
| * Override this method if there are objects in the hierarchy |
| * that depend on this handle being resolved before postProjectBuild(). |
| * Do not override unless the handle is for a meta data object or sub-object |
| * (column, schema component, etc.) |
| */ |
| public void resolveColumnHandles() { |
| // do nothing |
| } |
| |
| /** |
| * Override this method if there are objects in the hierarchy |
| * that depend on this handle being resolved before postProjectBuild(). |
| * Do not override unless the handle is for a meta data object or sub-object |
| * (field, schema component, etc.) |
| */ |
| public void resolveReferenceHandles() { |
| // do nothing |
| } |
| |
| /** |
| * Override this method if there are objects in the hierarchy |
| * that depend on this handle being resolved before postProjectBuild(). |
| * Do not override unless the handle is for a meta data object or sub-object |
| * (field, schema component, etc.) |
| */ |
| public void resolveMethodHandles() { |
| // do nothing |
| } |
| |
| public void postProjectBuild() { |
| if (this.scrubber == null) { |
| throw new NullPointerException("This handle's 'scrubber' should have been set by its parent upon creation."); |
| } |
| } |
| |
| |
| // ********** display methods ********** |
| |
| /** |
| * handles are not displayed |
| */ |
| @Override |
| public final String displayString() { |
| throw new UnsupportedOperationException(); |
| } |
| |
| // ********** standard methods ********** |
| |
| @Override |
| public String toString() { |
| StringBuffer sb = new StringBuffer(); |
| sb.append(" ("); |
| this.toString(sb); |
| sb.append(')'); |
| return sb.toString(); |
| } |
| |
| public abstract void toString(StringBuffer sb); |
| |
| // ************ Abstract, unused behavior ************* |
| |
| @Override |
| public Node root() { |
| return null; |
| } |
| |
| @Override |
| public Validator getValidator() { |
| return null; |
| } |
| |
| @Override |
| public void setValidator(Validator validator) { |
| |
| } |
| |
| @Override |
| public void validateBranch() { |
| |
| } |
| |
| @Override |
| public boolean validateBranchInternal() { |
| return false; |
| } |
| |
| @Override |
| public ListIterator<Problem> branchProblems() { |
| return null; |
| } |
| |
| @Override |
| public int branchProblemsSize() { |
| return 0; |
| } |
| |
| @Override |
| public boolean hasBranchProblems() { |
| return false; |
| } |
| |
| @Override |
| public boolean containsBranchProblem(Problem problem) { |
| return false; |
| } |
| |
| @Override |
| public void rebuildBranchProblems() { |
| |
| } |
| |
| @Override |
| public void addBranchProblemsTo(List<Problem> branchProblems) { |
| |
| } |
| |
| @Override |
| public void clearAllBranchProblems() { |
| |
| } |
| |
| @Override |
| public boolean clearAllBranchProblemsInternal() { |
| return false; |
| } |
| |
| @Override |
| public String comment() { |
| return null; |
| } |
| |
| @Override |
| public void setComment(String comment) { |
| |
| } |
| |
| @Override |
| public void addChangeListener(ChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void removeChangeListener(ChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void addStateChangeListener(StateChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void removeStateChangeListener(StateChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void addPropertyChangeListener(String propertyName, |
| PropertyChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void removePropertyChangeListener(String propertyName, |
| PropertyChangeListener listener) { |
| } |
| |
| @Override |
| public void addCollectionChangeListener(String collectionName, |
| CollectionChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void removeCollectionChangeListener(String collectionName, |
| CollectionChangeListener listener) { |
| |
| } |
| |
| @Override |
| public void addListChangeListener(String listName, |
| ListChangeListener listener) { |
| } |
| |
| @Override |
| public void removeListChangeListener(String listName, |
| ListChangeListener listener) { |
| } |
| |
| |
| // ********** member interface ********** |
| |
| /** |
| * This interface defines the method called by a handle when the node the |
| * handle references has been removed from the project. Typically the |
| * handle's parent will implement an adapter that will call the appropriate |
| * method to either remove or clear the handle. The handle itself will |
| * continue to hold the reference node - it is up to the parent to |
| * synchronize appropriately. |
| */ |
| public interface NodeReferenceScrubber { |
| |
| /** |
| * The specified node is no longer referenced by the specified handle. |
| */ |
| void nodeReferenceRemoved(Node node, MWHandle handle); |
| |
| NodeReferenceScrubber NULL_INSTANCE = new NodeReferenceScrubber() { |
| @Override |
| public void nodeReferenceRemoved(Node node, MWHandle handle) { |
| // do nothing |
| } |
| @Override |
| public String toString() { |
| return "NullReferenceScrubber"; |
| } |
| }; |
| } |
| } |