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 java.util.ArrayList;
024: import java.util.Iterator;
025: import java.util.List;
026: import java.util.Map;
027:
028: import javax.swing.tree.TreeNode;
029:
030: /**
031: * <p>
032: * A <tt>TreeRenderer</tt> for trees whose state is maintained on the server.
033: * The renderer only renders those nodes that are visible, and a request must
034: * be made to open or close a node. A mechanism must exist on the server
035: * side to handle those requests. While more elaborate to implement, this
036: * approach makes it possible maintain a tree's state across requests.
037: * </p>
038: */
039: public class ServerTreeRenderer extends TreeRenderer {
040:
041: // constructors /////////////////////////////////////////////////////////////
042:
043: // constants ////////////////////////////////////////////////////////////////
044:
045: // classes //////////////////////////////////////////////////////////////////
046:
047: // methods //////////////////////////////////////////////////////////////////
048:
049: /**
050: *
051: */
052: public String renderTree(FoldingTreeNode root) throws TreeException {
053:
054: if (openUrl_ == null)
055: throw new TreeException("openUrl has not been set.");
056:
057: if (closeUrl_ == null)
058: throw new TreeException("closeUrl has not been set.");
059:
060: //
061: // set defaults
062: //
063: if (getClosedHandleImage() == null)
064: closedHandleImageHtml_ = "+";
065: else
066: closedHandleImageHtml_ = "<img border=\"0\" src=\""
067: + getClosedHandleImage() + "\">";
068:
069: if (getOpenedHandleImage() == null)
070: openedHandleImageHtml_ = "-";
071: else
072: openedHandleImageHtml_ = "<img border=\"0\" src=\""
073: + getOpenedHandleImage() + "\">";
074:
075: //
076: // get visible nodes
077: //
078: List nodes = new ArrayList();
079: getVisibleNodes(nodes, root);
080:
081: //
082: // get maximum visible level
083: //
084: int maxVisibleLevel = getMaxLevel(nodes);
085:
086: //
087: // render the tree
088: //
089: StringBuffer buf = new StringBuffer();
090: renderHeader(buf);
091:
092: for (Iterator iter = nodes.iterator(); iter.hasNext();) {
093: FoldingTreeNode node = (FoldingTreeNode) iter.next();
094: renderNode(buf, maxVisibleLevel, node);
095: }
096:
097: renderFooter(buf);
098:
099: //
100: // return the html
101: //
102: return buf.toString();
103: }
104:
105: /**
106: * Transforms the tree into a list of nodes that should be displayed.
107: */
108: protected void getVisibleNodes(List rows, FoldingTreeNode node) {
109:
110: if (!node.isRoot() || !isRootHidden())
111: rows.add(node);
112:
113: if (!node.isLeaf() && node.getOpened())
114: for (int i = 0; i < node.getChildCount(); i++)
115: getVisibleNodes(rows, (FoldingTreeNode) node
116: .getChildAt(i));
117: }
118:
119: /**
120: * Returns the level of the deepest level node in <tt>nodes</tt>.
121: */
122: protected int getMaxLevel(List nodes) {
123:
124: int level = 0;
125: for (Iterator iter = nodes.iterator(); iter.hasNext();) {
126: FoldingTreeNode node = (FoldingTreeNode) iter.next();
127:
128: int tmp = node.getLevel();
129:
130: if (tmp > level)
131: level = tmp;
132: }
133:
134: return level;
135: }
136:
137: /**
138: * Renders the header of the tree: a <tt>table</tt> tag.
139: */
140: protected void renderHeader(StringBuffer buf) throws TreeException {
141:
142: buf.append("<table>\n");
143: }
144:
145: /**
146: * Renders the footer of the tree.
147: */
148: protected void renderFooter(StringBuffer buf) throws TreeException {
149:
150: buf.append("</table>\n");
151: }
152:
153: /**
154: * Renders a node.
155: */
156: protected void renderNode(StringBuffer buf, int maxVisibleLevel,
157: FoldingTreeNode node) {
158:
159: //
160: // if the root is hidden, it throws off most of these calculations by one;
161: // find this offset peppered through the code below
162: //
163: int hiddenRootOffset = 0;
164: if (isRootHidden())
165: hiddenRootOffset = 1;
166:
167: //
168: // scratch strings
169: //
170: String s = null;
171: String t = null;
172:
173: buf.append("<tr>");
174:
175: //
176: // insert an empty cell for each level deep the node is
177: //
178: for (int i = hiddenRootOffset; i < node.getLevel(); i++)
179: buf.append("<td> </td>");
180:
181: //
182: // render open/close link; leaf?
183: //
184: if (node.isLeaf()) {
185: buf.append("<td> </td>");
186: } else {
187: //
188: // opened?
189: //
190: if (!node.getOpened())
191: buf.append("<td><a href=\"" + openUrl_
192: + node.hashCode() + "\">"
193: + closedHandleImageHtml_ + "</a></td>");
194:
195: //
196: // closed
197: //
198: else
199: buf.append("<td><a href=\"" + closeUrl_
200: + node.hashCode() + "\">"
201: + openedHandleImageHtml_ + "</a></td>");
202: }
203:
204: //
205: // render icon
206: //
207: Map iconImages = getIconImages();
208:
209: if (iconImages != null) {
210:
211: s = (String) iconImages.get(node.getIconHint());
212:
213: if (s == null)
214: s = (String) iconImages.get(DEFAULT_ICON);
215:
216: if (s == null) {
217: buf.append("<td> </td>");
218: } else {
219: if (node.getUrl() == null)
220: buf.append("<td><img src=\"" + s + "\"></td>");
221: else
222: buf.append("<td><a href=\"" + node.getUrl()
223: + "\"><img border=\"0\" src=\"" + s
224: + "\"></a></td>");
225: }
226: }
227:
228: //
229: // render label
230: //
231: if (node.getUrl() == null)
232: buf.append("<td width=\"100%\" colspan=\""
233: + (maxVisibleLevel - node.getLevel() + 1) + "\">"
234: + node.getLabel() + "</td>");
235: else
236: buf.append("<td width=\"100%\" colspan=\""
237: + (maxVisibleLevel - node.getLevel() + 1)
238: + "\"><a href=\"" + node.getUrl() + "\">"
239: + node.getLabel() + "</a></td>");
240:
241: buf.append("</tr>\n");
242: }
243:
244: // properties ///////////////////////////////////////////////////////////////
245:
246: /**
247: * Sets the url to open a node; the hash code of the node to be opened will
248: * be appended to the end of this url.
249: */
250: public void setOpenUrl(String openUrl) {
251: openUrl_ = openUrl;
252: }
253:
254: /**
255: * Sets the url to close a node; the hash code of the node to be closed will
256: * be appended to the end of this url.
257: */
258: public void setCloseUrl(String closeUrl) {
259: closeUrl_ = closeUrl;
260: }
261:
262: // attributes ///////////////////////////////////////////////////////////////
263:
264: protected String openUrl_ = null;
265: protected String closeUrl_ = null;
266:
267: private String openedHandleImageHtml_ = null;
268: private String closedHandleImageHtml_ = null;
269: }
|