001 /*
002 * Copyright 2002-2004 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005 * use this file except in compliance with the License. You may obtain a copy of
006 * the License at
007 *
008 * http://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
012 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013 * License for the specific language governing permissions and limitations under
014 * the License.
015 */
016 package org.springframework.richclient.tree;
017
018 import java.beans.PropertyChangeEvent;
019 import java.beans.PropertyChangeListener;
020
021 import javax.swing.event.EventListenerList;
022 import javax.swing.event.TreeModelEvent;
023 import javax.swing.event.TreeModelListener;
024 import javax.swing.tree.TreeModel;
025
026 import org.springframework.binding.value.ValueModel;
027
028 /**
029 *
030 * @author Keith Donald
031 */
032 public abstract class AbstractTreeModel implements TreeModel {
033
034 private final RootHolderChangeHandler rootHolderChangeHandler = new RootHolderChangeHandler();
035
036 private final EventListenerList listenerList = new EventListenerList();
037
038 private Object root;
039
040 private ValueModel rootHolder;
041
042 protected AbstractTreeModel(Object root) {
043 this.root = root;
044 }
045
046 protected AbstractTreeModel(ValueModel rootHolder) {
047 this.rootHolder = rootHolder;
048 this.root = rootHolder.getValue();
049 this.rootHolder.addValueChangeListener(rootHolderChangeHandler);
050 }
051
052 /*
053 * @see javax.swing.tree.TreeModel#getRoot()
054 */
055 public Object getRoot() {
056 return root;
057 }
058
059 protected ValueModel getRootHolder() {
060 return rootHolder;
061 }
062
063 /**
064 * Adds a listener for the TreeModelEvent posted after the tree changes.
065 *
066 * @see #removeTreeModelListener
067 * @param l
068 * the listener to add
069 */
070 public void addTreeModelListener(TreeModelListener l) {
071 listenerList.add(TreeModelListener.class, l);
072 }
073
074 /**
075 * Removes a listener previously added with <B>addTreeModelListener() </B>.
076 *
077 * @see #addTreeModelListener
078 * @param l
079 * the listener to remove
080 */
081 public void removeTreeModelListener(TreeModelListener l) {
082 listenerList.remove(TreeModelListener.class, l);
083 }
084
085 /**
086 * Returns an array of all the tree model listeners registered on this
087 * model.
088 *
089 * @return all of this model's <code>TreeModelListener</code> s or an
090 * empty array if no tree model listeners are currently registered
091 *
092 * @see #addTreeModelListener
093 * @see #removeTreeModelListener
094 */
095 public TreeModelListener[] getTreeModelListeners() {
096 return (TreeModelListener[]) listenerList.getListeners(TreeModelListener.class);
097 }
098
099 /**
100 * Notifies all listeners that have registered interest for notification on
101 * this event type. The event instance is lazily created using the
102 * parameters passed into the fire method.
103 *
104 * @param source
105 * the node being changed
106 * @param path
107 * the path to the root node
108 * @param childIndices
109 * the indices of the changed elements
110 * @param children
111 * the changed elements
112 * @see EventListenerList
113 */
114 protected void fireTreeNodesChanged(Object[] path, int[] childIndices, Object[] children) {
115 // Guaranteed to return a non-null array
116 Object[] listeners = listenerList.getListenerList();
117 TreeModelEvent e = null;
118 // Process the listeners last to first, notifying
119 // those that are interested in this event
120 for (int i = listeners.length - 2; i >= 0; i -= 2) {
121 if (listeners[i] == TreeModelListener.class) {
122 // Lazily create the event:
123 if (e == null) {
124 e = new TreeModelEvent(this, path, childIndices, children);
125 }
126 ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e);
127 }
128 }
129 }
130
131 /**
132 * Notifies all listeners that have registered interest for notification on
133 * this event type. The event instance is lazily created using the
134 * parameters passed into the fire method.
135 *
136 * @param source
137 * the node where new elements are being inserted
138 * @param path
139 * the path to the root node
140 * @param childIndices
141 * the indices of the new elements
142 * @param children
143 * the new elements
144 * @see EventListenerList
145 */
146 protected void fireTreeNodesInserted(Object[] path, int[] childIndices, Object[] children) {
147 // Guaranteed to return a non-null array
148 Object[] listeners = listenerList.getListenerList();
149 TreeModelEvent e = null;
150 // Process the listeners last to first, notifying
151 // those that are interested in this event
152 for (int i = listeners.length - 2; i >= 0; i -= 2) {
153 if (listeners[i] == TreeModelListener.class) {
154 // Lazily create the event:
155 if (e == null) {
156 e = new TreeModelEvent(this, path, childIndices, children);
157 }
158 ((TreeModelListener) listeners[i + 1]).treeNodesInserted(e);
159 }
160 }
161 }
162
163 /**
164 * Notifies all listeners that have registered interest for notification on
165 * this event type. The event instance is lazily created using the
166 * parameters passed into the fire method.
167 *
168 * @param source
169 * the node where elements are being removed
170 * @param path
171 * the path to the root node
172 * @param childIndices
173 * the indices of the removed elements
174 * @param children
175 * the removed elements
176 * @see EventListenerList
177 */
178 protected void fireTreeNodesRemoved(Object[] path, int[] childIndices, Object[] children) {
179 // Guaranteed to return a non-null array
180 Object[] listeners = listenerList.getListenerList();
181 TreeModelEvent e = null;
182 // Process the listeners last to first, notifying
183 // those that are interested in this event
184 for (int i = listeners.length - 2; i >= 0; i -= 2) {
185 if (listeners[i] == TreeModelListener.class) {
186 // Lazily create the event:
187 if (e == null) {
188 e = new TreeModelEvent(this, path, childIndices, children);
189 }
190 ((TreeModelListener) listeners[i + 1]).treeNodesRemoved(e);
191 }
192 }
193 }
194
195 /**
196 * Notifies all listeners that have registered interest for notification on
197 * this event type. The event instance is lazily created using the
198 * parameters passed into the fire method.
199 *
200 * @param source
201 * the node where the tree model has changed
202 * @param path
203 * the path to the root node
204 * @param childIndices
205 * the indices of the affected elements
206 * @param children
207 * the affected elements
208 * @see EventListenerList
209 */
210 protected void fireTreeStructureChanged(Object[] path, int[] childIndices, Object[] children) {
211 // Guaranteed to return a non-null array
212 Object[] listeners = listenerList.getListenerList();
213 TreeModelEvent e = null;
214 // Process the listeners last to first, notifying
215 // those that are interested in this event
216 for (int i = listeners.length - 2; i >= 0; i -= 2) {
217 if (listeners[i] == TreeModelListener.class) {
218 // Lazily create the event:
219 if (e == null) {
220 e = new TreeModelEvent(this, path, childIndices, children);
221 }
222 ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e);
223 }
224 }
225 }
226
227 /**
228 * The only event raised by this model is TreeStructureChanged with the root
229 * as path, i.e. the whole tree has changed.
230 */
231 protected void fireRootNodeChanged(Object previousRoot) {
232 // Guaranteed to return a non-null array
233 Object[] listeners = listenerList.getListenerList();
234 TreeModelEvent e = null;
235 // Process the listeners last to first, notifying
236 // those that are interested in this event
237 for (int i = listeners.length - 2; i >= 0; i -= 2) {
238 if (listeners[i] == TreeModelListener.class) {
239 // Lazily create the event:
240 if (e == null) {
241 e = new TreeModelEvent(this, new Object[] { previousRoot });
242 }
243 ((TreeModelListener) listeners[i + 1]).treeNodesChanged(e);
244 }
245 }
246 }
247
248 /**
249 * The only event raised by this model is TreeStructureChanged with the root
250 * as path, i.e. the whole tree has changed.
251 */
252 protected void fireRootTreeStructureChanged(Object previousRoot) {
253 // Guaranteed to return a non-null array
254 Object[] listeners = listenerList.getListenerList();
255 TreeModelEvent e = null;
256 // Process the listeners last to first, notifying
257 // those that are interested in this event
258 for (int i = listeners.length - 2; i >= 0; i -= 2) {
259 if (listeners[i] == TreeModelListener.class) {
260 // Lazily create the event:
261 if (e == null) {
262 e = new TreeModelEvent(this, new Object[] { previousRoot });
263 }
264 ((TreeModelListener) listeners[i + 1]).treeStructureChanged(e);
265 }
266 }
267 }
268
269 protected void fireTreeNodeRemoved(Object[] path, int index, Object child) {
270 fireTreeNodesRemoved(path, new int[] { index }, new Object[] { child });
271 }
272
273 protected void fireTreeNodeInserted(Object[] path, int index, Object child) {
274 fireTreeNodesInserted(path, new int[] { index }, new Object[] { child });
275 }
276
277 protected void fireTreeStructureChanged(Object[] path, int index, Object child) {
278 fireTreeStructureChanged(path, new int[] { index }, new Object[] { child });
279 }
280
281 protected void fireTreeNodeChanged(Object[] path, int index, Object child) {
282 fireTreeNodesChanged(path, new int[] { index }, new Object[] { child });
283 }
284
285 private class RootHolderChangeHandler implements PropertyChangeListener {
286 public void propertyChange(PropertyChangeEvent evt) {
287 fireRootTreeStructureChanged(root);
288 root = AbstractTreeModel.this.rootHolder.getValue();
289 }
290 }
291 }