001: /*
002: * $Id: NestedPropertyHelper.java 471754 2006-11-06 14:55:09Z husted $
003: *
004: * Licensed to the Apache Software Foundation (ASF) under one
005: * or more contributor license agreements. See the NOTICE file
006: * distributed with this work for additional information
007: * regarding copyright ownership. The ASF licenses this file
008: * to you under the Apache License, Version 2.0 (the
009: * "License"); you may not use this file except in compliance
010: * with the License. You may obtain a copy of the License at
011: *
012: * http://www.apache.org/licenses/LICENSE-2.0
013: *
014: * Unless required by applicable law or agreed to in writing,
015: * software distributed under the License is distributed on an
016: * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017: * KIND, either express or implied. See the License for the
018: * specific language governing permissions and limitations
019: * under the License.
020: */
021: package org.apache.struts.taglib.nested;
022:
023: import org.apache.struts.taglib.html.Constants;
024: import org.apache.struts.taglib.html.FormTag;
025:
026: import javax.servlet.http.HttpServletRequest;
027: import javax.servlet.jsp.tagext.Tag;
028:
029: import java.util.StringTokenizer;
030:
031: /**
032: * <p>A simple helper class that does everything that needs to be done to get
033: * the nested tag extension to work. The tags will pass in their relative
034: * properties and this class will leverage the accessibility of the request
035: * object to calculate the nested references and manage them from a central
036: * place.</p>
037: *
038: * <p>The helper method {@link #setNestedProperties} takes a reference to the
039: * tag itself so all the simpler tags can have their references managed from a
040: * central location. From here, the reference to a provided name is also
041: * preserved for use.</p>
042: *
043: * <p>With all tags keeping track of themselves, we only have to seek to the
044: * next level, or parent tag, were a tag will append a dot and it's own
045: * property.</p>
046: *
047: * @version $Rev: 471754 $ $Date: 2004-10-16 12:38:42 -0400 (Sat, 16 Oct 2004)
048: * $
049: * @since Struts 1.1
050: */
051: public class NestedPropertyHelper {
052: /* key that the tags can rely on to set the details against */
053: public static final String NESTED_INCLUDES_KEY = "<nested-includes-key/>";
054:
055: /**
056: * Returns the current nesting property from the request object.
057: *
058: * @param request object to fetch the property reference from
059: * @return String of the bean name to nest against
060: */
061: public static final String getCurrentProperty(
062: HttpServletRequest request) {
063: // get the old one if any
064: NestedReference nr = (NestedReference) request
065: .getAttribute(NESTED_INCLUDES_KEY);
066:
067: // return null or the property
068: return (nr == null) ? null : nr.getNestedProperty();
069: }
070:
071: /**
072: * <p>Returns the bean name from the request object that the properties
073: * are nesting against.</p>
074: *
075: * <p>The requirement of the tag itself could be removed in the future,
076: * but is required if support for the <html:form> tag is maintained.</p>
077: *
078: * @param request object to fetch the bean reference from
079: * @param nested tag from which to start the search from
080: * @return the string of the bean name to be nesting against
081: */
082: public static final String getCurrentName(
083: HttpServletRequest request, NestedNameSupport nested) {
084: // get the old one if any
085: NestedReference nr = (NestedReference) request
086: .getAttribute(NESTED_INCLUDES_KEY);
087:
088: // return null or the property
089: if (nr != null) {
090: return nr.getBeanName();
091: } else {
092: // need to look for a form tag...
093: Tag tag = (Tag) nested;
094: Tag formTag = null;
095:
096: // loop all parent tags until we get one that can be nested against
097: do {
098: tag = tag.getParent();
099:
100: if ((tag != null) && tag instanceof FormTag) {
101: formTag = tag;
102: }
103: } while ((formTag == null) && (tag != null));
104:
105: if (formTag == null) {
106: return "";
107: }
108:
109: // return the form's name
110: return ((FormTag) formTag).getBeanName();
111: }
112: }
113:
114: /**
115: * Get the adjusted property. Apply the provided property, to the property
116: * already stored in the request object.
117: *
118: * @param request to pull the reference from
119: * @param property to retrieve the evaluated nested property with
120: * @return String of the final nested property reference.
121: */
122: public static final String getAdjustedProperty(
123: HttpServletRequest request, String property) {
124: // get the old one if any
125: String parent = getCurrentProperty(request);
126:
127: return calculateRelativeProperty(property, parent);
128: }
129:
130: /**
131: * Sets the provided property into the request object for reference by the
132: * other nested tags.
133: *
134: * @param request object to set the new property into
135: * @param property String to set the property to
136: */
137: public static final void setProperty(HttpServletRequest request,
138: String property) {
139: // get the old one if any
140: NestedReference nr = referenceInstance(request);
141:
142: nr.setNestedProperty(property);
143: }
144:
145: /**
146: * Sets the provided name into the request object for reference by the
147: * other nested tags.
148: *
149: * @param request object to set the new name into
150: * @param name String to set the name to
151: */
152: public static final void setName(HttpServletRequest request,
153: String name) {
154: // get the old one if any
155: NestedReference nr = referenceInstance(request);
156:
157: nr.setBeanName(name);
158: }
159:
160: /**
161: * Deletes the nested reference from the request object.
162: *
163: * @param request object to remove the reference from
164: */
165: public static final void deleteReference(HttpServletRequest request) {
166: // delete the reference
167: request.removeAttribute(NESTED_INCLUDES_KEY);
168: }
169:
170: /**
171: * Helper method that will set all the relevant nesting properties for the
172: * provided tag reference depending on the implementation.
173: *
174: * @param request object to pull references from
175: * @param tag to set the nesting values into
176: */
177: public static void setNestedProperties(HttpServletRequest request,
178: NestedPropertySupport tag) {
179: boolean adjustProperty = true;
180:
181: /* if the tag implements NestedNameSupport, set the name for the tag also */
182: if (tag instanceof NestedNameSupport) {
183: NestedNameSupport nameTag = (NestedNameSupport) tag;
184:
185: if ((nameTag.getName() == null)
186: || Constants.BEAN_KEY.equals(nameTag.getName())) {
187: nameTag.setName(getCurrentName(request,
188: (NestedNameSupport) tag));
189: } else {
190: adjustProperty = false;
191: }
192: }
193:
194: /* get and set the relative property, adjust if required */
195: String property = tag.getProperty();
196:
197: if (adjustProperty) {
198: property = getAdjustedProperty(request, property);
199: }
200:
201: tag.setProperty(property);
202: }
203:
204: /**
205: * Pulls the current nesting reference from the request object, and if
206: * there isn't one there, then it will create one and set it.
207: *
208: * @param request object to manipulate the reference into
209: * @return current nesting reference as stored in the request object
210: */
211: private static final NestedReference referenceInstance(
212: HttpServletRequest request) {
213: /* get the old one if any */
214: NestedReference nr = (NestedReference) request
215: .getAttribute(NESTED_INCLUDES_KEY);
216:
217: // make a new one if required
218: if (nr == null) {
219: nr = new NestedReference();
220: request.setAttribute(NESTED_INCLUDES_KEY, nr);
221: }
222:
223: // return the reference
224: return nr;
225: }
226:
227: /* This property, providing the property to be appended, and the parent tag
228: * to append the property to, will calculate the stepping of the property
229: * and return the qualified nested property
230: *
231: * @param property the property which is to be appended nesting style
232: * @param parent the "dot notated" string representing the structure
233: * @return qualified nested property that the property param is to the parent
234: */
235: private static String calculateRelativeProperty(String property,
236: String parent) {
237: if (parent == null) {
238: parent = "";
239: }
240:
241: if (property == null) {
242: property = "";
243: }
244:
245: /* Special case... reference my parent's nested property.
246: Otherwise impossible for things like indexed properties */
247: if ("./".equals(property) || "this/".equals(property)) {
248: return parent;
249: }
250:
251: /* remove the stepping from the property */
252: String stepping;
253:
254: /* isolate a parent reference */
255: if (property.endsWith("/")) {
256: stepping = property;
257: property = "";
258: } else {
259: stepping = property.substring(0,
260: property.lastIndexOf('/') + 1);
261:
262: /* isolate the property */
263: property = property.substring(
264: property.lastIndexOf('/') + 1, property.length());
265: }
266:
267: if (stepping.startsWith("/")) {
268: /* return from root */
269: return property;
270: } else {
271: /* tokenize the nested property */
272: StringTokenizer proT = new StringTokenizer(parent, ".");
273: int propCount = proT.countTokens();
274:
275: /* tokenize the stepping */
276: StringTokenizer strT = new StringTokenizer(stepping, "/");
277: int count = strT.countTokens();
278:
279: if (count >= propCount) {
280: /* return from root */
281: return property;
282: } else {
283: /* append the tokens up to the token difference */
284: count = propCount - count;
285:
286: StringBuffer result = new StringBuffer();
287:
288: for (int i = 0; i < count; i++) {
289: result.append(proT.nextToken());
290: result.append('.');
291: }
292:
293: result.append(property);
294:
295: /* parent reference will have a dot on the end. Leave it off */
296: if (result.charAt(result.length() - 1) == '.') {
297: return result.substring(0, result.length() - 1);
298: } else {
299: return result.toString();
300: }
301: }
302: }
303: }
304: }
|