1 package com.sri.common.taglib;
2
3 import com.jcorporate.expresso.core.security.filters.FilterManager;
4 import com.jcorporate.expresso.core.security.filters.ISO_8859_1;
5 import com.jcorporate.expresso.services.taglib.ELTagSupport;
6 import com.sri.common.controller.AbstractDBController;
7 import org.apache.log4j.Logger;
8
9 import javax.servlet.jsp.JspException;
10 import javax.servlet.jsp.JspWriter;
11 import javax.servlet.jsp.tagext.BodyTagSupport;
12 import javax.servlet.jsp.tagext.TagSupport;
13 import java.io.IOException;
14
15 /***
16 * Renders a tree menu tag. It renders the entire tree inside a <div> tag
17 * that may optionally have specified id (useful for javascript manipulation)
18 * or CSS class.
19 *
20 * @author Michael Rimov
21 */
22 public class TreeMenuTag extends BodyTagSupport {
23
24 /***
25 *
26 */
27 private static final long serialVersionUID = 1L;
28
29 /***
30 * Log used primarily for logging results of expression evaluation.
31 */
32 private static final Logger LOG = Logger.getLogger(TreeMenuTag.class);
33
34 /***
35 * Pre-allocate StringBuffer size.
36 */
37 private static final int BUFFER_SIZE = 512;
38
39
40 /***
41 * Instance of the filter manager for filtering attributes and content.
42 */
43 private final FilterManager filterManager = FilterManager.getInstance();
44
45 /***
46 * The bean to render the tree menu from.
47 */
48 private String value;
49
50 /***
51 * Name of the child frame id.
52 */
53 private String childFrame;
54
55 /***
56 * Default tag.
57 */
58 public TreeMenuTag() {
59 super();
60 }
61
62 /***
63 * Sets the name of the bean object we're supposed to render. Takes
64 * an EL expression.
65 *
66 * @param object String
67 */
68 public void setValue(final String object) {
69 this.value = object;
70 }
71
72 /***
73 * Sets the id of the child frame for a target with javascript. (Optional)
74 *
75 * @param childFrame String
76 */
77 public void setChildFrame(final String childFrame) {
78 this.childFrame = childFrame;
79 }
80
81 /***
82 * Retrieve the bean value.
83 *
84 * @return String
85 */
86 public String getValue() {
87 return value;
88 }
89
90 /***
91 * Retrieve the id of the child frame to target for javascript links.
92 *
93 * @return String
94 */
95 public String getChildFrame() {
96 return childFrame;
97 }
98
99 /***
100 * Process the end tag for this instance.
101 *
102 * @return indication of whether to continue evaluating the JSP page.
103 * @throws JspException if an error occurred while processing this tag
104 */
105 public int doEndTag() throws JspException {
106
107
108
109
110
111 TreeNode rootNode = getRootNode();
112
113 JspWriter out = pageContext.getOut();
114
115
116 StringBuffer buffer = new StringBuffer(BUFFER_SIZE);
117
118
119 writeTreeJavaScript(buffer);
120
121
122 writeTree(rootNode, buffer);
123
124 try {
125 out.println(buffer.toString());
126 } catch (IOException ex) {
127 LOG.warn("I/O Error writing out tree menu.", ex);
128 }
129
130 return TagSupport.EVAL_PAGE;
131 }
132
133 /***
134 * Writes the javascript associated with the tree menu.
135 *
136 * @param buffer StringBuffer
137 */
138 private void writeTreeJavaScript(final StringBuffer buffer) {
139 buffer.append("<script language=\"javascript\">\n//<!--\n");
140 buffer.append("function toggleStyles(targetObject, style1, style2){\n");
141 buffer.append("\tparentObject = targetObject.parentNode;\n");
142 buffer.append("\tparentObject.className = (parentObject.className == style1) ? style2 : style1;\n");
143 buffer.append("\ttargetObject.className = parentObject.className;\n");
144 buffer.append("}\n");
145 if (getChildFrame() != null) {
146 buffer.append("function followLink(href) {\n");
147 buffer.append("\t document.getElementById(\"");
148 buffer.append(getChildFrame());
149 buffer.append("\").src=href;\n");
150 buffer.append("\t return false;");
151 buffer.append("}\n");
152 }
153 buffer.append("//-->\n</script>\n");
154 }
155
156 /***
157 * Retrieve the root node based upon the JSTL 'value' parameter passed in.
158 *
159 * @return TreeNode the root node or throws an Exception based on the
160 * failure of the JSTL expression.
161 * @throws JspException upon evaluation error on being unable to find the
162 * error, or finding an object but it isn't an instance of TreeNode.
163 */
164 private TreeNode getRootNode() throws JspException {
165 TreeNode result;
166 ELTagSupport support = ELTagSupport.getInstance();
167 try {
168 result = (TreeNode) support.evaluate("value", this.getValue(),
169 TreeNode.class,
170 this, this.pageContext);
171
172 if (result == null) {
173 throw new JspException("Unable to locate tree menu bean: " + this.getValue());
174 }
175 } catch (ClassCastException ex1) {
176 LOG.error("ClassCastException evaluating parameter 'value'", ex1);
177 throw new JspException("Received wrong type back for parameter 'value'. Expected java.lang.String");
178 }
179
180 return result;
181 }
182
183 /***
184 * Function iterates over the tree nodes, rendering any of the node values, etc.
185 *
186 * @param root TreeNode the root of the nodes.
187 * @param out JspWriter
188 * @throws JspException upon error.
189 */
190 private void writeTree(final TreeNode root, final StringBuffer out) throws JspException {
191 out.append("<ul>\n");
192 writeNode(root, out);
193 out.append("</ul>");
194 }
195
196
197 /***
198 * Writes the contents of one node.
199 *
200 * @param node TreeNode
201 * @param out StringBuffer
202 * @throws JspException upon error.
203 */
204 private void writeNode(final TreeNode node, final StringBuffer out) throws JspException {
205 if (node.getNested() == TreeNode.NO_CHILDREN) {
206 writeOneLeaf(node, out);
207 } else {
208 writeOneFolder(node, out);
209 }
210 }
211
212 /***
213 * Writes the equivilant of a folder. Example HTML from prototype:
214 * <p><code>
215 * <li class="folder-open"><a class="folder-open" href="#"
216 * onClick="toggleStyles(this, 'folder-closed', 'folder-open')
217 * ;return false;">Parent</a></code><p>
218 *
219 * @param node TreeNode
220 * @param out StringBuffer
221 * @throws JspException upon error.
222 */
223 private void writeOneFolder(final TreeNode node, final StringBuffer out) throws JspException {
224 writeNodeIconAndLink(node, out);
225
226 TreeNode[] children = node.getNested();
227 for (int i = 0; i < children.length; i++) {
228 writeNode(children[i], out);
229 }
230 out.append("</ul>");
231 out.append("</li>\n");
232 }
233
234 /***
235 * Writes out the node icon and link.
236 *
237 * @param node TreeNode the tree node we're rendering.
238 * @param out StringBuffer the sink for the results.
239 * @throws JspException upon error.
240 */
241 private void writeNodeIconAndLink(final TreeNode node, final StringBuffer out) throws JspException {
242 out.append("<li class=\"");
243 if (node.isSelected()) {
244 out.append(node.getSelectedStyle());
245 } else {
246 out.append(node.getUnselectedStyle());
247 }
248 out.append("\">");
249 out.append("<a href=\"");
250 out.append((node.getLink() == null) ? "#" : node.getLink());
251 out.append("\" onClick=\"toggleStyles(this, '");
252 out.append(node.getSelectedStyle());
253 out.append("','");
254 out.append(node.getUnselectedStyle());
255 out.append("');return false;\"");
256 out.append(" class=\"");
257 out.append(node.isSelected() ? node.getSelectedStyle() : node.getUnselectedStyle());
258 out.append("\"");
259 out.append(">");
260 if (node.getLink() != null) {
261 out.append(" </a><a href=\"");
262 final String nodeURL = node.getLink();
263 out.append(nodeURL);
264 out.append("\"");
265 try {
266 writeFollowLinkJavascript(node, out);
267 } catch (Exception ex1) {
268 LOG.error("Error writing link through filter", ex1);
269 throw new JspException("Error writing link through filter. Details Logged" + ex1.getMessage());
270 }
271 out.append(" id=\"");
272 out.append(node.getSelectedStyle());
273 out.append("\"");
274 out.append(">");
275 }
276 writeNodeIcon(node, out);
277
278 try {
279 out.append(filterManager.filterString(node.getLabel(), ISO_8859_1.class, FilterManager.STANDARD_FILTER));
280 } catch (Exception ex) {
281 throw new JspException("Error filtering node label", ex);
282 }
283
284 out.append("</a>\n");
285
286 out.append("<ul>");
287 }
288
289 /***
290 * Writes a node with no children. Writes the equivalent of:
291 * <li><a class="subitem" href="#">Item 1</a></li>
292 *
293 * @param node TreeNode
294 * @param out StringBuffer
295 * @throws JspException upon error.
296 */
297 private void writeOneLeaf(final TreeNode node, final StringBuffer out) throws JspException {
298 try {
299 String nodeStyle = node.isSelected() ? filterManager.filterString(node.getSelectedStyle(),
300 ISO_8859_1.class, FilterManager.STANDARD_FILTER) : filterManager.filterString(
301 node.getUnselectedStyle(), ISO_8859_1.class, FilterManager.STANDARD_FILTER);
302
303
304 out.append("\t<li class=\"");
305 out.append(nodeStyle);
306 out.append("\">");
307 out.append("<a href=\"");
308 if (node.getLink() == null) {
309 out.append("#");
310 } else {
311 out.append(node.getLink());
312 }
313
314 out.append("\"");
315 writeFollowLinkJavascript(node, out);
316 out.append(" class=\"");
317 out.append(nodeStyle);
318 out.append("\"");
319 out.append(">");
320 out.append(" </a>");
321
322 out.append("<a href=\"");
323 out.append(
324 (node.getLink() == null) ? "#" : filterManager.filterString(node.getLink(), ISO_8859_1.class,
325 FilterManager.STANDARD_FILTER));
326 out.append("\"");
327 writeFollowLinkJavascript(node, out);
328 out.append("id=\"");
329 out.append(nodeStyle);
330 out.append("\">");
331 writeNodeIcon(node, out);
332 out.append(filterManager.filterString(node.getLabel(), ISO_8859_1.class, FilterManager.STANDARD_FILTER));
333 out.append("</a>");
334 out.append("</li>\n");
335
336 } catch (Exception ex) {
337 throw new JspException("Error filtering node label", ex);
338 }
339 }
340
341 private void writeFollowLinkJavascript(final TreeNode node, final StringBuffer out) throws Exception {
342 if (node.getLink() != null && this.getChildFrame() != null) {
343 String nodeLink = node.getLink();
344 if (nodeLink.indexOf("&") > 0) {
345 nodeLink = nodeLink + "?" + AbstractDBController.EMBEDDED_MODE + "=Y";
346 } else {
347 nodeLink = nodeLink + "&" + AbstractDBController.EMBEDDED_MODE + "=Y";
348 }
349 out.append(" onClick=\"return followLink('");
350 out.append(
351 filterManager.filterString(nodeLink, ISO_8859_1.class, FilterManager.STANDARD_FILTER));
352 out.append("')\" ");
353 }
354 }
355
356 /***
357 * Writes the node icon inside its own span.
358 *
359 * @param node TreeNode the tree node for the source of the image.
360 * @param out StringBuffer the buffer we're appending the html code to.
361 * @throws JspException upon error.
362 */
363 private void writeNodeIcon(final TreeNode node, final StringBuffer out) throws JspException {
364 try {
365 if (node.getIconUrl() != null) {
366 out.append("<span><img src=\"");
367 out.append(
368 filterManager.filterString(node.getIconUrl(), ISO_8859_1.class, FilterManager.STANDARD_FILTER));
369 out.append("\"/> </span>");
370
371 }
372 } catch (Exception ex) {
373 throw new JspException("Error writing node icon url", ex);
374 }
375 }
376
377 }