001: package com.xoetrope.swing.treetable;
002:
003: import java.util.Vector;
004: import java.util.Hashtable;
005: import javax.swing.table.TableModel;
006: import net.xoetrope.optional.data.sql.DatabaseRowModel;
007: import net.xoetrope.xui.XProjectManager;
008: import net.xoetrope.xui.data.XModel;
009:
010: /**
011: * A tree node wrapper for an XModel node
012: *
013: * <p> Copyright (c) Xoetrope Ltd., 2001-2006, This software is licensed under
014: * the GNU Public License (GPL), please see license.txt for more details. If
015: * you make commercial use of this software you must purchase a commercial
016: * license from Xoetrope.</p>
017: * <p> $Revision: 1.8 $</p>
018: */
019: public class XTreeTableModelNode extends AbstractTreeTableModel {
020: /**
021: * the table listeners
022: */
023: Vector listeners;
024:
025: private TableModel tableModel;
026:
027: protected static XNode[] EMPTY_CHILDREN = new XNode[0];
028:
029: // The the returned file length for directories.
030: public static final Integer ZERO = new Integer(0);
031:
032: /**
033: * Creates a XTreeTableModelNode rooted at File.separator, which is usually
034: * the root of the file system. This does not load it, you should invoke
035: * <code>reloadChildren</code> with the root to start loading.
036: */
037: public XTreeTableModelNode() {
038: this (XProjectManager.getCurrentProject().getModel());
039: }
040:
041: /**
042: * Creates a XTreeTableModelNode with the root being <code>rootPath</code>.
043: * This does not load it, you should invoke
044: * <code>reloadChildren</code> with the root to start loading.
045: */
046: public XTreeTableModelNode(XModel rootModel) {
047: super (null);
048: root = new XNode(rootModel, 0);
049: }
050:
051: public XTreeTableModelNode(XModel rootModel, TableModel aTableModel) {
052: super (null);
053: root = new XNode(rootModel, 0);
054: tableModel = aTableModel;
055: }
056:
057: //
058: // The TreeModel interface
059: //
060:
061: /**
062: * Returns the number of children of <code>node</code>.
063: */
064: public int getChildCount(Object node) {
065: Object[] children = getChildren(node);
066: return (children == null) ? 0 : children.length;
067: }
068:
069: /**
070: * Returns the child of <code>node</code> at index <code>i</code>.
071: */
072: public Object getChild(Object node, int i) {
073: return getChildren(node)[i];
074: }
075:
076: /**
077: * Returns true if the passed in object represents a leaf, false
078: * otherwise.
079: */
080: public boolean isLeaf(Object node) {
081: return ((XNode) node).isLeaf();
082: }
083:
084: //
085: // The TreeTableNode interface.
086: //
087:
088: /**
089: * Return the number of columns.
090: */
091: public int getColumnCount() {
092: // return cNames.length;
093: XModel model = ((XNode) root).getModel();
094: return model.getNumAttributes();
095: }
096:
097: /**
098: * Returns the name for a particular column.
099: */
100: public String getColumnName(int columnIndex) {
101: //return cNames[column];
102: XModel model = ((XNode) root).getModel();
103: return model.getAttribName(columnIndex);
104: }
105:
106: /**
107: * Returns the class for the particular column.
108: */
109: public Class getColumnClass(int column) {
110: if (column == 0)
111: return TreeTableModel.class;
112: else if (tableModel != null)
113: return tableModel.getColumnClass(column);
114: else
115: return String.class;
116: }
117:
118: /**
119: * Returns the value of the particular column.
120: */
121: public Object getValueAt(Object node, int columnIndex) {
122: XModel model = ((XNode) node).getModel();
123: return model.getAttribValueAsString(columnIndex);
124: }
125:
126: public void setValueAt(Object value, Object node, int columnIndex) {
127: XModel model = ((XNode) node).getModel();
128: model.setAttribValue(columnIndex, value);
129: }
130:
131: public boolean isCellEditable(Object node, int columnIndex) {
132: if (tableModel != null)
133: return tableModel.isCellEditable(0, columnIndex);
134: else
135: return super .isCellEditable(node, columnIndex);
136: }
137:
138: //
139: // Some convenience methods.
140: //
141:
142: /**
143: * Reloads the children of the specified node.
144: * @param level the depth of the tree
145: */
146: public void reloadChildren(Object node) {
147: nodeLoader((XNode) node);
148: }
149:
150: /**
151: * Returns the path <code>node</code> represents.
152: */
153: public String getPath(Object node) {
154: return ((XNode) node).getModel().getAttribValueAsString(0);
155: }
156:
157: protected XModel getModel(Object node) {
158: XNode XNode = ((XNode) node);
159: return XNode.getModel();
160: }
161:
162: protected Object[] getChildren(Object node) {
163: XNode xnode = ((XNode) node);
164: return xnode.getChildren();
165: }
166:
167: /**
168: * A XNode is a derivative of the File class - though we delegate to
169: * the File object rather than subclassing it. It is used to maintain a
170: * cache of a directory's children and therefore avoid repeated access
171: * to the underlying file system during rendering.
172: */
173: public class XNode {
174: /** java.io.File the receiver represents. */
175: protected XModel model;
176:
177: /** Parent XNode of the receiver. */
178: private XNode parent;
179:
180: /** Children of the receiver. */
181: protected XNode[] children;
182:
183: /** Path of the receiver. */
184: //protected String canonicalPath;
185: protected int level;
186:
187: protected XNode(XModel file, int this Level) {
188: this (null, file, this Level);
189: }
190:
191: protected XNode(XNode parent, XModel model, int this Level) {
192: this .parent = parent;
193: this .model = model;
194: this .level = this Level;
195: }
196:
197: /**
198: * Returns the the string to be used to display this leaf in the JTree.
199: */
200: public String toString() {
201: try {
202: return model.getAttribValueAsString(0);
203: } catch (Exception e) {
204: }
205: return null;
206: }
207:
208: /**
209: * Returns the java.io.File the receiver represents.
210: */
211: public XModel getModel() {
212: return model;
213: }
214:
215: /**
216: * Returns the parent of the receiver.
217: */
218: public XNode getParent() {
219: return parent;
220: }
221:
222: /**
223: * Returns true if the receiver represents a leaf, that is it is
224: * isn't a directory.
225: */
226: // public boolean isLeaf()
227: // {
228: // return (( model.getNumChildren() == 0 ) || ( children == null ) || ( children.length == 0 ));
229: // }
230: public boolean isLeaf() {
231: if (children == null)
232: children = createChildren();
233: return ((model.getNumChildren() == 0) || (children == null) || (children.length == 0));
234: }
235:
236: /**
237: * Loads the children, caching the results in the children
238: * instance variable.
239: */
240: protected XNode[] getChildren() {
241: if (children == null)
242: children = createChildren();
243: return children;
244: }
245:
246: /**
247: * Recursively loads all the children of the receiver.
248: */
249: protected void loadChildren() {
250: children = createChildren();
251: for (int counter = children.length - 1; counter >= 0; counter--) {
252: Thread.yield(); // Give the GUI CPU time to draw itself.
253: if (!children[counter].isLeaf()) {
254: children[counter].loadChildren();
255: }
256: }
257: }
258:
259: /**
260: * Loads the children of of the receiver.
261: */
262: protected XNode[] createChildren() {
263: XNode[] retArray = null;
264: if (model instanceof DatabaseRowModel)
265: return null;
266:
267: int numChildren = model.getNumChildren();
268: if (level == 0) {
269: // Storage for the group names, only for searching
270: Hashtable groupNames = new Hashtable();
271:
272: // Storage for the group elements in the order they were found
273: Vector groups = new Vector();
274:
275: // Storage for the latest/most recent group
276: Vector groupChildren = null;
277: XNode groupNode = null;
278: String lastGroupName = null;
279: for (int i = 0; i < numChildren; i++) {
280: XModel childModel = ((XModel) model.get(i));
281: String groupName = childModel
282: .getAttribValueAsString(0);
283: XNode searchNode = (XNode) groupNames
284: .get(groupName);
285: if ((searchNode == null)
286: || !groupName.equals(lastGroupName)) {
287: // Check that it's not the first pass and that the group is not empty
288: if ((groupNode != null)
289: && (groupChildren != null)
290: && (groupChildren.size() > 0)) {
291: int numKids = groupChildren.size();
292: retArray = new XNode[numKids];
293: for (int j = 0; j < numKids; j++)
294: retArray[j] = (XNode) groupChildren
295: .elementAt(j);
296: groupNode.setChildren(retArray, false);
297: }
298: groupChildren = new Vector();
299: lastGroupName = groupName;
300: groupNode = new XNode(childModel, level + 1);
301: groupNames.put(groupName, groupNode);
302: groups.add(groupNode);
303: } else {
304: // Store the children
305: groupChildren.add(new XNode(childModel,
306: level + 2));
307: }
308: }
309:
310: // Add the children of the last node
311: if ((groupChildren != null)
312: && (groupChildren.size() > 0)) {
313: int numKids = groupChildren.size();
314: retArray = new XNode[numKids];
315: for (int j = 0; j < numKids; j++)
316: retArray[j] = (XNode) groupChildren
317: .elementAt(j);
318: groupNode.setChildren(retArray, false);
319: }
320:
321: if (groups.size() > 0) {
322: int numKids = groups.size();
323: retArray = new XNode[numKids];
324: for (int i = 0; i < numKids; i++)
325: retArray[i] = (XNode) groups.elementAt(i);
326: return retArray;
327: }
328: }
329:
330: retArray = new XNode[numChildren];
331: for (int i = 0; i < numChildren; i++)
332: retArray[i] = new XNode(model.get(i), level + 1);
333:
334: return retArray;
335: }
336:
337: /**
338: * Gets the path from the root to the receiver.
339: */
340: public XNode[] getPath() {
341: return getPathToRoot(this , 0);
342: }
343:
344: protected XNode[] getPathToRoot(XNode aNode, int depth) {
345: XNode[] retNodes;
346:
347: if (aNode == null) {
348: if (depth == 0)
349: return null;
350: else
351: retNodes = new XNode[depth];
352: } else {
353: depth++;
354: retNodes = getPathToRoot(aNode.getParent(), depth);
355: retNodes[retNodes.length - depth] = aNode;
356: }
357: return retNodes;
358: }
359:
360: /**
361: * Sets the children of the receiver, updates the total size,
362: * and if generateEvent is true a tree structure changed event
363: * is created.
364: */
365: protected void setChildren(XNode[] newChildren,
366: boolean generateEvent) {
367: children = newChildren;
368: if (generateEvent) {
369: XNode[] path = getPath();
370: fireTreeStructureChanged(XTreeTableModelNode.this ,
371: path, null, null);
372: }
373: }
374:
375: /**
376: * Can be invoked when a node has changed, will create the
377: * appropriate event.
378: */
379: protected void nodeChanged() {
380: XNode parent = getParent();
381:
382: if (parent != null) {
383: XNode[] path = parent.getPath();
384: int[] index = { getIndexOfChild(parent, this ) };
385: Object[] children = { this };
386:
387: fireTreeNodesChanged(XTreeTableModelNode.this , path,
388: index, children);
389: }
390: }
391: }
392:
393: public void nodeLoader(XNode node) {
394: node.setChildren(node.createChildren(), true);
395:
396: XNode[] children = node.getChildren();
397:
398: for (int counter = children.length - 1; counter >= 0; counter--) {
399: if (!children[counter].isLeaf())
400: loadChildren(children[counter]);
401: }
402: node.setChildren(node.getChildren(), true);
403: }
404:
405: protected void loadChildren(XNode node) {
406: if (!node.isLeaf()) {
407: final XNode[] children = node.createChildren();
408: if (children == null)
409: return;
410:
411: for (int counter = children.length - 1; counter >= 0; counter--) {
412: if (!children[counter].isLeaf())
413: children[counter].loadChildren();
414: }
415: node.setChildren(children, true);
416: node.nodeChanged();
417: }
418: }
419: }
|