001 // Copyright 2005 The Apache Software Foundation 002 // 003 // Licensed under the Apache License, Version 2.0 (the "License"); 004 // you may not use this file except in compliance with the License. 005 // You may obtain a copy of the License at 006 // 007 // http://www.apache.org/licenses/LICENSE-2.0 008 // 009 // Unless required by applicable law or agreed to in writing, software 010 // distributed under the License is distributed on an "AS IS" BASIS, 011 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 012 // See the License for the specific language governing permissions and 013 // limitations under the License. 014 015 package org.apache.tapestry; 016 017 import java.util.ArrayList; 018 import java.util.List; 019 020 import org.apache.hivemind.ApplicationRuntimeException; 021 import org.apache.hivemind.HiveMind; 022 import org.apache.hivemind.Location; 023 import org.apache.hivemind.util.Defense; 024 025 /** 026 * Constants and static methods. 027 * 028 * @author Howard M. Lewis Ship 029 * @since 4.0 030 */ 031 public class TapestryUtils 032 { 033 private static final char QUOTE = '\''; 034 035 private static final char BACKSLASH = '\\'; 036 037 /** 038 * Stores an attribute into the request cycle, verifying that no object with that key is already 039 * present. 040 * 041 * @param cycle 042 * the cycle to store the attribute into 043 * @param key 044 * the key to store the attribute as 045 * @param object 046 * the attribute value to store 047 * @throws IllegalStateException 048 * if a non-null value has been stored into the cycle with the provided key. 049 */ 050 051 public static void storeUniqueAttribute(IRequestCycle cycle, String key, Object object) 052 { 053 Defense.notNull(cycle, "cycle"); 054 Defense.notNull(key, "key"); 055 Defense.notNull(object, "object"); 056 057 Object existing = cycle.getAttribute(key); 058 if (existing != null) 059 throw new IllegalStateException(TapestryMessages.nonUniqueAttribute( 060 object, 061 key, 062 existing)); 063 064 cycle.setAttribute(key, object); 065 } 066 067 public static final String PAGE_RENDER_SUPPORT_ATTRIBUTE = "org.apache.tapestry.PageRenderSupport"; 068 069 public static final String FORM_ATTRIBUTE = "org.apache.tapestry.Form"; 070 071 /** 072 * Stores the support object using {@link #storeUniqueAttribute(IRequestCycle, String, Object)}. 073 */ 074 075 public static void storePageRenderSupport(IRequestCycle cycle, PageRenderSupport support) 076 { 077 storeUniqueAttribute(cycle, PAGE_RENDER_SUPPORT_ATTRIBUTE, support); 078 } 079 080 /** 081 * Store the IForm instance using {@link #storeUniqueAttribute(IRequestCycle, String, Object)}. 082 */ 083 084 public static void storeForm(IRequestCycle cycle, IForm form) 085 { 086 storeUniqueAttribute(cycle, FORM_ATTRIBUTE, form); 087 } 088 089 /** 090 * Gets the previously stored {@link org.apache.tapestry.PageRenderSupport} object. 091 * 092 * @param cycle 093 * the request cycle storing the support object 094 * @param component 095 * the component which requires the support (used to report exceptions) 096 * @throws ApplicationRuntimeException 097 * if no support object has been stored 098 */ 099 100 public static PageRenderSupport getPageRenderSupport(IRequestCycle cycle, IComponent component) 101 { 102 Defense.notNull(component, "component"); 103 104 PageRenderSupport result = getOptionalPageRenderSupport(cycle); 105 if (result == null) 106 throw new ApplicationRuntimeException(TapestryMessages.noPageRenderSupport(component), 107 component.getLocation(), null); 108 109 return result; 110 } 111 112 /** 113 * Gets the previously stored {@link IForm} object. 114 * 115 * @param cycle 116 * the request cycle storing the support object 117 * @param component 118 * the component which requires the form (used to report exceptions) 119 * @throws ApplicationRuntimeException 120 * if no form object has been stored 121 */ 122 public static IForm getForm(IRequestCycle cycle, IComponent component) 123 { 124 Defense.notNull(cycle, "cycle"); 125 Defense.notNull(component, "component"); 126 127 IForm result = (IForm) cycle.getAttribute(FORM_ATTRIBUTE); 128 129 if (result == null) 130 throw new ApplicationRuntimeException(TapestryMessages.noForm(component), component 131 .getLocation(), null); 132 133 return result; 134 } 135 136 public static void removePageRenderSupport(IRequestCycle cycle) 137 { 138 cycle.removeAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE); 139 } 140 141 public static void removeForm(IRequestCycle cycle) 142 { 143 cycle.removeAttribute(FORM_ATTRIBUTE); 144 } 145 146 /** 147 * Returns the {@link PageRenderSupport} object if previously stored, or null otherwise. 148 * This is used in the rare case that a component wishes to adjust its behavior based on whether 149 * the page render support services are avaiable (typically, adjust for whether enclosed by a 150 * Body component, or not). 151 */ 152 153 public static PageRenderSupport getOptionalPageRenderSupport(IRequestCycle cycle) 154 { 155 return (PageRenderSupport) cycle.getAttribute(PAGE_RENDER_SUPPORT_ATTRIBUTE); 156 } 157 158 /** 159 * Splits a string using the default delimiter of ','. 160 */ 161 162 public static String[] split(String input) 163 { 164 return split(input, ','); 165 } 166 167 /** 168 * Splits a single string into an array of strings, using a specific delimiter character. 169 */ 170 171 public static String[] split(String input, char delimiter) 172 { 173 if (HiveMind.isBlank(input)) 174 return new String[0]; 175 176 List strings = new ArrayList(); 177 178 char[] buffer = input.toCharArray(); 179 180 int start = 0; 181 int length = 0; 182 183 for (int i = 0; i < buffer.length; i++) 184 { 185 if (buffer[i] != delimiter) 186 { 187 length++; 188 continue; 189 } 190 191 // Consecutive delimiters will result in a sequence 192 // of empty strings. 193 194 String token = new String(buffer, start, length); 195 strings.add(token); 196 197 start = i + 1; 198 length = 0; 199 } 200 201 // If the string contains no delimiters, then 202 // wrap it an an array and return it. 203 204 if (start == 0 && length == buffer.length) 205 { 206 return new String[] 207 { input }; 208 } 209 210 // The final token. 211 String token = new String(buffer, start, length); 212 strings.add(token); 213 214 return (String[]) strings.toArray(new String[strings.size()]); 215 } 216 217 /** 218 * Enquotes a string within single quotes, ready for insertion as part of a block of JavaScript. 219 * Single quotes and backslashes within the input string are properly escaped. 220 */ 221 222 public static String enquote(String input) 223 { 224 Defense.notNull(input, "input"); 225 226 char[] chars = input.toCharArray(); 227 228 // Add room for the two quotes and a couple of escaped characters 229 230 StringBuffer buffer = new StringBuffer(chars.length + 5); 231 232 buffer.append(QUOTE); 233 234 for (int i = 0; i < chars.length; i++) 235 { 236 char ch = chars[i]; 237 238 if (ch == QUOTE || ch == BACKSLASH) 239 buffer.append(BACKSLASH); 240 241 buffer.append(ch); 242 } 243 244 buffer.append(QUOTE); 245 246 return buffer.toString(); 247 } 248 249 /** 250 * A Tapestry component id is a little more liberal than an XML NMTOKEN. NMTOKEN must be 251 * [A-Za-z][A-Za-z0-9:_.-]*, but a component id might include a leading dollar sign (for an 252 * anonymous component with a fabricated id). 253 */ 254 255 public static String convertTapestryIdToNMToken(String baseId) 256 { 257 String result = baseId.replace('$', '_'); 258 259 while (result.startsWith("_")) 260 result = result.substring(1); 261 262 return result; 263 } 264 265 /** 266 * Converts a clientId into a client-side DOM reference; i.e. 267 * <code>document.getElementById('<i>id</i>')</code>. 268 */ 269 270 public static String buildClientElementReference(String clientId) 271 { 272 Defense.notNull(clientId, "clientId"); 273 274 return "document.getElementById('" + clientId + "')"; 275 } 276 277 /** 278 * Used by some generated code; obtains a component and ensures it is of the correct type. 279 */ 280 281 public static IComponent getComponent(IComponent container, String componentId, 282 Class expectedType, Location location) 283 { 284 Defense.notNull(container, "container"); 285 Defense.notNull(componentId, "componentId"); 286 Defense.notNull(expectedType, "expectedType"); 287 // Don't always have a location 288 289 IComponent component = null; 290 291 try 292 { 293 component = container.getComponent(componentId); 294 } 295 catch (Exception ex) 296 { 297 throw new ApplicationRuntimeException(ex.getMessage(), location, ex); 298 } 299 300 if (!expectedType.isAssignableFrom(component.getClass())) 301 throw new ApplicationRuntimeException(TapestryMessages.componentWrongType( 302 component, 303 expectedType), location, null); 304 305 return component; 306 } 307 }