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