View Javadoc

1   // Copyright (C) 2004 - 2009 Philip Aston
2   // All rights reserved.
3   //
4   // This file is part of The Grinder software distribution. Refer to
5   // the file LICENSE which is part of The Grinder distribution for
6   // licensing details. The Grinder distribution is available on the
7   // Internet at http://grinder.sourceforge.net/
8   //
9   // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
12  // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
13  // COPYRIGHT HOLDERS OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
14  // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
15  // (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
16  // SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
17  // HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
18  // STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
19  // ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
20  // OF THE POSSIBILITY OF SUCH DAMAGE.
21  
22  package net.grinder.console.swingui;
23  
24  import java.util.ArrayList;
25  import java.util.EventListener;
26  import java.util.HashMap;
27  import java.util.List;
28  import java.util.Map;
29  
30  import javax.swing.event.EventListenerList;
31  import javax.swing.event.TreeModelEvent;
32  import javax.swing.event.TreeModelListener;
33  import javax.swing.tree.TreeModel;
34  import javax.swing.tree.TreePath;
35  
36  
37  /**
38   * {@link TreeModel} that combines other tree models.
39   *
40   * <p>The composed {@link TreeModel} implementations must be aware of
41   * which nodes belong to them, and return null answers for nodes that
42   * don't belong to them.</p>
43   *
44   * @author Philip Aston
45   */
46  final class CompositeTreeModel implements TreeModel {
47  
48    private final List<DelegateWrapper> m_wrappers =
49      new ArrayList<DelegateWrapper>();
50  
51    private final EventListenerList m_listeners = new EventListenerList();
52  
53    private final Object m_rootNode = new Object();
54  
55    CompositeTreeModel() {
56    }
57  
58    void addTreeModel(TreeModel treeModel, boolean includeRoot) {
59  
60      final DelegateWrapper wrapper;
61  
62      if (includeRoot) {
63        wrapper = new RootWrapper(treeModel);
64      }
65      else {
66        wrapper = new FirstLevelWrapper(treeModel);
67      }
68  
69      final EventListener[] eventListeners =
70        m_listeners.getListeners(TreeModelListener.class);
71  
72      for (int i = 0; i < eventListeners.length; ++i) {
73        wrapper.addTreeModelListener((TreeModelListener)eventListeners[i]);
74      }
75  
76      m_wrappers.add(wrapper);
77    }
78  
79    public Object getRoot() {
80      return m_rootNode;
81    }
82  
83    public Object getChild(Object parent, int index) {
84      if (index < 0) {
85        return null;
86      }
87      else if (parent.equals(getRoot())) {
88        int base = 0;
89  
90        for (DelegateWrapper wrapper : m_wrappers) {
91          final int numberOfTopLevelNodes = wrapper.getNumberOfTopLevelNodes();
92  
93          if (index - base < numberOfTopLevelNodes) {
94            return wrapper.getTopLevelNode(index - base);
95          }
96  
97          base += numberOfTopLevelNodes;
98        }
99      }
100     else {
101       for (DelegateWrapper wrapper : m_wrappers) {
102         final Object delegateAnswer = wrapper.getChild(parent, index);
103 
104         if (delegateAnswer != null) {
105           return delegateAnswer;
106         }
107       }
108     }
109 
110     return null;
111   }
112 
113   public int getChildCount(Object parent) {
114     if (parent.equals(getRoot())) {
115       int answer = 0;
116 
117       for (DelegateWrapper wrapper : m_wrappers) {
118         answer += wrapper.getNumberOfTopLevelNodes();
119       }
120 
121       return answer;
122     }
123     else {
124       for (DelegateWrapper wrapper : m_wrappers) {
125         final int delegateAnswer = wrapper.getChildCount(parent);
126 
127         if (delegateAnswer != 0) {
128           return delegateAnswer;
129         }
130       }
131     }
132 
133     return 0;
134   }
135 
136   public int getIndexOfChild(Object parent, Object child) {
137 
138     if (parent == null || child == null) {
139       // The TreeModel Javadoc says we should do this.
140       return -1;
141     }
142 
143     if (parent.equals(getRoot())) {
144       int base = 0;
145 
146       for (DelegateWrapper wrapper : m_wrappers) {
147         final int delegateAnswer = wrapper.getIndexOfTopLevelNode(child);
148 
149         if (delegateAnswer != -1) {
150           return base + delegateAnswer;
151         }
152 
153         base += wrapper.getNumberOfTopLevelNodes();
154       }
155     }
156     else {
157       for (DelegateWrapper wrapper : m_wrappers) {
158         final int delegateAnswer = wrapper.getIndexOfChild(parent, child);
159 
160         if (delegateAnswer != -1) {
161           return delegateAnswer;
162         }
163       }
164     }
165 
166     return -1;
167   }
168 
169   public boolean isLeaf(Object node) {
170     for (DelegateWrapper wrapper : m_wrappers) {
171       if (wrapper.isLeaf(node)) {
172         return true;
173       }
174     }
175 
176     return false;
177   }
178 
179   public void addTreeModelListener(TreeModelListener listener) {
180     m_listeners.add(TreeModelListener.class, listener);
181 
182     for (DelegateWrapper wrapper : m_wrappers) {
183       wrapper.addTreeModelListener(listener);
184     }
185   }
186 
187   public void removeTreeModelListener(TreeModelListener listener) {
188     m_listeners.remove(TreeModelListener.class, listener);
189 
190     for (DelegateWrapper wrapper : m_wrappers) {
191       wrapper.removeTreeModelListener(listener);
192     }
193   }
194 
195   public void valueForPathChanged(TreePath path, Object newValue) {
196     // Do nothing.
197   }
198 
199   private abstract static class DelegateWrapper {
200     private final TreeModel m_model;
201     private final
202       Map<TreeModelListener, TreeModelListener> m_delegateListenerMap =
203         new HashMap<TreeModelListener, TreeModelListener>();
204 
205     protected DelegateWrapper(TreeModel model) {
206       m_model = model;
207     }
208 
209     public abstract Object getTopLevelNode(int i);
210 
211     public abstract int getNumberOfTopLevelNodes();
212 
213     public abstract int getIndexOfTopLevelNode(Object node);
214 
215     public final TreeModel getModel() {
216       return m_model;
217     }
218 
219     public final Object getChild(Object parent, int index) {
220       try {
221         return getModel().getChild(parent, index);
222       }
223       catch (ClassCastException e) {
224         return null;
225       }
226     }
227 
228     public int getChildCount(Object parent) {
229       try {
230         return getModel().getChildCount(parent);
231       }
232       catch (ClassCastException e) {
233         return 0;
234       }
235     }
236 
237     public int getIndexOfChild(Object parent, Object child) {
238       try {
239         return getModel().getIndexOfChild(parent, child);
240       }
241       catch (ClassCastException e) {
242         return -1;
243       }
244     }
245 
246     public final boolean isLeaf(Object node) {
247       try {
248         return getModel().isLeaf(node);
249       }
250       catch (ClassCastException e) {
251         return false;
252       }
253     }
254 
255     protected abstract TreeModelEvent mapTreeModelEvent(TreeModelEvent e);
256 
257     public void addTreeModelListener(final TreeModelListener listener) {
258 
259       final TreeModelListener delegateListener = new TreeModelListener() {
260           public void treeNodesChanged(TreeModelEvent e) {
261             listener.treeNodesChanged(mapTreeModelEvent(e));
262           }
263 
264           public void treeNodesInserted(TreeModelEvent e) {
265             listener.treeNodesInserted(mapTreeModelEvent(e));
266           }
267 
268           public void treeNodesRemoved(TreeModelEvent e) {
269             listener.treeNodesRemoved(mapTreeModelEvent(e));
270           }
271 
272           public void treeStructureChanged(TreeModelEvent e) {
273             listener.treeStructureChanged(mapTreeModelEvent(e));
274           }
275         };
276 
277       m_delegateListenerMap.put(listener, delegateListener);
278       getModel().addTreeModelListener(delegateListener);
279     }
280 
281     public void removeTreeModelListener(TreeModelListener listener) {
282       final TreeModelListener delegateListener =
283         m_delegateListenerMap.remove(listener);
284 
285       if (delegateListener != null) {
286         getModel().removeTreeModelListener(delegateListener);
287       }
288     }
289   }
290 
291   private final class RootWrapper extends DelegateWrapper {
292     public RootWrapper(TreeModel model) {
293       super(model);
294     }
295 
296     public Object getTopLevelNode(int i) {
297       return i == 0 ? getModel().getRoot() : null;
298     }
299 
300     public int getNumberOfTopLevelNodes() {
301       return 1;
302     }
303 
304     public int getIndexOfTopLevelNode(Object node) {
305       return node.equals(getModel().getRoot()) ? 0 : -1;
306     }
307 
308     protected TreeModelEvent mapTreeModelEvent(TreeModelEvent e) {
309 
310       final Object[] path = e.getPath();
311 
312       if (path.length > 0 && path[0].equals(getRoot())) {
313         return e;
314       }
315 
316       final Object[] newPath = new Object[path.length + 1];
317       System.arraycopy(path, 0, newPath, 1, path.length);
318       newPath[0] = getRoot();
319 
320       return new TreeModelEvent(this,
321                                 newPath,
322                                 e.getChildIndices(),
323                                 e.getChildren());
324     }
325   }
326 
327   private final class FirstLevelWrapper extends DelegateWrapper {
328     public FirstLevelWrapper(TreeModel model) {
329       super(model);
330     }
331 
332     public Object getTopLevelNode(int i) {
333       return super.getChild(getModel().getRoot(), i);
334     }
335 
336     public int getNumberOfTopLevelNodes() {
337       return super.getChildCount(getModel().getRoot());
338     }
339 
340     public int getIndexOfTopLevelNode(Object node) {
341       return super.getIndexOfChild(getModel().getRoot(), node);
342     }
343 
344     protected TreeModelEvent mapTreeModelEvent(TreeModelEvent e) {
345 
346       final Object[] path = e.getPath();
347 
348       if (path.length > 0) {
349         path[0] = getRoot();
350       }
351 
352       return
353         new TreeModelEvent(this, path, e.getChildIndices(), e.getChildren());
354     }
355   }
356 }