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