KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > apache > cocoon > generation > JXTemplateGenerator


1 /*
2  * Copyright 1999-2005 The Apache Software Foundation.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * You may obtain a copy of the License at
7  *
8  * http://www.apache.org/licenses/LICENSE-2.0
9  *
10  * Unless required by applicable law or agreed to in writing, software
11  * distributed under the License is distributed on an "AS IS" BASIS,
12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  * See the License for the specific language governing permissions and
14  * limitations under the License.
15  */

16 package org.apache.cocoon.generation;
17
18 import java.beans.PropertyDescriptor JavaDoc;
19 import java.io.CharArrayReader JavaDoc;
20 import java.io.IOException JavaDoc;
21 import java.io.PrintStream JavaDoc;
22 import java.io.PrintWriter JavaDoc;
23 import java.io.Serializable JavaDoc;
24 import java.io.StringReader JavaDoc;
25 import java.lang.reflect.Field JavaDoc;
26 import java.lang.reflect.InvocationTargetException JavaDoc;
27 import java.lang.reflect.Method JavaDoc;
28 import java.text.DateFormat JavaDoc;
29 import java.text.DecimalFormat JavaDoc;
30 import java.text.DecimalFormatSymbols JavaDoc;
31 import java.text.NumberFormat JavaDoc;
32 import java.text.SimpleDateFormat JavaDoc;
33 import java.util.Enumeration JavaDoc;
34 import java.util.HashMap JavaDoc;
35 import java.util.Iterator JavaDoc;
36 import java.util.LinkedList JavaDoc;
37 import java.util.List JavaDoc;
38 import java.util.Locale JavaDoc;
39 import java.util.Map JavaDoc;
40 import java.util.Properties JavaDoc;
41 import java.util.Stack JavaDoc;
42 import java.util.TimeZone JavaDoc;
43
44 import org.apache.avalon.framework.parameters.Parameters;
45 import org.apache.avalon.framework.service.ServiceException;
46 import org.apache.avalon.framework.service.ServiceManager;
47 import org.apache.cocoon.ProcessingException;
48 import org.apache.cocoon.caching.CacheableProcessingComponent;
49 import org.apache.cocoon.components.flow.FlowHelper;
50 import org.apache.cocoon.components.flow.WebContinuation;
51 import org.apache.cocoon.components.flow.javascript.fom.FOM_JavaScriptFlowHelper;
52 import org.apache.cocoon.components.source.SourceUtil;
53 import org.apache.cocoon.environment.ObjectModelHelper;
54 import org.apache.cocoon.environment.Request;
55 import org.apache.cocoon.environment.SourceResolver;
56 import org.apache.cocoon.transformation.ServiceableTransformer;
57 import org.apache.cocoon.util.jxpath.NamespacesTablePointer;
58 import org.apache.cocoon.util.location.LocatedRuntimeException;
59 import org.apache.cocoon.util.location.Location;
60 import org.apache.cocoon.util.location.LocationUtils;
61 import org.apache.cocoon.xml.IncludeXMLConsumer;
62 import org.apache.cocoon.xml.NamespacesTable;
63 import org.apache.cocoon.xml.RedundantNamespacesFilter;
64 import org.apache.cocoon.xml.XMLConsumer;
65 import org.apache.cocoon.xml.XMLUtils;
66 import org.apache.cocoon.xml.dom.DOMBuilder;
67 import org.apache.cocoon.xml.dom.DOMStreamer;
68 import org.apache.commons.jexl.Expression;
69 import org.apache.commons.jexl.ExpressionFactory;
70 import org.apache.commons.jexl.JexlContext;
71 import org.apache.commons.jexl.util.Introspector;
72 import org.apache.commons.jexl.util.introspection.Info;
73 import org.apache.commons.jexl.util.introspection.UberspectImpl;
74 import org.apache.commons.jexl.util.introspection.VelMethod;
75 import org.apache.commons.jexl.util.introspection.VelPropertyGet;
76 import org.apache.commons.jexl.util.introspection.VelPropertySet;
77 import org.apache.commons.jxpath.*;
78 import org.apache.commons.lang.ArrayUtils;
79 import org.apache.commons.lang.StringUtils;
80 import org.apache.excalibur.source.Source;
81 import org.apache.excalibur.source.SourceException;
82 import org.apache.excalibur.source.SourceValidity;
83 import org.apache.excalibur.xml.sax.XMLizable;
84 import org.mozilla.javascript.Context;
85 import org.mozilla.javascript.Function;
86 import org.mozilla.javascript.JavaScriptException;
87 import org.mozilla.javascript.NativeArray;
88 import org.mozilla.javascript.NativeJavaClass;
89 import org.mozilla.javascript.ScriptRuntime;
90 import org.mozilla.javascript.Scriptable;
91 import org.mozilla.javascript.ScriptableObject;
92 import org.mozilla.javascript.Undefined;
93 import org.mozilla.javascript.Wrapper;
94 import org.w3c.dom.Node JavaDoc;
95 import org.w3c.dom.NodeList JavaDoc;
96 import org.xml.sax.Attributes JavaDoc;
97 import org.xml.sax.ContentHandler JavaDoc;
98 import org.xml.sax.Locator JavaDoc;
99 import org.xml.sax.SAXException JavaDoc;
100 import org.xml.sax.ext.LexicalHandler JavaDoc;
101 import org.xml.sax.helpers.AttributesImpl JavaDoc;
102
103 /**
104  * @cocoon.sitemap.component.documentation
105  * Provides a generic page template with embedded JSTL and XPath
106  * expression substitution to access data sent by Cocoon Flowscripts.
107  *
108  * @cocoon.sitemap.component.name jx
109  * @cocoon.sitemap.component.label content
110  * @cocoon.sitemap.component.logger sitemap.generator.jx
111  *
112  * @cocoon.sitemap.component.pooling.max 16
113  *
114  * @version $Id: JXTemplateGenerator.java 314824 2005-10-12 08:41:55Z bruno $
115  */

116 public class JXTemplateGenerator extends ServiceableGenerator implements CacheableProcessingComponent {
117
118     // Quick'n dirty hack to replace all SAXParseException by a located runtime exception
119
private static final class JXTException extends LocatedRuntimeException {
120         JXTException(String JavaDoc message, Location loc, Throwable JavaDoc thr) {
121             super(message, thr, loc);
122         }
123     }
124
125     private static final JXPathContextFactory jxpathContextFactory = JXPathContextFactory.newInstance();
126
127     private static final Attributes JavaDoc EMPTY_ATTRS = XMLUtils.EMPTY_ATTRIBUTES;
128
129     private final NamespacesTable namespaces = new NamespacesTable();
130
131     private static final Iterator JavaDoc EMPTY_ITER = new Iterator JavaDoc() {
132         public boolean hasNext() {
133             return false;
134         }
135
136         public Object JavaDoc next() {
137             return null;
138         }
139
140         public void remove() {
141             // EMPTY
142
}
143     };
144
145     private static final Iterator JavaDoc NULL_ITER = new Iterator JavaDoc() {
146             public boolean hasNext() {
147                 return true;
148             }
149
150             public Object JavaDoc next() {
151                 return null;
152             }
153
154             public void remove() {
155                 // EMPTY
156
}
157     };
158
159     private XMLConsumer getConsumer() {
160         return this.xmlConsumer;
161     }
162
163     public static class ErrorHolder extends Exception JavaDoc {
164
165         private Error JavaDoc err;
166
167         public ErrorHolder(Error JavaDoc err) {
168             super(err.getMessage());
169             this.err = err;
170         }
171
172         public void printStackTrace(PrintStream JavaDoc ps) {
173             err.printStackTrace(ps);
174         }
175
176         public void printStackTrace(PrintWriter JavaDoc pw) {
177             err.printStackTrace(pw);
178         }
179
180         public void printStackTrace() {
181             err.printStackTrace();
182         }
183
184         public Error JavaDoc getError() {
185             return err;
186         }
187
188     }
189
190     /**
191      * Facade to the Location to be set on the consumer prior to
192      * sending other events, location member changeable
193      */

194     public static class LocationFacade implements Locator JavaDoc {
195         private Location locator;
196
197         public LocationFacade(Location initialLocation) {
198             this.locator = initialLocation;
199         }
200
201         public void setDocumentLocation(Location newLocation) {
202             this.locator = newLocation;
203         }
204
205         public int getColumnNumber() {
206             return this.locator.getColumnNumber();
207         }
208
209         public int getLineNumber() {
210             return this.locator.getLineNumber();
211         }
212
213         public String JavaDoc getPublicId() {
214             return null;
215         }
216
217         public String JavaDoc getSystemId() {
218             return this.locator.getURI();
219         }
220     }
221
222     /**
223      * Jexl Introspector that supports Rhino JavaScript objects
224      * as well as Java Objects
225      */

226     static class JSIntrospector extends UberspectImpl {
227
228         static class JSMethod implements VelMethod {
229
230             Scriptable scope;
231             String JavaDoc name;
232
233             public JSMethod(Scriptable scope, String JavaDoc name) {
234                 this.scope = scope;
235                 this.name = name;
236             }
237
238             public Object JavaDoc invoke(Object JavaDoc thisArg, Object JavaDoc[] args) throws Exception JavaDoc {
239                 Context cx = Context.enter();
240                 try {
241                     Object JavaDoc result;
242                     Scriptable thisObj = !(thisArg instanceof Scriptable) ?
243                             Context.toObject(thisArg, scope) : (Scriptable)thisArg;
244                     result = ScriptableObject.getProperty(thisObj, name);
245                     Object JavaDoc[] newArgs = null;
246                     if (args != null) {
247                         newArgs = new Object JavaDoc[args.length];
248                         int len = args.length;
249                         for (int i = 0; i < len; i++) {
250                             newArgs[i] = args[i];
251                             if (args[i] != null &&
252                                 !(args[i] instanceof Number JavaDoc) &&
253                                 !(args[i] instanceof Boolean JavaDoc) &&
254                                 !(args[i] instanceof String JavaDoc) &&
255                                 !(args[i] instanceof Scriptable)) {
256                                 newArgs[i] = Context.toObject(args[i], scope);
257                             }
258                         }
259                     }
260                     result = ScriptRuntime.call(cx, result, thisObj, newArgs, scope);
261                     if (result == Undefined.instance || result == Scriptable.NOT_FOUND) {
262                         result = null;
263                     } else if (!(result instanceof NativeJavaClass)) {
264                         while (result instanceof Wrapper) {
265                             result = ((Wrapper)result).unwrap();
266                         }
267                     }
268                     return result;
269                 } catch (JavaScriptException e) {
270                     throw new java.lang.reflect.InvocationTargetException JavaDoc(e);
271                 } finally {
272                     Context.exit();
273                 }
274             }
275
276             public boolean isCacheable() {
277                 return false;
278             }
279
280             public String JavaDoc getMethodName() {
281                 return name;
282             }
283
284             public Class JavaDoc getReturnType() {
285                 return Object JavaDoc.class;
286             }
287
288         }
289
290         static class JSPropertyGet implements VelPropertyGet {
291
292             Scriptable scope;
293             String JavaDoc name;
294
295             public JSPropertyGet(Scriptable scope, String JavaDoc name) {
296                 this.scope = scope;
297                 this.name = name;
298             }
299
300             public Object JavaDoc invoke(Object JavaDoc thisArg) throws Exception JavaDoc {
301                 Context cx = Context.enter();
302                 try {
303                     Scriptable thisObj = !(thisArg instanceof Scriptable) ?
304                             Context.toObject(thisArg, scope) : (Scriptable)thisArg;
305                     Object JavaDoc result = ScriptableObject.getProperty(thisObj, name);
306                     if (result == Scriptable.NOT_FOUND) {
307                         result = ScriptableObject.getProperty(thisObj, "get" + StringUtils.capitalize(name));
308                         if (result != Scriptable.NOT_FOUND && result instanceof Function) {
309                             try {
310                                 result = ((Function)result).call(
311                                         cx, ScriptableObject.getTopLevelScope(thisObj), thisObj, new Object JavaDoc[] {});
312                             } catch (JavaScriptException exc) {
313                                 exc.printStackTrace();
314                                 result = null;
315                             }
316                         }
317                     }
318                     if (result == Scriptable.NOT_FOUND || result == Undefined.instance) {
319                         result = null;
320                     } else if (result instanceof Wrapper && !(result instanceof NativeJavaClass)) {
321                         result = ((Wrapper)result).unwrap();
322                     }
323                     return result;
324                 } finally {
325                     Context.exit();
326                 }
327             }
328
329             public boolean isCacheable() {
330                 return false;
331             }
332
333             public String JavaDoc getMethodName() {
334                 return name;
335             }
336         }
337
338         static class JSPropertySet implements VelPropertySet {
339
340             Scriptable scope;
341             String JavaDoc name;
342
343             public JSPropertySet(Scriptable scope, String JavaDoc name) {
344                 this.scope = scope;
345                 this.name = name;
346             }
347
348             public Object JavaDoc invoke(Object JavaDoc thisArg, Object JavaDoc rhs) throws Exception JavaDoc {
349                 Context.enter();
350                 try {
351                     Scriptable thisObj;
352                     Object JavaDoc arg = rhs;
353                     if (!(thisArg instanceof Scriptable)) {
354                         thisObj = Context.toObject(thisArg, scope);
355                     } else {
356                         thisObj = (Scriptable)thisArg;
357                     }
358                     if (arg != null &&
359                         !(arg instanceof Number JavaDoc) &&
360                         !(arg instanceof Boolean JavaDoc) &&
361                         !(arg instanceof String JavaDoc) &&
362                         !(arg instanceof Scriptable)) {
363                         arg = Context.toObject(arg, scope);
364                     }
365                     ScriptableObject.putProperty(thisObj, name, arg);
366                     return rhs;
367                 } finally {
368                     Context.exit();
369                 }
370             }
371
372             public boolean isCacheable() {
373                 return false;
374             }
375
376             public String JavaDoc getMethodName() {
377                 return name;
378             }
379         }
380
381         static class NativeArrayIterator implements Iterator JavaDoc {
382
383             NativeArray arr;
384             int index;
385
386             public NativeArrayIterator(NativeArray arr) {
387                 this.arr = arr;
388                 this.index = 0;
389             }
390
391             public boolean hasNext() {
392                 return index < (int)arr.jsGet_length();
393             }
394
395             public Object JavaDoc next() {
396                 Context.enter();
397                 try {
398                     Object JavaDoc result = arr.get(index++, arr);
399                     if (result == Undefined.instance ||
400                         result == Scriptable.NOT_FOUND) {
401                         result = null;
402                     } else {
403                         if (!(result instanceof NativeJavaClass)) {
404                             while (result instanceof Wrapper) {
405                                 result = ((Wrapper)result).unwrap();
406                             }
407                         }
408                     }
409                     return result;
410                 } finally {
411                     Context.exit();
412                 }
413             }
414
415             public void remove() {
416                 arr.delete(index);
417             }
418         }
419
420         static class ScriptableIterator implements Iterator JavaDoc {
421
422             Scriptable scope;
423             Object JavaDoc[] ids;
424             int index;
425
426             public ScriptableIterator(Scriptable scope) {
427                 this.scope = scope;
428                 this.ids = scope.getIds();
429                 this.index = 0;
430             }
431
432             public boolean hasNext() {
433                 return index < ids.length;
434             }
435
436             public Object JavaDoc next() {
437                 Context.enter();
438                 try {
439                     Object JavaDoc result = ScriptableObject.getProperty(scope, ids[index++].toString());
440                     if (result == Undefined.instance || result == Scriptable.NOT_FOUND) {
441                         result = null;
442                     } else if (!(result instanceof NativeJavaClass)) {
443                         while (result instanceof Wrapper) {
444                             result = ((Wrapper)result).unwrap();
445                         }
446                     }
447                     return result;
448                 } finally {
449                     Context.exit();
450                 }
451             }
452
453             public void remove() {
454                 Context.enter();
455                 try {
456                     scope.delete(ids[index].toString());
457                 } finally {
458                     Context.exit();
459                 }
460             }
461         }
462
463         public Iterator JavaDoc getIterator(Object JavaDoc obj, Info i) throws Exception JavaDoc {
464             if (!(obj instanceof Scriptable)) {
465                 // support Enumeration
466
if (obj instanceof Enumeration JavaDoc) {
467                     final Enumeration JavaDoc e = (Enumeration JavaDoc)obj;
468                     return new Iterator JavaDoc() {
469
470                             public boolean hasNext() {
471                                 return e.hasMoreElements();
472                             }
473
474                             public Object JavaDoc next() {
475                                 return e.nextElement();
476                             }
477
478                             public void remove() {
479                                 // no action
480
}
481
482                         };
483                 }
484                 if (obj instanceof Iterator JavaDoc) {
485                     // support Iterator
486
return (Iterator JavaDoc)obj;
487                 }
488                 return super.getIterator(obj, i);
489             }
490             if (obj instanceof NativeArray) {
491                 return new NativeArrayIterator((NativeArray)obj);
492             }
493             return new ScriptableIterator((Scriptable)obj);
494         }
495
496         public VelMethod getMethod(Object JavaDoc obj, String JavaDoc methodName, Object JavaDoc[] args, Info i) throws Exception JavaDoc {
497             return !(obj instanceof Scriptable) ?
498                     super.getMethod(obj, methodName, args, i) : new JSMethod((Scriptable)obj, methodName);
499         }
500
501         public VelPropertyGet getPropertyGet(Object JavaDoc obj, String JavaDoc identifier, Info i) throws Exception JavaDoc {
502             return !(obj instanceof Scriptable) ?
503                     super.getPropertyGet(obj, identifier, i) : new JSPropertyGet((Scriptable)obj, identifier);
504         }
505
506         public VelPropertySet getPropertySet(Object JavaDoc obj, String JavaDoc identifier, Object JavaDoc arg, Info i) throws Exception JavaDoc {
507             return !(obj instanceof Scriptable) ?
508                     super.getPropertySet(obj, identifier, arg, i) : new JSPropertySet((Scriptable)obj, identifier);
509         }
510     }
511
512     static class MyJexlContext extends HashMap JavaDoc implements JexlContext {
513
514         private MyJexlContext closure;
515
516         MyJexlContext() {
517             this(null);
518         }
519
520         MyJexlContext(MyJexlContext closure) {
521             this.closure = closure;
522         }
523
524         public Map JavaDoc getVars() {
525             return this;
526         }
527
528         public void setVars(Map JavaDoc map) {
529             putAll(map);
530         }
531
532         public boolean containsKey(Object JavaDoc key) {
533             return this.get(key) !=null;
534         }
535
536         public Object JavaDoc get(Object JavaDoc key) {
537             if (key.equals("this")) {
538                 return this;
539             }
540             Object JavaDoc result = super.get(key);
541             if (result == null && closure != null) {
542                 result = closure.get(key);
543             }
544             return result;
545         }
546     }
547
548     static class MyVariables implements Variables {
549
550         MyVariables closure;
551
552         Map JavaDoc localVariables = new HashMap JavaDoc();
553
554         static final String JavaDoc[] VARIABLES = new String JavaDoc[] {
555             "cocoon",
556             "continuation",
557             "flowContext",
558             "request",
559             "response",
560             "context",
561             "session",
562             "parameters"
563         };
564
565         Object JavaDoc cocoon;
566
567         // backward compatibility
568
Object JavaDoc bean, kont, request, response,
569             session, context, parameters;
570
571         MyVariables(Object JavaDoc cocoon,
572                     Object JavaDoc bean,
573                     WebContinuation kont,
574                     Object JavaDoc request,
575                     Object JavaDoc session,
576                     Object JavaDoc context,
577                     Object JavaDoc parameters) {
578             this.cocoon = cocoon;
579             this.bean = bean;
580             this.kont = kont;
581             this.request = request;
582             this.session = session;
583             this.context = context;
584             this.parameters = parameters;
585         }
586
587         public MyVariables(MyVariables parent) {
588             this.closure = parent;
589         }
590
591         public boolean isDeclaredVariable(String JavaDoc varName) {
592             int len = VARIABLES.length;
593             for (int i = 0; i < len; i++) {
594                 if (varName.equals(VARIABLES[i])) {
595                     return true;
596                 }
597             }
598             if (localVariables.containsKey(varName)) {
599                 return true;
600             }
601             if (closure != null) {
602                 return closure.isDeclaredVariable(varName);
603             }
604             return false;
605         }
606
607         public Object JavaDoc getVariable(String JavaDoc varName) {
608             Object JavaDoc result = localVariables.get(varName);
609             if (result != null) {
610                 return result;
611             }
612             if (closure != null) {
613                 return closure.getVariable(varName);
614             }
615             if (varName.equals("cocoon")) {
616                 return cocoon;
617             }
618             // backward compatibility
619
if (varName.equals("continuation")) {
620                 return kont;
621             } else if (varName.equals("flowContext")) {
622                 return bean;
623             } else if (varName.equals("request")) {
624                 return request;
625             } else if (varName.equals("session")) {
626                 return session;
627             } else if (varName.equals("context")) {
628                 return context;
629             } else if (varName.equals("parameters")) {
630                 return parameters;
631             }
632             return null;
633         }
634
635         public void declareVariable(String JavaDoc varName, Object JavaDoc value) {
636             localVariables.put(varName, value);
637         }
638
639         public void undeclareVariable(String JavaDoc varName) {
640             localVariables.remove(varName);
641         }
642     }
643
644     static {
645         // Hack: there's no _nice_ way to add my introspector to Jexl right now
646
try {
647             Field JavaDoc field = Introspector.class.getDeclaredField("uberSpect");
648             field.setAccessible(true);
649             field.set(null, new JSIntrospector());
650         } catch (Exception JavaDoc e) {
651             e.printStackTrace();
652         }
653     }
654
655
656     /** The namespace used by this generator */
657     public final static String JavaDoc NS = "http://apache.org/cocoon/templates/jx/1.0";
658
659     final static String JavaDoc TEMPLATE = "template";
660     final static String JavaDoc FOR_EACH = "forEach";
661     final static String JavaDoc IF = "if";
662     final static String JavaDoc CHOOSE = "choose";
663     final static String JavaDoc WHEN = "when";
664     final static String JavaDoc OTHERWISE = "otherwise";
665     final static String JavaDoc OUT = "out";
666     final static String JavaDoc IMPORT = "import";
667     final static String JavaDoc SET = "set";
668     final static String JavaDoc MACRO = "macro";
669     final static String JavaDoc EVALBODY = "evalBody";
670     final static String JavaDoc EVAL = "eval";
671     final static String JavaDoc PARAMETER = "parameter";
672     final static String JavaDoc FORMAT_NUMBER = "formatNumber";
673     final static String JavaDoc FORMAT_DATE = "formatDate";
674     final static String JavaDoc COMMENT = "comment";
675     final static String JavaDoc CACHE_KEY = "cache-key";
676     final static String JavaDoc VALIDITY = "cache-validity";
677
678     /**
679      * Compile a single Jexl expr (contained in ${}) or XPath expression
680      * (contained in #{})
681      */

682
683     private static JXTExpression compileExpr(String JavaDoc expr, String JavaDoc errorPrefix, Location location) throws JXTException {
684         try {
685             return compileExpr(expr);
686         } catch (Exception JavaDoc exc) {
687             throw new JXTException(errorPrefix + exc.getMessage(), location, exc);
688         }
689     }
690
691     private static JXTExpression compileExpr(String JavaDoc inStr) throws Exception JavaDoc {
692         try {
693             if (inStr == null) {
694                 return null;
695             }
696             StringReader JavaDoc in = new StringReader JavaDoc(inStr.trim());
697             int ch;
698             boolean xpath = false;
699             boolean inExpr = false;
700             StringBuffer JavaDoc expr = new StringBuffer JavaDoc();
701             while ((ch = in.read()) != -1) {
702                 char c = (char)ch;
703                 if (inExpr) {
704                     if (c == '\\') {
705                         ch = in.read();
706                         expr.append((ch == -1) ? '\\' : (char)ch);
707                     } else if (c == '}') {
708                         return compile(expr.toString(), xpath);
709                     } else {
710                         expr.append(c);
711                     }
712                 } else {
713                     if (c == '$' || c == '#') {
714                         ch = in.read();
715                         if (ch == '{') {
716                             inExpr = true;
717                             xpath = c == '#';
718                             continue;
719                         }
720                     }
721                     // hack: invalid expression?
722
// just return the original and swallow exception
723
return new JXTExpression(inStr, null);
724                 }
725             }
726             if (inExpr) {
727                 // unclosed #{} or ${}
728
throw new Exception JavaDoc("Unterminated " + (xpath ? "#" : "$") + "{");
729             }
730         } catch (IOException JavaDoc ignored) {
731             ignored.printStackTrace();
732         }
733         return new JXTExpression(inStr, null);
734     }
735
736     /*
737      * Compile an integer expression (returns either a Compiled Expression
738      * or an Integer literal)
739      */

740     private static JXTExpression compileInt(String JavaDoc val, String JavaDoc msg, Location location) throws SAXException JavaDoc {
741         JXTExpression res = compileExpr(val, msg, location);
742         if (res != null) {
743             if (res.compiledExpression == null) {
744                 res.compiledExpression = Integer.valueOf(res.raw);
745             }
746             return res;
747         }
748         return null;
749     }
750
751     private static JXTExpression compileBoolean(String JavaDoc val, String JavaDoc msg, Location location) throws SAXException JavaDoc {
752         JXTExpression res = compileExpr(val, msg, location);
753         if (res != null) {
754             if (res.compiledExpression == null) {
755                 res.compiledExpression = Boolean.valueOf(res.raw);
756             }
757             return res;
758         }
759         return null;
760     }
761
762     private static JXTExpression compile(final String JavaDoc variable, boolean xpath) throws Exception JavaDoc {
763         Object JavaDoc compiled;
764         if (xpath) {
765             compiled = JXPathContext.compile(variable);
766         } else {
767             compiled = ExpressionFactory.createExpression(variable);
768         }
769         return new JXTExpression(variable, compiled);
770     }
771
772     static private Object JavaDoc getValue(JXTExpression expr, JexlContext jexlContext,
773                                 JXPathContext jxpathContext, Boolean JavaDoc lenient) throws Exception JavaDoc {
774         if (expr != null) {
775             Object JavaDoc compiled = expr.compiledExpression;
776             try {
777                 if (compiled instanceof CompiledExpression) {
778                     CompiledExpression e = (CompiledExpression)compiled;
779                     boolean oldLenient = jxpathContext.isLenient();
780                     if (lenient != null) {
781                         jxpathContext.setLenient(lenient.booleanValue());
782                     }
783                     try {
784                         return e.getValue(jxpathContext);
785                     } finally {
786                         jxpathContext.setLenient(oldLenient);
787                     }
788                 } else if (compiled instanceof Expression) {
789                     Expression e = (Expression)compiled;
790                     return e.evaluate(jexlContext);
791                 }
792                 return compiled;
793             } catch (InvocationTargetException JavaDoc e) {
794                 Throwable JavaDoc t = e.getTargetException();
795                 if (t instanceof Exception JavaDoc) {
796                     throw (Exception JavaDoc)t;
797                 }
798                 throw (Error JavaDoc)t;
799             }
800         } else {
801             return null;
802         }
803     }
804
805     static private Object JavaDoc getValue(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception JavaDoc {
806         return getValue(expr, jexlContext, jxpathContext, null);
807     }
808
809     static private int getIntValue(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception JavaDoc {
810         Object JavaDoc res = getValue(expr, jexlContext, jxpathContext);
811         return res instanceof Number JavaDoc ? ((Number JavaDoc)res).intValue() : 0;
812     }
813
814     static private Number JavaDoc getNumberValue(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception JavaDoc {
815         Object JavaDoc res = getValue(expr, jexlContext, jxpathContext);
816         if (res instanceof Number JavaDoc) {
817             return (Number JavaDoc)res;
818         }
819         if (res != null) {
820             return Double.valueOf(res.toString());
821         }
822         return null;
823     }
824
825     static private String JavaDoc getStringValue(JXTExpression expr, JexlContext jexlContext,
826                                 JXPathContext jxpathContext) throws Exception JavaDoc {
827         Object JavaDoc res = getValue(expr, jexlContext, jxpathContext);
828         if (res != null) {
829             return res.toString();
830         }
831         if (expr != null && expr.compiledExpression == null) {
832             return expr.raw;
833         }
834         return null;
835     }
836
837     static private Boolean JavaDoc getBooleanValue(JXTExpression expr, JexlContext jexlContext,
838                                     JXPathContext jxpathContext) throws Exception JavaDoc {
839         Object JavaDoc res = getValue(expr, jexlContext, jxpathContext);
840         return res instanceof Boolean JavaDoc ? (Boolean JavaDoc)res : null;
841     }
842
843     private Object JavaDoc getNode(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception JavaDoc {
844         return getNode(expr, jexlContext, jxpathContext, null);
845     }
846
847     // Hack: try to prevent JXPath from converting result to a String
848
private Object JavaDoc getNode(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext, Boolean JavaDoc lenient)
849             throws Exception JavaDoc {
850         try {
851             Object JavaDoc compiled = expr.compiledExpression;
852             if (compiled instanceof CompiledExpression) {
853                 CompiledExpression e = (CompiledExpression)compiled;
854                 boolean oldLenient = jxpathContext.isLenient();
855                 if (lenient != null) jxpathContext.setLenient(lenient.booleanValue());
856                 try {
857                     Iterator JavaDoc iter = e.iteratePointers(jxpathContext);
858                     if (iter.hasNext()) {
859                         Pointer first = (Pointer)iter.next();
860                         if (iter.hasNext()) {
861                             List JavaDoc result = new LinkedList JavaDoc();
862                             result.add(first.getNode());
863                             boolean dom = (first.getNode() instanceof Node JavaDoc);
864                             while (iter.hasNext()) {
865                                 Object JavaDoc obj = ((Pointer)iter.next()).getNode();
866                                 dom = dom && (obj instanceof Node JavaDoc);
867                                 result.add(obj);
868                             }
869                             Object JavaDoc[] arr;
870                             if (dom) {
871                                 arr = new Node JavaDoc[result.size()];
872                             } else {
873                                 arr = new Object JavaDoc[result.size()];
874                             }
875                             result.toArray(arr);
876                             return arr;
877                         }
878                         return first.getNode();
879                     }
880                     return null;
881                 } finally {
882                     jxpathContext.setLenient(oldLenient);
883                 }
884             } else if (compiled instanceof Expression) {
885                 Expression e = (Expression)compiled;
886                 return e.evaluate(jexlContext);
887             }
888             return expr.raw;
889         } catch (InvocationTargetException JavaDoc e) {
890             Throwable JavaDoc t = e.getTargetException();
891             if (t instanceof Exception JavaDoc) {
892                 throw (Exception JavaDoc)t;
893             }
894             throw (Error JavaDoc)t;
895         }
896     }
897
898     static class Event {
899         final Location location;
900         Event next; // in document order
901
Event(Location locator) {
902             this.location = locator != null ? locator : Location.UNKNOWN;
903         }
904
905         public String JavaDoc locationString() {
906             return location.toString();
907         }
908     }
909
910     static class TextEvent extends Event {
911         TextEvent(Location location, char[] chars, int start, int length)
912                                                           throws SAXException JavaDoc {
913             super(location);
914             StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
915             this.raw = new char[length];
916             System.arraycopy(chars, start, this.raw, 0, length);
917             CharArrayReader JavaDoc in = new CharArrayReader JavaDoc(chars, start, length);
918             int ch;
919             boolean inExpr = false;
920             boolean xpath = false;
921             try {
922                 top: while ((ch = in.read()) != -1) {
923                     // column++;
924
char c = (char)ch;
925                     processChar: while (true) {
926                         if (inExpr) {
927                             if (c == '\\') {
928                                 ch = in.read();
929                                 buf.append(ch == -1 ? '\\' : (char)ch);
930                             } else if (c == '}') {
931                                 String JavaDoc str = buf.toString();
932                                 Object JavaDoc compiledExpression;
933                                 try {
934                                     if (xpath) {
935                                         compiledExpression = JXPathContext.compile(str);
936                                     } else {
937                                         compiledExpression = ExpressionFactory.createExpression(str);
938                                     }
939                                 } catch (Exception JavaDoc exc) {
940                                     throw new JXTException(exc.getMessage(), this.location, exc);
941                                 }
942                                 substitutions.add(new JXTExpression(str, compiledExpression));
943                                 buf.setLength(0);
944                                 inExpr = false;
945                             } else {
946                                 buf.append(c);
947                             }
948                         } else if (c == '$' || c == '#') {
949                             ch = in.read();
950                             if (ch == '{') {
951                                 xpath = c == '#';
952                                 inExpr = true;
953                                 if (buf.length() > 0) {
954                                     char[] charArray = new char[buf.length()];
955
956                                     buf.getChars(0, buf.length(), charArray, 0);
957                                     substitutions.add(charArray);
958                                     buf.setLength(0);
959                                 }
960                                 continue top;
961                             }
962                             buf.append(c);
963                             if (ch != -1) {
964                                 c = (char)ch;
965                                 continue processChar;
966                             }
967                         } else {
968                             buf.append(c);
969                         }
970                         break;
971                     }
972                 }
973             } catch (IOException JavaDoc ignored) {
974                 // won't happen
975
ignored.printStackTrace();
976             }
977             if (inExpr) {
978                 // unclosed #{} or ${}
979
buf.insert(0, (xpath ? "#" : "$") + "{");
980             }
981             if (buf.length() > 0) {
982                 char[] charArray = new char[buf.length()];
983
984                 buf.getChars(0, buf.length(), charArray, 0);
985                 substitutions.add(charArray);
986             } else if (substitutions.isEmpty()) {
987                 substitutions.add(ArrayUtils.EMPTY_CHAR_ARRAY);
988             }
989         }
990         final List JavaDoc substitutions = new LinkedList JavaDoc();
991         final char[] raw;
992     }
993
994     static class Characters extends TextEvent {
995         Characters(Location location, char[] chars, int start, int length) throws SAXException JavaDoc {
996             super(location, chars, start, length);
997         }
998     }
999
1000    static class StartDocument extends Event {
1001        StartDocument(Location location) {
1002            super(location);
1003            templateProperties = new HashMap JavaDoc();
1004        }
1005        SourceValidity compileTime;
1006        EndDocument endDocument; // null if document fragment
1007
Map JavaDoc templateProperties;
1008    }
1009
1010    static class EndDocument extends Event {
1011        EndDocument(Location location) {
1012            super(location);
1013        }
1014    }
1015
1016    static class EndElement extends Event {
1017        EndElement(Location location, StartElement startElement) {
1018            super(location);
1019            this.startElement = startElement;
1020        }
1021        final StartElement startElement;
1022    }
1023
1024    static class EndPrefixMapping extends Event {
1025        EndPrefixMapping(Location location, String JavaDoc prefix) {
1026            super(location);
1027            this.prefix = prefix;
1028        }
1029        final String JavaDoc prefix;
1030    }
1031
1032    static class IgnorableWhitespace extends TextEvent {
1033        IgnorableWhitespace(Location location, char[] chars, int start, int length) throws SAXException JavaDoc {
1034            super(location, chars, start, length);
1035        }
1036    }
1037
1038    static class ProcessingInstruction extends Event {
1039        ProcessingInstruction(Location location, String JavaDoc target, String JavaDoc data) {
1040            super(location);
1041            this.target = target;
1042            this.data = data;
1043        }
1044        final String JavaDoc target;
1045        final String JavaDoc data;
1046    }
1047
1048    static class SkippedEntity extends Event {
1049        SkippedEntity(Location location, String JavaDoc name) {
1050            super(location);
1051            this.name = name;
1052        }
1053        final String JavaDoc name;
1054    }
1055
1056    abstract static class AttributeEvent {
1057        AttributeEvent(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc raw, String JavaDoc type) {
1058            this.namespaceURI = namespaceURI;
1059            this.localName = localName;
1060            this.raw = raw;
1061            this.type = type;
1062        }
1063        final String JavaDoc namespaceURI;
1064        final String JavaDoc localName;
1065        final String JavaDoc raw;
1066        final String JavaDoc type;
1067    }
1068
1069    static class CopyAttribute extends AttributeEvent {
1070        CopyAttribute(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc raw, String JavaDoc type, String JavaDoc value) {
1071            super(namespaceURI, localName, raw, type);
1072            this.value = value;
1073        }
1074        final String JavaDoc value;
1075    }
1076
1077    static class Subst {
1078        // VOID
1079
}
1080
1081    static class Literal extends Subst {
1082        Literal(String JavaDoc val) {
1083            this.value = val;
1084        }
1085        final String JavaDoc value;
1086    }
1087
1088    static class JXTExpression extends Subst {
1089        JXTExpression(String JavaDoc raw, Object JavaDoc expr) {
1090            this.raw = raw;
1091            this.compiledExpression = expr;
1092        }
1093        String JavaDoc raw;
1094        Object JavaDoc compiledExpression;
1095    }
1096
1097    static class SubstituteAttribute extends AttributeEvent {
1098        SubstituteAttribute(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc raw, String JavaDoc type, List JavaDoc substs) {
1099            super(namespaceURI, localName, raw, type);
1100            this.substitutions = substs;
1101        }
1102        final List JavaDoc substitutions;
1103    }
1104
1105    static class StartElement extends Event {
1106        StartElement(Location location, String JavaDoc namespaceURI, String JavaDoc localName,
1107                     String JavaDoc raw, Attributes JavaDoc attrs)
1108        throws SAXException JavaDoc {
1109            super(location);
1110
1111            this.namespaceURI = namespaceURI;
1112            this.localName = localName;
1113            this.raw = raw;
1114            this.qname = "{" + namespaceURI + "}" + localName;
1115
1116            StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
1117            int len = attrs.getLength();
1118            for (int i = 0; i < len; i++) {
1119                String JavaDoc uri = attrs.getURI(i);
1120                String JavaDoc local = attrs.getLocalName(i);
1121                String JavaDoc qname = attrs.getQName(i);
1122                String JavaDoc type = attrs.getType(i);
1123                String JavaDoc value = attrs.getValue(i);
1124                StringReader JavaDoc in = new StringReader JavaDoc(value);
1125
1126                int ch;
1127                buf.setLength(0);
1128                boolean inExpr = false;
1129                List JavaDoc substEvents = new LinkedList JavaDoc();
1130                boolean xpath = false;
1131                try {
1132                    top: while ((ch = in.read()) != -1) {
1133                        char c = (char)ch;
1134                        processChar: while (true) {
1135                            if (inExpr) {
1136                                if (c == '\\') {
1137                                    ch = in.read();
1138                                    buf.append(ch == -1 ? '\\' : (char)ch);
1139                                } else if (c == '}') {
1140                                    String JavaDoc str = buf.toString();
1141                                    JXTExpression compiledExpression;
1142                                    try {
1143                                        compiledExpression = compile(str, xpath);
1144                                    } catch (Exception JavaDoc exc) {
1145                                        throw new JXTException(exc.getMessage(), location, exc);
1146                                    }
1147                                    substEvents.add(compiledExpression);
1148                                    buf.setLength(0);
1149                                    inExpr = false;
1150                                } else {
1151                                    buf.append(c);
1152                                }
1153                            } else if (c == '$' || c == '#') {
1154                                ch = in.read();
1155                                if (ch == '{') {
1156                                    if (buf.length() > 0) {
1157                                        substEvents.add(new Literal(buf.toString()));
1158                                        buf.setLength(0);
1159                                    }
1160                                    inExpr = true;
1161                                    xpath = c == '#';
1162                                    continue top;
1163                                }
1164                                buf.append(c);
1165                                if (ch != -1) {
1166                                    c = (char)ch;
1167                                    continue processChar;
1168                                }
1169                            } else {
1170                                buf.append(c);
1171                            }
1172                            break;
1173                        }
1174                    }
1175                } catch (IOException JavaDoc ignored) {
1176                    ignored.printStackTrace();
1177                }
1178
1179                if (inExpr) {
1180                    // unclosed #{} or ${}
1181
String JavaDoc msg = "Unterminated " + (xpath ? "#" : "$") + "{";
1182                    throw new JXTException(msg, location, null);
1183                }
1184
1185                if (buf.length() > 0) {
1186                    if (substEvents.size() == 0) {
1187                        attributeEvents.add(new CopyAttribute(uri, local, qname, type, buf.toString()));
1188                    } else {
1189                        substEvents.add(new Literal(buf.toString()));
1190                        attributeEvents.add(new SubstituteAttribute(uri, local, qname, type, substEvents));
1191                    }
1192                } else {
1193                    if (substEvents.size() > 0) {
1194                        attributeEvents.add(new SubstituteAttribute(uri, local, qname, type, substEvents));
1195                    } else {
1196                        attributeEvents.add(new CopyAttribute(uri, local, qname, type, ""));
1197                    }
1198                }
1199            }
1200            this.attributes = new AttributesImpl JavaDoc(attrs);
1201        }
1202        final String JavaDoc namespaceURI;
1203        final String JavaDoc localName;
1204        final String JavaDoc raw;
1205        final String JavaDoc qname;
1206        final List JavaDoc attributeEvents = new LinkedList JavaDoc();
1207        final Attributes JavaDoc attributes;
1208        EndElement endElement;
1209    }
1210
1211    static class StartPrefixMapping extends Event {
1212        StartPrefixMapping(Location location, String JavaDoc prefix, String JavaDoc uri) {
1213            super(location);
1214            this.prefix = prefix;
1215            this.uri = uri;
1216        }
1217        final String JavaDoc prefix;
1218        final String JavaDoc uri;
1219    }
1220
1221    static class EndCDATA extends Event {
1222        EndCDATA(Location location) {
1223            super(location);
1224        }
1225    }
1226
1227    static class EndDTD extends Event {
1228        EndDTD(Location location) {
1229            super(location);
1230        }
1231    }
1232
1233    static class EndEntity extends Event {
1234        EndEntity(Location location, String JavaDoc name) {
1235            super(location);
1236            this.name = name;
1237        }
1238        final String JavaDoc name;
1239    }
1240
1241    static class StartCDATA extends Event {
1242        StartCDATA(Location location) {
1243            super(location);
1244        }
1245    }
1246
1247    static class StartDTD extends Event {
1248        StartDTD(Location location, String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId) {
1249            super(location);
1250            this.name = name;
1251            this.publicId = publicId;
1252            this.systemId = systemId;
1253        }
1254        final String JavaDoc name;
1255        final String JavaDoc publicId;
1256        final String JavaDoc systemId;
1257    }
1258
1259    static class StartEntity extends Event {
1260        public StartEntity(Location location, String JavaDoc name) {
1261            super(location);
1262            this.name = name;
1263        }
1264        final String JavaDoc name;
1265    }
1266
1267    static class StartInstruction extends Event {
1268        StartInstruction(StartElement startElement) {
1269            super(startElement.location);
1270            this.startElement = startElement;
1271        }
1272        final StartElement startElement;
1273        EndInstruction endInstruction;
1274    }
1275
1276    static class EndInstruction extends Event {
1277        EndInstruction(Location locator, StartInstruction startInstruction) {
1278            super(locator);
1279            this.startInstruction = startInstruction;
1280            startInstruction.endInstruction = this;
1281        }
1282        final StartInstruction startInstruction;
1283    }
1284
1285    static class StartForEach extends StartInstruction {
1286        StartForEach(StartElement raw, JXTExpression items, JXTExpression var,
1287                JXTExpression varStatus, JXTExpression begin, JXTExpression end,
1288                JXTExpression step, Boolean JavaDoc lenient) {
1289            super(raw);
1290            this.items = items;
1291            this.var = var;
1292            this.varStatus = varStatus;
1293            this.begin = begin;
1294            this.end = end;
1295            this.step = step;
1296            this.lenient = lenient;
1297        }
1298        final JXTExpression items;
1299        final JXTExpression var;
1300        final JXTExpression varStatus;
1301        final JXTExpression begin;
1302        final JXTExpression end;
1303        final JXTExpression step;
1304        final Boolean JavaDoc lenient;
1305    }
1306
1307    static class StartIf extends StartInstruction {
1308        StartIf(StartElement raw, JXTExpression test) {
1309            super(raw);
1310            this.test = test;
1311        }
1312        final JXTExpression test;
1313    }
1314
1315    static class StartChoose extends StartInstruction {
1316        StartChoose(StartElement raw) {
1317            super(raw);
1318        }
1319        StartWhen firstChoice;
1320        StartOtherwise otherwise;
1321    }
1322
1323    static class StartWhen extends StartInstruction {
1324        StartWhen(StartElement raw, JXTExpression test) {
1325            super(raw);
1326            this.test = test;
1327        }
1328        final JXTExpression test;
1329        StartWhen nextChoice;
1330    }
1331
1332    static class StartOtherwise extends StartInstruction {
1333        StartOtherwise(StartElement raw) {
1334            super(raw);
1335        }
1336    }
1337
1338    static class StartOut extends StartInstruction {
1339        StartOut(StartElement raw, JXTExpression expr, Boolean JavaDoc lenient) {
1340            super(raw);
1341            this.compiledExpression = expr;
1342            this.lenient = lenient;
1343        }
1344        final JXTExpression compiledExpression;
1345        final Boolean JavaDoc lenient;
1346    }
1347
1348    static class StartImport extends StartInstruction {
1349        StartImport(StartElement raw, AttributeEvent uri, JXTExpression select) {
1350            super(raw);
1351            this.uri = uri;
1352            this.select = select;
1353        }
1354        final AttributeEvent uri;
1355        final JXTExpression select;
1356    }
1357
1358    static class StartTemplate extends StartInstruction {
1359        StartTemplate(StartElement raw) {
1360            super(raw);
1361        }
1362    }
1363
1364    static class StartEvalBody extends StartInstruction {
1365        StartEvalBody(StartElement raw) {
1366            super(raw);
1367        }
1368    }
1369
1370    static class StartEval extends StartInstruction {
1371        StartEval(StartElement raw, JXTExpression value) {
1372            super(raw);
1373            this.value = value;
1374        }
1375        final JXTExpression value;
1376    }
1377
1378    static class StartDefine extends StartInstruction {
1379        StartDefine(StartElement raw, String JavaDoc namespace, String JavaDoc name) {
1380            super(raw);
1381            this.namespace = namespace;
1382            this.name = name;
1383            this.qname = "{" + namespace + "}" + name;
1384            this.parameters = new HashMap JavaDoc();
1385        }
1386        final String JavaDoc name;
1387        final String JavaDoc namespace;
1388        final String JavaDoc qname;
1389        final Map JavaDoc parameters;
1390        Event body;
1391        void finish() throws SAXException JavaDoc {
1392            Event e = next;
1393            boolean params = true;
1394            while (e != this.endInstruction) {
1395                if (e instanceof StartParameter) {
1396                    StartParameter startParam = (StartParameter)e;
1397                    if (!params) {
1398                        throw new JXTException("<parameter> not allowed here: \"" + startParam.name + "\"",
1399                                startParam.location, null);
1400                    }
1401                    Object JavaDoc prev = parameters.put(startParam.name, startParam);
1402                    if (prev != null) {
1403                        throw new JXTException("duplicate parameter: \"" + startParam.name + "\"", location, null);
1404                    }
1405                    e = startParam.endInstruction;
1406                } else if (e instanceof IgnorableWhitespace) {
1407                    // EMPTY
1408
} else if (e instanceof Characters) {
1409                    // check for whitespace
1410
char[] ch = ((TextEvent)e).raw;
1411                    int len = ch.length;
1412                    for (int i = 0; i < len; i++) {
1413                        if (!Character.isWhitespace(ch[i])) {
1414                            if (params) {
1415                                params = false;
1416                                body = e;
1417                            }
1418                            break;
1419                        }
1420                    }
1421                } else {
1422                    if (params) {
1423                        params = false;
1424                        body = e;
1425                    }
1426                }
1427                e = e.next;
1428            }
1429            if (this.body == null) {
1430                this.body = this.endInstruction;
1431            }
1432        }
1433    }
1434
1435    static class StartParameter extends StartInstruction {
1436        StartParameter(StartElement raw, String JavaDoc name, String JavaDoc optional, String JavaDoc default_) {
1437            super(raw);
1438            this.name = name;
1439            this.optional = optional;
1440            this.default_ = default_;
1441        }
1442        final String JavaDoc name;
1443        final String JavaDoc optional;
1444        final String JavaDoc default_;
1445    }
1446
1447    static class StartSet extends StartInstruction {
1448        StartSet(StartElement raw, JXTExpression var, JXTExpression value) {
1449            super(raw);
1450            this.var = var;
1451            this.value = value;
1452        }
1453        final JXTExpression var;
1454        final JXTExpression value;
1455    }
1456
1457
1458     static class StartComment extends StartInstruction {
1459        StartComment(StartElement raw) {
1460            super(raw);
1461        }
1462    }
1463
1464    // formatNumber tag (borrows from Jakarta taglibs JSTL)
1465

1466    private static Locale JavaDoc parseLocale(String JavaDoc locale, String JavaDoc variant) {
1467        Locale JavaDoc ret = null;
1468        String JavaDoc language = locale;
1469        String JavaDoc country = null;
1470        int index = StringUtils.indexOfAny(locale, "-_");
1471
1472        if (index > -1) {
1473            language = locale.substring(0, index);
1474            country = locale.substring(index + 1);
1475        }
1476        if (StringUtils.isEmpty(language)) {
1477            throw new IllegalArgumentException JavaDoc("No language in locale");
1478        }
1479        if (country == null) {
1480            ret = variant != null ? new Locale JavaDoc(language, "", variant) : new Locale JavaDoc(language, "");
1481        } else if (country.length() > 0) {
1482            ret = variant != null ? new Locale JavaDoc(language, country, variant) : new Locale JavaDoc(language, country);
1483        } else {
1484            throw new IllegalArgumentException JavaDoc("Empty country in locale");
1485        }
1486        return ret;
1487    }
1488
1489    private static final String JavaDoc NUMBER = "number";
1490    private static final String JavaDoc CURRENCY = "currency";
1491    private static final String JavaDoc PERCENT = "percent";
1492
1493    static class LocaleAwareInstruction extends StartInstruction {
1494        private JXTExpression locale;
1495
1496        LocaleAwareInstruction(StartElement startElement, JXTExpression locale) {
1497            super(startElement);
1498            this.locale = locale;
1499        }
1500
1501        protected Locale JavaDoc getLocale(JexlContext jexl, JXPathContext jxp) throws Exception JavaDoc {
1502            Object JavaDoc locVal = getValue(this.locale, jexl, jxp);
1503            if (locVal == null)
1504                locVal = getStringValue(this.locale, jexl, jxp);
1505
1506            if (locVal != null) {
1507                return locVal instanceof Locale JavaDoc ? (Locale JavaDoc) locVal : parseLocale(locVal.toString(), null);
1508            } else {
1509                return Locale.getDefault();
1510            }
1511        }
1512    }
1513
1514    static class StartFormatNumber extends LocaleAwareInstruction {
1515
1516        JXTExpression value;
1517        JXTExpression type;
1518        JXTExpression pattern;
1519        JXTExpression currencyCode;
1520        JXTExpression currencySymbol;
1521        JXTExpression isGroupingUsed;
1522        JXTExpression maxIntegerDigits;
1523        JXTExpression minIntegerDigits;
1524        JXTExpression maxFractionDigits;
1525        JXTExpression minFractionDigits;
1526
1527        JXTExpression var;
1528
1529        private static Class JavaDoc currencyClass;
1530
1531        static {
1532            try {
1533                currencyClass = Class.forName("java.util.Currency");
1534                // container's runtime is J2SE 1.4 or greater
1535
} catch (Exception JavaDoc cnfe) {
1536                // EMPTY
1537
}
1538        }
1539
1540        public StartFormatNumber(StartElement raw,
1541                                 JXTExpression var,
1542                                 JXTExpression value,
1543                                 JXTExpression type,
1544                                 JXTExpression pattern,
1545                                 JXTExpression currencyCode,
1546                                 JXTExpression currencySymbol,
1547                                 JXTExpression isGroupingUsed,
1548                                 JXTExpression maxIntegerDigits,
1549                                 JXTExpression minIntegerDigits,
1550                                 JXTExpression maxFractionDigits,
1551                                 JXTExpression minFractionDigits,
1552                                 JXTExpression locale) {
1553            super(raw, locale);
1554            this.var = var;
1555            this.value = value;
1556            this.type = type;
1557            this.pattern = pattern;
1558            this.currencyCode = currencyCode;
1559            this.currencySymbol = currencySymbol;
1560            this.isGroupingUsed = isGroupingUsed;
1561            this.maxIntegerDigits = maxIntegerDigits;
1562            this.minIntegerDigits = minIntegerDigits;
1563            this.maxFractionDigits = maxFractionDigits;
1564            this.minFractionDigits = minFractionDigits;
1565        }
1566
1567        String JavaDoc format(JexlContext jexl, JXPathContext jxp) throws Exception JavaDoc {
1568            // Determine formatting locale
1569
String JavaDoc var = getStringValue(this.var, jexl, jxp);
1570            Number JavaDoc input = getNumberValue(this.value, jexl, jxp);
1571            String JavaDoc type = getStringValue(this.type, jexl, jxp);
1572            String JavaDoc pattern = getStringValue(this.pattern, jexl, jxp);
1573            String JavaDoc currencyCode = getStringValue(this.currencyCode, jexl, jxp);
1574            String JavaDoc currencySymbol = getStringValue(this.currencySymbol, jexl, jxp);
1575            Boolean JavaDoc isGroupingUsed = getBooleanValue(this.isGroupingUsed, jexl, jxp);
1576            Number JavaDoc maxIntegerDigits = getNumberValue(this.maxIntegerDigits, jexl, jxp);
1577            Number JavaDoc minIntegerDigits = getNumberValue(this.minIntegerDigits, jexl, jxp);
1578            Number JavaDoc maxFractionDigits = getNumberValue(this.maxFractionDigits, jexl, jxp);
1579            Number JavaDoc minFractionDigits = getNumberValue(this.minFractionDigits, jexl, jxp);
1580            Locale JavaDoc loc = getLocale(jexl,jxp);
1581            String JavaDoc formatted;
1582            if (loc != null) {
1583                // Create formatter
1584
NumberFormat JavaDoc formatter = null;
1585                if (StringUtils.isNotEmpty(pattern)) {
1586                    // if 'pattern' is specified, 'type' is ignored
1587
DecimalFormatSymbols JavaDoc symbols = new DecimalFormatSymbols JavaDoc(loc);
1588                    formatter = new DecimalFormat JavaDoc(pattern, symbols);
1589                } else {
1590                    formatter = createFormatter(loc, type);
1591                }
1592                if (StringUtils.isNotEmpty(pattern) || CURRENCY.equalsIgnoreCase(type)) {
1593                    setCurrency(formatter, currencyCode, currencySymbol);
1594                }
1595                configureFormatter(formatter,
1596                                   isGroupingUsed,
1597                                   maxIntegerDigits,
1598                                   minIntegerDigits,
1599                                   maxFractionDigits,
1600                                   minFractionDigits);
1601                formatted = formatter.format(input);
1602            } else {
1603                // no formatting locale available, use toString()
1604
formatted = input.toString();
1605            }
1606            if (var != null) {
1607                jexl.getVars().put(var, formatted);
1608                jxp.getVariables().declareVariable(var, formatted);
1609                return null;
1610            }
1611            return formatted;
1612        }
1613
1614        private NumberFormat JavaDoc createFormatter(Locale JavaDoc loc, String JavaDoc type) throws Exception JavaDoc {
1615            NumberFormat JavaDoc formatter = null;
1616            if ((type == null) || NUMBER.equalsIgnoreCase(type)) {
1617                formatter = NumberFormat.getNumberInstance(loc);
1618            } else if (CURRENCY.equalsIgnoreCase(type)) {
1619                formatter = NumberFormat.getCurrencyInstance(loc);
1620            } else if (PERCENT.equalsIgnoreCase(type)) {
1621                formatter = NumberFormat.getPercentInstance(loc);
1622            } else {
1623                throw new IllegalArgumentException JavaDoc("Invalid type: \"" + type + "\": should be \"number\" or \"currency\" or \"percent\"");
1624            }
1625            return formatter;
1626        }
1627
1628        /*
1629         * Applies the 'groupingUsed', 'maxIntegerDigits', 'minIntegerDigits',
1630         * 'maxFractionDigits', and 'minFractionDigits' attributes to the given
1631         * formatter.
1632         */

1633        private void configureFormatter(NumberFormat JavaDoc formatter,
1634                                        Boolean JavaDoc isGroupingUsed,
1635                                        Number JavaDoc maxIntegerDigits,
1636                                        Number JavaDoc minIntegerDigits,
1637                                        Number JavaDoc maxFractionDigits,
1638                                        Number JavaDoc minFractionDigits) {
1639            if (isGroupingUsed != null)
1640                formatter.setGroupingUsed(isGroupingUsed.booleanValue());
1641            if (maxIntegerDigits != null)
1642                formatter.setMaximumIntegerDigits(maxIntegerDigits.intValue());
1643            if (minIntegerDigits != null)
1644                formatter.setMinimumIntegerDigits(minIntegerDigits.intValue());
1645            if (maxFractionDigits != null)
1646                formatter.setMaximumFractionDigits(maxFractionDigits.intValue());
1647            if (minFractionDigits != null)
1648                formatter.setMinimumFractionDigits(minFractionDigits.intValue());
1649        }
1650
1651        /*
1652         * Override the formatting locale's default currency symbol with the
1653         * specified currency code (specified via the "currencyCode" attribute) or
1654         * currency symbol (specified via the "currencySymbol" attribute).
1655         *
1656         * If both "currencyCode" and "currencySymbol" are present,
1657         * "currencyCode" takes precedence over "currencySymbol" if the
1658         * java.util.Currency class is defined in the container's runtime (that
1659         * is, if the container's runtime is J2SE 1.4 or greater), and
1660         * "currencySymbol" takes precendence over "currencyCode" otherwise.
1661         *
1662         * If only "currencyCode" is given, it is used as a currency symbol if
1663         * java.util.Currency is not defined.
1664         *
1665         * Example:
1666         *
1667         * JDK "currencyCode" "currencySymbol" Currency symbol being displayed
1668         * -----------------------------------------------------------------------
1669         * all --- --- Locale's default currency symbol
1670         *
1671         * <1.4 EUR --- EUR
1672         * >=1.4 EUR --- Locale's currency symbol for Euro
1673         *
1674         * all --- € €
1675         *
1676         * <1.4 EUR € €
1677         * >=1.4 EUR € Locale's currency symbol for Euro
1678         */

1679        private void setCurrency(NumberFormat JavaDoc formatter, String JavaDoc currencyCode, String JavaDoc currencySymbol) throws Exception JavaDoc {
1680            String JavaDoc code = null;
1681            String JavaDoc symbol = null;
1682
1683            if (currencyCode == null) {
1684                if (currencySymbol == null) {
1685                    return;
1686                }
1687                symbol = currencySymbol;
1688            } else if (currencySymbol != null) {
1689                if (currencyClass != null) {
1690                    code = currencyCode;
1691                } else {
1692                    symbol = currencySymbol;
1693                }
1694            } else if (currencyClass != null) {
1695                code = currencyCode;
1696            } else {
1697                symbol = currencyCode;
1698            }
1699            if (code != null) {
1700                Object JavaDoc[] methodArgs = new Object JavaDoc[1];
1701
1702                /*
1703                 * java.util.Currency.getInstance()
1704                 */

1705                Method JavaDoc m = currencyClass.getMethod("getInstance", new Class JavaDoc[] {String JavaDoc.class});
1706
1707                methodArgs[0] = code;
1708                Object JavaDoc currency = m.invoke(null, methodArgs);
1709
1710                /*
1711                 * java.text.NumberFormat.setCurrency()
1712                 */

1713                Class JavaDoc[] paramTypes = new Class JavaDoc[1];
1714                paramTypes[0] = currencyClass;
1715                Class JavaDoc numberFormatClass = Class.forName("java.text.NumberFormat");
1716                m = numberFormatClass.getMethod("setCurrency", paramTypes);
1717                methodArgs[0] = currency;
1718                m.invoke(formatter, methodArgs);
1719            } else {
1720                /*
1721                 * Let potential ClassCastException propagate up (will almost
1722                 * never happen)
1723                 */

1724                DecimalFormat JavaDoc df = (DecimalFormat JavaDoc) formatter;
1725                DecimalFormatSymbols JavaDoc dfs = df.getDecimalFormatSymbols();
1726                dfs.setCurrencySymbol(symbol);
1727                df.setDecimalFormatSymbols(dfs);
1728            }
1729        }
1730    }
1731
1732    // formatDate tag (borrows from Jakarta taglibs JSTL)
1733

1734    static class StartFormatDate extends LocaleAwareInstruction {
1735
1736        private static final String JavaDoc DATE = "date";
1737        private static final String JavaDoc TIME = "time";
1738        private static final String JavaDoc DATETIME = "both";
1739
1740        JXTExpression var;
1741        JXTExpression value;
1742        JXTExpression type;
1743        JXTExpression pattern;
1744        JXTExpression timeZone;
1745        JXTExpression dateStyle;
1746        JXTExpression timeStyle;
1747        JXTExpression locale;
1748
1749        StartFormatDate(StartElement raw,
1750                        JXTExpression var,
1751                        JXTExpression value,
1752                        JXTExpression type,
1753                        JXTExpression pattern,
1754                        JXTExpression timeZone,
1755                        JXTExpression dateStyle,
1756                        JXTExpression timeStyle,
1757                        JXTExpression locale) {
1758            super(raw, locale);
1759            this.var = var;
1760            this.value = value;
1761            this.type = type;
1762            this.pattern = pattern;
1763            this.timeZone = timeZone;
1764            this.dateStyle = dateStyle;
1765            this.timeStyle = timeStyle;
1766        }
1767
1768        String JavaDoc format(JexlContext jexl, JXPathContext jxp)
1769            throws Exception JavaDoc {
1770            String JavaDoc var = getStringValue(this.var, jexl, jxp);
1771            Object JavaDoc value = getValue(this.value, jexl, jxp);
1772            String JavaDoc pattern = getStringValue(this.pattern, jexl, jxp);
1773            Object JavaDoc timeZone = getValue(this.timeZone, jexl, jxp);
1774
1775            String JavaDoc type = getStringValue(this.type, jexl, jxp);
1776            String JavaDoc timeStyle = getStringValue(this.timeStyle, jexl, jxp);
1777            String JavaDoc dateStyle = getStringValue(this.dateStyle, jexl, jxp);
1778
1779            String JavaDoc formatted = null;
1780
1781            // Create formatter
1782
Locale JavaDoc locale = getLocale(jexl, jxp);
1783            DateFormat JavaDoc formatter = createFormatter(locale, type, dateStyle, timeStyle);
1784            // Apply pattern, if present
1785
if (pattern != null) {
1786                try {
1787                    ((SimpleDateFormat JavaDoc) formatter).applyPattern(pattern);
1788                } catch (ClassCastException JavaDoc cce) {
1789                    formatter = new SimpleDateFormat JavaDoc(pattern, locale);
1790                }
1791            }
1792            // Set time zone
1793
TimeZone JavaDoc tz = null;
1794            if ((timeZone instanceof String JavaDoc) && timeZone.equals("")) {
1795                timeZone = null;
1796            }
1797            if (timeZone != null) {
1798                if (timeZone instanceof String JavaDoc) {
1799                    tz = TimeZone.getTimeZone((String JavaDoc) timeZone);
1800                } else if (timeZone instanceof TimeZone JavaDoc) {
1801                    tz = (TimeZone JavaDoc) timeZone;
1802                } else {
1803                    throw new IllegalArgumentException JavaDoc("Illegal timeZone value: \""+timeZone+"\"");
1804                }
1805            }
1806            if (tz != null) {
1807                formatter.setTimeZone(tz);
1808            }
1809            formatted = formatter.format(value);
1810            if (var != null) {
1811                jexl.getVars().put(var, formatted);
1812                jxp.getVariables().declareVariable(var, formatted);
1813                return null;
1814            }
1815            return formatted;
1816        }
1817
1818        private DateFormat JavaDoc createFormatter(Locale JavaDoc loc, String JavaDoc type, String JavaDoc dateStyle, String JavaDoc timeStyle)
1819            throws Exception JavaDoc {
1820            DateFormat JavaDoc formatter = null;
1821            if ((type == null) || DATE.equalsIgnoreCase(type)) {
1822                formatter = DateFormat.getDateInstance(getStyle(dateStyle), loc);
1823            } else if (TIME.equalsIgnoreCase(type)) {
1824                formatter = DateFormat.getTimeInstance(getStyle(timeStyle), loc);
1825            } else if (DATETIME.equalsIgnoreCase(type)) {
1826                formatter = DateFormat.getDateTimeInstance(getStyle(dateStyle), getStyle(timeStyle), loc);
1827            } else {
1828                throw new IllegalArgumentException JavaDoc("Invalid type: \""+ type+"\"");
1829            }
1830            return formatter;
1831        }
1832
1833        private static final String JavaDoc DEFAULT = "default";
1834        private static final String JavaDoc SHORT = "short";
1835        private static final String JavaDoc MEDIUM = "medium";
1836        private static final String JavaDoc LONG = "long";
1837        private static final String JavaDoc FULL = "full";
1838
1839        private int getStyle(String JavaDoc style) {
1840            int ret = DateFormat.DEFAULT;
1841            if (style != null) {
1842                if (DEFAULT.equalsIgnoreCase(style)) {
1843                    ret = DateFormat.DEFAULT;
1844                } else if (SHORT.equalsIgnoreCase(style)) {
1845                    ret = DateFormat.SHORT;
1846                } else if (MEDIUM.equalsIgnoreCase(style)) {
1847                    ret = DateFormat.MEDIUM;
1848                } else if (LONG.equalsIgnoreCase(style)) {
1849                    ret = DateFormat.LONG;
1850                } else if (FULL.equalsIgnoreCase(style)) {
1851                    ret = DateFormat.FULL;
1852                } else {
1853                    throw new IllegalArgumentException JavaDoc("Invalid style: \"" + style + "\": should be \"default\" or \"short\" or \"medium\" or \"long\" or \"full\"");
1854                }
1855            }
1856            return ret;
1857        }
1858    }
1859
1860
1861    static class Parser implements ContentHandler JavaDoc, LexicalHandler JavaDoc {
1862
1863        StartDocument startEvent;
1864        Event lastEvent;
1865        Stack JavaDoc stack = new Stack JavaDoc();
1866        Locator JavaDoc locator;
1867        Location charLocation;
1868        StringBuffer JavaDoc charBuf;
1869
1870        public Parser() {
1871            // EMPTY
1872
}
1873
1874        StartDocument getStartEvent() {
1875            return this.startEvent;
1876        }
1877
1878
1879        void recycle() {
1880            startEvent = null;
1881            lastEvent = null;
1882            stack.clear();
1883            locator = null;
1884            charLocation = null;
1885            charBuf = null;
1886        }
1887
1888        private void addEvent(Event ev) throws SAXException JavaDoc {
1889            if (ev != null) {
1890                if (lastEvent == null) {
1891                    lastEvent = startEvent = new StartDocument(LocationUtils.getLocation(locator, "template"));
1892                } else {
1893                    flushChars();
1894                }
1895                lastEvent.next = ev;
1896                lastEvent = ev;
1897            } else {
1898                throw new NullPointerException JavaDoc("null event");
1899            }
1900        }
1901
1902        void flushChars() throws SAXException JavaDoc {
1903            if (charBuf != null) {
1904                char[] chars = new char[charBuf.length()];
1905                charBuf.getChars(0, charBuf.length(), chars, 0);
1906                Characters ev = new Characters(charLocation, chars, 0, chars.length);
1907                lastEvent.next = ev;
1908                lastEvent = ev;
1909                charLocation = null;
1910                charBuf = null;
1911            }
1912        }
1913
1914        public void characters(char[] ch, int start, int length)
1915            throws SAXException JavaDoc {
1916            if (charBuf == null) {
1917                charBuf = new StringBuffer JavaDoc(length);
1918                charLocation = LocationUtils.getLocation(locator, "[text]");
1919            }
1920            charBuf.append(ch, start, length);
1921        }
1922
1923        public void endDocument() throws SAXException JavaDoc {
1924            StartDocument startDoc = (StartDocument)stack.pop();
1925            EndDocument endDoc = new EndDocument(LocationUtils.getLocation(locator, "template"));
1926            startDoc.endDocument = endDoc;
1927            addEvent(endDoc);
1928        }
1929
1930        public void endElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc raw) throws SAXException JavaDoc {
1931            Event start = (Event)stack.pop();
1932            Event newEvent = null;
1933            if (NS.equals(namespaceURI)) {
1934                StartInstruction startInstruction = (StartInstruction)start;
1935                EndInstruction endInstruction = new EndInstruction(LocationUtils.getLocation(locator, "<"+raw+">"), startInstruction);
1936                newEvent = endInstruction;
1937                if (start instanceof StartWhen) {
1938                    StartWhen startWhen = (StartWhen)start;
1939                    StartChoose startChoose = (StartChoose)stack.peek();
1940                    if (startChoose.firstChoice != null) {
1941                        StartWhen w = startChoose.firstChoice;
1942                        while (w.nextChoice != null) {
1943                            w = w.nextChoice;
1944                        }
1945                        w.nextChoice = startWhen;
1946                    } else {
1947                        startChoose.firstChoice = startWhen;
1948                    }
1949                } else if (start instanceof StartOtherwise) {
1950                    StartOtherwise startOtherwise = (StartOtherwise)start;
1951                    StartChoose startChoose = (StartChoose)stack.peek();
1952                    startChoose.otherwise = startOtherwise;
1953                }
1954            } else {
1955                StartElement startElement = (StartElement)start;
1956                newEvent = startElement.endElement = new EndElement(LocationUtils.getLocation(locator, "<"+raw+">"), startElement);
1957            }
1958            addEvent(newEvent);
1959            if (start instanceof StartDefine) {
1960                StartDefine startDefine = (StartDefine)start;
1961                startDefine.finish();
1962            }
1963        }
1964
1965        public void endPrefixMapping(String JavaDoc prefix) throws SAXException JavaDoc {
1966            EndPrefixMapping endPrefixMapping = new EndPrefixMapping(LocationUtils.getLocation(locator, null), prefix);
1967            addEvent(endPrefixMapping);
1968        }
1969
1970        public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException JavaDoc {
1971            Event ev = new IgnorableWhitespace(LocationUtils.getLocation(locator, null), ch, start, length);
1972            addEvent(ev);
1973        }
1974
1975        public void processingInstruction(String JavaDoc target, String JavaDoc data) throws SAXException JavaDoc {
1976            Event pi = new ProcessingInstruction(LocationUtils.getLocation(locator, null), target, data);
1977            addEvent(pi);
1978        }
1979
1980        public void setDocumentLocator(Locator JavaDoc locator) {
1981            this.locator = locator;
1982        }
1983
1984        public void skippedEntity(String JavaDoc name) throws SAXException JavaDoc {
1985            addEvent(new SkippedEntity(LocationUtils.getLocation(locator, null), name));
1986        }
1987
1988        public void startDocument() {
1989            startEvent = new StartDocument(LocationUtils.getLocation(locator, null));
1990            lastEvent = startEvent;
1991            stack.push(lastEvent);
1992        }
1993
1994        public void startElement(String JavaDoc namespaceURI, String JavaDoc localName, String JavaDoc qname, Attributes JavaDoc attrs) throws SAXException JavaDoc {
1995            Event newEvent = null;
1996            Location locator = LocationUtils.getLocation(this.locator, "<"+qname+">");
1997            AttributesImpl JavaDoc elementAttributes = new AttributesImpl JavaDoc(attrs);
1998            int attributeCount = elementAttributes.getLength();
1999            for (int i = 0; i < attributeCount; i++) {
2000                String JavaDoc attributeURI = elementAttributes.getURI(i);
2001                if (StringUtils.equals(attributeURI, NS)) {
2002                    getStartEvent().templateProperties.put(elementAttributes.getLocalName(i),
2003                                compileExpr(elementAttributes.getValue(i), null, locator));
2004                    elementAttributes.removeAttribute(i--);
2005                }
2006            }
2007            StartElement startElement = new StartElement(locator, namespaceURI,
2008                                                     localName, qname, elementAttributes);
2009            if (NS.equals(namespaceURI)) {
2010                if (localName.equals(FOR_EACH)) {
2011                    String JavaDoc items = attrs.getValue("items");
2012                    String JavaDoc select = attrs.getValue("select");
2013                    JXTExpression begin = compileInt(attrs.getValue("begin"), FOR_EACH, locator);
2014                    JXTExpression end = compileInt(attrs.getValue("end"), FOR_EACH, locator);
2015                    JXTExpression step = compileInt(attrs.getValue("step"), FOR_EACH, locator);
2016                    JXTExpression var = compileExpr(attrs.getValue("var"), null, locator);
2017                    JXTExpression varStatus = compileExpr(attrs.getValue("varStatus"), null, locator);
2018                    if (items == null) {
2019                        if (select == null && (begin == null || end == null)) {
2020                            throw new JXTException("forEach: \"select\", \"items\", or both \"begin\" and \"end\" must be specified", locator, null);
2021                        }
2022                    } else if (select != null) {
2023                        throw new JXTException("forEach: only one of \"select\" or \"items\" may be specified", locator, null);
2024                    }
2025                    JXTExpression expr = compileExpr(items == null ? select : items, null, locator);
2026                    String JavaDoc lenientValue = attrs.getValue("lenient");
2027                    Boolean JavaDoc lenient = (lenientValue == null) ? null : Boolean.valueOf(lenientValue);
2028                    StartForEach startForEach = new StartForEach(startElement, expr, var, varStatus, begin, end, step,lenient);
2029                    newEvent = startForEach;
2030                } else if (localName.equals(FORMAT_NUMBER)) {
2031                    JXTExpression value = compileExpr(attrs.getValue("value"), null, locator);
2032                    JXTExpression type = compileExpr(attrs.getValue("type"), null, locator);
2033                    JXTExpression pattern = compileExpr(attrs.getValue("pattern"), null, locator);
2034                    JXTExpression currencyCode = compileExpr(attrs.getValue("currencyCode"), null, locator);
2035                    JXTExpression currencySymbol = compileExpr(attrs.getValue("currencySymbol"), null, locator);
2036                    JXTExpression isGroupingUsed = compileBoolean(attrs.getValue("isGroupingUsed"), null, locator);
2037                    JXTExpression maxIntegerDigits = compileInt(attrs.getValue("maxIntegerDigits"), null, locator);
2038                    JXTExpression minIntegerDigits = compileInt(attrs.getValue("minIntegerDigits"), null, locator);
2039                    JXTExpression maxFractionDigits = compileInt(attrs.getValue("maxFractionDigits"), null, locator);
2040                    JXTExpression minFractionDigits = compileInt(attrs.getValue("minFractionDigits"), null, locator);
2041                    JXTExpression var = compileExpr(attrs.getValue("var"), null, locator);
2042                    JXTExpression locale = compileExpr(attrs.getValue("locale"), null, locator);
2043                    StartFormatNumber startFormatNumber =
2044                        new StartFormatNumber(startElement,
2045                                              var,
2046                                              value,
2047                                              type,
2048                                              pattern,
2049                                              currencyCode,
2050                                              currencySymbol,
2051                                              isGroupingUsed,
2052                                              maxIntegerDigits,
2053                                              minIntegerDigits,
2054                                              maxFractionDigits,
2055                                              minFractionDigits,
2056                                              locale);
2057                    newEvent = startFormatNumber;
2058                } else if (localName.equals(FORMAT_DATE)) {
2059                    JXTExpression var = compileExpr(attrs.getValue("var"), null, locator);
2060                    JXTExpression value = compileExpr(attrs.getValue("value"), null, locator);
2061                    JXTExpression type = compileExpr(attrs.getValue("type"), null, locator);
2062                    JXTExpression pattern = compileExpr(attrs.getValue("pattern"), null, locator);
2063                    JXTExpression timeZone = compileExpr(attrs.getValue("timeZone"), null, locator);
2064                    JXTExpression dateStyle = compileExpr(attrs.getValue("dateStyle"), null, locator);
2065                    JXTExpression timeStyle = compileExpr(attrs.getValue("timeStyle"), null, locator);
2066                    JXTExpression locale = compileExpr(attrs.getValue("locale"), null, locator);
2067                    StartFormatDate startFormatDate =
2068                        new StartFormatDate(startElement,
2069                                            var,
2070                                            value,
2071                                            type,
2072                                            pattern,
2073                                            timeZone,
2074                                            dateStyle,
2075                                            timeStyle,
2076                                            locale);
2077                    newEvent = startFormatDate;
2078                } else if (localName.equals(CHOOSE)) {
2079                    StartChoose startChoose = new StartChoose(startElement);
2080                    newEvent = startChoose;
2081                } else if (localName.equals(WHEN)) {
2082                    if (stack.size() == 0 || !(stack.peek() instanceof StartChoose)) {
2083                        throw new JXTException("<when> must be within <choose>", locator, null);
2084                    }
2085                    String JavaDoc test = attrs.getValue("test");
2086                    if (test != null) {
2087                        JXTExpression expr = compileExpr(test, "when: \"test\": ", locator);
2088                        StartWhen startWhen = new StartWhen(startElement, expr);
2089                        newEvent = startWhen;
2090                    } else {
2091                        throw new JXTException("when: \"test\" is required", locator, null);
2092                    }
2093                } else if (localName.equals(OUT)) {
2094                    String JavaDoc value = attrs.getValue("value");
2095                    if (value != null) {
2096                        JXTExpression expr = compileExpr(value, "out: \"value\": ", locator);
2097                        String JavaDoc lenientValue = attrs.getValue("lenient");
2098                        Boolean JavaDoc lenient = lenientValue == null ? null : Boolean.valueOf(lenientValue);
2099                        newEvent = new StartOut(startElement, expr, lenient);
2100                    } else {
2101                        throw new JXTException("out: \"value\" is required", locator, null);
2102                    }
2103                } else if (localName.equals(OTHERWISE)) {
2104                    if (stack.size() != 0 && (stack.peek() instanceof StartChoose)) {
2105                        StartOtherwise startOtherwise = new StartOtherwise(startElement);
2106                        newEvent = startOtherwise;
2107                    } else {
2108                        throw new JXTException( "<otherwise> must be within <choose>", locator, null);
2109                    }
2110                } else if (localName.equals(IF)) {
2111                    String JavaDoc test = attrs.getValue("test");
2112                    if (test != null) {
2113                        JXTExpression expr = compileExpr(test, "if: \"test\": ", locator);
2114                        StartIf startIf = new StartIf(startElement, expr);
2115                        newEvent = startIf;
2116                    } else {
2117                        throw new JXTException("if: \"test\" is required", locator, null);
2118                    }
2119                } else if (localName.equals(MACRO)) {
2120                    // <macro name="myTag" targetNamespace="myNamespace">
2121
// <parameter name="paramName" required="Boolean" default="value"/>
2122
// body
2123
// </macro>
2124
String JavaDoc namespace = StringUtils.defaultString(attrs.getValue("targetNamespace"));
2125                    String JavaDoc name = attrs.getValue("name");
2126                    if (name != null) {
2127                        StartDefine startDefine = new StartDefine(startElement, namespace, name);
2128                        newEvent = startDefine;
2129                    } else {
2130                        throw new JXTException("macro: \"name\" is required", locator, null);
2131                    }
2132                } else if (localName.equals(PARAMETER)) {
2133                    if (stack.size() == 0 || !(stack.peek() instanceof StartDefine)) {
2134                        throw new JXTException("<parameter> not allowed here", locator, null);
2135                    } else {
2136                        String JavaDoc name = attrs.getValue("name");
2137                        String JavaDoc optional = attrs.getValue("optional");
2138                        String JavaDoc default_ = attrs.getValue("default");
2139                        if (name != null) {
2140                            StartParameter startParameter = new StartParameter(startElement, name, optional, default_);
2141                            newEvent = startParameter;
2142                        } else {
2143                            throw new JXTException("parameter: \"name\" is required", locator, null);
2144                        }
2145                    }
2146                } else if (localName.equals(EVALBODY)) {
2147                    newEvent = new StartEvalBody(startElement);
2148                } else if (localName.equals(EVAL)) {
2149                    String JavaDoc value = attrs.getValue("select");
2150                    JXTExpression valueExpr = compileExpr(value, "eval: \"select\":", locator);
2151                    newEvent = new StartEval(startElement, valueExpr);
2152                } else if (localName.equals(SET)) {
2153                    String JavaDoc var = attrs.getValue("var");
2154                    String JavaDoc value = attrs.getValue("value");
2155                    JXTExpression varExpr = null;
2156                    JXTExpression valueExpr = null;
2157                    if (var != null) {
2158                        varExpr = compileExpr(var, "set: \"var\":", locator);
2159                    }
2160                    if (value != null) {
2161                        valueExpr = compileExpr(value, "set: \"value\":", locator);
2162                    }
2163                    StartSet startSet = new StartSet(startElement, varExpr, valueExpr);
2164                    newEvent = startSet;
2165                } else if (localName.equals(IMPORT)) {
2166                    // <import uri="${root}/foo/bar.xml" context="${foo}"/>
2167
AttributeEvent uri = null;
2168                    Iterator JavaDoc iter = startElement.attributeEvents.iterator();
2169                    while (iter.hasNext()) {
2170                        AttributeEvent e = (AttributeEvent)iter.next();
2171                        if (e.localName.equals("uri")) {
2172                            uri = e;
2173                            break;
2174                        }
2175                    }
2176                    if (uri != null) {
2177                        // If "context" is present then its value will be used
2178
// as the context object in the imported template
2179
String JavaDoc select = attrs.getValue("context");
2180                        JXTExpression expr = null;
2181                        if (select != null) {
2182                            expr = compileExpr(select, "import: \"context\": ", locator);
2183                        }
2184                        StartImport startImport = new StartImport(startElement, uri, expr);
2185                        newEvent = startImport;
2186                    } else {
2187                        throw new JXTException("import: \"uri\" is required", locator, null);
2188                    }
2189                } else if (localName.equals(TEMPLATE)) {
2190                    StartTemplate startTemplate = new StartTemplate(startElement);
2191                    newEvent = startTemplate;
2192                } else if (localName.equals(COMMENT)) {
2193                    // <jx:comment>This will be parsed</jx:comment>
2194
StartComment startJXComment = new StartComment(startElement);
2195                    newEvent = startJXComment;
2196                } else {
2197                    throw new JXTException("unrecognized tag: " + localName, locator, null);
2198                }
2199            } else {
2200                newEvent = startElement;
2201            }
2202            stack.push(newEvent);
2203            addEvent(newEvent);
2204        }
2205
2206        public void startPrefixMapping(String JavaDoc prefix, String JavaDoc uri) throws SAXException JavaDoc {
2207            addEvent(new StartPrefixMapping(LocationUtils.getLocation(locator, null), prefix, uri));
2208        }
2209
2210        public void comment(char ch[], int start, int length) throws SAXException JavaDoc {
2211            // DO NOTHING
2212
}
2213
2214        public void endCDATA() throws SAXException JavaDoc {
2215            addEvent(new EndCDATA(LocationUtils.getLocation(locator, null)));
2216        }
2217
2218        public void endDTD() throws SAXException JavaDoc {
2219            addEvent(new EndDTD(LocationUtils.getLocation(locator, null)));
2220        }
2221
2222        public void endEntity(String JavaDoc name) throws SAXException JavaDoc {
2223            addEvent(new EndEntity(LocationUtils.getLocation(locator, null), name));
2224        }
2225
2226        public void startCDATA() throws SAXException JavaDoc {
2227            addEvent(new StartCDATA(LocationUtils.getLocation(locator, null)));
2228        }
2229
2230        public void startDTD(String JavaDoc name, String JavaDoc publicId, String JavaDoc systemId) throws SAXException JavaDoc {
2231            addEvent(new StartDTD(LocationUtils.getLocation(locator, null), name, publicId, systemId));
2232        }
2233
2234        public void startEntity(String JavaDoc name) throws SAXException JavaDoc {
2235            addEvent(new StartEntity(LocationUtils.getLocation(locator, null), name));
2236        }
2237    }
2238
2239    /**
2240     * Adapter that makes this generator usable as a transformer
2241     * (Note there is a performance penalty for this however:
2242     * you effectively recompile the template for every instance document)
2243     */

2244
2245    public static class TransformerAdapter extends ServiceableTransformer {
2246        static class TemplateConsumer extends Parser implements XMLConsumer {
2247
2248            public TemplateConsumer() {
2249                this.gen = new JXTemplateGenerator();
2250            }
2251
2252            public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters parameters)
2253                throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
2254                this.gen.setup(resolver, objectModel, null, parameters);
2255            }
2256
2257            public void service(ServiceManager manager)
2258                throws ServiceException {
2259                this.gen.service(manager);
2260            }
2261
2262            public void endDocument() throws SAXException JavaDoc {
2263                super.endDocument();
2264                gen.performGeneration(gen.getConsumer(), gen.getJexlContext(), gen.getJXPathContext(), null, getStartEvent(), null);
2265            }
2266
2267            void setConsumer(XMLConsumer consumer) {
2268                gen.setConsumer(consumer);
2269            }
2270
2271            void recycle() {
2272                super.recycle();
2273                gen.recycle();
2274            }
2275
2276            JXTemplateGenerator gen;
2277        }
2278
2279        TemplateConsumer templateConsumer = new TemplateConsumer();
2280
2281        public void recycle() {
2282            super.recycle();
2283            templateConsumer.recycle();
2284        }
2285
2286        public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters parameters)
2287            throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
2288            super.setup(resolver, objectModel, src, parameters);
2289            templateConsumer.setup(resolver, objectModel, src, parameters);
2290        }
2291
2292        public void service(ServiceManager manager)
2293            throws ServiceException {
2294            super.service(manager);
2295            templateConsumer.service(manager);
2296        }
2297
2298        public void setConsumer(XMLConsumer xmlConsumer) {
2299            super.setConsumer(templateConsumer);
2300            templateConsumer.setConsumer(xmlConsumer);
2301        }
2302    }
2303
2304    private JXPathContext jxpathContext;
2305    private MyJexlContext globalJexlContext;
2306    private Variables variables;
2307    private static Map JavaDoc cache = new HashMap JavaDoc();
2308    private Source inputSource;
2309    private Map JavaDoc definitions;
2310    private Map JavaDoc cocoon;
2311
2312    private JXPathContext getJXPathContext() {
2313        return jxpathContext;
2314    }
2315
2316    private MyJexlContext getJexlContext() {
2317        return globalJexlContext;
2318    }
2319
2320    /* (non-Javadoc)
2321     * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
2322     */

2323    public void recycle() {
2324        if (this.resolver != null) {
2325            this.resolver.release(this.inputSource);
2326        }
2327        this.inputSource = null;
2328        this.jxpathContext = null;
2329        this.globalJexlContext = null;
2330        this.variables = null;
2331        this.definitions = null;
2332        this.cocoon = null;
2333        this.namespaces.clear();
2334        super.recycle();
2335    }
2336
2337    /* (non-Javadoc)
2338     * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
2339     */

2340    public void setup(SourceResolver resolver, Map JavaDoc objectModel, String JavaDoc src, Parameters parameters)
2341                throws ProcessingException, SAXException JavaDoc, IOException JavaDoc {
2342
2343        super.setup(resolver, objectModel, src, parameters);
2344        if (src != null) {
2345            try {
2346                this.inputSource = resolver.resolveURI(src);
2347            } catch (SourceException se) {
2348                throw SourceUtil.handle("Error during resolving of '" + src + "'.", se);
2349            }
2350            final String JavaDoc uri = inputSource.getURI();
2351            boolean regenerate = false;
2352            StartDocument startEvent = null;
2353            synchronized (cache) {
2354                startEvent = (StartDocument)cache.get(uri);
2355                if (startEvent != null) {
2356                    int valid = SourceValidity.UNKNOWN;
2357                    if (startEvent.compileTime != null) {
2358                        valid = startEvent.compileTime.isValid();
2359                    }
2360                    if (valid == SourceValidity.UNKNOWN && startEvent.compileTime != null) {
2361                        SourceValidity validity = inputSource.getValidity();
2362                        valid = startEvent.compileTime.isValid(validity);
2363                    }
2364                    if (valid != SourceValidity.VALID) {
2365                        regenerate = true;
2366                    }
2367                } else {
2368                    regenerate = true;
2369                }
2370            }
2371            if (regenerate) {
2372                Parser parser = new Parser();
2373                SourceUtil.parse(this.manager, this.inputSource, parser);
2374                startEvent = parser.getStartEvent();
2375                startEvent.compileTime = this.inputSource.getValidity();
2376                synchronized (cache) {
2377                    cache.put(uri, startEvent);
2378                }
2379            }
2380        }
2381        Object JavaDoc bean = FlowHelper.getContextObject(objectModel);
2382        WebContinuation kont = FlowHelper.getWebContinuation(objectModel);
2383        setContexts(bean, kont, parameters, objectModel);
2384        this.definitions = new HashMap JavaDoc();
2385    }
2386
2387    private void fillContext(Object JavaDoc contextObject, Map JavaDoc map) {
2388        if (contextObject != null) {
2389            // Hack: I use jxpath to populate the context object's properties
2390
// in the jexl context
2391
final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(contextObject.getClass());
2392            if (bi.isDynamic()) {
2393                Class JavaDoc cl = bi.getDynamicPropertyHandlerClass();
2394                try {
2395                    DynamicPropertyHandler h = (DynamicPropertyHandler)cl.newInstance();
2396                    String JavaDoc[] result = h.getPropertyNames(contextObject);
2397                    int len = result.length;
2398                    for (int i = 0; i < len; i++) {
2399                        try {
2400                            map.put(result[i], h.getProperty(contextObject, result[i]));
2401                        } catch (Exception JavaDoc exc) {
2402                            exc.printStackTrace();
2403                        }
2404                    }
2405                } catch (Exception JavaDoc ignored) {
2406                    ignored.printStackTrace();
2407                }
2408            } else {
2409                PropertyDescriptor JavaDoc[] props = bi.getPropertyDescriptors();
2410                int len = props.length;
2411                for (int i = 0; i < len; i++) {
2412                    try {
2413                        Method JavaDoc read = props[i].getReadMethod();
2414                        if (read != null) {
2415                            map.put(props[i].getName(), read.invoke(contextObject, null));
2416                        }
2417                    } catch (Exception JavaDoc ignored) {
2418                        ignored.printStackTrace();
2419                    }
2420                }
2421            }
2422        }
2423    }
2424
2425    private void setContexts(Object JavaDoc contextObject, WebContinuation kont, Parameters parameters, Map JavaDoc objectModel) {
2426        final Request request = ObjectModelHelper.getRequest(objectModel);
2427        final Object JavaDoc session = request.getSession(false);
2428        final Object JavaDoc app = ObjectModelHelper.getContext(objectModel);
2429        cocoon = new HashMap JavaDoc();
2430        Object JavaDoc fomRequest = FOM_JavaScriptFlowHelper.getFOM_Request(objectModel);
2431        cocoon.put("request", fomRequest != null ? fomRequest : request);
2432        if (session != null) {
2433            cocoon.put("session", FOM_JavaScriptFlowHelper.getFOM_Session(objectModel));
2434        }
2435        cocoon.put("context", FOM_JavaScriptFlowHelper.getFOM_Context(objectModel));
2436        cocoon.put("continuation", FOM_JavaScriptFlowHelper.getFOM_WebContinuation(objectModel));
2437        cocoon.put("parameters", Parameters.toProperties(parameters));
2438        this.variables = new MyVariables(cocoon, contextObject, kont, request, session, app, parameters);
2439        Map JavaDoc map;
2440        if (contextObject instanceof Map JavaDoc) {
2441            map = (Map JavaDoc)contextObject;
2442        } else {
2443            map = new HashMap JavaDoc();
2444            fillContext(contextObject, map);
2445        }
2446        jxpathContext = jxpathContextFactory.newContext(null, contextObject);
2447        jxpathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces));
2448        jxpathContext.setVariables(variables);
2449        jxpathContext.setLenient(parameters.getParameterAsBoolean("lenient-xpath", false));
2450        globalJexlContext = new MyJexlContext();
2451        globalJexlContext.setVars(map);
2452        map = globalJexlContext.getVars();
2453        map.put("cocoon", cocoon);
2454        if (contextObject != null) {
2455            map.put("flowContext", contextObject);
2456            // FIXME (VG): Is this required (what it's used for - examples)?
2457
// Here I use Rhino's live-connect objects to allow Jexl to call
2458
// java constructors
2459
Object JavaDoc javaPkg = FOM_JavaScriptFlowHelper.getJavaPackage(objectModel);
2460            Object JavaDoc pkgs = FOM_JavaScriptFlowHelper.getPackages(objectModel);
2461            map.put("java", javaPkg);
2462            map.put("Packages", pkgs);
2463        }
2464        if (kont != null) {
2465            map.put("continuation", kont);
2466        }
2467        map.put("request", request);
2468        map.put("context", app);
2469        map.put("parameters", parameters);
2470        if (session != null) {
2471            map.put("session", session);
2472        }
2473    }
2474
2475    /* (non-Javadoc)
2476     * @see org.apache.cocoon.generation.Generator#generate()
2477     */

2478    public void generate()
2479    throws IOException JavaDoc, SAXException JavaDoc, ProcessingException {
2480        final String JavaDoc cacheKey = this.inputSource.getURI();
2481
2482        StartDocument startEvent;
2483        synchronized (cache) {
2484            startEvent = (StartDocument)cache.get(cacheKey);
2485        }
2486        performGeneration(this.xmlConsumer, globalJexlContext, jxpathContext, null, startEvent, null);
2487    }
2488
2489    private void performGeneration(final XMLConsumer consumer, MyJexlContext jexlContext, JXPathContext jxpathContext,
2490                StartElement macroCall, Event startEvent, Event endEvent) throws SAXException JavaDoc {
2491        cocoon.put("consumer", consumer);
2492        RedundantNamespacesFilter filter = new RedundantNamespacesFilter(this.xmlConsumer);
2493// EventPrinterPipe log = new EventPrinterPipe();
2494
// log.setConsumer(filter);
2495
execute(filter, globalJexlContext, jxpathContext, null, startEvent, null);
2496    }
2497
2498    interface CharHandler {
2499        public void characters(char[] ch, int offset, int length)
2500            throws SAXException JavaDoc;
2501    }
2502
2503    private void characters(JexlContext jexlContext, JXPathContext jxpathContext, TextEvent event, CharHandler handler)
2504            throws SAXException JavaDoc {
2505        Iterator JavaDoc iter = event.substitutions.iterator();
2506        while (iter.hasNext()) {
2507            Object JavaDoc subst = iter.next();
2508            char[] chars;
2509            if (subst instanceof char[]) {
2510                chars = (char[])subst;
2511            } else {
2512                JXTExpression expr = (JXTExpression)subst;
2513                try {
2514                    Object JavaDoc val = getValue(expr, jexlContext, jxpathContext);
2515                    chars = val != null ? val.toString().toCharArray() : ArrayUtils.EMPTY_CHAR_ARRAY;
2516                } catch (Exception JavaDoc e) {
2517                    throw new JXTException(e.getMessage(), event.location, e);
2518                }
2519            }
2520            handler.characters(chars, 0, chars.length);
2521        }
2522    }
2523
2524    /** dump a DOM document, using an IncludeXMLConsumer to filter out start/end document events */
2525    private void executeDOM(final XMLConsumer consumer, MyJexlContext jexlContext, JXPathContext jxpathContext, Node JavaDoc node)
2526            throws SAXException JavaDoc {
2527        IncludeXMLConsumer includer = new IncludeXMLConsumer(consumer);
2528        DOMStreamer streamer = new DOMStreamer(includer);
2529        streamer.stream(node);
2530   }
2531
2532    private void call(Location location, StartElement macroCall, final XMLConsumer consumer, MyJexlContext jexlContext,
2533            JXPathContext jxpathContext, Event startEvent, Event endEvent) throws SAXException JavaDoc {
2534        try {
2535            execute(consumer, jexlContext, jxpathContext, macroCall, startEvent, endEvent);
2536        } catch (Exception JavaDoc exc) {
2537            throw new JXTException(macroCall.localName + ": " + exc.getMessage(), location, exc);
2538        }
2539    }
2540
2541    public static class LoopTagStatus {
2542        Object JavaDoc current;
2543        int index;
2544        int count;
2545        boolean first;
2546        boolean last;
2547        int begin;
2548        int end;
2549        int step;
2550
2551        public Object JavaDoc getCurrent() {
2552            return current;
2553        }
2554        public int getIndex() {
2555            return index;
2556        }
2557        public int getCount() {
2558            return count;
2559        }
2560        public boolean isFirst() {
2561            return first;
2562        }
2563        public boolean isLast() {
2564            return last;
2565        }
2566        public int getBegin() {
2567            return begin;
2568        }
2569        public int getEnd() {
2570            return end;
2571        }
2572        public int getStep() {
2573            return step;
2574        }
2575    }
2576
2577    private void execute(final XMLConsumer consumer, MyJexlContext jexlContext, JXPathContext jxpathContext,
2578            StartElement macroCall, Event startEvent, Event endEvent) throws SAXException JavaDoc {
2579        Event ev = startEvent;
2580        LocationFacade loc = new LocationFacade(ev.location);
2581        consumer.setDocumentLocator(loc);
2582        while (ev != endEvent) {
2583            loc.setDocumentLocation(ev.location);
2584            if (ev instanceof Characters) {
2585                TextEvent text = (TextEvent)ev;
2586                Iterator JavaDoc iter = text.substitutions.iterator();
2587                while (iter.hasNext()) {
2588                    Object JavaDoc subst = iter.next();
2589                    char[] chars;
2590                    if (subst instanceof char[]) {
2591                        chars = (char[])subst;
2592                    } else {
2593                        JXTExpression expr = (JXTExpression)subst;
2594                        try {
2595                            Object JavaDoc val = getNode(expr, jexlContext, jxpathContext);
2596                            if (val instanceof Node JavaDoc) {
2597                                executeDOM(consumer, jexlContext, jxpathContext, (Node JavaDoc)val);
2598                                continue;
2599                            } else if (val instanceof NodeList JavaDoc) {
2600                                NodeList JavaDoc nodeList = (NodeList JavaDoc)val;
2601                                int len = nodeList.getLength();
2602                                for (int i = 0; i < len; i++) {
2603                                    Node JavaDoc n = nodeList.item(i);
2604                                    executeDOM(consumer, jexlContext, jxpathContext, n);
2605                                }
2606                                continue;
2607                            } else if (val instanceof Node JavaDoc[]) {
2608                                Node JavaDoc[] nodeList = (Node JavaDoc[])val;
2609                                int len = nodeList.length;
2610                                for (int i = 0; i < len; i++) {
2611                                    Node JavaDoc n = nodeList[i];
2612                                    executeDOM(consumer, jexlContext, jxpathContext, n);
2613                                }
2614                                continue;
2615                            } else if (val instanceof XMLizable) {
2616                                ((XMLizable)val).toSAX(new IncludeXMLConsumer(consumer));
2617                                continue;
2618                            }
2619                            chars = val != null ? val.toString().toCharArray() : ArrayUtils.EMPTY_CHAR_ARRAY;
2620                        } catch (Exception JavaDoc e) {
2621                            throw new JXTException(e.getMessage(), ev.location, e);
2622                        }
2623                    }
2624                    consumer.characters(chars, 0, chars.length);
2625                }
2626            } else if (ev instanceof EndElement) {
2627                EndElement endElement = (EndElement)ev;
2628                StartElement startElement = endElement.startElement;
2629                StartDefine def =
2630                    (StartDefine)definitions.get(startElement.qname);
2631                if (def == null) {
2632                    consumer.endElement(startElement.namespaceURI, startElement.localName, startElement.raw);
2633                    namespaces.leaveScope(consumer);
2634                }
2635            } else if (ev instanceof EndPrefixMapping) {
2636                EndPrefixMapping endPrefixMapping = (EndPrefixMapping)ev;
2637                namespaces.removeDeclaration(endPrefixMapping.prefix);
2638            } else if (ev instanceof IgnorableWhitespace) {
2639                TextEvent text = (TextEvent)ev;
2640                characters(jexlContext, jxpathContext, text, new CharHandler() {
2641                       public void characters(char[] ch, int offset, int len) throws SAXException JavaDoc {
2642                           consumer.ignorableWhitespace(ch, offset, len);
2643                       }
2644                   });
2645            } else if (ev instanceof SkippedEntity) {
2646                SkippedEntity skippedEntity = (SkippedEntity)ev;
2647                consumer.skippedEntity(skippedEntity.name);
2648            } else if (ev instanceof StartIf) {
2649                StartIf startIf = (StartIf)ev;
2650                Object JavaDoc val;
2651                try {
2652                    val = getValue(startIf.test, jexlContext, jxpathContext, Boolean.TRUE);
2653                } catch (Exception JavaDoc e) {
2654                    throw new JXTException(e.getMessage(), ev.location, e);
2655                }
2656                boolean result = false;
2657                if (val instanceof Boolean JavaDoc) {
2658                    result = ((Boolean JavaDoc)val).booleanValue();
2659                } else {
2660                    result = (val != null);
2661                }
2662                if (!result) {
2663                    ev = startIf.endInstruction.next;
2664                    continue;
2665                }
2666            } else if (ev instanceof StartForEach) {
2667                StartForEach startForEach = (StartForEach)ev;
2668                final Object JavaDoc items = startForEach.items;
2669                Iterator JavaDoc iter = null;
2670                int begin, end, step;
2671                String JavaDoc var, varStatus;
2672                try {
2673                    if (items != null) {
2674                        JXTExpression expr = (JXTExpression)items;
2675                        if (expr.compiledExpression instanceof CompiledExpression) {
2676                            CompiledExpression compiledExpression = (CompiledExpression)expr.compiledExpression;
2677                            Object JavaDoc val =
2678                                compiledExpression.getPointer(jxpathContext, expr.raw).getNode();
2679                            // FIXME: workaround for JXPath bug
2680
iter = val instanceof NativeArray ? new JSIntrospector.NativeArrayIterator((NativeArray)val) :
2681                                        compiledExpression.iteratePointers(jxpathContext);
2682                        } else if (expr.compiledExpression instanceof Expression) {
2683                            Expression e = (Expression)expr.compiledExpression;
2684                            Object JavaDoc result = e.evaluate(jexlContext);
2685                            if (result != null) {
2686                                iter = Introspector.getUberspect().getIterator(result,
2687                                        new Info(ev.location.getURI(), ev.location.getLineNumber(), ev.location.getColumnNumber()));
2688                            }
2689                            if (iter == null) {
2690                                iter = EMPTY_ITER;
2691                            }
2692                        } else {
2693                            // literal value
2694
iter = new Iterator JavaDoc() {
2695                                Object JavaDoc val = items;
2696
2697                                public boolean hasNext() {
2698                                    return val != null;
2699                                }
2700
2701                                public Object JavaDoc next() {
2702                                    Object JavaDoc res = val;
2703                                    val = null;
2704                                    return res;
2705                                }
2706
2707                                public void remove() {
2708                                    // EMPTY
2709
}
2710                            };
2711                        }
2712                    } else {
2713                        iter = NULL_ITER;
2714                    }
2715                    begin = startForEach.begin == null ? 0 :
2716                        getIntValue(startForEach.begin, jexlContext, jxpathContext);
2717                    end = startForEach.end == null ? Integer.MAX_VALUE :
2718                            getIntValue(startForEach.end, jexlContext, jxpathContext);
2719                    step = startForEach.step == null ? 1 :
2720                            getIntValue(startForEach.step, jexlContext, jxpathContext);
2721                    var = getStringValue(startForEach.var, jexlContext, jxpathContext);
2722                    varStatus = getStringValue(startForEach.varStatus, jexlContext, jxpathContext);
2723                } catch (Exception JavaDoc exc) {
2724                    throw new JXTException(exc.getMessage(), ev.location, exc);
2725                }
2726                MyJexlContext localJexlContext = new MyJexlContext(jexlContext);
2727                MyVariables localJXPathVariables = new MyVariables((MyVariables)jxpathContext.getVariables());
2728                int i = 0;
2729                // Move to the begin row
2730
while (i < begin && iter.hasNext()) {
2731                    iter.next();
2732                    i++;
2733                }
2734                LoopTagStatus status = null;
2735                if (varStatus != null) {
2736                    status = new LoopTagStatus();
2737                    status.begin = begin;
2738                    status.end = end;
2739                    status.step = step;
2740                    status.first = true;
2741                    localJexlContext.put(varStatus, status);
2742                    localJXPathVariables.declareVariable(varStatus, status);
2743                }
2744                int skipCounter, count = 1;
2745                JXPathContext localJXPathContext = null;
2746                while (i <= end && iter.hasNext()) {
2747                    Object JavaDoc value = iter.next();
2748                    if (value instanceof Pointer) {
2749                        Pointer ptr = (Pointer)value;
2750                        localJXPathContext = jxpathContext.getRelativeContext(ptr);
2751                        localJXPathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces));
2752                        try {
2753                            value = ptr.getNode();
2754                        } catch (Exception JavaDoc exc) {
2755                            throw new JXTException(exc.getMessage(), ev.location, null);
2756                        }
2757                    } else {
2758                        localJXPathContext = jxpathContextFactory.newContext(jxpathContext, value);
2759                        localJXPathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces));
2760                    }
2761                    localJXPathContext.setVariables(localJXPathVariables);
2762                    if (var != null) {
2763                        localJexlContext.put(var, value);
2764                    }
2765                    if (status != null) {
2766                        status.index = i;
2767                        status.count = count;
2768                        status.first = i == begin;
2769                        status.current = value;
2770                        status.last = (i == end || !iter.hasNext());
2771                    }
2772                    execute(consumer, localJexlContext, localJXPathContext, macroCall, startForEach.next, startForEach.endInstruction);
2773                    // Skip rows
2774
skipCounter = step;
2775                    while (--skipCounter > 0 && iter.hasNext()) {
2776                        iter.next();
2777                    }
2778                    // Increase index
2779
i += step;
2780                    count++;
2781                }
2782                ev = startForEach.endInstruction.next;
2783                continue;
2784            } else if (ev instanceof StartChoose) {
2785                StartChoose startChoose = (StartChoose)ev;
2786                StartWhen startWhen = startChoose.firstChoice;
2787                while (startWhen != null) {
2788                    Object JavaDoc val;
2789                    try {
2790                        val = getValue(startWhen.test, jexlContext, jxpathContext, Boolean.TRUE);
2791                    } catch (Exception JavaDoc e) {
2792                        throw new JXTException(e.getMessage(), ev.location, e);
2793                    }
2794                    boolean result;
2795                    if (val instanceof Boolean JavaDoc) {
2796                        result = ((Boolean JavaDoc)val).booleanValue();
2797                    } else {
2798                        result = (val != null);
2799                    }
2800                    if (result) {
2801                        execute(consumer, jexlContext, jxpathContext, macroCall, startWhen.next, startWhen.endInstruction);
2802                        break;
2803                    }
2804                    startWhen = startWhen.nextChoice;
2805                }
2806                if (startWhen == null && startChoose.otherwise != null) {
2807                    execute(consumer, jexlContext, jxpathContext, macroCall, startChoose.otherwise.next, startChoose.otherwise.endInstruction);
2808                }
2809                ev = startChoose.endInstruction.next;
2810                continue;
2811            } else if (ev instanceof StartSet) {
2812                StartSet startSet = (StartSet)ev;
2813                Object JavaDoc value = null;
2814                String JavaDoc var = null;
2815                try {
2816                    if (startSet.var != null) {
2817                        var = getStringValue(startSet.var, jexlContext, jxpathContext);
2818                    }
2819                    if (startSet.value != null) {
2820                        value = getNode(startSet.value, jexlContext, jxpathContext);
2821                    }
2822                } catch (Exception JavaDoc exc) {
2823                    throw new JXTException(exc.getMessage(), ev.location, exc);
2824                }
2825                if (value == null) {
2826                    NodeList JavaDoc nodeList = toDOMNodeList("set", startSet, jexlContext, macroCall);
2827                    // JXPath doesn't handle NodeList, so convert it to an array
2828
int len = nodeList.getLength();
2829                    Node JavaDoc[] nodeArr = new Node JavaDoc[len];
2830                    for (int i = 0; i < len; i++) {
2831                        nodeArr[i] = nodeList.item(i);
2832                    }
2833                    value = nodeArr;
2834                }
2835                if (var != null) {
2836                    jxpathContext.getVariables().declareVariable(var, value);
2837                    jexlContext.put(var, value);
2838                }
2839                ev = startSet.endInstruction.next;
2840                continue;
2841            } else if (ev instanceof StartElement) {
2842                StartElement startElement = (StartElement)ev;
2843                StartDefine def =
2844                    (StartDefine)definitions.get(startElement.qname);
2845                if (def != null) {
2846                    Map JavaDoc attributeMap = new HashMap JavaDoc();
2847                    Iterator JavaDoc i = startElement.attributeEvents.iterator();
2848                    while (i.hasNext()) {
2849                        String JavaDoc attributeName;
2850                        Object JavaDoc attributeValue;
2851                        AttributeEvent attrEvent = (AttributeEvent) i.next();
2852                        attributeName = attrEvent.localName;
2853                        if (attrEvent instanceof CopyAttribute) {
2854                            CopyAttribute copy = (CopyAttribute)attrEvent;
2855                            attributeValue = copy.value;
2856                        } else if (attrEvent instanceof SubstituteAttribute) {
2857                            SubstituteAttribute substEvent = (SubstituteAttribute)attrEvent;
2858                            if (substEvent.substitutions.size() == 1 && substEvent.substitutions.get(0) instanceof JXTExpression) {
2859                                JXTExpression expr = (JXTExpression)substEvent.substitutions.get(0);
2860                                Object JavaDoc val;
2861                                try {
2862                                    val = getNode(expr, jexlContext, jxpathContext);
2863                                } catch (Exception JavaDoc e) {
2864                                    throw new JXTException(e.getMessage(), ev.location, e);
2865                                }
2866                                attributeValue = val != null ? val : "";
2867                            } else {
2868                                StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2869                                Iterator JavaDoc iterSubst = substEvent.substitutions.iterator();
2870                                while (iterSubst.hasNext()) {
2871                                    Subst subst = (Subst)iterSubst.next();
2872                                    if (subst instanceof Literal) {
2873                                        Literal lit = (Literal)subst;
2874                                        buf.append(lit.value);
2875                                    } else if (subst instanceof JXTExpression) {
2876                                        JXTExpression expr = (JXTExpression)subst;
2877                                        Object JavaDoc val;
2878                                        try {
2879                                            val = getValue(expr, jexlContext, jxpathContext);
2880                                        } catch (Exception JavaDoc e) {
2881                                            throw new JXTException(e.getMessage(), ev.location, e);
2882                                        }
2883                                        buf.append(val != null ? val.toString() : "");
2884                                    }
2885                                }
2886                                attributeValue = buf.toString();
2887                            }
2888                        } else {
2889                            throw new Error JavaDoc("this shouldn't have happened");
2890                        }
2891                        attributeMap.put(attributeName, attributeValue);
2892                    }
2893                    MyVariables parent =(MyVariables)jxpathContext.getVariables();
2894                    MyVariables vars = new MyVariables(parent);
2895                    MyJexlContext localJexlContext = new MyJexlContext(jexlContext);
2896                    HashMap JavaDoc macro = new HashMap JavaDoc();
2897                    macro.put("body", startElement);
2898                    macro.put("arguments", attributeMap);
2899                    localJexlContext.put("macro", macro);
2900                    vars.declareVariable("macro", macro);
2901                    Iterator JavaDoc iter = def.parameters.entrySet().iterator();
2902                    while (iter.hasNext()) {
2903                        Map.Entry JavaDoc e = (Map.Entry JavaDoc)iter.next();
2904                        String JavaDoc key = (String JavaDoc)e.getKey();
2905                        StartParameter startParam = (StartParameter)e.getValue();
2906                        Object JavaDoc default_ = startParam.default_;
2907                        Object JavaDoc val = attributeMap.get(key);
2908                        if (val == null) {
2909                            val = default_;
2910                        }
2911                        localJexlContext.put(key, val);
2912                        vars.declareVariable(key, val);
2913                    }
2914                    JXPathContext localJXPathContext = jxpathContextFactory.newContext(null, jxpathContext.getContextBean());
2915                    localJXPathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces));
2916                    localJXPathContext.setVariables(vars);
2917                    call(ev.location, startElement, consumer, localJexlContext, localJXPathContext, def.body, def.endInstruction);
2918                    ev = startElement.endElement.next;
2919                    continue;
2920                }
2921                Iterator JavaDoc i = startElement.attributeEvents.iterator();
2922                AttributesImpl JavaDoc attrs = new AttributesImpl JavaDoc();
2923                while (i.hasNext()) {
2924                    AttributeEvent attrEvent = (AttributeEvent)i.next();
2925                    if (attrEvent instanceof CopyAttribute) {
2926                        CopyAttribute copy = (CopyAttribute)attrEvent;
2927                        attrs.addAttribute(copy.namespaceURI, copy.localName, copy.raw, copy.type, copy.value);
2928                    } else if (attrEvent instanceof SubstituteAttribute) {
2929                        StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2930                        SubstituteAttribute substEvent = (SubstituteAttribute)attrEvent;
2931                        Iterator JavaDoc iterSubst = substEvent.substitutions.iterator();
2932                        while (iterSubst.hasNext()) {
2933                            Subst subst = (Subst)iterSubst.next();
2934                            if (subst instanceof Literal) {
2935                                Literal lit = (Literal)subst;
2936                                buf.append(lit.value);
2937                            } else if (subst instanceof JXTExpression) {
2938                                JXTExpression expr = (JXTExpression)subst;
2939                                Object JavaDoc val;
2940                                try {
2941                                    val = getValue(expr, jexlContext, jxpathContext);
2942                                } catch (Exception JavaDoc e) {
2943                                    throw new JXTException(e.getMessage(), ev.location, e);
2944                               }
2945                               buf.append(val != null ? val.toString() : "");
2946                            }
2947                        }
2948                        attrs.addAttribute(attrEvent.namespaceURI, attrEvent.localName, attrEvent.raw, attrEvent.type, buf.toString());
2949                    }
2950                }
2951                namespaces.enterScope(consumer);
2952                consumer.startElement(startElement.namespaceURI, startElement.localName, startElement.raw, attrs);
2953            } else if (ev instanceof StartFormatNumber) {
2954                StartFormatNumber startFormatNumber = (StartFormatNumber)ev;
2955                try {
2956                    String JavaDoc result = startFormatNumber.format(jexlContext, jxpathContext);
2957                    if (result != null) {
2958                        char[] chars = result.toCharArray();
2959                        consumer.characters(chars, 0, chars.length);
2960                    }
2961                } catch (Exception JavaDoc e) {
2962                    throw new JXTException(e.getMessage(), ev.location, e);
2963                }
2964            } else if (ev instanceof StartFormatDate) {
2965                StartFormatDate startFormatDate = (StartFormatDate)ev;
2966                try {
2967                    String JavaDoc result = startFormatDate.format(jexlContext, jxpathContext);
2968                    if (result != null) {
2969                        char[] chars = result.toCharArray();
2970                        consumer.characters(chars, 0, chars.length);
2971                    }
2972                } catch (Exception JavaDoc e) {
2973                    throw new JXTException(e.getMessage(), ev.location, e);
2974                }
2975            } else if (ev instanceof StartPrefixMapping) {
2976                StartPrefixMapping startPrefixMapping = (StartPrefixMapping)ev;
2977                namespaces.addDeclaration(startPrefixMapping.prefix, startPrefixMapping.uri);
2978            } else if (ev instanceof StartComment) {
2979                StartComment startJXComment = (StartComment)ev;
2980                // Parse the body of the comment
2981
NodeList JavaDoc nodeList = toDOMNodeList("comment", startJXComment, jexlContext, macroCall);
2982                // JXPath doesn't handle NodeList, so convert it to an array
2983
int len = nodeList.getLength();
2984                final StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
2985                Properties JavaDoc omit = XMLUtils.createPropertiesForXML(true);
2986                for (int i = 0; i < len; i++) {
2987                    try {
2988                        String JavaDoc str = XMLUtils.serializeNode(nodeList.item(i), omit);
2989                        buf.append(StringUtils.substringAfter(str, ">")); // cut the XML header
2990
} catch (Exception JavaDoc e) {
2991                        throw new JXTException(e.getMessage(), startJXComment.location, e);
2992                    }
2993                }
2994                char[] chars = new char[buf.length()];
2995                buf.getChars(0, chars.length, chars, 0);
2996                consumer.comment(chars, 0, chars.length);
2997                ev = startJXComment.endInstruction.next;
2998                continue;
2999            } else if (ev instanceof EndCDATA) {
3000                consumer.endCDATA();
3001            } else if (ev instanceof EndDTD) {
3002                consumer.endDTD();
3003            } else if (ev instanceof EndEntity) {
3004                consumer.endEntity(((EndEntity)ev).name);
3005            } else if (ev instanceof StartCDATA) {
3006                consumer.startCDATA();
3007            } else if (ev instanceof StartDTD) {
3008                StartDTD startDTD = (StartDTD)ev;
3009                consumer.startDTD(startDTD.name, startDTD.publicId, startDTD.systemId);
3010            } else if (ev instanceof StartEntity) {
3011                consumer.startEntity(((StartEntity)ev).name);
3012            } else if (ev instanceof StartOut) {
3013                StartOut startOut = (StartOut)ev;
3014                Object JavaDoc val;
3015                try {
3016                    val = getNode(startOut.compiledExpression, jexlContext, jxpathContext, startOut.lenient);
3017                    if (val instanceof Node JavaDoc) {
3018                        executeDOM(consumer, jexlContext, jxpathContext, (Node JavaDoc)val);
3019                    } else if (val instanceof NodeList JavaDoc) {
3020                        NodeList JavaDoc nodeList = (NodeList JavaDoc)val;
3021                        int len = nodeList.getLength();
3022                        for (int i = 0; i < len; i++) {
3023                            Node JavaDoc n = nodeList.item(i);
3024                            executeDOM(consumer, jexlContext, jxpathContext, n);
3025                        }
3026                    } else if (val instanceof Node JavaDoc[]) {
3027                        Node JavaDoc[] nodeList = (Node JavaDoc[])val;
3028                        int len = nodeList.length;
3029                        for (int i = 0;i < len; i++) {
3030                            Node JavaDoc n = nodeList[i];
3031                            executeDOM(consumer, jexlContext, jxpathContext, n);
3032                        }
3033                    } else if (val instanceof XMLizable) {
3034                        ((XMLizable)val).toSAX(new IncludeXMLConsumer(consumer));
3035                    } else {
3036                        char[] ch = val == null ? ArrayUtils.EMPTY_CHAR_ARRAY : val.toString().toCharArray();
3037                        consumer.characters(ch, 0, ch.length);
3038                    }
3039                } catch (Exception JavaDoc e) {
3040                    throw new JXTException(e.getMessage(), ev.location, e);
3041                }
3042            } else if (ev instanceof StartTemplate) {
3043                // EMPTY
3044
} else if (ev instanceof StartEval) {
3045                StartEval startEval = (StartEval)ev;
3046                JXTExpression expr = startEval.value;
3047                try {
3048                    Object JavaDoc val = getNode(expr, jexlContext, jxpathContext);
3049                    if (!(val instanceof StartElement)) {
3050                        throw new Exception JavaDoc("macro invocation required instead of: " + val);
3051                    }
3052                    StartElement call = (StartElement)val;
3053                    execute(consumer, jexlContext, jxpathContext, call, call.next, call.endElement);
3054                } catch (Exception JavaDoc exc) {
3055                    throw new JXTException(exc.getMessage(), ev.location, exc);
3056                }
3057                ev = startEval.endInstruction.next;
3058                continue;
3059            } else if (ev instanceof StartEvalBody) {
3060                StartEvalBody startEval = (StartEvalBody)ev;
3061                try {
3062                    execute(consumer, jexlContext, jxpathContext, null, macroCall.next, macroCall.endElement);
3063                } catch (Exception JavaDoc exc) {
3064                    throw new JXTException(exc.getMessage(), ev.location, exc);
3065                }
3066                ev = startEval.endInstruction.next;
3067                continue;
3068            } else if (ev instanceof StartDefine) {
3069                StartDefine startDefine = (StartDefine)ev;
3070                definitions.put(startDefine.qname, startDefine);
3071                ev = startDefine.endInstruction.next;
3072                continue;
3073            } else if (ev instanceof StartImport) {
3074                StartImport startImport = (StartImport)ev;
3075                String JavaDoc uri;
3076                AttributeEvent e = startImport.uri;
3077                if (e instanceof CopyAttribute) {
3078                    CopyAttribute copy = (CopyAttribute)e;
3079                    uri = copy.value;
3080                } else {
3081                    StringBuffer JavaDoc buf = new StringBuffer JavaDoc();
3082                    SubstituteAttribute substAttr = (SubstituteAttribute)e;
3083                    Iterator JavaDoc i = substAttr.substitutions.iterator();
3084                    while (i.hasNext()) {
3085                        Subst subst = (Subst)i.next();
3086                        if (subst instanceof Literal) {
3087                            Literal lit = (Literal)subst;
3088                            buf.append(lit.value);
3089                        } else if (subst instanceof JXTExpression) {
3090                            JXTExpression expr = (JXTExpression)subst;
3091                            Object JavaDoc val;
3092                            try {
3093                                val = getValue(expr, jexlContext, jxpathContext);
3094                            } catch (Exception JavaDoc exc) {
3095                                throw new JXTException(exc.getMessage(), ev.location, exc);
3096                            }
3097                            buf.append(val != null ? val.toString() : "");
3098                        }
3099                    }
3100                    uri = buf.toString();
3101                }
3102                Source input = null;
3103                StartDocument doc;
3104                try {
3105                    input = resolver.resolveURI(uri);
3106                    SourceValidity validity = null;
3107                    synchronized (cache) {
3108                        doc = (StartDocument)cache.get(input.getURI());
3109                        if (doc != null) {
3110                            boolean recompile = false;
3111                            if (doc.compileTime == null) {
3112                                recompile = true;
3113                            } else {
3114                                int valid = doc.compileTime.isValid();
3115                                if (valid == SourceValidity.UNKNOWN) {
3116                                    validity = input.getValidity();
3117                                    valid = doc.compileTime.isValid(validity);
3118                                }
3119                                if (valid != SourceValidity.VALID) {
3120                                    recompile = true;
3121                                }
3122                            }
3123                            if (recompile) {
3124                                doc = null; // recompile
3125
}
3126                        }
3127                    }
3128                    if (doc == null) {
3129                        Parser parser = new Parser();
3130                        // call getValidity before using the stream is faster if the source is a SitemapSource
3131
if (validity == null) {
3132                            validity = input.getValidity();
3133                        }
3134                        SourceUtil.parse(this.manager, input, parser);
3135                        doc = parser.getStartEvent();
3136                        doc.compileTime = validity;
3137                        synchronized (cache) {
3138                            cache.put(input.getURI(), doc);
3139                        }
3140                    }
3141                } catch (Exception JavaDoc exc) {
3142                    throw new JXTException(exc.getMessage(), ev.location, exc);
3143                }
3144                finally {
3145                    resolver.release(input);
3146                }
3147                JXPathContext selectJXPath = jxpathContext;
3148                MyJexlContext selectJexl = jexlContext;
3149                if (startImport.select != null) {
3150                    try {
3151                        Object JavaDoc obj = getValue(startImport.select, jexlContext, jxpathContext);
3152                        selectJXPath = jxpathContextFactory.newContext(null, obj);
3153                        selectJXPath.setNamespaceContextPointer(new NamespacesTablePointer(namespaces));
3154                        selectJXPath.setVariables(variables);
3155                        selectJexl = new MyJexlContext(jexlContext);
3156                        fillContext(obj, selectJexl);
3157                    } catch (Exception JavaDoc exc) {
3158                        throw new JXTException(exc.getMessage(), ev.location, exc);
3159                    }
3160                }
3161                try {
3162                    execute(consumer, selectJexl, selectJXPath, macroCall, doc.next, doc.endDocument);
3163                } catch (Exception JavaDoc exc) {
3164                        throw new JXTException("Exception occurred in imported template " + uri + ": "+ exc.getMessage(), ev.location, exc);
3165                }
3166                ev = startImport.endInstruction.next;
3167                continue;
3168            } else if (ev instanceof StartDocument) {
3169                if (((StartDocument)ev).endDocument != null) {
3170                    // if this isn't a document fragment
3171
consumer.startDocument();
3172                }
3173            } else if (ev instanceof EndDocument) {
3174                consumer.endDocument();
3175            } else if (ev instanceof ProcessingInstruction) {
3176                ProcessingInstruction pi = (ProcessingInstruction)ev;
3177                consumer.processingInstruction(pi.target, pi.data);
3178            }
3179            ev = ev.next;
3180        }
3181    }
3182
3183    /* (non-Javadoc)
3184     * @see org.apache.cocoon.caching.CacheableProcessingComponent#getKey()
3185     */

3186    public Serializable JavaDoc getKey() {
3187        JXTExpression cacheKeyExpr = (JXTExpression)getCurrentTemplateProperty(CACHE_KEY);
3188        try {
3189            final Serializable JavaDoc templateKey = (Serializable JavaDoc) getValue(cacheKeyExpr, globalJexlContext, jxpathContext);
3190            if (templateKey != null) {
3191                return new JXCacheKey(this.inputSource.getURI(), templateKey);
3192            }
3193        } catch (Exception JavaDoc e) {
3194            getLogger().error("error evaluating cache key", e);
3195        }
3196        return null;
3197    }
3198
3199    /* (non-Javadoc)
3200     * @see org.apache.cocoon.caching.CacheableProcessingComponent#getValidity()
3201     */

3202    public SourceValidity getValidity() {
3203        JXTExpression validityExpr = (JXTExpression)getCurrentTemplateProperty(VALIDITY);
3204        try {
3205            final SourceValidity sourceValidity = this.inputSource.getValidity();
3206            final SourceValidity templateValidity = (SourceValidity) getValue(validityExpr, globalJexlContext, jxpathContext);
3207            if (sourceValidity != null && templateValidity != null) {
3208                return new JXSourceValidity(sourceValidity, templateValidity);
3209            }
3210        } catch (Exception JavaDoc e) {
3211            getLogger().error("error evaluating cache validity", e);
3212        }
3213        return null;
3214    }
3215
3216    private Object JavaDoc getCurrentTemplateProperty(String JavaDoc propertyName) {
3217        final String JavaDoc uri = this.inputSource.getURI();
3218        StartDocument startEvent;
3219        synchronized (cache) {
3220            startEvent = (StartDocument)cache.get(uri);
3221        }
3222        return (startEvent != null) ? startEvent.templateProperties.get(propertyName) : null;
3223    }
3224
3225    private NodeList JavaDoc toDOMNodeList(String JavaDoc elementName, StartInstruction si,
3226            MyJexlContext jexlContext, StartElement macroCall) throws SAXException JavaDoc{
3227        DOMBuilder builder = new DOMBuilder();
3228        builder.startDocument();
3229        builder.startElement(NS, elementName, elementName, EMPTY_ATTRS);
3230        execute(builder, jexlContext, jxpathContext, macroCall, si.next, si.endInstruction);
3231        builder.endElement(NS, elementName, elementName);
3232        builder.endDocument();
3233        Node JavaDoc node = builder.getDocument().getDocumentElement();
3234        return node.getChildNodes();
3235    }
3236
3237    static final class JXCacheKey implements Serializable JavaDoc {
3238        private final String JavaDoc templateUri;
3239        private final Serializable JavaDoc templateKey;
3240        private JXCacheKey(String JavaDoc templateUri, Serializable JavaDoc templateKey) {
3241            this.templateUri = templateUri;
3242            this.templateKey = templateKey;
3243        }
3244        public int hashCode() {
3245            return templateUri.hashCode() + templateKey.hashCode();
3246        }
3247        public String JavaDoc toString() {
3248            return "TK:" + templateUri + "_" + templateKey;
3249        }
3250        public boolean equals(Object JavaDoc o) {
3251            if (o instanceof JXCacheKey) {
3252                JXCacheKey jxck = (JXCacheKey)o;
3253                return this.templateUri.equals(jxck.templateUri)
3254                       && this.templateKey.equals(jxck.templateKey);
3255            }
3256            return false;
3257        }
3258    }
3259
3260    static final class JXSourceValidity implements SourceValidity, Serializable JavaDoc {
3261        private final SourceValidity sourceValidity;
3262        private final SourceValidity templateValidity;
3263
3264        private JXSourceValidity(SourceValidity sourceValidity, SourceValidity templateValidity) {
3265            this.sourceValidity = sourceValidity;
3266            this.templateValidity = templateValidity;
3267        }
3268
3269        public int isValid() {
3270            switch (sourceValidity.isValid()) {
3271                case SourceValidity.INVALID:
3272                    return SourceValidity.INVALID;
3273
3274                case SourceValidity.UNKNOWN:
3275                    if (templateValidity.isValid() == SourceValidity.INVALID) {
3276                        return SourceValidity.INVALID;
3277                    }
3278                    return SourceValidity.UNKNOWN;
3279
3280                case SourceValidity.VALID:
3281                    return templateValidity.isValid();
3282            }
3283
3284            return SourceValidity.UNKNOWN;
3285        }
3286
3287        public int isValid(SourceValidity otherValidity) {
3288            if (otherValidity instanceof JXSourceValidity) {
3289                JXSourceValidity otherJXValidity = (JXSourceValidity) otherValidity;
3290                switch (sourceValidity.isValid(otherJXValidity.sourceValidity)) {
3291                    case SourceValidity.INVALID:
3292                        return SourceValidity.INVALID;
3293
3294                    case SourceValidity.UNKNOWN:
3295                        if (templateValidity.isValid(otherJXValidity.templateValidity) == SourceValidity.INVALID) {
3296                            return SourceValidity.INVALID;
3297                        }
3298                        return SourceValidity.UNKNOWN;
3299
3300                    case SourceValidity.VALID:
3301                        return templateValidity.isValid(otherJXValidity.templateValidity);
3302                }
3303            }
3304            return SourceValidity.UNKNOWN;
3305        }
3306    }
3307}
3308
Popular Tags