001: /*
002: * Copyright (C) 2006 Methodhead Software LLC. All rights reserved.
003: *
004: * This file is part of TransferCM.
005: *
006: * TransferCM is free software; you can redistribute it and/or modify it under the
007: * terms of the GNU General Public License as published by the Free Software
008: * Foundation; either version 2 of the License, or (at your option) any later
009: * version.
010: *
011: * TransferCM is distributed in the hope that it will be useful, but WITHOUT ANY
012: * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
013: * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
014: * details.
015: *
016: * You should have received a copy of the GNU General Public License along with
017: * TransferCM; if not, write to the Free Software Foundation, Inc., 51 Franklin St,
018: * Fifth Floor, Boston, MA 02110-1301 USA
019: */
020:
021: package com.methodhead.tree;
022:
023: import javax.swing.tree.DefaultMutableTreeNode;
024: import java.util.Enumeration;
025: import com.methodhead.MhfException;
026: import java.util.Collections;
027: import java.util.Iterator;
028: import java.util.List;
029: import java.util.ArrayList;
030: import java.util.Comparator;
031:
032: /**
033: * A tree of <tt>DefaultMutableTreeNode</tt>s. This class methods to manage
034: * nodes in the tree.
035: */
036: public class Tree {
037:
038: // constructors /////////////////////////////////////////////////////////////
039:
040: // constants ////////////////////////////////////////////////////////////////
041:
042: public static final String POSITION_UNDER = "POSITION_UNDER";
043: public static final String POSITION_AFTER = "POSITION_AFTER";
044: public static final String POSITION_BEFORE = "POSITION_BEFORE";
045:
046: public static final String INVALIDMOVE_CANNOTMOVEROOT = "INVALIDMOVE_CANNOTMOVEROOT";
047: public static final String INVALIDMOVE_CANNOTMOVEBEFOREROOT = "INVALIDMOVE_CANNOTMOVEBEFOREROOT";
048: public static final String INVALIDMOVE_CANNOTMOVEAFTERROOT = "INVALIDMOVE_CANNOTMOVEAFTERROOT";
049: public static final String INVALIDMOVE_CANNOTMOVENEARSELF = "INVALIDMOVE_CANNOTMOVENEARSELF";
050: public static final String INVALIDMOVE_CANNOTMOVEUNDERSELF = "INVALIDMOVE_CANNOTMOVEUNDERSELF";
051:
052: public static final String VALIDMOVE = "VALIDMOVE";
053:
054: // classes //////////////////////////////////////////////////////////////////
055:
056: // methods //////////////////////////////////////////////////////////////////
057:
058: /**
059: * Returns the root of the tree or <tt>null</tt> if there is none.
060: */
061: public DefaultMutableTreeNode getRoot() {
062: return root_;
063: }
064:
065: /**
066: * Sets the root of the tree; note: any existing nodes will be lost.
067: */
068: public void setRoot(DefaultMutableTreeNode link) {
069: root_ = link;
070: }
071:
072: /**
073: * Inserts <tt>node</tt> under <tt>dest</tt>.
074: */
075: public void insertUnder(DefaultMutableTreeNode dest,
076: DefaultMutableTreeNode node) {
077:
078: dest.add(node);
079: }
080:
081: /**
082: * Inserts <tt>node</tt> after <tt>dest</tt>, throwing an
083: * exception if <tt>dest</tt> is the root.
084: */
085: public void insertAfter(DefaultMutableTreeNode dest,
086: DefaultMutableTreeNode node) {
087:
088: if (dest == root_)
089: throw new MhfException("Can't insert after the root node.");
090:
091: DefaultMutableTreeNode p = (DefaultMutableTreeNode) dest
092: .getParent();
093: p.insert(node, p.getIndex(dest) + 1);
094: }
095:
096: /**
097: * Inserts <tt>node</tt> before <tt>dest</tt>, throwing an
098: * exception if <tt>dest</tt> is the root.
099: */
100: public void insertBefore(DefaultMutableTreeNode dest,
101: DefaultMutableTreeNode node) {
102:
103: if (dest == root_)
104: throw new MhfException("Can't insert before the root node.");
105:
106: DefaultMutableTreeNode p = (DefaultMutableTreeNode) dest
107: .getParent();
108: p.insert(node, p.getIndex(dest));
109: }
110:
111: /**
112: * Removes <tt>node</tt> and all of its children from the tree.
113: */
114: public void remove(DefaultMutableTreeNode node) {
115:
116: if (node == root_)
117: root_ = null;
118: else
119: node.removeFromParent();
120:
121: }
122:
123: /**
124: * Removes <tt>node</tt>, adding its child nodes to the parent node. If
125: * <tt>node</tt> is the root, its first child is promoted to root, adding its
126: * children after the root's remaining children.
127: */
128: public void removeAndPromoteChildren(DefaultMutableTreeNode node) {
129:
130: DefaultMutableTreeNode tmp = null;
131: DefaultMutableTreeNode tmp2 = null;
132:
133: if (node == root_) {
134: if (root_.getChildCount() == 0)
135: root_ = null;
136:
137: else if (root_.getChildCount() == 1) {
138: tmp = (DefaultMutableTreeNode) root_.getChildAt(0);
139: root_.remove(tmp);
140: root_ = tmp;
141: } else {
142: tmp = (DefaultMutableTreeNode) root_.getChildAt(0);
143: root_.remove(tmp);
144:
145: for (int i = root_.getChildCount() - 1; i >= 0; i--) {
146: tmp2 = (DefaultMutableTreeNode) root_.getChildAt(i);
147: root_.remove(tmp2);
148: tmp.insert(tmp2, 0);
149: }
150:
151: root_ = tmp;
152: }
153: } else {
154: for (int i = 0; i < node.getChildCount(); i++) {
155: ((DefaultMutableTreeNode) node.getParent())
156: .add((DefaultMutableTreeNode) node
157: .getChildAt(i));
158: }
159:
160: node.removeFromParent();
161: }
162: }
163:
164: /**
165: * Confirms if <tt>dest</tt> can be positioned <tt>position</tt> (one of the
166: * <tt>POSITION_</tt> constants) near <tt>node</tt> successfully. If so,
167: * <tt>VALIDMOVE</tt> is returned, otherwise on of the <tt>INVALIDMOVE_</tt>
168: * constants is returned.
169: */
170: public String validateMove(DefaultMutableTreeNode node,
171: DefaultMutableTreeNode dest, String position) {
172:
173: if (node == root_)
174: return INVALIDMOVE_CANNOTMOVEROOT;
175:
176: if ((dest == root_) && POSITION_BEFORE.equals(position))
177: return INVALIDMOVE_CANNOTMOVEBEFOREROOT;
178:
179: if ((dest == root_) && POSITION_AFTER.equals(position))
180: return INVALIDMOVE_CANNOTMOVEAFTERROOT;
181:
182: if (dest == node)
183: return INVALIDMOVE_CANNOTMOVENEARSELF;
184:
185: if (dest.isNodeAncestor(node))
186: return INVALIDMOVE_CANNOTMOVEUNDERSELF;
187:
188: return VALIDMOVE;
189: }
190:
191: /**
192: * Moves <tt>node</tt> under <tt>dest</tt>. An exception is thrown if
193: * <tt>node</tt> is an ancestor of <tt>dest</tt>.
194: */
195: public void moveUnder(DefaultMutableTreeNode dest,
196: DefaultMutableTreeNode node) {
197:
198: if (dest.isNodeAncestor(node))
199: throw new MhfException("Can't move node under itself.");
200:
201: node.removeFromParent();
202: dest.add(node);
203: }
204:
205: /**
206: * Moves <tt>node</tt> after <tt>dest</tt>. An exception is thrown if
207: * <tt>node</tt> is an ancestor of <tt>dest</tt>, or if <tt>dest</tt> is the
208: * root.
209: */
210: public void moveAfter(DefaultMutableTreeNode dest,
211: DefaultMutableTreeNode node) {
212:
213: if (dest == root_)
214: throw new MhfException("Can't move node after root.");
215:
216: if (dest.isNodeAncestor(node))
217: throw new MhfException("Can't move node under itself.");
218:
219: node.removeFromParent();
220: insertAfter(dest, node);
221: }
222:
223: /**
224: * Moves <tt>node</tt> after <tt>dest</tt>. An exception is thrown if
225: * <tt>node</tt> is an ancestor of <tt>dest</tt>, or if <tt>dest</tt> is the
226: * root.
227: */
228: public void moveBefore(DefaultMutableTreeNode dest,
229: DefaultMutableTreeNode node) {
230:
231: if (dest == root_)
232: throw new MhfException("Can't move node after root.");
233:
234: if (dest.isNodeAncestor(node))
235: throw new MhfException("Can't move node under itself.");
236:
237: node.removeFromParent();
238: insertBefore(dest, node);
239: }
240:
241: /**
242: * Sorts the children of <tt>node</tt> using <tt>comparator</tt>;
243: * <tt>comparator</tt> should expect <tt>DefaultMutableTreeNode</tt>s to be
244: * passed to it.
245: */
246: public static void sort(DefaultMutableTreeNode node,
247: Comparator comparator) {
248:
249: //
250: // no children
251: //
252: if (node.getChildCount() == 0)
253: return;
254:
255: List list = new ArrayList();
256: for (int i = 0; i < node.getChildCount(); i++) {
257: DefaultMutableTreeNode n = (DefaultMutableTreeNode) node
258: .getChildAt(i);
259: n.removeFromParent();
260: list.add(n);
261: }
262:
263: Collections.sort(list, comparator);
264:
265: for (Iterator iter = list.iterator(); iter.hasNext();) {
266: DefaultMutableTreeNode n = (DefaultMutableTreeNode) iter
267: .next();
268: node.add(n);
269: }
270: }
271:
272: /**
273: * Recursively copies <tt>node</tt> using its <tt>clone()</tt> method (which,
274: * unless overridden, is a shallow copy).
275: */
276: public static DefaultMutableTreeNode copy(
277: DefaultMutableTreeNode node) {
278:
279: DefaultMutableTreeNode n = (DefaultMutableTreeNode) node
280: .clone();
281:
282: for (int i = 0; i < node.getChildCount(); i++)
283: n.add(copy((DefaultMutableTreeNode) node.getChildAt(i)));
284:
285: return n;
286: }
287:
288: // properties ///////////////////////////////////////////////////////////////
289:
290: // attributes ///////////////////////////////////////////////////////////////
291:
292: protected DefaultMutableTreeNode root_ = null;
293: }
|