Skip to main content
summaryrefslogtreecommitdiffstats
blob: 72f255127c42d532f35dd9d9a7535b6adf0f729c (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
/*******************************************************************************
 * Copyright (c) 2007, 2016 Oracle. All rights reserved.
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v1.0, which accompanies this distribution
 * and is available at http://www.eclipse.org/legal/epl-v10.html.
 * 
 * Contributors:
 *     Oracle - initial API and implementation
 ******************************************************************************/
package org.eclipse.jpt.common.utility.internal.model.value;

import java.util.Iterator;
import java.util.List;
import org.eclipse.jpt.common.utility.internal.collection.ListTools;
import org.eclipse.jpt.common.utility.internal.iterator.IteratorTools;
import org.eclipse.jpt.common.utility.internal.model.AbstractModel;
import org.eclipse.jpt.common.utility.internal.model.ChangeSupport;
import org.eclipse.jpt.common.utility.internal.transformer.TransformerTools;
import org.eclipse.jpt.common.utility.model.listener.StateChangeListener;
import org.eclipse.jpt.common.utility.model.value.ListValueModel;
import org.eclipse.jpt.common.utility.model.value.TreeNodeValueModel;

/**
 * Subclasses need only implement the following methods:
 * 
 * #value()
 *	    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
 * 
 * #parent()
 *     return the parent of the node, which should be another
 *     TreeNodeValueModel
 * 
 * #childrenModel()
 *     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<V>
	extends AbstractModel
	implements TreeNodeValueModel<V>
{


	// ********** constructors **********
	
	/**
	 * Default constructor.
	 */
	protected AbstractTreeNodeValueModel() {
		super();
	}
	
	@Override
	protected ChangeSupport buildChangeSupport() {
		// this model fires *both* "value property change" and "state change" events...
//		return new SingleAspectChangeSupport(this, PropertyChangeListener.class, PropertyValueModel.VALUE);
		return super.buildChangeSupport();
	}


	// ********** extend AbstractModel implementation **********

	/**
	 * Clients should be adding both "state change" and "value property change"
	 * listeners.
	 */
	@Override
	public void addStateChangeListener(StateChangeListener listener) {
		if (this.hasNoStateChangeListeners()) {
			this.engageValue();
		}
		super.addStateChangeListener(listener);
	}

	/**
	 * Begin listening to the node's value's state. If the state of the node changes
	 * in a way that should be reflected in the tree, fire a "state change" event.
	 */
	protected abstract void engageValue();

	/**
	 * @see #addStateChangeListener(StateChangeListener)
	 */
	@Override
	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();


	// ********** WritablePropertyValueModel implementation **********
	
	public void setValue(V value) {
		throw new UnsupportedOperationException();
	}


	// ********** TreeNodeValueModel implementation **********
	
	@SuppressWarnings("unchecked")
	public TreeNodeValueModel<V>[] path() {
		List<TreeNodeValueModel<V>> path = ListTools.reverse(ListTools.arrayList(this.backPath()));
		return 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.
	 */
	@SuppressWarnings("unchecked")
	protected Iterator<TreeNodeValueModel<V>> backPath() {
		return IteratorTools.chainIterator(this, TransformerTools.nullCheck(PARENT_TRANSFORMER));
	}

	public TreeNodeValueModel<V> child(int index) {
		return this.childrenModel().get(index);
	}

	public int childrenSize() {
		return this.childrenModel().size();
	}

	public int indexOfChild(TreeNodeValueModel<V> child) {
		ListValueModel<TreeNodeValueModel<V>> children = this.childrenModel();
		int size = children.size();
		for (int i = 0; i < size; i++) {
			if (children.get(i) == child) {
				return i;
			}
		}
		return -1;
	}

	public boolean isLeaf() {
		return this.childrenModel().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
	 */
	@Override
	public boolean equals(Object o) {
		if (o == null) {
			return false;
		}
		if (o.getClass() != this.getClass()) {
			return false;
		}
		@SuppressWarnings("unchecked")
		AbstractTreeNodeValueModel<V> other = (AbstractTreeNodeValueModel<V>) o;
		return this.getValue().equals(other.getValue());
	}

	@Override
	public int hashCode() {
		return this.getValue().hashCode();
	}

	@Override
	public void toString(StringBuilder sb) {
		sb.append(this.getValue());
	}

}

Back to the top