1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.apache.webapp.admin;
20
21
22 import java.io.IOException;
23 import java.net.URLEncoder;
24 import javax.servlet.http.HttpServletResponse;
25 import javax.servlet.jsp.JspException;
26 import javax.servlet.jsp.JspWriter;
27 import javax.servlet.jsp.PageContext;
28 import javax.servlet.jsp.tagext.TagSupport;
29
30
31 /***
32 * <p>JSP custom tag that renders a tree control represented by the
33 * <code>TreeControl</code> and <code>TreeControlNode</code> classes.
34 * This tag has the following user-settable attributes:</p>
35 * <ul>
36 * <li><strong>action</strong> - Hyperlink to which expand/contract actions
37 * should be sent, with a string "<code>${node}</code> marking where
38 * the node name of the affected node should be included.</li>
39 * <li><strong>images</strong> - Name of the directory containing the images
40 * for our icons, relative to the page including this tag. If not
41 * specified, defaults to "images".</li>
42 * <li><strong>scope</strong> - Attribute scope in which the <code>tree</code>
43 * attribute is to be found (page, request, session, application). If
44 * not specified, the attribute is searched for in all scopes.</li>
45 * <li><strong>style</strong> - CSS style <code>class</code> to be applied
46 * to be applied to the entire rendered output of the tree control.
47 * If not specified, no style class is applied.</li>
48 * <li><strong>styleSelected</strong> - CSS style <code>class</code> to be
49 * applied to the text of any element that is currently selected. If not
50 * specified, no additional style class is applied.</li>
51 * <li><strong>styleUnselected</strong> - CSS style <code>class</code> to be
52 * applied to the text of any element that is not currently selected.
53 * If not specified, no additional style class is applied.</li>
54 * <li><strong>tree</strong> - Attribute name under which the
55 * <code>TreeControl</code> bean of the tree we are rendering
56 * is stored, in the scope specified by the <code>scope</code>
57 * attribute. This attribute is required.</li>
58 * </ul>
59 *
60 * <strong>FIXME</strong> - Internationalize the exception messages!
61 *
62 * @author Craig R. McClanahan
63 * @version $Revision: 516448 $ $Date: 2007-03-09 17:25:47 +0100 (Fri, 09 Mar 2007) $
64 */
65
66 public class TreeControlTag extends TagSupport
67 {
68 private static final long serialVersionUID = 1;
69
70
71 /***
72 * The default directory name for icon images.
73 */
74 static final String DEFAULT_IMAGES = "images";
75
76
77 /***
78 * The names of tree state images that we need.
79 */
80 static final String IMAGE_HANDLE_DOWN_LAST = "handledownlast.gif";
81 static final String IMAGE_HANDLE_DOWN_MIDDLE = "handledownmiddle.gif";
82 static final String IMAGE_HANDLE_RIGHT_LAST = "handlerightlast.gif";
83 static final String IMAGE_HANDLE_RIGHT_MIDDLE = "handlerightmiddle.gif";
84 static final String IMAGE_LINE_LAST = "linelastnode.gif";
85 static final String IMAGE_LINE_MIDDLE = "linemiddlenode.gif";
86 static final String IMAGE_LINE_VERTICAL = "linevertical.gif";
87
88
89
90
91
92 /***
93 * The hyperlink to be used for submitting requests to expand and
94 * contract tree nodes. The placeholder "<code>${name}</code>" will
95 * be replaced by the <code>name</code> property of the current
96 * tree node.
97 */
98 protected String action = null;
99
100 public String getAction() {
101 return (this.action);
102 }
103
104 public void setAction(String action) {
105 this.action = action;
106 }
107
108
109 /***
110 * The name of the directory containing the images for our icons,
111 * relative to the page including this tag.
112 */
113 protected String images = DEFAULT_IMAGES;
114
115 public String getImages() {
116 return (this.images);
117 }
118
119 public void setImages(String images) {
120 this.images = images;
121 }
122
123
124 /***
125 * The name of the scope in which to search for the <code>tree</code>
126 * attribute. Must be "page", "request", "session", or "application"
127 * (or <code>null</code> for an ascending-visibility search).
128 */
129 protected String scope = null;
130
131 public String getScope() {
132 return (this.scope);
133 }
134
135 public void setScope(String scope) {
136 if (!"page".equals(scope) &&
137 !"request".equals(scope) &&
138 !"session".equals(scope) &&
139 !"application".equals(scope))
140 throw new IllegalArgumentException("Invalid scope '" +
141 scope + "'");
142 this.scope = scope;
143 }
144
145
146 /***
147 * The CSS style <code>class</code> to be applied to the entire tree.
148 */
149 protected String style = null;
150
151 public String getStyle() {
152 return (this.style);
153 }
154
155 public void setStyle(String style) {
156 this.style = style;
157 }
158
159
160 /***
161 * The CSS style <code>class</code> to be applied to the text
162 * of selected nodes.
163 */
164 protected String styleSelected = null;
165
166 public String getStyleSelected() {
167 return (this.styleSelected);
168 }
169
170 public void setStyleSelected(String styleSelected) {
171 this.styleSelected = styleSelected;
172 }
173
174
175 /***
176 * The CSS style <code>class</code> to be applied to the text
177 * of unselected nodes.
178 */
179 protected String styleUnselected = null;
180
181 public String getStyleUnselected() {
182 return (this.styleUnselected);
183 }
184
185 public void setStyleUnselected(String styleUnselected) {
186 this.styleUnselected = styleUnselected;
187 }
188
189
190 /***
191 * The name of the attribute (in the specified scope) under which our
192 * <code>TreeControl</code> instance is stored.
193 */
194 protected String tree = null;
195
196 public String getTree() {
197 return (this.tree);
198 }
199
200 public void setTree(String tree) {
201 this.tree = tree;
202 }
203
204
205
206
207
208 /***
209 * Render this tree control.
210 *
211 * @exception JspException if a processing error occurs
212 */
213 public int doEndTag() throws JspException {
214
215 TreeControl treeControl = getTreeControl();
216 JspWriter out = pageContext.getOut();
217 try {
218 out.print
219 ("<table border=\"0\" cellspacing=\"0\" cellpadding=\"0\"");
220 if (style != null) {
221 out.print(" class=\"");
222 out.print(style);
223 out.print("\"");
224 }
225 out.println(">");
226 int level = 0;
227 TreeControlNode node = treeControl.getRoot();
228 render(out, node, level, treeControl.getWidth(), true);
229 out.println("</table>");
230 } catch (IOException e) {
231 throw new JspException(e);
232 }
233
234 return (EVAL_PAGE);
235
236 }
237
238
239 /***
240 * Release all state information set by this tag.
241 */
242 public void release() {
243
244 this.action = null;
245 this.images = DEFAULT_IMAGES;
246 this.scope = null;
247 this.style = null;
248 this.styleSelected = null;
249 this.styleUnselected = null;
250 this.tree = null;
251
252 }
253
254
255
256
257
258 /***
259 * Return the <code>TreeControl</code> instance for the tree control that
260 * we are rendering.
261 *
262 * @exception JspException if no TreeControl instance can be found
263 */
264 protected TreeControl getTreeControl() throws JspException {
265
266 Object treeControl = null;
267 if (scope == null)
268 treeControl = pageContext.findAttribute(tree);
269 else if ("page".equals(scope))
270 treeControl =
271 pageContext.getAttribute(tree, PageContext.PAGE_SCOPE);
272 else if ("request".equals(scope))
273 treeControl =
274 pageContext.getAttribute(tree, PageContext.REQUEST_SCOPE);
275 else if ("session".equals(scope))
276 treeControl =
277 pageContext.getAttribute(tree, PageContext.SESSION_SCOPE);
278 else if ("application".equals(scope))
279 treeControl =
280 pageContext.getAttribute(tree, PageContext.APPLICATION_SCOPE);
281 if (treeControl == null)
282 throw new JspException("Cannot find tree control attribute '" +
283 tree + "'");
284 else if (!(treeControl instanceof TreeControl))
285 throw new JspException("Invalid tree control attribute '" +
286 tree + "'");
287 else
288 return ((TreeControl) treeControl);
289
290 }
291
292
293 /***
294 * Render the specified node, as controlled by the specified parameters.
295 *
296 * @param out The <code>JspWriter</code> to which we are writing
297 * @param node The <code>TreeControlNode</code> we are currently
298 * rendering
299 * @param level The indentation level of this node in the tree
300 * @param width Total displayable width of the tree
301 * @param last Is this the last node in a list?
302 *
303 * @exception IOException if an input/output error occurs
304 */
305 protected void render(JspWriter out, TreeControlNode node,
306 int level, int width, boolean last)
307 throws IOException {
308
309 HttpServletResponse response =
310 (HttpServletResponse) pageContext.getResponse();
311
312
313
314
315 if ("ROOT-NODE".equalsIgnoreCase(node.getName()) &&
316 (node.getLabel() == null)) {
317
318 TreeControlNode children[] = node.findChildren();
319 int lastIndex = children.length - 1;
320 int newLevel = level + 1;
321 for (int i = 0; i < children.length; i++) {
322 render(out, children[i], newLevel, width, i == lastIndex);
323 }
324 return;
325 }
326
327
328 out.println(" <tr valign=\"middle\">");
329 out.print("<td><table cellspacing=\"0\" cellpadding=\"0\" border=\"0\"><tr valign=\"middle\">");
330
331
332 for (int i = 0; i < level; i++) {
333 int levels = level - i;
334 TreeControlNode parent = node;
335 for (int j = 1; j <= levels; j++)
336 parent = parent.getParent();
337 if (parent.isLast())
338 out.print(" <td> </td>");
339 else {
340 out.print(" <td><img src=\"");
341 out.print(images);
342 out.print("/");
343 out.print(IMAGE_LINE_VERTICAL);
344 out.print("\" alt=\"\" border=\"0\"></td>");
345 }
346 out.println();
347 }
348
349
350
351
352
353
354
355 String encodedNodeName = URLEncoder.encode(node.getName());
356
357 String action = replace(getAction(), "${name}", encodedNodeName);
358
359
360 String updateTreeAction =
361 replace(getAction(), "tree=${name}", "select=" + encodedNodeName);
362 updateTreeAction =
363 ((HttpServletResponse) pageContext.getResponse()).
364 encodeURL(updateTreeAction);
365
366 out.print(" <td>");
367
368
369 out.print("<a name=\"");
370 out.print(node.getName());
371 out.print("\">");
372
373 if ((action != null) && !node.isLeaf()) {
374 out.print("<a href=\"");
375 out.print(response.encodeURL(action));
376 out.print("\">");
377 }
378 out.print("<img src=\"");
379 out.print(images);
380 out.print("/");
381 if (node.isLeaf()) {
382 if (node.isLast())
383 out.print(IMAGE_LINE_LAST);
384 else
385 out.print(IMAGE_LINE_MIDDLE);
386 out.print("\" alt=\"");
387 } else if (node.isExpanded()) {
388 if (node.isLast())
389 out.print(IMAGE_HANDLE_DOWN_LAST);
390 else
391 out.print(IMAGE_HANDLE_DOWN_MIDDLE);
392 out.print("\" alt=\"close node");
393 } else {
394 if (node.isLast())
395 out.print(IMAGE_HANDLE_RIGHT_LAST);
396 else
397 out.print(IMAGE_HANDLE_RIGHT_MIDDLE);
398 out.print("\" alt=\"expand node");
399 }
400 out.print("\" border=\"0\">");
401 if ((action != null) && !node.isLeaf())
402 out.print("</a>");
403 out.println("</td>");
404
405
406 String hyperlink = null;
407 String nodeAction = node.getAction();
408 if(nodeAction == null && node.isExpandWhenClicked())
409 {
410 hyperlink = action;
411 }
412 if (nodeAction != null)
413 hyperlink = ((HttpServletResponse) pageContext.getResponse()).
414 encodeURL(node.getAction());
415
416
417 out.print(" <td ");
418
419 if(node.getLabel() != null)
420 {
421
422 out.print(" style=\"");
423 out.print("white-space:nowrap; vertical-align:middle;");
424 out.print("\"");
425 }
426
427 out.print(">");
428 if (node.getIcon() != null) {
429 if (hyperlink != null) {
430 out.print("<a href=\"");
431 out.print(hyperlink);
432 out.print("\"");
433 String target = node.getTarget();
434 if(target != null) {
435 out.print(" target=\"");
436 out.print(target);
437 out.print("\"");
438 }
439
440 out.print(" onclick=\"");
441 out.print("self.location.href='" + updateTreeAction + "'");
442 out.print("\"");
443 out.print(">");
444 }
445 out.print("<img src=\"");
446 out.print(images);
447 out.print("/");
448 out.print(node.getIcon());
449 out.print("\" alt=\"");
450 out.print("\" border=\"0\">");
451 if (hyperlink != null)
452 out.print("</a>");
453 }
454
455
456
457 if (node.getLabel() != null) {
458 String labelStyle = null;
459 if (node.isSelected() && (styleSelected != null))
460 labelStyle = styleSelected;
461 else if (!node.isSelected() && (styleUnselected != null))
462 labelStyle = styleUnselected;
463 if (hyperlink != null) {
464
465
466 out.print(" <a href=\"");
467 out.print(hyperlink);
468 out.print("\"");
469 String target = node.getTarget();
470 if(target != null) {
471 out.print(" target=\"");
472 out.print(target);
473 out.print("\"");
474 }
475 if (labelStyle != null) {
476 out.print(" class=\"");
477 out.print(labelStyle);
478 out.print("\"");
479 }
480
481 if(node.getTitle() != null)
482 {
483 out.print(" title=\"");
484 out.print(node.getTitle());
485 out.print("\"");
486 }
487
488
489 out.print(" onclick=\"");
490 out.print("self.location.href='" + updateTreeAction + "'");
491 out.print("\"");
492 out.print(">");
493 } else if (labelStyle != null) {
494 out.print("<span class=\"");
495 out.print(labelStyle);
496 out.print("\">");
497 }
498 out.print(node.getLabel());
499 if (hyperlink != null)
500 out.print("</a>");
501 else if (labelStyle != null)
502 out.print("</span>");
503 }
504 out.println("</td>");
505
506
507 out.println(" </tr>");
508 out.println("</table></td></tr>");
509
510
511 if (node.isExpanded()) {
512 TreeControlNode children[] = node.findChildren();
513 int lastIndex = children.length - 1;
514 int newLevel = level + 1;
515 for (int i = 0; i < children.length; i++) {
516 render(out, children[i], newLevel, width, i == lastIndex);
517 }
518 }
519
520 }
521
522
523 /***
524 * Replace any occurrence of the specified placeholder in the specified
525 * template string with the specified replacement value.
526 *
527 * @param template Pattern string possibly containing the placeholder
528 * @param placeholder Placeholder expression to be replaced
529 * @param value Replacement value for the placeholder
530 */
531 protected String replace(String template, String placeholder,
532 String value) {
533
534 if (template == null)
535 return (null);
536 if ((placeholder == null) || (value == null))
537 return (template);
538 while (true) {
539 int index = template.indexOf(placeholder);
540 if (index < 0)
541 break;
542 StringBuffer temp = new StringBuffer(template.substring(0, index));
543 temp.append(value);
544 temp.append(template.substring(index + placeholder.length()));
545 template = temp.toString();
546 }
547 return (template);
548
549 }
550 }