1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18 package org.apache.struts.taglib.nested;
19
20 import org.apache.struts.taglib.html.Constants;
21 import org.apache.struts.taglib.html.FormTag;
22
23 import javax.servlet.http.HttpServletRequest;
24 import javax.servlet.jsp.tagext.Tag;
25
26 import java.util.StringTokenizer;
27
28 /***
29 * <p>A simple helper class that does everything that needs to be done to get
30 * the nested tag extension to work. The tags will pass in their relative
31 * properties and this class will leverage the accessibility of the request
32 * object to calculate the nested references and manage them from a central
33 * place.</p>
34 *
35 * <p>The helper method {@link #setNestedProperties} takes a reference to the
36 * tag itself so all the simpler tags can have their references managed from a
37 * central location. From here, the reference to a provided name is also
38 * preserved for use.</p>
39 *
40 * <p>With all tags keeping track of themselves, we only have to seek to the
41 * next level, or parent tag, were a tag will append a dot and it's own
42 * property.</p>
43 *
44 * @version $Rev: 376843 $ $Date: 2004-10-16 12:38:42 -0400 (Sat, 16 Oct 2004)
45 * $
46 * @since Struts 1.1
47 */
48 public class NestedPropertyHelper {
49
50 public static final String NESTED_INCLUDES_KEY = "<nested-includes-key/>";
51
52 /***
53 * Returns the current nesting property from the request object.
54 *
55 * @param request object to fetch the property reference from
56 * @return String of the bean name to nest against
57 */
58 public static final String getCurrentProperty(HttpServletRequest request) {
59
60 NestedReference nr =
61 (NestedReference) request.getAttribute(NESTED_INCLUDES_KEY);
62
63
64 return (nr == null) ? null : nr.getNestedProperty();
65 }
66
67 /***
68 * <p>Returns the bean name from the request object that the properties
69 * are nesting against.</p>
70 *
71 * <p>The requirement of the tag itself could be removed in the future,
72 * but is required if support for the <html:form> tag is maintained.</p>
73 *
74 * @param request object to fetch the bean reference from
75 * @param nested tag from which to start the search from
76 * @return the string of the bean name to be nesting against
77 */
78 public static final String getCurrentName(HttpServletRequest request,
79 NestedNameSupport nested) {
80
81 NestedReference nr =
82 (NestedReference) request.getAttribute(NESTED_INCLUDES_KEY);
83
84
85 if (nr != null) {
86 return nr.getBeanName();
87 } else {
88
89 Tag tag = (Tag) nested;
90 Tag formTag = null;
91
92
93 do {
94 tag = tag.getParent();
95
96 if ((tag != null) && tag instanceof FormTag) {
97 formTag = tag;
98 }
99 } while ((formTag == null) && (tag != null));
100
101 if (formTag == null) {
102 return "";
103 }
104
105
106 return ((FormTag) formTag).getBeanName();
107 }
108 }
109
110 /***
111 * Get the adjusted property. Apply the provided property, to the property
112 * already stored in the request object.
113 *
114 * @param request to pull the reference from
115 * @param property to retrieve the evaluated nested property with
116 * @return String of the final nested property reference.
117 */
118 public static final String getAdjustedProperty(HttpServletRequest request,
119 String property) {
120
121 String parent = getCurrentProperty(request);
122
123 return calculateRelativeProperty(property, parent);
124 }
125
126 /***
127 * Sets the provided property into the request object for reference by the
128 * other nested tags.
129 *
130 * @param request object to set the new property into
131 * @param property String to set the property to
132 */
133 public static final void setProperty(HttpServletRequest request,
134 String property) {
135
136 NestedReference nr = referenceInstance(request);
137
138 nr.setNestedProperty(property);
139 }
140
141 /***
142 * Sets the provided name into the request object for reference by the
143 * other nested tags.
144 *
145 * @param request object to set the new name into
146 * @param name String to set the name to
147 */
148 public static final void setName(HttpServletRequest request, String name) {
149
150 NestedReference nr = referenceInstance(request);
151
152 nr.setBeanName(name);
153 }
154
155 /***
156 * Deletes the nested reference from the request object.
157 *
158 * @param request object to remove the reference from
159 */
160 public static final void deleteReference(HttpServletRequest request) {
161
162 request.removeAttribute(NESTED_INCLUDES_KEY);
163 }
164
165 /***
166 * Helper method that will set all the relevant nesting properties for the
167 * provided tag reference depending on the implementation.
168 *
169 * @param request object to pull references from
170 * @param tag to set the nesting values into
171 */
172 public static void setNestedProperties(HttpServletRequest request,
173 NestedPropertySupport tag) {
174 boolean adjustProperty = true;
175
176
177 if (tag instanceof NestedNameSupport) {
178 NestedNameSupport nameTag = (NestedNameSupport) tag;
179
180 if ((nameTag.getName() == null)
181 || Constants.BEAN_KEY.equals(nameTag.getName())) {
182 nameTag.setName(getCurrentName(request, (NestedNameSupport) tag));
183 } else {
184 adjustProperty = false;
185 }
186 }
187
188
189 String property = tag.getProperty();
190
191 if (adjustProperty) {
192 property = getAdjustedProperty(request, property);
193 }
194
195 tag.setProperty(property);
196 }
197
198 /***
199 * Pulls the current nesting reference from the request object, and if
200 * there isn't one there, then it will create one and set it.
201 *
202 * @param request object to manipulate the reference into
203 * @return current nesting reference as stored in the request object
204 */
205 private static final NestedReference referenceInstance(
206 HttpServletRequest request) {
207
208 NestedReference nr =
209 (NestedReference) request.getAttribute(NESTED_INCLUDES_KEY);
210
211
212 if (nr == null) {
213 nr = new NestedReference();
214 request.setAttribute(NESTED_INCLUDES_KEY, nr);
215 }
216
217
218 return nr;
219 }
220
221
222
223
224
225
226
227
228
229 private static String calculateRelativeProperty(String property,
230 String parent) {
231 if (parent == null) {
232 parent = "";
233 }
234
235 if (property == null) {
236 property = "";
237 }
238
239
240
241 if ("./".equals(property) || "this/".equals(property)) {
242 return parent;
243 }
244
245
246 String stepping;
247
248
249 if (property.endsWith("/")) {
250 stepping = property;
251 property = "";
252 } else {
253 stepping = property.substring(0, property.lastIndexOf('/') + 1);
254
255
256 property =
257 property.substring(property.lastIndexOf('/') + 1,
258 property.length());
259 }
260
261 if (stepping.startsWith("/")) {
262
263 return property;
264 } else {
265
266 StringTokenizer proT = new StringTokenizer(parent, ".");
267 int propCount = proT.countTokens();
268
269
270 StringTokenizer strT = new StringTokenizer(stepping, "/");
271 int count = strT.countTokens();
272
273 if (count >= propCount) {
274
275 return property;
276 } else {
277
278 count = propCount - count;
279
280 StringBuffer result = new StringBuffer();
281
282 for (int i = 0; i < count; i++) {
283 result.append(proT.nextToken());
284 result.append('.');
285 }
286
287 result.append(property);
288
289
290 if (result.charAt(result.length() - 1) == '.') {
291 return result.substring(0, result.length() - 1);
292 } else {
293 return result.toString();
294 }
295 }
296 }
297 }
298 }