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<