1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts2.views.velocity.components;
19
20 import java.io.IOException;
21 import java.io.Writer;
22 import java.util.HashMap;
23 import java.util.Map;
24
25 import javax.servlet.http.HttpServletRequest;
26 import javax.servlet.http.HttpServletResponse;
27
28 import org.apache.struts2.ServletActionContext;
29 import org.apache.struts2.components.Component;
30 import org.apache.velocity.context.InternalContextAdapter;
31 import org.apache.velocity.exception.MethodInvocationException;
32 import org.apache.velocity.exception.ParseErrorException;
33 import org.apache.velocity.exception.ResourceNotFoundException;
34 import org.apache.velocity.runtime.directive.Directive;
35 import org.apache.velocity.runtime.parser.node.Node;
36
37 import com.opensymphony.xwork2.util.ValueStack;
38
39 public abstract class AbstractDirective extends Directive {
40 public String getName() {
41 return "s" + getBeanName();
42 }
43
44 public abstract String getBeanName();
45
46 /***
47 * All components, unless otherwise stated, are LINE-level directives.
48 */
49 public int getType() {
50 return LINE;
51 }
52
53 protected abstract Component getBean(ValueStack stack, HttpServletRequest req, HttpServletResponse res);
54
55 public boolean render(InternalContextAdapter ctx, Writer writer, Node node) throws IOException, ResourceNotFoundException, ParseErrorException, MethodInvocationException {
56
57 ValueStack stack = (ValueStack) ctx.get("stack");
58 HttpServletRequest req = (HttpServletRequest) stack.getContext().get(ServletActionContext.HTTP_REQUEST);
59 HttpServletResponse res = (HttpServletResponse) stack.getContext().get(ServletActionContext.HTTP_RESPONSE);
60 Component bean = getBean(stack, req, res);
61
62
63 Map params = createPropertyMap(ctx, node);
64 bean.copyParams(params);
65
66 bean.start(writer);
67
68 if (getType() == BLOCK) {
69 Node body = node.jjtGetChild(node.jjtGetNumChildren() - 1);
70 body.render(ctx, writer);
71 }
72
73 bean.end(writer, "");
74 return true;
75 }
76
77 /***
78 * create a Map of properties that the user has passed in. for example,
79 * <pre>
80 * #xxx("name=hello" "value=world" "template=foo")
81 * </pre>
82 * would yield a params that contains {["name", "hello"], ["value", "world"], ["template", "foo"]}
83 *
84 * @param node the Node passed in to the render method
85 * @return a Map of the user specified properties
86 * @throws org.apache.velocity.exception.ParseErrorException
87 * if the was an error in the format of the property
88 */
89 protected Map createPropertyMap(InternalContextAdapter contextAdapter, Node node) throws ParseErrorException, MethodInvocationException {
90 Map propertyMap = new HashMap();
91
92 int children = node.jjtGetNumChildren();
93 if (getType() == BLOCK) {
94 children--;
95 }
96
97 for (int index = 0, length = children; index < length; index++) {
98 this.putProperty(propertyMap, contextAdapter, node.jjtGetChild(index));
99 }
100
101 return propertyMap;
102 }
103
104 /***
105 * adds a given Node's key/value pair to the propertyMap. For example, if this Node contained the value "rows=20",
106 * then the key, rows, would be added to the propertyMap with the String value, 20.
107 *
108 * @param propertyMap a params containing all the properties that we wish to set
109 * @param node the parameter to set expressed in "name=value" format
110 */
111 protected void putProperty(Map propertyMap, InternalContextAdapter contextAdapter, Node node) throws ParseErrorException, MethodInvocationException {
112
113 String param = node.value(contextAdapter).toString();
114
115 int idx = param.indexOf("=");
116
117 if (idx != -1) {
118 String property = param.substring(0, idx);
119
120 String value = param.substring(idx + 1);
121 propertyMap.put(property, value);
122 } else {
123 throw new ParseErrorException("#" + this.getName() + " arguments must include an assignment operator! For example #tag( Component \"template=mytemplate\" ). #tag( TextField \"mytemplate\" ) is illegal!");
124 }
125 }
126 }