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