View Javadoc
1 /* 2 * $Header: /home/cvs/jakarta-commons-sandbox/jelly/src/java/org/apache/commons/jelly/impl/TagScript.java,v 1.19 2002/08/19 21:38:09 jstrachan Exp $ 3 * $Revision: 1.19 $ 4 * $Date: 2002/08/19 21:38:09 $ 5 * 6 * ==================================================================== 7 * 8 * The Apache Software License, Version 1.1 9 * 10 * Copyright (c) 1999-2002 The Apache Software Foundation. All rights 11 * reserved. 12 * 13 * Redistribution and use in source and binary forms, with or without 14 * modification, are permitted provided that the following conditions 15 * are met: 16 * 17 * 1. Redistributions of source code must retain the above copyright 18 * notice, this list of conditions and the following disclaimer. 19 * 20 * 2. Redistributions in binary form must reproduce the above copyright 21 * notice, this list of conditions and the following disclaimer in 22 * the documentation and/or other materials provided with the 23 * distribution. 24 * 25 * 3. The end-user documentation included with the redistribution, if 26 * any, must include the following acknowlegement: 27 * "This product includes software developed by the 28 * Apache Software Foundation (http://www.apache.org/)." 29 * Alternately, this acknowlegement may appear in the software itself, 30 * if and wherever such third-party acknowlegements normally appear. 31 * 32 * 4. The names "The Jakarta Project", "Commons", and "Apache Software 33 * Foundation" must not be used to endorse or promote products derived 34 * from this software without prior written permission. For written 35 * permission, please contact apache@apache.org. 36 * 37 * 5. Products derived from this software may not be called "Apache" 38 * nor may "Apache" appear in their names without prior written 39 * permission of the Apache Group. 40 * 41 * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED 42 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES 43 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 44 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR 45 * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 46 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 47 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF 48 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 49 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 50 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT 51 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 52 * SUCH DAMAGE. 53 * ==================================================================== 54 * 55 * This software consists of voluntary contributions made by many 56 * individuals on behalf of the Apache Software Foundation. For more 57 * information on the Apache Software Foundation, please see 58 * <http://www.apache.org/>;. 59 * 60 * $Id: TagScript.java,v 1.19 2002/08/19 21:38:09 jstrachan Exp $ 61 */ 62 package org.apache.commons.jelly.impl; 63 64 import java.beans.BeanInfo; 65 import java.beans.Introspector; 66 import java.beans.PropertyDescriptor; 67 import java.lang.reflect.Method; 68 import java.lang.reflect.InvocationTargetException; 69 import java.io.Writer; 70 import java.util.ArrayList; 71 import java.util.Hashtable; 72 import java.util.Iterator; 73 import java.util.List; 74 import java.util.Map; 75 76 import org.apache.commons.beanutils.ConvertUtils; 77 78 import org.apache.commons.jelly.CompilableTag; 79 import org.apache.commons.jelly.JellyContext; 80 import org.apache.commons.jelly.JellyException; 81 import org.apache.commons.jelly.DynaTag; 82 import org.apache.commons.jelly.Script; 83 import org.apache.commons.jelly.Tag; 84 import org.apache.commons.jelly.XMLOutput; 85 import org.apache.commons.jelly.expression.Expression; 86 87 import org.apache.commons.logging.Log; 88 import org.apache.commons.logging.LogFactory; 89 90 import org.xml.sax.Locator; 91 import org.xml.sax.SAXException; 92 93 /*** 94 * <p><code>TagScript</code> abstract base class for a 95 * script that evaluates a custom tag.</p> 96 * 97 * <b>Note</b> that this class should be re-entrant and used 98 * concurrently by multiple threads. 99 * 100 * @author <a href="mailto:jstrachan@apache.org">James Strachan</a> 101 * @version $Revision: 1.19 $ 102 */ 103 public abstract class TagScript implements Script { 104 105 /*** The Log to which logging calls will be made. */ 106 private static final Log log = LogFactory.getLog(TagScript.class); 107 108 /*** 109 * Thread local storage for the tag used by the current thread. 110 * This allows us to pool tag instances, per thread to reduce object construction 111 * over head, if we need it. 112 * 113 * Note that we could use the stack and create a new tag for each invocation 114 * if we made a slight change to the Script API to pass in the parent tag. 115 */ 116 private ThreadLocal tagHolder = new ThreadLocal(); 117 118 /*** The attribute expressions that are created */ 119 protected Map attributes = new Hashtable(); 120 121 /*** the optional namespaces Map of prefix -> URI */ 122 private Map namespacesMap; 123 124 /*** the Jelly file which caused the problem */ 125 private String fileName; 126 127 /*** the tag name which caused the problem */ 128 private String elementName; 129 130 /*** the line number of the tag */ 131 private int lineNumber = -1; 132 133 /*** the column number of the tag */ 134 private int columnNumber = -1; 135 136 /*** the factory of Tag instances */ 137 private TagFactory tagFactory; 138 139 /*** the body script used for this tag */ 140 private Script tagBody; 141 142 /*** the parent TagScript */ 143 private TagScript parent; 144 145 /*** 146 * @return a new TagScript based on whether 147 * the given Tag class is a bean tag or DynaTag 148 */ 149 public static TagScript newInstance(Class tagClass) { 150 TagFactory factory = new DefaultTagFactory(tagClass); 151 152 if ( DynaTag.class.isAssignableFrom(tagClass) ) { 153 return new DynaTagScript(factory); 154 } 155 return new BeanTagScript(factory); 156 } 157 158 public TagScript() { 159 } 160 161 public TagScript(TagFactory tagFactory) { 162 this.tagFactory = tagFactory; 163 } 164 165 public String toString() { 166 return super.toString() + "[tag=" + elementName + ";at=" + lineNumber + ":" + columnNumber + "]"; 167 } 168 169 /*** 170 * Compiles the tags body 171 */ 172 public Script compile() throws Exception { 173 if (tagBody != null) { 174 tagBody = tagBody.compile(); 175 } 176 return this; 177 } 178 179 /*** 180 * Sets the optional namespaces prefix -> URI map 181 */ 182 public void setNamespacesMap(Map namespacesMap) { 183 // lets check that this is a thread-safe map 184 if ( ! (namespacesMap instanceof Hashtable) ) { 185 namespacesMap = new Hashtable( namespacesMap ); 186 } 187 this.namespacesMap = namespacesMap; 188 } 189 190 /*** 191 * Configures this TagScript from the SAX Locator, setting the column 192 * and line numbers 193 */ 194 public void setLocator(Locator locator) { 195 setLineNumber( locator.getLineNumber() ); 196 setColumnNumber( locator.getColumnNumber() ); 197 } 198 199 200 /*** Add an initialization attribute for the tag. 201 * This method must be called after the setTag() method 202 */ 203 public void addAttribute(String name, Expression expression) { 204 if (log.isDebugEnabled()) { 205 log.debug("adding attribute name: " + name + " expression: " + expression); 206 } 207 attributes.put(name, expression); 208 } 209 210 // Properties 211 //------------------------------------------------------------------------- 212 213 /*** 214 * @return the tag to be evaluated, creating it lazily if required. 215 */ 216 public Tag getTag() throws Exception { 217 Tag tag = (Tag) tagHolder.get(); 218 if ( tag == null ) { 219 tag = createTag(); 220 if ( tag != null ) { 221 configureTag(tag); 222 tagHolder.set(tag); 223 } 224 } 225 return tag; 226 } 227 228 /*** 229 * Returns the Factory of Tag instances. 230 * @return the factory 231 */ 232 public TagFactory getTagFactory() { 233 return tagFactory; 234 } 235 236 /*** 237 * Sets the Factory of Tag instances. 238 * @param tagFactory The factory to set 239 */ 240 public void setTagFactory(TagFactory tagFactory) { 241 this.tagFactory = tagFactory; 242 } 243 244 /*** 245 * Returns the parent. 246 * @return TagScript 247 */ 248 public TagScript getParent() { 249 return parent; 250 } 251 252 /*** 253 * Returns the tagBody. 254 * @return Script 255 */ 256 public Script getTagBody() { 257 return tagBody; 258 } 259 260 /*** 261 * Sets the parent. 262 * @param parent The parent to set 263 */ 264 public void setParent(TagScript parent) { 265 this.parent = parent; 266 } 267 268 /*** 269 * Sets the tagBody. 270 * @param tagBody The tagBody to set 271 */ 272 public void setTagBody(Script tagBody) { 273 this.tagBody = tagBody; 274 } 275 276 /*** 277 * @return the Jelly file which caused the problem 278 */ 279 public String getFileName() { 280 return fileName; 281 } 282 283 /*** 284 * Sets the Jelly file which caused the problem 285 */ 286 public void setFileName(String fileName) { 287 this.fileName = fileName; 288 } 289 290 291 /*** 292 * @return the element name which caused the problem 293 */ 294 public String getElementName() { 295 return elementName; 296 } 297 298 /*** 299 * Sets the element name which caused the problem 300 */ 301 public void setElementName(String elementName) { 302 this.elementName = elementName; 303 } 304 /*** 305 * @return the line number of the tag 306 */ 307 public int getLineNumber() { 308 return lineNumber; 309 } 310 311 /*** 312 * Sets the line number of the tag 313 */ 314 public void setLineNumber(int lineNumber) { 315 this.lineNumber = lineNumber; 316 } 317 318 /*** 319 * @return the column number of the tag 320 */ 321 public int getColumnNumber() { 322 return columnNumber; 323 } 324 325 /*** 326 * Sets the column number of the tag 327 */ 328 public void setColumnNumber(int columnNumber) { 329 this.columnNumber = columnNumber; 330 } 331 332 // Implementation methods 333 //------------------------------------------------------------------------- 334 335 /*** 336 * Factory method to create a new Tag instance. 337 * The default implementation is to delegate to the TagFactory 338 */ 339 protected Tag createTag() throws Exception { 340 if ( tagFactory != null) { 341 return tagFactory.createTag(); 342 } 343 return null; 344 } 345 346 347 /*** 348 * Compiles a newly created tag if required, sets its parent and body. 349 */ 350 protected void configureTag(Tag tag) throws Exception { 351 if (tag instanceof CompilableTag) { 352 ((CompilableTag) tag).compile(); 353 } 354 Tag parentTag = null; 355 if ( parent != null ) { 356 parentTag = parent.getTag(); 357 } 358 tag.setParent( parentTag ); 359 tag.setBody( tagBody ); 360 } 361 362 /*** 363 * Flushes the current cached tag so that it will be created, lazily, next invocation 364 */ 365 protected void clearTag() { 366 tagHolder.set(null); 367 } 368 369 /*** 370 * Allows the script to set the tag instance to be used, such as in a StaticTagScript 371 * when a StaticTag is switched with a DynamicTag 372 */ 373 protected void setTag(Tag tag) { 374 tagHolder.set(tag); 375 } 376 377 /*** 378 * Output the new namespace prefixes used for this element 379 */ 380 protected void startNamespacePrefixes(XMLOutput output) throws SAXException { 381 if ( namespacesMap != null ) { 382 for ( Iterator iter = namespacesMap.entrySet().iterator(); iter.hasNext(); ) { 383 Map.Entry entry = (Map.Entry) iter.next(); 384 String prefix = (String) entry.getKey(); 385 String uri = (String) entry.getValue(); 386 output.startPrefixMapping(prefix, uri); 387 } 388 } 389 } 390 391 /*** 392 * End the new namespace prefixes mapped for the current element 393 */ 394 protected void endNamespacePrefixes(XMLOutput output) throws SAXException { 395 if ( namespacesMap != null ) { 396 for ( Iterator iter = namespacesMap.keySet().iterator(); iter.hasNext(); ) { 397 String prefix = (String) iter.next(); 398 output.endPrefixMapping(prefix); 399 } 400 } 401 } 402 403 /*** 404 * Converts the given value to the required type. 405 * 406 * @param value is the value to be converted. This will not be null 407 * @param requiredType the type that the value should be converted to 408 */ 409 protected Object convertType(Object value, Class requiredType) 410 throws Exception { 411 if (requiredType.isInstance(value)) { 412 return value; 413 } 414 if (value instanceof String) { 415 return ConvertUtils.convert((String) value, requiredType); 416 } 417 return value; 418 } 419 420 /*** 421 * A helper method to handle this non-Jelly exception. 422 * This method will rethrow the exception, wrapped in a JellyException 423 * while adding line number information etc. 424 */ 425 protected void handleException(Exception e) throws Exception { 426 log.error( "Caught exception: " + e, e ); 427 428 if ( e instanceof JellyException ) 429 { 430 e.fillInStackTrace(); 431 throw e; 432 } 433 434 if ( e instanceof InvocationTargetException) 435 { 436 throw new JellyException( ((InvocationTargetException)e).getTargetException(), 437 fileName, 438 elementName, 439 columnNumber, 440 lineNumber ); 441 } 442 443 throw new JellyException(e, fileName, elementName, columnNumber, lineNumber); 444 } 445 446 /*** 447 * Creates a new Jelly exception, adorning it with location information 448 */ 449 protected JellyException createJellyException(String reason) { 450 return new JellyException( 451 reason, fileName, elementName, columnNumber, lineNumber 452 ); 453 } 454 455 /*** 456 * Creates a new Jelly exception, adorning it with location information 457 */ 458 protected JellyException createJellyException(String reason, Exception cause) { 459 if ( cause instanceof JellyException ) 460 { 461 return (JellyException) cause; 462 } 463 464 if ( cause instanceof InvocationTargetException) 465 { 466 return new JellyException( 467 reason, 468 ((InvocationTargetException)cause).getTargetException(), 469 fileName, 470 elementName, 471 columnNumber, 472 lineNumber); 473 } 474 475 return new JellyException( 476 reason, cause, fileName, elementName, columnNumber, lineNumber 477 ); 478 } 479 480 /*** 481 * A helper method to handle this Jelly exception. 482 * This method adorns the JellyException with location information 483 * such as adding line number information etc. 484 */ 485 protected void handleException(JellyException e) throws Exception { 486 if (e.getLineNumber() == -1) { 487 e.setColumnNumber(columnNumber); 488 e.setLineNumber(lineNumber); 489 } 490 if ( e.getFileName() == null ) { 491 e.setFileName( fileName ); 492 } 493 if ( e.getElementName() == null ) { 494 e.setElementName( elementName ); 495 } 496 throw e; 497 } 498 499 }

This page was automatically generated by Maven