001: /*
002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
003: *
004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
005: *
006: * The contents of this file are subject to the terms of either the GNU
007: * General Public License Version 2 only ("GPL") or the Common
008: * Development and Distribution License("CDDL") (collectively, the
009: * "License"). You may not use this file except in compliance with the
010: * License. You can obtain a copy of the License at
011: * http://www.netbeans.org/cddl-gplv2.html
012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
013: * specific language governing permissions and limitations under the
014: * License. When distributing the software, include this License Header
015: * Notice in each file and include the License file at
016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
017: * particular file as subject to the "Classpath" exception as provided
018: * by Sun in the GPL Version 2 section of the License file that
019: * accompanied this code. If applicable, add the following below the
020: * License Header, with the fields enclosed by brackets [] replaced by
021: * your own identifying information:
022: * "Portions Copyrighted [year] [name of copyright owner]"
023: *
024: * Contributor(s):
025: * The Original Software is NetBeans. The Initial Developer of the Original
026: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2006 Sun
027: * Microsystems, Inc. All Rights Reserved.
028: *
029: * If you wish your version of this file to be governed by only the CDDL
030: * or only the GPL Version 2, indicate your decision by adding
031: * "[Contributor] elects to include this software in this distribution
032: * under the [CDDL or GPL Version 2] license." If you do not indicate a
033: * single choice of license, a recipient has the option to distribute
034: * your version of this file under either the CDDL, the GPL Version 2 or
035: * to extend the choice of license to its licensees as provided above.
036: * However, if you add GPL Version 2 code and therefore, elected the GPL
037: * Version 2 license, then the option applies only if the new code is
038: * made subject to such option by the copyright holder.
039: */
040:
041: package org.netbeans.lib.profiler.ui.components.treetable;
042:
043: import org.netbeans.lib.profiler.results.CCTNode;
044: import org.netbeans.lib.profiler.ui.components.*;
045: import javax.swing.event.TreeModelEvent;
046: import javax.swing.event.TreeModelListener;
047: import javax.swing.table.DefaultTableModel;
048: import javax.swing.tree.TreePath;
049:
050: public abstract class AbstractTreeTableModel extends DefaultTableModel
051: implements TreeTableModel {
052: //~ Instance fields ----------------------------------------------------------------------------------------------------------
053:
054: /**
055: * The root of the tree.
056: */
057: protected CCTNode root;
058: protected boolean initialSortingOrder;
059: protected boolean supportsSorting;
060: protected int initialSortingColumn;
061:
062: //~ Constructors -------------------------------------------------------------------------------------------------------------
063:
064: /**
065: * Creates a new AbstractTreeTableModel which supports sorting.
066: */
067: public AbstractTreeTableModel(CCTNode root) {
068: this (root, false, -1, false);
069: }
070:
071: public AbstractTreeTableModel(CCTNode root, int sortingColumn,
072: boolean sortingOrder) {
073: this (root, true, sortingColumn, sortingOrder);
074: }
075:
076: public AbstractTreeTableModel(CCTNode root,
077: boolean supportsSorting, int sortingColumn,
078: boolean sortingOrder) {
079: super ();
080: this .root = root;
081: this .supportsSorting = supportsSorting;
082: this .initialSortingColumn = sortingColumn;
083: this .initialSortingOrder = sortingOrder;
084: }
085:
086: //~ Methods ------------------------------------------------------------------------------------------------------------------
087:
088: /**
089: * Returns the child at index 'num' of the given node.
090: * <p/>
091: * Although the method expects an Object because of the TreeModel contract,
092: * in reality it assumes the given node is a 'TreeTableNode'.
093: *
094: * @see javax.swing.tree.TreeModel#getChild(java.lang.Object, int)
095: */
096: public Object getChild(Object node, int num) {
097: return ((CCTNode) node).getChild(num);
098: }
099:
100: /**
101: * Returns the number of children a given node has.
102: * <p/>
103: * Although the method expects an Object because of the TreeModel contract,
104: * in reality it assumes the given node is a 'TreeTableNode'.
105: *
106: * @see javax.swing.tree.TreeModel#getChildCount(java.lang.Object)
107: */
108: public int getChildCount(Object node) {
109: return ((CCTNode) node).getNChildren();
110: }
111:
112: /**
113: * Returns the column class for column <code>column</code>. This is set
114: * in the constructor.
115: */
116: public abstract Class getColumnClass(int column);
117:
118: /**
119: * Returns the number of column names passed into the constructor.
120: */
121: public abstract int getColumnCount();
122:
123: /**
124: * Returns the column name passed into the constructor.
125: */
126: public abstract String getColumnName(int column);
127:
128: public String getColumnToolTipText(int column) {
129: return null;
130: }
131:
132: /**
133: * Returns the index of the child node in the parent node.
134: * <p/>
135: * Although the method expects Objects because of the TreeModel contract,
136: * it assumes both parent and child are TreeTableNodes.
137: */
138: public int getIndexOfChild(Object parent, Object child) {
139: if ((parent == null) || (child == null)) {
140: return -1;
141: }
142:
143: return ((CCTNode) parent).getIndexOfChild(child);
144: }
145:
146: /**
147: * This method should be overriden for TreeTableModel descendant which supports sorting.
148: *
149: * @param column The table column index
150: * @return Initial sorting for the specified column - if true, ascending, if false descending
151: */
152: public boolean getInitialSorting(int column) {
153: return false;
154: }
155:
156: public int getInitialSortingColumn() {
157: return initialSortingColumn;
158: }
159:
160: public boolean getInitialSortingOrder() {
161: return initialSortingOrder;
162: }
163:
164: /**
165: * Returns true when the given node is a 'leaf'.
166: * <p/>
167: * Although the method expects an Object because of the TreeModel contract,
168: * in reality it assumes the given node is a 'TreeTableNode'.
169: *
170: * @see javax.swing.tree.TreeModel#isLeaf(java.lang.Object)
171: */
172: public boolean isLeaf(Object node) {
173: return ((CCTNode) node).getNChildren() == 0;
174: }
175:
176: public void setRoot(Object root) {
177: this .root = (CCTNode) root;
178: }
179:
180: /**
181: * Returns the root node.
182: */
183: public Object getRoot() {
184: return root;
185: }
186:
187: /**
188: * Returns the value for the column <code>column</code> and object <code>node</code>.
189: * The return value is determined by invoking the method specified in
190: * constructor for the passed in column.
191: */
192: public abstract Object getValueAt(Object node, int column);
193:
194: /**
195: * Returns true if there is a setter method name for column <code>column</code>.
196: * This is set in the constructor.
197: */
198: public boolean isCellEditable(Object node, int column) {
199: return false;
200: }
201:
202: /**
203: * Builds the parents of the node up to and including the root node, where
204: * the original node is the last element in the returned array. The length
205: * of the returned array gives the node's depth in the tree.
206: *
207: * @param aNode the TreeNode to get the path for
208: */
209: public CCTNode[] getPathToRoot(CCTNode aNode) {
210: return getPathToRoot(aNode, 0);
211: }
212:
213: /**
214: * Sets the value to <code>aValue</code> for the object <code>node</code>
215: * in column <code>column</code>. This is done by using the setter
216: * method name, and coercing the passed in value to the specified type.
217: */
218:
219: // Note: This looks up the methods each time! This is rather inefficient;
220: // it should really be changed to cache matching methods/constructors
221: // based on <code>node</code>'s class, and <code>aValue</code>'s class.
222: public void setValueAt(Object aValue, Object node, int column) {
223: }
224:
225: /**
226: * Adds a listener for the TreeModelEvent posted after the tree changes.
227: */
228: public void addTreeModelListener(TreeModelListener l) {
229: listenerList.add(TreeModelListener.class, l);
230: }
231:
232: /**
233: * Notify all listeners that have registered interest for notification on
234: * this event type. The event instance is lazily created using the
235: * parameters passed into the fire method.
236: *
237: * @see javax.swing.event.EventListenerList
238: */
239: public void fireTreeNodesChanged(Object source, Object[] path,
240: int[] childIndices, Object[] children) {
241: // Guaranteed to return a non-null array
242: Object[] listeners = listenerList.getListenerList();
243: TreeModelEvent e = null;
244:
245: // Process the listeners last to first, notifying
246: // those that are interested in this event
247: for (int i = listeners.length - 2; i >= 0; i -= 2) {
248: if (listeners[i] == TreeModelListener.class) {
249: // Lazily create the event:
250: if (e == null) {
251: e = new TreeModelEvent(source, path, childIndices,
252: children);
253: }
254:
255: ((TreeModelListener) listeners[i + 1])
256: .treeNodesChanged(e);
257: }
258: }
259: }
260:
261: /**
262: * Notify all listeners that have registered interest for notification on
263: * this event type. The event instance is lazily created using the
264: * parameters passed into the fire method.
265: *
266: * @see javax.swing.event.EventListenerList
267: */
268: public void fireTreeNodesInserted(Object source, Object[] path,
269: int[] childIndices, Object[] children) {
270: // Guaranteed to return a non-null array
271: Object[] listeners = listenerList.getListenerList();
272: TreeModelEvent e = null;
273:
274: // Process the listeners last to first, notifying
275: // those that are interested in this event
276: for (int i = listeners.length - 2; i >= 0; i -= 2) {
277: if (listeners[i] == TreeModelListener.class) {
278: // Lazily create the event:
279: if (e == null) {
280: e = new TreeModelEvent(source, path, childIndices,
281: children);
282: }
283:
284: ((TreeModelListener) listeners[i + 1])
285: .treeNodesInserted(e);
286: }
287: }
288: }
289:
290: /**
291: * Notify all listeners that have registered interest for notification on
292: * this event type. The event instance is lazily created using the
293: * parameters passed into the fire method.
294: *
295: * @see javax.swing.event.EventListenerList
296: */
297: public void fireTreeNodesRemoved(Object source, Object[] path,
298: int[] childIndices, Object[] children) {
299: // Guaranteed to return a non-null array
300: Object[] listeners = listenerList.getListenerList();
301: TreeModelEvent e = null;
302:
303: // Process the listeners last to first, notifying
304: // those that are interested in this event
305: for (int i = listeners.length - 2; i >= 0; i -= 2) {
306: if (listeners[i] == TreeModelListener.class) {
307: // Lazily create the event:
308: if (e == null) {
309: e = new TreeModelEvent(source, path, childIndices,
310: children);
311: }
312:
313: ((TreeModelListener) listeners[i + 1])
314: .treeNodesRemoved(e);
315: }
316: }
317: }
318:
319: /**
320: * Notify all listeners that have registered interest for notification on
321: * this event type. The event instance is lazily created using the
322: * parameters passed into the fire method.
323: *
324: * @see javax.swing.event.EventListenerList
325: */
326: public void fireTreeStructureChanged(Object source, Object[] path,
327: int[] childIndices, Object[] children) {
328: // Guaranteed to return a non-null array
329: Object[] listeners = listenerList.getListenerList();
330: TreeModelEvent e = null;
331:
332: // Process the listeners last to first, notifying
333: // those that are interested in this event
334: for (int i = listeners.length - 2; i >= 0; i -= 2) {
335: if (listeners[i] == TreeModelListener.class) {
336: // Lazily create the event:
337: if (e == null) {
338: e = new TreeModelEvent(source, path, childIndices,
339: children);
340: }
341:
342: ((TreeModelListener) listeners[i + 1])
343: .treeStructureChanged(e);
344: }
345: }
346: }
347:
348: /**
349: * Removes a listener previously added with addTreeModelListener().
350: */
351: public void removeTreeModelListener(TreeModelListener l) {
352: listenerList.remove(TreeModelListener.class, l);
353: }
354:
355: /**
356: * This method should be overriden for TreeTableModel descendant which supports sorting.
357: *
358: * @param column The table column index
359: * @param order sorting for the specified column - if true, ascending, if false descending
360: */
361: public void sortByColumn(int column, boolean order) {
362: }
363:
364: public boolean supportsSorting() {
365: return supportsSorting;
366: }
367:
368: /**
369: * Overwrite if you are going to user editors in the JTree.
370: * <p/>
371: * The default implementation does nothing (dummy method).
372: *
373: * @see javax.swing.tree.DefaultTreeModel#valueForPathChanged(javax.swing.tree.TreePath, java.lang.Object)
374: */
375: public void valueForPathChanged(TreePath path, Object newValue) {
376: }
377:
378: /**
379: * Builds the parents of the node up to and including the root node, where
380: * the original node is the last element in the returned array. The length
381: * of the returned array gives the node's depth in the tree.
382: *
383: * @param aNode the TreeNode to get the path for
384: * @param depth an int giving the number of steps already taken towards the
385: * root (on recursive calls), used to size the returned array
386: * @return an array of TreeNodes giving the path from the root to the
387: * specified node
388: */
389: private CCTNode[] getPathToRoot(CCTNode aNode, int depth) {
390: CCTNode[] retNodes;
391:
392: // This method recurses, traversing towards the root in order
393: // size the array. On the way back, it fills in the nodes,
394: // starting from the root and working back to the original node.
395:
396: /*
397: * Check for null, in case someone passed in a null node, or
398: */
399: if (aNode == null) {
400: if (depth == 0) {
401: return null;
402: } else {
403: retNodes = new CCTNode[depth];
404: }
405: } else {
406: depth++;
407:
408: if (aNode == root) {
409: retNodes = new CCTNode[depth];
410: } else {
411: retNodes = getPathToRoot((CCTNode) aNode.getParent(),
412: depth);
413: }
414:
415: retNodes[retNodes.length - depth] = aNode;
416: }
417:
418: return retNodes;
419: }
420: }
|