/******************************************************************************* * 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.uitools.app; import java.util.Iterator; import java.util.List; import org.eclipse.persistence.tools.workbench.utility.AbstractModel; import org.eclipse.persistence.tools.workbench.utility.CollectionTools; import org.eclipse.persistence.tools.workbench.utility.events.ChangeSupport; import org.eclipse.persistence.tools.workbench.utility.events.StateChangeListener; import org.eclipse.persistence.tools.workbench.utility.iterators.ChainIterator; /** * Subclasses need only implement the following methods: * * #getValue() * return the user-determined "value" of the node, * i.e. the object "wrapped" by the node * * #setValue(Object) * set the user-determined "value" of the node, * i.e. the object "wrapped" by the node; * typically only overridden for nodes with "primitive" values * * #getParent() * return the parent of the node, which should be another * TreeNodeValueModel * * #getChildrenModel() * return a ListValueModel for the node's children * * #engageValue() and #disengageValue() * override these methods to listen to the node's value if * it can change in a way that should be reflected in the tree */ public abstract class AbstractTreeNodeValueModel extends AbstractModel implements TreeNodeValueModel { // ********** constructors ********** /** * Default constructor. */ protected AbstractTreeNodeValueModel() { super(); } /** * @see org.eclipse.persistence.tools.workbench.utility.AbstractModel#buildDefaultChangeSupport() */ protected ChangeSupport buildDefaultChangeSupport() { // this value model is allowed to fire state change events... // return new ValueModelChangeSupport(this); return super.buildDefaultChangeSupport(); } // ********** extend AbstractModel implementation ********** public void addStateChangeListener(StateChangeListener listener) { if (this.hasNoStateChangeListeners()) { this.engageValue(); } super.addStateChangeListener(listener); } /** * Begin listening to the node's value. If the state of the node changes * in a way that should be reflected in the tree, fire a "state change" event. * If the entire value of the node changes, fire a "value property change" * event. */ protected abstract void engageValue(); public void removeStateChangeListener(StateChangeListener listener) { super.removeStateChangeListener(listener); if (this.hasNoStateChangeListeners()) { this.disengageValue(); } } /** * Stop listening to the node's value. * @see #engageValue() */ protected abstract void disengageValue(); // ********** PropertyValueModel implementation ********** /** * @see PropertyValueModel#setValue(Object) */ public void setValue(Object value) { throw new UnsupportedOperationException(); } // ********** TreeNodeValueModel implementation ********** /** * @see TreeNodeValueModel#path() */ public TreeNodeValueModel[] path() { List path = CollectionTools.reverseList(this.backPath()); return (TreeNodeValueModel[]) path.toArray(new TreeNodeValueModel[path.size()]); } /** * Return an iterator that climbs up the node's path, * starting with, and including, the node * and up to, and including, the root node. */ protected Iterator backPath() { return new ChainIterator(this) { protected Object nextLink(Object currentLink) { return ((TreeNodeValueModel) currentLink).getParent(); } }; } /** * @see TreeNodeValueModel#getChild(int) */ public TreeNodeValueModel getChild(int index) { return (TreeNodeValueModel) this.getChildrenModel().getItem(index); } /** * @see TreeNodeValueModel#childrenSize() */ public int childrenSize() { return this.getChildrenModel().size(); } /** * @see TreeNodeValueModel#indexOfChild(Object) */ public int indexOfChild(TreeNodeValueModel child) { ListValueModel children = this.getChildrenModel(); int size = children.size(); for (int i = 0; i < size; i++) { if (children.getItem(i) == child) { return i; } } return -1; } /** * @see TreeNodeValueModel#isLeaf() */ public boolean isLeaf() { return this.getChildrenModel().size() == 0; } // ********** standard methods ********** /** * We implement #equals(Object) so that TreePaths containing these nodes * will resolve properly when the nodes contain the same values. This is * necessary because nodes are dropped and rebuilt willy-nilly when dealing * with a sorted list of children; and this allows us to save and restore * a tree's expanded paths. The nodes in the expanded paths that are * saved before any modification (e.g. renaming a node) will be different * from the nodes in the tree's paths after the modification, if the modification * results in a possible change in the node sort order. -bjv * @see Object#equals(Object) */ public boolean equals(Object o) { if (o == null) { return false; } if (o.getClass() != this.getClass()) { return false; } AbstractTreeNodeValueModel other = (AbstractTreeNodeValueModel) o; return this.getValue().equals(other.getValue()); } /** * @see Object#hashCode() */ public int hashCode() { return this.getValue().hashCode(); } /** * @see org.eclipse.persistence.tools.workbench.utility.AbstractModel#toString(StringBuffer) */ public void toString(StringBuffer sb) { sb.append(this.getValue()); } }