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 }