View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    * 
9    *      http://www.apache.org/licenses/LICENSE-2.0
10   * 
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.log4j.lf5.viewer.categoryexplorer;
18  
19  import java.awt.AWTEventMulticaster;
20  import java.awt.event.ActionEvent;
21  import java.awt.event.ActionListener;
22  import java.util.Enumeration;
23  
24  import javax.swing.SwingUtilities;
25  import javax.swing.tree.DefaultTreeModel;
26  import javax.swing.tree.TreeNode;
27  import javax.swing.tree.TreePath;
28  
29  import org.apache.log4j.lf5.LogRecord;
30  
31  /**
32   * CategoryExplorerModel
33   *
34   * @author Michael J. Sikorsky
35   * @author Robert Shaw
36   * @author Brent Sprecher
37   * @author Richard Hurst
38   */
39  
40  // Contributed by ThoughtWorks Inc.
41  
42  public class CategoryExplorerModel extends DefaultTreeModel {
43    private static final long serialVersionUID = -3413887384316015901L;
44  
45    //--------------------------------------------------------------------------
46    //   Constants:
47    //--------------------------------------------------------------------------
48  
49    //--------------------------------------------------------------------------
50    //   Protected Variables:
51    //--------------------------------------------------------------------------
52  
53    protected boolean _renderFatal = true;
54    protected ActionListener _listener = null;
55    protected ActionEvent _event = new ActionEvent(this,
56        ActionEvent.ACTION_PERFORMED,
57        "Nodes Selection changed");
58  
59    //--------------------------------------------------------------------------
60    //   Private Variables:
61    //--------------------------------------------------------------------------
62  
63    //--------------------------------------------------------------------------
64    //   Constructors:
65    //--------------------------------------------------------------------------
66  
67    public CategoryExplorerModel(CategoryNode node) {
68      super(node);
69    }
70    //--------------------------------------------------------------------------
71    //   Public Methods:
72    //--------------------------------------------------------------------------
73  
74    public void addLogRecord(LogRecord lr) {
75      CategoryPath path = new CategoryPath(lr.getCategory());
76      addCategory(path); // create category path if it is new
77      CategoryNode node = getCategoryNode(path);
78      node.addRecord(); // update category node
79      if (_renderFatal && lr.isFatal()) {
80        TreeNode[] nodes = getPathToRoot(node);
81        int len = nodes.length;
82        CategoryNode parent;
83  
84        // i = 0 gives root node
85        // skip node and root, loop through "parents" in between
86        for (int i = 1; i < len - 1; i++) {
87          parent = (CategoryNode) nodes[i];
88          parent.setHasFatalChildren(true);
89          nodeChanged(parent);
90        }
91        node.setHasFatalRecords(true);
92        nodeChanged(node);
93      }
94    }
95  
96    public CategoryNode getRootCategoryNode() {
97      return (CategoryNode) getRoot();
98    }
99  
100   public CategoryNode getCategoryNode(String category) {
101     CategoryPath path = new CategoryPath(category);
102     return (getCategoryNode(path));
103   }
104 
105   /**
106    * returns null if no CategoryNode exists.
107    */
108   public CategoryNode getCategoryNode(CategoryPath path) {
109     CategoryNode root = (CategoryNode) getRoot();
110     CategoryNode parent = root; // Start condition.
111 
112     for (int i = 0; i < path.size(); i++) {
113       CategoryElement element = path.categoryElementAt(i);
114 
115       // If the two nodes have matching titles they are considered equal.
116       Enumeration children = parent.children();
117 
118       boolean categoryAlreadyExists = false;
119       while (children.hasMoreElements()) {
120         CategoryNode node = (CategoryNode) children.nextElement();
121         String title = node.getTitle().toLowerCase();
122 
123         String pathLC = element.getTitle().toLowerCase();
124         if (title.equals(pathLC)) {
125           categoryAlreadyExists = true;
126           // This is now the new parent node.
127           parent = node;
128           break; // out of the while, and back to the for().
129         }
130       }
131 
132       if (categoryAlreadyExists == false) {
133         return null; // Didn't find the Node.
134       }
135     }
136 
137     return (parent);
138   }
139 
140   /**
141    * @return true if all the nodes in the specified CategoryPath are
142    * selected.
143    */
144   public boolean isCategoryPathActive(CategoryPath path) {
145     CategoryNode root = (CategoryNode) getRoot();
146     CategoryNode parent = root; // Start condition.
147     boolean active = false;
148 
149     for (int i = 0; i < path.size(); i++) {
150       CategoryElement element = path.categoryElementAt(i);
151 
152       // If the two nodes have matching titles they are considered equal.
153       Enumeration children = parent.children();
154 
155       boolean categoryAlreadyExists = false;
156       active = false;
157 
158       while (children.hasMoreElements()) {
159         CategoryNode node = (CategoryNode) children.nextElement();
160         String title = node.getTitle().toLowerCase();
161 
162         String pathLC = element.getTitle().toLowerCase();
163         if (title.equals(pathLC)) {
164           categoryAlreadyExists = true;
165           // This is now the new parent node.
166           parent = node;
167 
168           if (parent.isSelected()) {
169             active = true;
170           }
171 
172           break; // out of the while, and back to the for().
173         }
174       }
175 
176       if (active == false || categoryAlreadyExists == false) {
177         return false;
178       }
179     }
180 
181     return (active);
182   }
183 
184 
185   /**
186    * <p>Method altered by Richard Hurst such that it returns the CategoryNode
187    * corresponding to the CategoryPath</p>
188    *
189    * @param path category path.
190    * @return CategoryNode
191    */
192   public CategoryNode addCategory(CategoryPath path) {
193     CategoryNode root = (CategoryNode) getRoot();
194     CategoryNode parent = root; // Start condition.
195 
196     for (int i = 0; i < path.size(); i++) {
197       CategoryElement element = path.categoryElementAt(i);
198 
199       // If the two nodes have matching titles they are considered equal.
200       Enumeration children = parent.children();
201 
202       boolean categoryAlreadyExists = false;
203       while (children.hasMoreElements()) {
204         CategoryNode node = (CategoryNode) children.nextElement();
205         String title = node.getTitle().toLowerCase();
206 
207         String pathLC = element.getTitle().toLowerCase();
208         if (title.equals(pathLC)) {
209           categoryAlreadyExists = true;
210           // This is now the new parent node.
211           parent = node;
212           break;
213         }
214       }
215 
216       if (categoryAlreadyExists == false) {
217         // We need to add the node.
218         CategoryNode newNode = new CategoryNode(element.getTitle());
219 
220         //This method of adding a new node cause parent roots to be
221         // collapsed.
222         //parent.add( newNode );
223         //reload(parent);
224 
225         // This doesn't force the nodes to collapse.
226         insertNodeInto(newNode, parent, parent.getChildCount());
227         refresh(newNode);
228 
229         // The newly added node is now the parent.
230         parent = newNode;
231 
232       }
233     }
234 
235     return parent;
236   }
237 
238   public void update(CategoryNode node, boolean selected) {
239     if (node.isSelected() == selected) {
240       return; // nothing was changed, nothing to do
241     }
242     // select parents or deselect children
243     if (selected) {
244       setParentSelection(node, true);
245     } else {
246       setDescendantSelection(node, false);
247     }
248   }
249 
250   public void setDescendantSelection(CategoryNode node, boolean selected) {
251     Enumeration descendants = node.depthFirstEnumeration();
252     CategoryNode current;
253     while (descendants.hasMoreElements()) {
254       current = (CategoryNode) descendants.nextElement();
255       // does the current node need to be changed?
256       if (current.isSelected() != selected) {
257         current.setSelected(selected);
258         nodeChanged(current);
259       }
260     }
261     notifyActionListeners();
262   }
263 
264   public void setParentSelection(CategoryNode node, boolean selected) {
265     TreeNode[] nodes = getPathToRoot(node);
266     int len = nodes.length;
267     CategoryNode parent;
268 
269     // i = 0 gives root node, i=len-1 gives this node
270     // skip the root node
271     for (int i = 1; i < len; i++) {
272       parent = (CategoryNode) nodes[i];
273       if (parent.isSelected() != selected) {
274         parent.setSelected(selected);
275         nodeChanged(parent);
276       }
277     }
278     notifyActionListeners();
279   }
280 
281 
282   public synchronized void addActionListener(ActionListener l) {
283     _listener = AWTEventMulticaster.add(_listener, l);
284   }
285 
286   public synchronized void removeActionListener(ActionListener l) {
287     _listener = AWTEventMulticaster.remove(_listener, l);
288   }
289 
290   public void resetAllNodeCounts() {
291     Enumeration nodes = getRootCategoryNode().depthFirstEnumeration();
292     CategoryNode current;
293     while (nodes.hasMoreElements()) {
294       current = (CategoryNode) nodes.nextElement();
295       current.resetNumberOfContainedRecords();
296       nodeChanged(current);
297     }
298   }
299 
300   /**
301    * <p>Returns the CategoryPath to the specified CategoryNode</p>
302    *
303    * @param node The target CategoryNode
304    * @return CategoryPath
305    */
306   public TreePath getTreePathToRoot(CategoryNode node) {
307     if (node == null) {
308       return null;
309     }
310     return (new TreePath(getPathToRoot(node)));
311   }
312 
313   //--------------------------------------------------------------------------
314   //   Protected Methods:
315   //--------------------------------------------------------------------------
316   protected void notifyActionListeners() {
317     if (_listener != null) {
318       _listener.actionPerformed(_event);
319     }
320   }
321 
322   /**
323    * Fires a nodechanged event on the SwingThread.
324    */
325   protected void refresh(final CategoryNode node) {
326     SwingUtilities.invokeLater(new Runnable() {
327       public void run() {
328         nodeChanged(node); // remind the tree to render the new node
329       }
330     });
331   }
332 
333   //--------------------------------------------------------------------------
334   //   Private Methods:
335   //--------------------------------------------------------------------------
336 
337   //--------------------------------------------------------------------------
338   //   Nested Top-Level Classes or Interfaces:
339   //--------------------------------------------------------------------------
340 
341 }
342 
343 
344 
345 
346 
347