0001: /*
0002: * Licensed to the Apache Software Foundation (ASF) under one or more
0003: * contributor license agreements. See the NOTICE file distributed with
0004: * this work for additional information regarding copyright ownership.
0005: * The ASF licenses this file to You under the Apache License, Version 2.0
0006: * (the "License"); you may not use this file except in compliance with
0007: * the License. You may obtain a copy of the License at
0008: *
0009: * http://www.apache.org/licenses/LICENSE-2.0
0010: *
0011: * Unless required by applicable law or agreed to in writing, software
0012: * distributed under the License is distributed on an "AS IS" BASIS,
0013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
0014: * See the License for the specific language governing permissions and
0015: * limitations under the License.
0016: */
0017: package org.apache.cocoon.generation;
0018:
0019: import java.beans.PropertyDescriptor;
0020: import java.io.CharArrayReader;
0021: import java.io.IOException;
0022: import java.io.PrintStream;
0023: import java.io.PrintWriter;
0024: import java.io.Serializable;
0025: import java.io.StringReader;
0026: import java.lang.reflect.Field;
0027: import java.lang.reflect.InvocationTargetException;
0028: import java.lang.reflect.Method;
0029: import java.text.DateFormat;
0030: import java.text.DecimalFormat;
0031: import java.text.DecimalFormatSymbols;
0032: import java.text.NumberFormat;
0033: import java.text.SimpleDateFormat;
0034: import java.util.Enumeration;
0035: import java.util.HashMap;
0036: import java.util.Iterator;
0037: import java.util.LinkedList;
0038: import java.util.List;
0039: import java.util.Locale;
0040: import java.util.Map;
0041: import java.util.Properties;
0042: import java.util.Stack;
0043: import java.util.TimeZone;
0044:
0045: import org.apache.avalon.framework.parameters.Parameters;
0046: import org.apache.avalon.framework.service.ServiceException;
0047: import org.apache.avalon.framework.service.ServiceManager;
0048: import org.apache.cocoon.ProcessingException;
0049: import org.apache.cocoon.caching.CacheableProcessingComponent;
0050: import org.apache.cocoon.components.flow.FlowHelper;
0051: import org.apache.cocoon.components.flow.WebContinuation;
0052: import org.apache.cocoon.components.flow.javascript.fom.FOM_JavaScriptFlowHelper;
0053: import org.apache.cocoon.components.source.SourceUtil;
0054: import org.apache.cocoon.environment.ObjectModelHelper;
0055: import org.apache.cocoon.environment.Request;
0056: import org.apache.cocoon.environment.SourceResolver;
0057: import org.apache.cocoon.transformation.ServiceableTransformer;
0058: import org.apache.cocoon.util.jxpath.NamespacesTablePointer;
0059: import org.apache.cocoon.util.location.LocatedRuntimeException;
0060: import org.apache.cocoon.util.location.Location;
0061: import org.apache.cocoon.util.location.LocationUtils;
0062: import org.apache.cocoon.xml.IncludeXMLConsumer;
0063: import org.apache.cocoon.xml.NamespacesTable;
0064: import org.apache.cocoon.xml.RedundantNamespacesFilter;
0065: import org.apache.cocoon.xml.XMLConsumer;
0066: import org.apache.cocoon.xml.XMLUtils;
0067: import org.apache.cocoon.xml.dom.DOMBuilder;
0068: import org.apache.cocoon.xml.dom.DOMStreamer;
0069: import org.apache.commons.jexl.Expression;
0070: import org.apache.commons.jexl.ExpressionFactory;
0071: import org.apache.commons.jexl.JexlContext;
0072: import org.apache.commons.jexl.util.Introspector;
0073: import org.apache.commons.jexl.util.introspection.Info;
0074: import org.apache.commons.jexl.util.introspection.UberspectImpl;
0075: import org.apache.commons.jexl.util.introspection.VelMethod;
0076: import org.apache.commons.jexl.util.introspection.VelPropertyGet;
0077: import org.apache.commons.jexl.util.introspection.VelPropertySet;
0078: import org.apache.commons.jxpath.*;
0079: import org.apache.commons.lang.ArrayUtils;
0080: import org.apache.commons.lang.StringUtils;
0081: import org.apache.excalibur.source.Source;
0082: import org.apache.excalibur.source.SourceException;
0083: import org.apache.excalibur.source.SourceValidity;
0084: import org.apache.excalibur.xml.sax.XMLizable;
0085: import org.mozilla.javascript.Context;
0086: import org.mozilla.javascript.Function;
0087: import org.mozilla.javascript.JavaScriptException;
0088: import org.mozilla.javascript.NativeArray;
0089: import org.mozilla.javascript.NativeJavaClass;
0090: import org.mozilla.javascript.ScriptRuntime;
0091: import org.mozilla.javascript.Scriptable;
0092: import org.mozilla.javascript.ScriptableObject;
0093: import org.mozilla.javascript.Undefined;
0094: import org.mozilla.javascript.Wrapper;
0095: import org.w3c.dom.Node;
0096: import org.w3c.dom.NodeList;
0097: import org.xml.sax.Attributes;
0098: import org.xml.sax.ContentHandler;
0099: import org.xml.sax.Locator;
0100: import org.xml.sax.SAXException;
0101: import org.xml.sax.ext.LexicalHandler;
0102: import org.xml.sax.helpers.AttributesImpl;
0103:
0104: /**
0105: * @cocoon.sitemap.component.documentation
0106: * Provides a generic page template with embedded JSTL and XPath
0107: * expression substitution to access data sent by Cocoon Flowscripts.
0108: *
0109: * @cocoon.sitemap.component.name jx
0110: * @cocoon.sitemap.component.label content
0111: * @cocoon.sitemap.component.logger sitemap.generator.jx
0112: *
0113: * @cocoon.sitemap.component.pooling.max 16
0114: *
0115: * @deprecated Replaced with the template block: {@link org.apache.cocoon.template.JXTemplateGenerator}.
0116: *
0117: * @version $Id: JXTemplateGenerator.java 452138 2006-10-02 17:32:00Z joerg $
0118: */
0119: public class JXTemplateGenerator extends ServiceableGenerator implements
0120: CacheableProcessingComponent {
0121:
0122: // Quick'n dirty hack to replace all SAXParseException by a located runtime exception
0123: private static final class JXTException extends
0124: LocatedRuntimeException {
0125: JXTException(String message, Location loc, Throwable thr) {
0126: super (message, thr, loc);
0127: }
0128: }
0129:
0130: private static final JXPathContextFactory jxpathContextFactory = JXPathContextFactory
0131: .newInstance();
0132:
0133: private static final Attributes EMPTY_ATTRS = XMLUtils.EMPTY_ATTRIBUTES;
0134:
0135: private final NamespacesTable namespaces = new NamespacesTable();
0136:
0137: private static final Iterator EMPTY_ITER = new Iterator() {
0138: public boolean hasNext() {
0139: return false;
0140: }
0141:
0142: public Object next() {
0143: return null;
0144: }
0145:
0146: public void remove() {
0147: // EMPTY
0148: }
0149: };
0150:
0151: private static final Iterator NULL_ITER = new Iterator() {
0152: public boolean hasNext() {
0153: return true;
0154: }
0155:
0156: public Object next() {
0157: return null;
0158: }
0159:
0160: public void remove() {
0161: // EMPTY
0162: }
0163: };
0164:
0165: private XMLConsumer getConsumer() {
0166: return this .xmlConsumer;
0167: }
0168:
0169: public static class ErrorHolder extends Exception {
0170:
0171: private Error err;
0172:
0173: public ErrorHolder(Error err) {
0174: super (err.getMessage());
0175: this .err = err;
0176: }
0177:
0178: public void printStackTrace(PrintStream ps) {
0179: err.printStackTrace(ps);
0180: }
0181:
0182: public void printStackTrace(PrintWriter pw) {
0183: err.printStackTrace(pw);
0184: }
0185:
0186: public void printStackTrace() {
0187: err.printStackTrace();
0188: }
0189:
0190: public Error getError() {
0191: return err;
0192: }
0193:
0194: }
0195:
0196: /**
0197: * Facade to the Location to be set on the consumer prior to
0198: * sending other events, location member changeable
0199: */
0200: public static class LocationFacade implements Locator {
0201: private Location locator;
0202:
0203: public LocationFacade(Location initialLocation) {
0204: this .locator = initialLocation;
0205: }
0206:
0207: public void setDocumentLocation(Location newLocation) {
0208: this .locator = newLocation;
0209: }
0210:
0211: public int getColumnNumber() {
0212: return this .locator.getColumnNumber();
0213: }
0214:
0215: public int getLineNumber() {
0216: return this .locator.getLineNumber();
0217: }
0218:
0219: public String getPublicId() {
0220: return null;
0221: }
0222:
0223: public String getSystemId() {
0224: return this .locator.getURI();
0225: }
0226: }
0227:
0228: /**
0229: * Jexl Introspector that supports Rhino JavaScript objects
0230: * as well as Java Objects
0231: */
0232: static class JSIntrospector extends UberspectImpl {
0233:
0234: static class JSMethod implements VelMethod {
0235:
0236: Scriptable scope;
0237: String name;
0238:
0239: public JSMethod(Scriptable scope, String name) {
0240: this .scope = scope;
0241: this .name = name;
0242: }
0243:
0244: public Object invoke(Object this Arg, Object[] args)
0245: throws Exception {
0246: Context cx = Context.enter();
0247: try {
0248: Object result;
0249: Scriptable this Obj = !(this Arg instanceof Scriptable) ? Context
0250: .toObject(this Arg, scope)
0251: : (Scriptable) this Arg;
0252: result = ScriptableObject
0253: .getProperty(this Obj, name);
0254: Object[] newArgs = null;
0255: if (args != null) {
0256: newArgs = new Object[args.length];
0257: int len = args.length;
0258: for (int i = 0; i < len; i++) {
0259: newArgs[i] = args[i];
0260: if (args[i] != null
0261: && !(args[i] instanceof Number)
0262: && !(args[i] instanceof Boolean)
0263: && !(args[i] instanceof String)
0264: && !(args[i] instanceof Scriptable)) {
0265: newArgs[i] = Context.toObject(args[i],
0266: scope);
0267: }
0268: }
0269: }
0270: result = ScriptRuntime.call(cx, result, this Obj,
0271: newArgs, scope);
0272: if (result == Undefined.instance
0273: || result == Scriptable.NOT_FOUND) {
0274: result = null;
0275: } else if (!(result instanceof NativeJavaClass)) {
0276: while (result instanceof Wrapper) {
0277: result = ((Wrapper) result).unwrap();
0278: }
0279: }
0280: return result;
0281: } catch (JavaScriptException e) {
0282: throw new java.lang.reflect.InvocationTargetException(
0283: e);
0284: } finally {
0285: Context.exit();
0286: }
0287: }
0288:
0289: public boolean isCacheable() {
0290: return false;
0291: }
0292:
0293: public String getMethodName() {
0294: return name;
0295: }
0296:
0297: public Class getReturnType() {
0298: return Object.class;
0299: }
0300:
0301: }
0302:
0303: static class JSPropertyGet implements VelPropertyGet {
0304:
0305: Scriptable scope;
0306: String name;
0307:
0308: public JSPropertyGet(Scriptable scope, String name) {
0309: this .scope = scope;
0310: this .name = name;
0311: }
0312:
0313: public Object invoke(Object this Arg) throws Exception {
0314: Context cx = Context.enter();
0315: try {
0316: Scriptable this Obj = !(this Arg instanceof Scriptable) ? Context
0317: .toObject(this Arg, scope)
0318: : (Scriptable) this Arg;
0319: Object result = ScriptableObject.getProperty(
0320: this Obj, name);
0321: if (result == Scriptable.NOT_FOUND) {
0322: result = ScriptableObject.getProperty(this Obj,
0323: "get" + StringUtils.capitalize(name));
0324: if (result != Scriptable.NOT_FOUND
0325: && result instanceof Function) {
0326: try {
0327: result = ((Function) result)
0328: .call(
0329: cx,
0330: ScriptableObject
0331: .getTopLevelScope(this Obj),
0332: this Obj,
0333: new Object[] {});
0334: } catch (JavaScriptException exc) {
0335: exc.printStackTrace();
0336: result = null;
0337: }
0338: }
0339: }
0340: if (result == Scriptable.NOT_FOUND
0341: || result == Undefined.instance) {
0342: result = null;
0343: } else if (result instanceof Wrapper
0344: && !(result instanceof NativeJavaClass)) {
0345: result = ((Wrapper) result).unwrap();
0346: }
0347: return result;
0348: } finally {
0349: Context.exit();
0350: }
0351: }
0352:
0353: public boolean isCacheable() {
0354: return false;
0355: }
0356:
0357: public String getMethodName() {
0358: return name;
0359: }
0360: }
0361:
0362: static class JSPropertySet implements VelPropertySet {
0363:
0364: Scriptable scope;
0365: String name;
0366:
0367: public JSPropertySet(Scriptable scope, String name) {
0368: this .scope = scope;
0369: this .name = name;
0370: }
0371:
0372: public Object invoke(Object this Arg, Object rhs)
0373: throws Exception {
0374: Context.enter();
0375: try {
0376: Scriptable this Obj;
0377: Object arg = rhs;
0378: if (!(this Arg instanceof Scriptable)) {
0379: this Obj = Context.toObject(this Arg, scope);
0380: } else {
0381: this Obj = (Scriptable) this Arg;
0382: }
0383: if (arg != null && !(arg instanceof Number)
0384: && !(arg instanceof Boolean)
0385: && !(arg instanceof String)
0386: && !(arg instanceof Scriptable)) {
0387: arg = Context.toObject(arg, scope);
0388: }
0389: ScriptableObject.putProperty(this Obj, name, arg);
0390: return rhs;
0391: } finally {
0392: Context.exit();
0393: }
0394: }
0395:
0396: public boolean isCacheable() {
0397: return false;
0398: }
0399:
0400: public String getMethodName() {
0401: return name;
0402: }
0403: }
0404:
0405: static class NativeArrayIterator implements Iterator {
0406:
0407: NativeArray arr;
0408: int index;
0409:
0410: public NativeArrayIterator(NativeArray arr) {
0411: this .arr = arr;
0412: this .index = 0;
0413: }
0414:
0415: public boolean hasNext() {
0416: return index < (int) arr.jsGet_length();
0417: }
0418:
0419: public Object next() {
0420: Context.enter();
0421: try {
0422: Object result = arr.get(index++, arr);
0423: if (result == Undefined.instance
0424: || result == Scriptable.NOT_FOUND) {
0425: result = null;
0426: } else {
0427: if (!(result instanceof NativeJavaClass)) {
0428: while (result instanceof Wrapper) {
0429: result = ((Wrapper) result).unwrap();
0430: }
0431: }
0432: }
0433: return result;
0434: } finally {
0435: Context.exit();
0436: }
0437: }
0438:
0439: public void remove() {
0440: arr.delete(index);
0441: }
0442: }
0443:
0444: static class ScriptableIterator implements Iterator {
0445:
0446: Scriptable scope;
0447: Object[] ids;
0448: int index;
0449:
0450: public ScriptableIterator(Scriptable scope) {
0451: this .scope = scope;
0452: this .ids = scope.getIds();
0453: this .index = 0;
0454: }
0455:
0456: public boolean hasNext() {
0457: return index < ids.length;
0458: }
0459:
0460: public Object next() {
0461: Context.enter();
0462: try {
0463: Object result = ScriptableObject.getProperty(scope,
0464: ids[index++].toString());
0465: if (result == Undefined.instance
0466: || result == Scriptable.NOT_FOUND) {
0467: result = null;
0468: } else if (!(result instanceof NativeJavaClass)) {
0469: while (result instanceof Wrapper) {
0470: result = ((Wrapper) result).unwrap();
0471: }
0472: }
0473: return result;
0474: } finally {
0475: Context.exit();
0476: }
0477: }
0478:
0479: public void remove() {
0480: Context.enter();
0481: try {
0482: scope.delete(ids[index].toString());
0483: } finally {
0484: Context.exit();
0485: }
0486: }
0487: }
0488:
0489: public Iterator getIterator(Object obj, Info i)
0490: throws Exception {
0491: if (!(obj instanceof Scriptable)) {
0492: // support Enumeration
0493: if (obj instanceof Enumeration) {
0494: final Enumeration e = (Enumeration) obj;
0495: return new Iterator() {
0496:
0497: public boolean hasNext() {
0498: return e.hasMoreElements();
0499: }
0500:
0501: public Object next() {
0502: return e.nextElement();
0503: }
0504:
0505: public void remove() {
0506: // no action
0507: }
0508:
0509: };
0510: }
0511: if (obj instanceof Iterator) {
0512: // support Iterator
0513: return (Iterator) obj;
0514: }
0515: return super .getIterator(obj, i);
0516: }
0517: if (obj instanceof NativeArray) {
0518: return new NativeArrayIterator((NativeArray) obj);
0519: }
0520: return new ScriptableIterator((Scriptable) obj);
0521: }
0522:
0523: public VelMethod getMethod(Object obj, String methodName,
0524: Object[] args, Info i) throws Exception {
0525: return !(obj instanceof Scriptable) ? super .getMethod(obj,
0526: methodName, args, i) : new JSMethod(
0527: (Scriptable) obj, methodName);
0528: }
0529:
0530: public VelPropertyGet getPropertyGet(Object obj,
0531: String identifier, Info i) throws Exception {
0532: return !(obj instanceof Scriptable) ? super .getPropertyGet(
0533: obj, identifier, i) : new JSPropertyGet(
0534: (Scriptable) obj, identifier);
0535: }
0536:
0537: public VelPropertySet getPropertySet(Object obj,
0538: String identifier, Object arg, Info i) throws Exception {
0539: return !(obj instanceof Scriptable) ? super .getPropertySet(
0540: obj, identifier, arg, i) : new JSPropertySet(
0541: (Scriptable) obj, identifier);
0542: }
0543: }
0544:
0545: static class MyJexlContext extends HashMap implements JexlContext {
0546:
0547: private MyJexlContext closure;
0548:
0549: MyJexlContext() {
0550: this (null);
0551: }
0552:
0553: MyJexlContext(MyJexlContext closure) {
0554: this .closure = closure;
0555: }
0556:
0557: public Map getVars() {
0558: return this ;
0559: }
0560:
0561: public void setVars(Map map) {
0562: putAll(map);
0563: }
0564:
0565: public boolean containsKey(Object key) {
0566: return this .get(key) != null;
0567: }
0568:
0569: public Object get(Object key) {
0570: if (key.equals("this")) {
0571: return this ;
0572: }
0573: Object result = super .get(key);
0574: if (result == null && closure != null) {
0575: result = closure.get(key);
0576: }
0577: return result;
0578: }
0579: }
0580:
0581: static class MyVariables implements Variables {
0582:
0583: MyVariables closure;
0584:
0585: Map localVariables = new HashMap();
0586:
0587: static final String[] VARIABLES = new String[] { "cocoon",
0588: "continuation", "flowContext", "request", "response",
0589: "context", "session", "parameters" };
0590:
0591: Object cocoon;
0592:
0593: // backward compatibility
0594: Object bean, kont, request, response, session, context,
0595: parameters;
0596:
0597: MyVariables(Object cocoon, Object bean, WebContinuation kont,
0598: Object request, Object session, Object context,
0599: Object parameters) {
0600: this .cocoon = cocoon;
0601: this .bean = bean;
0602: this .kont = kont;
0603: this .request = request;
0604: this .session = session;
0605: this .context = context;
0606: this .parameters = parameters;
0607: }
0608:
0609: public MyVariables(MyVariables parent) {
0610: this .closure = parent;
0611: }
0612:
0613: public boolean isDeclaredVariable(String varName) {
0614: int len = VARIABLES.length;
0615: for (int i = 0; i < len; i++) {
0616: if (varName.equals(VARIABLES[i])) {
0617: return true;
0618: }
0619: }
0620: if (localVariables.containsKey(varName)) {
0621: return true;
0622: }
0623: if (closure != null) {
0624: return closure.isDeclaredVariable(varName);
0625: }
0626: return false;
0627: }
0628:
0629: public Object getVariable(String varName) {
0630: Object result = localVariables.get(varName);
0631: if (result != null) {
0632: return result;
0633: }
0634: if (closure != null) {
0635: return closure.getVariable(varName);
0636: }
0637: if (varName.equals("cocoon")) {
0638: return cocoon;
0639: }
0640: // backward compatibility
0641: if (varName.equals("continuation")) {
0642: return kont;
0643: } else if (varName.equals("flowContext")) {
0644: return bean;
0645: } else if (varName.equals("request")) {
0646: return request;
0647: } else if (varName.equals("session")) {
0648: return session;
0649: } else if (varName.equals("context")) {
0650: return context;
0651: } else if (varName.equals("parameters")) {
0652: return parameters;
0653: }
0654: return null;
0655: }
0656:
0657: public void declareVariable(String varName, Object value) {
0658: localVariables.put(varName, value);
0659: }
0660:
0661: public void undeclareVariable(String varName) {
0662: localVariables.remove(varName);
0663: }
0664: }
0665:
0666: static {
0667: // Hack: there's no _nice_ way to add my introspector to Jexl right now
0668: try {
0669: Field field = Introspector.class
0670: .getDeclaredField("uberSpect");
0671: field.setAccessible(true);
0672: field.set(null, new JSIntrospector());
0673: } catch (Exception e) {
0674: e.printStackTrace();
0675: }
0676: }
0677:
0678: /** The namespace used by this generator */
0679: public final static String NS = "http://apache.org/cocoon/templates/jx/1.0";
0680:
0681: final static String TEMPLATE = "template";
0682: final static String FOR_EACH = "forEach";
0683: final static String IF = "if";
0684: final static String CHOOSE = "choose";
0685: final static String WHEN = "when";
0686: final static String OTHERWISE = "otherwise";
0687: final static String OUT = "out";
0688: final static String IMPORT = "import";
0689: final static String SET = "set";
0690: final static String MACRO = "macro";
0691: final static String EVALBODY = "evalBody";
0692: final static String EVAL = "eval";
0693: final static String PARAMETER = "parameter";
0694: final static String FORMAT_NUMBER = "formatNumber";
0695: final static String FORMAT_DATE = "formatDate";
0696: final static String COMMENT = "comment";
0697: final static String CACHE_KEY = "cache-key";
0698: final static String VALIDITY = "cache-validity";
0699:
0700: /**
0701: * Compile a single Jexl expr (contained in ${}) or XPath expression
0702: * (contained in #{})
0703: */
0704:
0705: private static JXTExpression compileExpr(String expr,
0706: String errorPrefix, Location location) throws JXTException {
0707: try {
0708: return compileExpr(expr);
0709: } catch (Exception exc) {
0710: throw new JXTException(errorPrefix + exc.getMessage(),
0711: location, exc);
0712: }
0713: }
0714:
0715: private static JXTExpression compileExpr(String inStr)
0716: throws Exception {
0717: try {
0718: if (inStr == null) {
0719: return null;
0720: }
0721: StringReader in = new StringReader(inStr.trim());
0722: int ch;
0723: boolean xpath = false;
0724: boolean inExpr = false;
0725: StringBuffer expr = new StringBuffer();
0726: while ((ch = in.read()) != -1) {
0727: char c = (char) ch;
0728: if (inExpr) {
0729: if (c == '\\') {
0730: ch = in.read();
0731: expr.append((ch == -1) ? '\\' : (char) ch);
0732: } else if (c == '}') {
0733: return compile(expr.toString(), xpath);
0734: } else {
0735: expr.append(c);
0736: }
0737: } else {
0738: if (c == '$' || c == '#') {
0739: ch = in.read();
0740: if (ch == '{') {
0741: inExpr = true;
0742: xpath = c == '#';
0743: continue;
0744: }
0745: }
0746: // hack: invalid expression?
0747: // just return the original and swallow exception
0748: return new JXTExpression(inStr, null);
0749: }
0750: }
0751: if (inExpr) {
0752: // unclosed #{} or ${}
0753: throw new Exception("Unterminated "
0754: + (xpath ? "#" : "$") + "{");
0755: }
0756: } catch (IOException ignored) {
0757: ignored.printStackTrace();
0758: }
0759: return new JXTExpression(inStr, null);
0760: }
0761:
0762: /*
0763: * Compile an integer expression (returns either a Compiled Expression
0764: * or an Integer literal)
0765: */
0766: private static JXTExpression compileInt(String val, String msg,
0767: Location location) throws SAXException {
0768: JXTExpression res = compileExpr(val, msg, location);
0769: if (res != null) {
0770: if (res.compiledExpression == null) {
0771: res.compiledExpression = Integer.valueOf(res.raw);
0772: }
0773: return res;
0774: }
0775: return null;
0776: }
0777:
0778: private static JXTExpression compileBoolean(String val, String msg,
0779: Location location) throws SAXException {
0780: JXTExpression res = compileExpr(val, msg, location);
0781: if (res != null) {
0782: if (res.compiledExpression == null) {
0783: res.compiledExpression = Boolean.valueOf(res.raw);
0784: }
0785: return res;
0786: }
0787: return null;
0788: }
0789:
0790: private static JXTExpression compile(final String variable,
0791: boolean xpath) throws Exception {
0792: Object compiled;
0793: if (xpath) {
0794: compiled = JXPathContext.compile(variable);
0795: } else {
0796: compiled = ExpressionFactory.createExpression(variable);
0797: }
0798: return new JXTExpression(variable, compiled);
0799: }
0800:
0801: static private Object getValue(JXTExpression expr,
0802: JexlContext jexlContext, JXPathContext jxpathContext,
0803: Boolean lenient) throws Exception {
0804: if (expr != null) {
0805: Object compiled = expr.compiledExpression;
0806: try {
0807: if (compiled instanceof CompiledExpression) {
0808: CompiledExpression e = (CompiledExpression) compiled;
0809: boolean oldLenient = jxpathContext.isLenient();
0810: if (lenient != null) {
0811: jxpathContext
0812: .setLenient(lenient.booleanValue());
0813: }
0814: try {
0815: return e.getValue(jxpathContext);
0816: } finally {
0817: jxpathContext.setLenient(oldLenient);
0818: }
0819: } else if (compiled instanceof Expression) {
0820: Expression e = (Expression) compiled;
0821: return e.evaluate(jexlContext);
0822: }
0823: return compiled;
0824: } catch (InvocationTargetException e) {
0825: Throwable t = e.getTargetException();
0826: if (t instanceof Exception) {
0827: throw (Exception) t;
0828: }
0829: throw (Error) t;
0830: }
0831: } else {
0832: return null;
0833: }
0834: }
0835:
0836: static private Object getValue(JXTExpression expr,
0837: JexlContext jexlContext, JXPathContext jxpathContext)
0838: throws Exception {
0839: return getValue(expr, jexlContext, jxpathContext, null);
0840: }
0841:
0842: static private int getIntValue(JXTExpression expr,
0843: JexlContext jexlContext, JXPathContext jxpathContext)
0844: throws Exception {
0845: Object res = getValue(expr, jexlContext, jxpathContext);
0846: return res instanceof Number ? ((Number) res).intValue() : 0;
0847: }
0848:
0849: static private Number getNumberValue(JXTExpression expr,
0850: JexlContext jexlContext, JXPathContext jxpathContext)
0851: throws Exception {
0852: Object res = getValue(expr, jexlContext, jxpathContext);
0853: if (res instanceof Number) {
0854: return (Number) res;
0855: }
0856: if (res != null) {
0857: return Double.valueOf(res.toString());
0858: }
0859: return null;
0860: }
0861:
0862: static private String getStringValue(JXTExpression expr,
0863: JexlContext jexlContext, JXPathContext jxpathContext)
0864: throws Exception {
0865: Object res = getValue(expr, jexlContext, jxpathContext);
0866: if (res != null) {
0867: return res.toString();
0868: }
0869: if (expr != null && expr.compiledExpression == null) {
0870: return expr.raw;
0871: }
0872: return null;
0873: }
0874:
0875: static private Boolean getBooleanValue(JXTExpression expr,
0876: JexlContext jexlContext, JXPathContext jxpathContext)
0877: throws Exception {
0878: Object res = getValue(expr, jexlContext, jxpathContext);
0879: return res instanceof Boolean ? (Boolean) res : null;
0880: }
0881:
0882: private Object getNode(JXTExpression expr, JexlContext jexlContext,
0883: JXPathContext jxpathContext) throws Exception {
0884: return getNode(expr, jexlContext, jxpathContext, null);
0885: }
0886:
0887: // Hack: try to prevent JXPath from converting result to a String
0888: private Object getNode(JXTExpression expr, JexlContext jexlContext,
0889: JXPathContext jxpathContext, Boolean lenient)
0890: throws Exception {
0891: try {
0892: Object compiled = expr.compiledExpression;
0893: if (compiled instanceof CompiledExpression) {
0894: CompiledExpression e = (CompiledExpression) compiled;
0895: boolean oldLenient = jxpathContext.isLenient();
0896: if (lenient != null)
0897: jxpathContext.setLenient(lenient.booleanValue());
0898: try {
0899: Iterator iter = e.iteratePointers(jxpathContext);
0900: if (iter.hasNext()) {
0901: Pointer first = (Pointer) iter.next();
0902: if (iter.hasNext()) {
0903: List result = new LinkedList();
0904: result.add(first.getNode());
0905: boolean dom = (first.getNode() instanceof Node);
0906: while (iter.hasNext()) {
0907: Object obj = ((Pointer) iter.next())
0908: .getNode();
0909: dom = dom && (obj instanceof Node);
0910: result.add(obj);
0911: }
0912: Object[] arr;
0913: if (dom) {
0914: arr = new Node[result.size()];
0915: } else {
0916: arr = new Object[result.size()];
0917: }
0918: result.toArray(arr);
0919: return arr;
0920: }
0921: return first.getNode();
0922: }
0923: return null;
0924: } finally {
0925: jxpathContext.setLenient(oldLenient);
0926: }
0927: } else if (compiled instanceof Expression) {
0928: Expression e = (Expression) compiled;
0929: return e.evaluate(jexlContext);
0930: }
0931: return expr.raw;
0932: } catch (InvocationTargetException e) {
0933: Throwable t = e.getTargetException();
0934: if (t instanceof Exception) {
0935: throw (Exception) t;
0936: }
0937: throw (Error) t;
0938: }
0939: }
0940:
0941: static class Event {
0942: final Location location;
0943: Event next; // in document order
0944:
0945: Event(Location locator) {
0946: this .location = locator != null ? locator
0947: : Location.UNKNOWN;
0948: }
0949:
0950: public String locationString() {
0951: return location.toString();
0952: }
0953: }
0954:
0955: static class TextEvent extends Event {
0956: TextEvent(Location location, char[] chars, int start, int length)
0957: throws SAXException {
0958: super (location);
0959: StringBuffer buf = new StringBuffer();
0960: this .raw = new char[length];
0961: System.arraycopy(chars, start, this .raw, 0, length);
0962: CharArrayReader in = new CharArrayReader(chars, start,
0963: length);
0964: int ch;
0965: boolean inExpr = false;
0966: boolean xpath = false;
0967: try {
0968: top: while ((ch = in.read()) != -1) {
0969: // column++;
0970: char c = (char) ch;
0971: processChar: while (true) {
0972: if (inExpr) {
0973: if (c == '\\') {
0974: ch = in.read();
0975: buf.append(ch == -1 ? '\\' : (char) ch);
0976: } else if (c == '}') {
0977: String str = buf.toString();
0978: Object compiledExpression;
0979: try {
0980: if (xpath) {
0981: compiledExpression = JXPathContext
0982: .compile(str);
0983: } else {
0984: compiledExpression = ExpressionFactory
0985: .createExpression(str);
0986: }
0987: } catch (Exception exc) {
0988: throw new JXTException(exc
0989: .getMessage(),
0990: this .location, exc);
0991: }
0992: substitutions.add(new JXTExpression(
0993: str, compiledExpression));
0994: buf.setLength(0);
0995: inExpr = false;
0996: } else {
0997: buf.append(c);
0998: }
0999: } else if (c == '$' || c == '#') {
1000: ch = in.read();
1001: if (ch == '{') {
1002: xpath = c == '#';
1003: inExpr = true;
1004: if (buf.length() > 0) {
1005: char[] charArray = new char[buf
1006: .length()];
1007:
1008: buf.getChars(0, buf.length(),
1009: charArray, 0);
1010: substitutions.add(charArray);
1011: buf.setLength(0);
1012: }
1013: continue top;
1014: }
1015: buf.append(c);
1016: if (ch != -1) {
1017: c = (char) ch;
1018: continue processChar;
1019: }
1020: } else {
1021: buf.append(c);
1022: }
1023: break;
1024: }
1025: }
1026: } catch (IOException ignored) {
1027: // won't happen
1028: ignored.printStackTrace();
1029: }
1030: if (inExpr) {
1031: // unclosed #{} or ${}
1032: buf.insert(0, (xpath ? "#" : "$") + "{");
1033: }
1034: if (buf.length() > 0) {
1035: char[] charArray = new char[buf.length()];
1036:
1037: buf.getChars(0, buf.length(), charArray, 0);
1038: substitutions.add(charArray);
1039: } else if (substitutions.isEmpty()) {
1040: substitutions.add(ArrayUtils.EMPTY_CHAR_ARRAY);
1041: }
1042: }
1043:
1044: final List substitutions = new LinkedList();
1045: final char[] raw;
1046: }
1047:
1048: static class Characters extends TextEvent {
1049: Characters(Location location, char[] chars, int start,
1050: int length) throws SAXException {
1051: super (location, chars, start, length);
1052: }
1053: }
1054:
1055: static class StartDocument extends Event {
1056: StartDocument(Location location) {
1057: super (location);
1058: templateProperties = new HashMap();
1059: }
1060:
1061: SourceValidity compileTime;
1062: EndDocument endDocument; // null if document fragment
1063: Map templateProperties;
1064: }
1065:
1066: static class EndDocument extends Event {
1067: EndDocument(Location location) {
1068: super (location);
1069: }
1070: }
1071:
1072: static class EndElement extends Event {
1073: EndElement(Location location, StartElement startElement) {
1074: super (location);
1075: this .startElement = startElement;
1076: }
1077:
1078: final StartElement startElement;
1079: }
1080:
1081: static class EndPrefixMapping extends Event {
1082: EndPrefixMapping(Location location, String prefix) {
1083: super (location);
1084: this .prefix = prefix;
1085: }
1086:
1087: final String prefix;
1088: }
1089:
1090: static class IgnorableWhitespace extends TextEvent {
1091: IgnorableWhitespace(Location location, char[] chars, int start,
1092: int length) throws SAXException {
1093: super (location, chars, start, length);
1094: }
1095: }
1096:
1097: static class ProcessingInstruction extends Event {
1098: ProcessingInstruction(Location location, String target,
1099: String data) {
1100: super (location);
1101: this .target = target;
1102: this .data = data;
1103: }
1104:
1105: final String target;
1106: final String data;
1107: }
1108:
1109: static class SkippedEntity extends Event {
1110: SkippedEntity(Location location, String name) {
1111: super (location);
1112: this .name = name;
1113: }
1114:
1115: final String name;
1116: }
1117:
1118: abstract static class AttributeEvent {
1119: AttributeEvent(String namespaceURI, String localName,
1120: String raw, String type) {
1121: this .namespaceURI = namespaceURI;
1122: this .localName = localName;
1123: this .raw = raw;
1124: this .type = type;
1125: }
1126:
1127: final String namespaceURI;
1128: final String localName;
1129: final String raw;
1130: final String type;
1131: }
1132:
1133: static class CopyAttribute extends AttributeEvent {
1134: CopyAttribute(String namespaceURI, String localName,
1135: String raw, String type, String value) {
1136: super (namespaceURI, localName, raw, type);
1137: this .value = value;
1138: }
1139:
1140: final String value;
1141: }
1142:
1143: static class Subst {
1144: // VOID
1145: }
1146:
1147: static class Literal extends Subst {
1148: Literal(String val) {
1149: this .value = val;
1150: }
1151:
1152: final String value;
1153: }
1154:
1155: static class JXTExpression extends Subst {
1156: JXTExpression(String raw, Object expr) {
1157: this .raw = raw;
1158: this .compiledExpression = expr;
1159: }
1160:
1161: String raw;
1162: Object compiledExpression;
1163: }
1164:
1165: static class SubstituteAttribute extends AttributeEvent {
1166: SubstituteAttribute(String namespaceURI, String localName,
1167: String raw, String type, List substs) {
1168: super (namespaceURI, localName, raw, type);
1169: this .substitutions = substs;
1170: }
1171:
1172: final List substitutions;
1173: }
1174:
1175: static class StartElement extends Event {
1176: StartElement(Location location, String namespaceURI,
1177: String localName, String raw, Attributes attrs)
1178: throws SAXException {
1179: super (location);
1180:
1181: this .namespaceURI = namespaceURI;
1182: this .localName = localName;
1183: this .raw = raw;
1184: this .qname = "{" + namespaceURI + "}" + localName;
1185:
1186: StringBuffer buf = new StringBuffer();
1187: int len = attrs.getLength();
1188: for (int i = 0; i < len; i++) {
1189: String uri = attrs.getURI(i);
1190: String local = attrs.getLocalName(i);
1191: String qname = attrs.getQName(i);
1192: String type = attrs.getType(i);
1193: String value = attrs.getValue(i);
1194: StringReader in = new StringReader(value);
1195:
1196: int ch;
1197: buf.setLength(0);
1198: boolean inExpr = false;
1199: List substEvents = new LinkedList();
1200: boolean xpath = false;
1201: try {
1202: top: while ((ch = in.read()) != -1) {
1203: char c = (char) ch;
1204: processChar: while (true) {
1205: if (inExpr) {
1206: if (c == '\\') {
1207: ch = in.read();
1208: buf.append(ch == -1 ? '\\'
1209: : (char) ch);
1210: } else if (c == '}') {
1211: String str = buf.toString();
1212: JXTExpression compiledExpression;
1213: try {
1214: compiledExpression = compile(
1215: str, xpath);
1216: } catch (Exception exc) {
1217: throw new JXTException(exc
1218: .getMessage(),
1219: location, exc);
1220: }
1221: substEvents.add(compiledExpression);
1222: buf.setLength(0);
1223: inExpr = false;
1224: } else {
1225: buf.append(c);
1226: }
1227: } else if (c == '$' || c == '#') {
1228: ch = in.read();
1229: if (ch == '{') {
1230: if (buf.length() > 0) {
1231: substEvents.add(new Literal(buf
1232: .toString()));
1233: buf.setLength(0);
1234: }
1235: inExpr = true;
1236: xpath = c == '#';
1237: continue top;
1238: }
1239: buf.append(c);
1240: if (ch != -1) {
1241: c = (char) ch;
1242: continue processChar;
1243: }
1244: } else {
1245: buf.append(c);
1246: }
1247: break;
1248: }
1249: }
1250: } catch (IOException ignored) {
1251: ignored.printStackTrace();
1252: }
1253:
1254: if (inExpr) {
1255: // unclosed #{} or ${}
1256: String msg = "Unterminated " + (xpath ? "#" : "$")
1257: + "{";
1258: throw new JXTException(msg, location, null);
1259: }
1260:
1261: if (buf.length() > 0) {
1262: if (substEvents.size() == 0) {
1263: attributeEvents.add(new CopyAttribute(uri,
1264: local, qname, type, buf.toString()));
1265: } else {
1266: substEvents.add(new Literal(buf.toString()));
1267: attributeEvents.add(new SubstituteAttribute(
1268: uri, local, qname, type, substEvents));
1269: }
1270: } else {
1271: if (substEvents.size() > 0) {
1272: attributeEvents.add(new SubstituteAttribute(
1273: uri, local, qname, type, substEvents));
1274: } else {
1275: attributeEvents.add(new CopyAttribute(uri,
1276: local, qname, type, ""));
1277: }
1278: }
1279: }
1280: this .attributes = new AttributesImpl(attrs);
1281: }
1282:
1283: final String namespaceURI;
1284: final String localName;
1285: final String raw;
1286: final String qname;
1287: final List attributeEvents = new LinkedList();
1288: final Attributes attributes;
1289: EndElement endElement;
1290: }
1291:
1292: static class StartPrefixMapping extends Event {
1293: StartPrefixMapping(Location location, String prefix, String uri) {
1294: super (location);
1295: this .prefix = prefix;
1296: this .uri = uri;
1297: }
1298:
1299: final String prefix;
1300: final String uri;
1301: }
1302:
1303: static class EndCDATA extends Event {
1304: EndCDATA(Location location) {
1305: super (location);
1306: }
1307: }
1308:
1309: static class EndDTD extends Event {
1310: EndDTD(Location location) {
1311: super (location);
1312: }
1313: }
1314:
1315: static class EndEntity extends Event {
1316: EndEntity(Location location, String name) {
1317: super (location);
1318: this .name = name;
1319: }
1320:
1321: final String name;
1322: }
1323:
1324: static class StartCDATA extends Event {
1325: StartCDATA(Location location) {
1326: super (location);
1327: }
1328: }
1329:
1330: static class StartDTD extends Event {
1331: StartDTD(Location location, String name, String publicId,
1332: String systemId) {
1333: super (location);
1334: this .name = name;
1335: this .publicId = publicId;
1336: this .systemId = systemId;
1337: }
1338:
1339: final String name;
1340: final String publicId;
1341: final String systemId;
1342: }
1343:
1344: static class StartEntity extends Event {
1345: public StartEntity(Location location, String name) {
1346: super (location);
1347: this .name = name;
1348: }
1349:
1350: final String name;
1351: }
1352:
1353: static class StartInstruction extends Event {
1354: StartInstruction(StartElement startElement) {
1355: super (startElement.location);
1356: this .startElement = startElement;
1357: }
1358:
1359: final StartElement startElement;
1360: EndInstruction endInstruction;
1361: }
1362:
1363: static class EndInstruction extends Event {
1364: EndInstruction(Location locator,
1365: StartInstruction startInstruction) {
1366: super (locator);
1367: this .startInstruction = startInstruction;
1368: startInstruction.endInstruction = this ;
1369: }
1370:
1371: final StartInstruction startInstruction;
1372: }
1373:
1374: static class StartForEach extends StartInstruction {
1375: StartForEach(StartElement raw, JXTExpression items,
1376: JXTExpression var, JXTExpression varStatus,
1377: JXTExpression begin, JXTExpression end,
1378: JXTExpression step, Boolean lenient) {
1379: super (raw);
1380: this .items = items;
1381: this .var = var;
1382: this .varStatus = varStatus;
1383: this .begin = begin;
1384: this .end = end;
1385: this .step = step;
1386: this .lenient = lenient;
1387: }
1388:
1389: final JXTExpression items;
1390: final JXTExpression var;
1391: final JXTExpression varStatus;
1392: final JXTExpression begin;
1393: final JXTExpression end;
1394: final JXTExpression step;
1395: final Boolean lenient;
1396: }
1397:
1398: static class StartIf extends StartInstruction {
1399: StartIf(StartElement raw, JXTExpression test) {
1400: super (raw);
1401: this .test = test;
1402: }
1403:
1404: final JXTExpression test;
1405: }
1406:
1407: static class StartChoose extends StartInstruction {
1408: StartChoose(StartElement raw) {
1409: super (raw);
1410: }
1411:
1412: StartWhen firstChoice;
1413: StartOtherwise otherwise;
1414: }
1415:
1416: static class StartWhen extends StartInstruction {
1417: StartWhen(StartElement raw, JXTExpression test) {
1418: super (raw);
1419: this .test = test;
1420: }
1421:
1422: final JXTExpression test;
1423: StartWhen nextChoice;
1424: }
1425:
1426: static class StartOtherwise extends StartInstruction {
1427: StartOtherwise(StartElement raw) {
1428: super (raw);
1429: }
1430: }
1431:
1432: static class StartOut extends StartInstruction {
1433: StartOut(StartElement raw, JXTExpression expr, Boolean lenient) {
1434: super (raw);
1435: this .compiledExpression = expr;
1436: this .lenient = lenient;
1437: }
1438:
1439: final JXTExpression compiledExpression;
1440: final Boolean lenient;
1441: }
1442:
1443: static class StartImport extends StartInstruction {
1444: StartImport(StartElement raw, AttributeEvent uri,
1445: JXTExpression select) {
1446: super (raw);
1447: this .uri = uri;
1448: this .select = select;
1449: }
1450:
1451: final AttributeEvent uri;
1452: final JXTExpression select;
1453: }
1454:
1455: static class StartTemplate extends StartInstruction {
1456: StartTemplate(StartElement raw) {
1457: super (raw);
1458: }
1459: }
1460:
1461: static class StartEvalBody extends StartInstruction {
1462: StartEvalBody(StartElement raw) {
1463: super (raw);
1464: }
1465: }
1466:
1467: static class StartEval extends StartInstruction {
1468: StartEval(StartElement raw, JXTExpression value) {
1469: super (raw);
1470: this .value = value;
1471: }
1472:
1473: final JXTExpression value;
1474: }
1475:
1476: static class StartDefine extends StartInstruction {
1477: StartDefine(StartElement raw, String namespace, String name) {
1478: super (raw);
1479: this .namespace = namespace;
1480: this .name = name;
1481: this .qname = "{" + namespace + "}" + name;
1482: this .parameters = new HashMap();
1483: }
1484:
1485: final String name;
1486: final String namespace;
1487: final String qname;
1488: final Map parameters;
1489: Event body;
1490:
1491: void finish() throws SAXException {
1492: Event e = next;
1493: boolean params = true;
1494: while (e != this .endInstruction) {
1495: if (e instanceof StartParameter) {
1496: StartParameter startParam = (StartParameter) e;
1497: if (!params) {
1498: throw new JXTException(
1499: "<parameter> not allowed here: \""
1500: + startParam.name + "\"",
1501: startParam.location, null);
1502: }
1503: Object prev = parameters.put(startParam.name,
1504: startParam);
1505: if (prev != null) {
1506: throw new JXTException(
1507: "duplicate parameter: \""
1508: + startParam.name + "\"",
1509: location, null);
1510: }
1511: e = startParam.endInstruction;
1512: } else if (e instanceof IgnorableWhitespace) {
1513: // EMPTY
1514: } else if (e instanceof Characters) {
1515: // check for whitespace
1516: char[] ch = ((TextEvent) e).raw;
1517: int len = ch.length;
1518: for (int i = 0; i < len; i++) {
1519: if (!Character.isWhitespace(ch[i])) {
1520: if (params) {
1521: params = false;
1522: body = e;
1523: }
1524: break;
1525: }
1526: }
1527: } else {
1528: if (params) {
1529: params = false;
1530: body = e;
1531: }
1532: }
1533: e = e.next;
1534: }
1535: if (this .body == null) {
1536: this .body = this .endInstruction;
1537: }
1538: }
1539: }
1540:
1541: static class StartParameter extends StartInstruction {
1542: StartParameter(StartElement raw, String name, String optional,
1543: String default_) {
1544: super (raw);
1545: this .name = name;
1546: this .optional = optional;
1547: this .default_ = default_;
1548: }
1549:
1550: final String name;
1551: final String optional;
1552: final String default_;
1553: }
1554:
1555: static class StartSet extends StartInstruction {
1556: StartSet(StartElement raw, JXTExpression var,
1557: JXTExpression value) {
1558: super (raw);
1559: this .var = var;
1560: this .value = value;
1561: }
1562:
1563: final JXTExpression var;
1564: final JXTExpression value;
1565: }
1566:
1567: static class StartComment extends StartInstruction {
1568: StartComment(StartElement raw) {
1569: super (raw);
1570: }
1571: }
1572:
1573: // formatNumber tag (borrows from Jakarta taglibs JSTL)
1574:
1575: private static Locale parseLocale(String locale, String variant) {
1576: Locale ret = null;
1577: String language = locale;
1578: String country = null;
1579: int index = StringUtils.indexOfAny(locale, "-_");
1580:
1581: if (index > -1) {
1582: language = locale.substring(0, index);
1583: country = locale.substring(index + 1);
1584: }
1585: if (StringUtils.isEmpty(language)) {
1586: throw new IllegalArgumentException("No language in locale");
1587: }
1588: if (country == null) {
1589: ret = variant != null ? new Locale(language, "", variant)
1590: : new Locale(language, "");
1591: } else if (country.length() > 0) {
1592: ret = variant != null ? new Locale(language, country,
1593: variant) : new Locale(language, country);
1594: } else {
1595: throw new IllegalArgumentException(
1596: "Empty country in locale");
1597: }
1598: return ret;
1599: }
1600:
1601: private static final String NUMBER = "number";
1602: private static final String CURRENCY = "currency";
1603: private static final String PERCENT = "percent";
1604:
1605: static class LocaleAwareInstruction extends StartInstruction {
1606: private JXTExpression locale;
1607:
1608: LocaleAwareInstruction(StartElement startElement,
1609: JXTExpression locale) {
1610: super (startElement);
1611: this .locale = locale;
1612: }
1613:
1614: protected Locale getLocale(JexlContext jexl, JXPathContext jxp)
1615: throws Exception {
1616: Object locVal = getValue(this .locale, jexl, jxp);
1617: if (locVal == null)
1618: locVal = getStringValue(this .locale, jexl, jxp);
1619:
1620: if (locVal != null) {
1621: return locVal instanceof Locale ? (Locale) locVal
1622: : parseLocale(locVal.toString(), null);
1623: } else {
1624: return Locale.getDefault();
1625: }
1626: }
1627: }
1628:
1629: static class StartFormatNumber extends LocaleAwareInstruction {
1630:
1631: JXTExpression value;
1632: JXTExpression type;
1633: JXTExpression pattern;
1634: JXTExpression currencyCode;
1635: JXTExpression currencySymbol;
1636: JXTExpression isGroupingUsed;
1637: JXTExpression maxIntegerDigits;
1638: JXTExpression minIntegerDigits;
1639: JXTExpression maxFractionDigits;
1640: JXTExpression minFractionDigits;
1641:
1642: JXTExpression var;
1643:
1644: private static Class currencyClass;
1645:
1646: static {
1647: try {
1648: currencyClass = Class.forName("java.util.Currency");
1649: // container's runtime is J2SE 1.4 or greater
1650: } catch (Exception cnfe) {
1651: // EMPTY
1652: }
1653: }
1654:
1655: public StartFormatNumber(StartElement raw, JXTExpression var,
1656: JXTExpression value, JXTExpression type,
1657: JXTExpression pattern, JXTExpression currencyCode,
1658: JXTExpression currencySymbol,
1659: JXTExpression isGroupingUsed,
1660: JXTExpression maxIntegerDigits,
1661: JXTExpression minIntegerDigits,
1662: JXTExpression maxFractionDigits,
1663: JXTExpression minFractionDigits, JXTExpression locale) {
1664: super (raw, locale);
1665: this .var = var;
1666: this .value = value;
1667: this .type = type;
1668: this .pattern = pattern;
1669: this .currencyCode = currencyCode;
1670: this .currencySymbol = currencySymbol;
1671: this .isGroupingUsed = isGroupingUsed;
1672: this .maxIntegerDigits = maxIntegerDigits;
1673: this .minIntegerDigits = minIntegerDigits;
1674: this .maxFractionDigits = maxFractionDigits;
1675: this .minFractionDigits = minFractionDigits;
1676: }
1677:
1678: String format(JexlContext jexl, JXPathContext jxp)
1679: throws Exception {
1680: // Determine formatting locale
1681: String var = getStringValue(this .var, jexl, jxp);
1682: Number input = getNumberValue(this .value, jexl, jxp);
1683: String type = getStringValue(this .type, jexl, jxp);
1684: String pattern = getStringValue(this .pattern, jexl, jxp);
1685: String currencyCode = getStringValue(this .currencyCode,
1686: jexl, jxp);
1687: String currencySymbol = getStringValue(this .currencySymbol,
1688: jexl, jxp);
1689: Boolean isGroupingUsed = getBooleanValue(
1690: this .isGroupingUsed, jexl, jxp);
1691: Number maxIntegerDigits = getNumberValue(
1692: this .maxIntegerDigits, jexl, jxp);
1693: Number minIntegerDigits = getNumberValue(
1694: this .minIntegerDigits, jexl, jxp);
1695: Number maxFractionDigits = getNumberValue(
1696: this .maxFractionDigits, jexl, jxp);
1697: Number minFractionDigits = getNumberValue(
1698: this .minFractionDigits, jexl, jxp);
1699: Locale loc = getLocale(jexl, jxp);
1700: String formatted;
1701: if (loc != null) {
1702: // Create formatter
1703: NumberFormat formatter = null;
1704: if (StringUtils.isNotEmpty(pattern)) {
1705: // if 'pattern' is specified, 'type' is ignored
1706: DecimalFormatSymbols symbols = new DecimalFormatSymbols(
1707: loc);
1708: formatter = new DecimalFormat(pattern, symbols);
1709: } else {
1710: formatter = createFormatter(loc, type);
1711: }
1712: if (StringUtils.isNotEmpty(pattern)
1713: || CURRENCY.equalsIgnoreCase(type)) {
1714: setCurrency(formatter, currencyCode, currencySymbol);
1715: }
1716: configureFormatter(formatter, isGroupingUsed,
1717: maxIntegerDigits, minIntegerDigits,
1718: maxFractionDigits, minFractionDigits);
1719: formatted = formatter.format(input);
1720: } else {
1721: // no formatting locale available, use toString()
1722: formatted = input.toString();
1723: }
1724: if (var != null) {
1725: jexl.getVars().put(var, formatted);
1726: jxp.getVariables().declareVariable(var, formatted);
1727: return null;
1728: }
1729: return formatted;
1730: }
1731:
1732: private NumberFormat createFormatter(Locale loc, String type)
1733: throws Exception {
1734: NumberFormat formatter = null;
1735: if ((type == null) || NUMBER.equalsIgnoreCase(type)) {
1736: formatter = NumberFormat.getNumberInstance(loc);
1737: } else if (CURRENCY.equalsIgnoreCase(type)) {
1738: formatter = NumberFormat.getCurrencyInstance(loc);
1739: } else if (PERCENT.equalsIgnoreCase(type)) {
1740: formatter = NumberFormat.getPercentInstance(loc);
1741: } else {
1742: throw new IllegalArgumentException(
1743: "Invalid type: \""
1744: + type
1745: + "\": should be \"number\" or \"currency\" or \"percent\"");
1746: }
1747: return formatter;
1748: }
1749:
1750: /*
1751: * Applies the 'groupingUsed', 'maxIntegerDigits', 'minIntegerDigits',
1752: * 'maxFractionDigits', and 'minFractionDigits' attributes to the given
1753: * formatter.
1754: */
1755: private void configureFormatter(NumberFormat formatter,
1756: Boolean isGroupingUsed, Number maxIntegerDigits,
1757: Number minIntegerDigits, Number maxFractionDigits,
1758: Number minFractionDigits) {
1759: if (isGroupingUsed != null)
1760: formatter
1761: .setGroupingUsed(isGroupingUsed.booleanValue());
1762: if (maxIntegerDigits != null)
1763: formatter.setMaximumIntegerDigits(maxIntegerDigits
1764: .intValue());
1765: if (minIntegerDigits != null)
1766: formatter.setMinimumIntegerDigits(minIntegerDigits
1767: .intValue());
1768: if (maxFractionDigits != null)
1769: formatter.setMaximumFractionDigits(maxFractionDigits
1770: .intValue());
1771: if (minFractionDigits != null)
1772: formatter.setMinimumFractionDigits(minFractionDigits
1773: .intValue());
1774: }
1775:
1776: /*
1777: * Override the formatting locale's default currency symbol with the
1778: * specified currency code (specified via the "currencyCode" attribute) or
1779: * currency symbol (specified via the "currencySymbol" attribute).
1780: *
1781: * If both "currencyCode" and "currencySymbol" are present,
1782: * "currencyCode" takes precedence over "currencySymbol" if the
1783: * java.util.Currency class is defined in the container's runtime (that
1784: * is, if the container's runtime is J2SE 1.4 or greater), and
1785: * "currencySymbol" takes precendence over "currencyCode" otherwise.
1786: *
1787: * If only "currencyCode" is given, it is used as a currency symbol if
1788: * java.util.Currency is not defined.
1789: *
1790: * Example:
1791: *
1792: * JDK "currencyCode" "currencySymbol" Currency symbol being displayed
1793: * -----------------------------------------------------------------------
1794: * all --- --- Locale's default currency symbol
1795: *
1796: * <1.4 EUR --- EUR
1797: * >=1.4 EUR --- Locale's currency symbol for Euro
1798: *
1799: * all --- \u20AC \u20AC
1800: *
1801: * <1.4 EUR \u20AC \u20AC
1802: * >=1.4 EUR \u20AC Locale's currency symbol for Euro
1803: */
1804: private void setCurrency(NumberFormat formatter,
1805: String currencyCode, String currencySymbol)
1806: throws Exception {
1807: String code = null;
1808: String symbol = null;
1809:
1810: if (currencyCode == null) {
1811: if (currencySymbol == null) {
1812: return;
1813: }
1814: symbol = currencySymbol;
1815: } else if (currencySymbol != null) {
1816: if (currencyClass != null) {
1817: code = currencyCode;
1818: } else {
1819: symbol = currencySymbol;
1820: }
1821: } else if (currencyClass != null) {
1822: code = currencyCode;
1823: } else {
1824: symbol = currencyCode;
1825: }
1826: if (code != null) {
1827: Object[] methodArgs = new Object[1];
1828:
1829: /*
1830: * java.util.Currency.getInstance()
1831: */
1832: Method m = currencyClass.getMethod("getInstance",
1833: new Class[] { String.class });
1834:
1835: methodArgs[0] = code;
1836: Object currency = m.invoke(null, methodArgs);
1837:
1838: /*
1839: * java.text.NumberFormat.setCurrency()
1840: */
1841: Class[] paramTypes = new Class[1];
1842: paramTypes[0] = currencyClass;
1843: Class numberFormatClass = Class
1844: .forName("java.text.NumberFormat");
1845: m = numberFormatClass.getMethod("setCurrency",
1846: paramTypes);
1847: methodArgs[0] = currency;
1848: m.invoke(formatter, methodArgs);
1849: } else {
1850: /*
1851: * Let potential ClassCastException propagate up (will almost
1852: * never happen)
1853: */
1854: DecimalFormat df = (DecimalFormat) formatter;
1855: DecimalFormatSymbols dfs = df.getDecimalFormatSymbols();
1856: dfs.setCurrencySymbol(symbol);
1857: df.setDecimalFormatSymbols(dfs);
1858: }
1859: }
1860: }
1861:
1862: // formatDate tag (borrows from Jakarta taglibs JSTL)
1863:
1864: static class StartFormatDate extends LocaleAwareInstruction {
1865:
1866: private static final String DATE = "date";
1867: private static final String TIME = "time";
1868: private static final String DATETIME = "both";
1869:
1870: JXTExpression var;
1871: JXTExpression value;
1872: JXTExpression type;
1873: JXTExpression pattern;
1874: JXTExpression timeZone;
1875: JXTExpression dateStyle;
1876: JXTExpression timeStyle;
1877: JXTExpression locale;
1878:
1879: StartFormatDate(StartElement raw, JXTExpression var,
1880: JXTExpression value, JXTExpression type,
1881: JXTExpression pattern, JXTExpression timeZone,
1882: JXTExpression dateStyle, JXTExpression timeStyle,
1883: JXTExpression locale) {
1884: super (raw, locale);
1885: this .var = var;
1886: this .value = value;
1887: this .type = type;
1888: this .pattern = pattern;
1889: this .timeZone = timeZone;
1890: this .dateStyle = dateStyle;
1891: this .timeStyle = timeStyle;
1892: }
1893:
1894: String format(JexlContext jexl, JXPathContext jxp)
1895: throws Exception {
1896: String var = getStringValue(this .var, jexl, jxp);
1897: Object value = getValue(this .value, jexl, jxp);
1898: String pattern = getStringValue(this .pattern, jexl, jxp);
1899: Object timeZone = getValue(this .timeZone, jexl, jxp);
1900:
1901: String type = getStringValue(this .type, jexl, jxp);
1902: String timeStyle = getStringValue(this .timeStyle, jexl, jxp);
1903: String dateStyle = getStringValue(this .dateStyle, jexl, jxp);
1904:
1905: String formatted = null;
1906:
1907: // Create formatter
1908: Locale locale = getLocale(jexl, jxp);
1909: DateFormat formatter = createFormatter(locale, type,
1910: dateStyle, timeStyle);
1911: // Apply pattern, if present
1912: if (pattern != null) {
1913: try {
1914: ((SimpleDateFormat) formatter)
1915: .applyPattern(pattern);
1916: } catch (ClassCastException cce) {
1917: formatter = new SimpleDateFormat(pattern, locale);
1918: }
1919: }
1920: // Set time zone
1921: TimeZone tz = null;
1922: if ((timeZone instanceof String) && timeZone.equals("")) {
1923: timeZone = null;
1924: }
1925: if (timeZone != null) {
1926: if (timeZone instanceof String) {
1927: tz = TimeZone.getTimeZone((String) timeZone);
1928: } else if (timeZone instanceof TimeZone) {
1929: tz = (TimeZone) timeZone;
1930: } else {
1931: throw new IllegalArgumentException(
1932: "Illegal timeZone value: \"" + timeZone
1933: + "\"");
1934: }
1935: }
1936: if (tz != null) {
1937: formatter.setTimeZone(tz);
1938: }
1939: formatted = formatter.format(value);
1940: if (var != null) {
1941: jexl.getVars().put(var, formatted);
1942: jxp.getVariables().declareVariable(var, formatted);
1943: return null;
1944: }
1945: return formatted;
1946: }
1947:
1948: private DateFormat createFormatter(Locale loc, String type,
1949: String dateStyle, String timeStyle) throws Exception {
1950: DateFormat formatter = null;
1951: if ((type == null) || DATE.equalsIgnoreCase(type)) {
1952: formatter = DateFormat.getDateInstance(
1953: getStyle(dateStyle), loc);
1954: } else if (TIME.equalsIgnoreCase(type)) {
1955: formatter = DateFormat.getTimeInstance(
1956: getStyle(timeStyle), loc);
1957: } else if (DATETIME.equalsIgnoreCase(type)) {
1958: formatter = DateFormat.getDateTimeInstance(
1959: getStyle(dateStyle), getStyle(timeStyle), loc);
1960: } else {
1961: throw new IllegalArgumentException("Invalid type: \""
1962: + type + "\"");
1963: }
1964: return formatter;
1965: }
1966:
1967: private static final String DEFAULT = "default";
1968: private static final String SHORT = "short";
1969: private static final String MEDIUM = "medium";
1970: private static final String LONG = "long";
1971: private static final String FULL = "full";
1972:
1973: private int getStyle(String style) {
1974: int ret = DateFormat.DEFAULT;
1975: if (style != null) {
1976: if (DEFAULT.equalsIgnoreCase(style)) {
1977: ret = DateFormat.DEFAULT;
1978: } else if (SHORT.equalsIgnoreCase(style)) {
1979: ret = DateFormat.SHORT;
1980: } else if (MEDIUM.equalsIgnoreCase(style)) {
1981: ret = DateFormat.MEDIUM;
1982: } else if (LONG.equalsIgnoreCase(style)) {
1983: ret = DateFormat.LONG;
1984: } else if (FULL.equalsIgnoreCase(style)) {
1985: ret = DateFormat.FULL;
1986: } else {
1987: throw new IllegalArgumentException(
1988: "Invalid style: \""
1989: + style
1990: + "\": should be \"default\" or \"short\" or \"medium\" or \"long\" or \"full\"");
1991: }
1992: }
1993: return ret;
1994: }
1995: }
1996:
1997: static class Parser implements ContentHandler, LexicalHandler {
1998:
1999: StartDocument startEvent;
2000: Event lastEvent;
2001: Stack stack = new Stack();
2002: Locator locator;
2003: Location charLocation;
2004: StringBuffer charBuf;
2005:
2006: public Parser() {
2007: // EMPTY
2008: }
2009:
2010: StartDocument getStartEvent() {
2011: return this .startEvent;
2012: }
2013:
2014: void recycle() {
2015: startEvent = null;
2016: lastEvent = null;
2017: stack.clear();
2018: locator = null;
2019: charLocation = null;
2020: charBuf = null;
2021: }
2022:
2023: private void addEvent(Event ev) throws SAXException {
2024: if (ev != null) {
2025: if (lastEvent == null) {
2026: lastEvent = startEvent = new StartDocument(
2027: LocationUtils.getLocation(locator,
2028: "template"));
2029: } else {
2030: flushChars();
2031: }
2032: lastEvent.next = ev;
2033: lastEvent = ev;
2034: } else {
2035: throw new NullPointerException("null event");
2036: }
2037: }
2038:
2039: void flushChars() throws SAXException {
2040: if (charBuf != null) {
2041: char[] chars = new char[charBuf.length()];
2042: charBuf.getChars(0, charBuf.length(), chars, 0);
2043: Characters ev = new Characters(charLocation, chars, 0,
2044: chars.length);
2045: lastEvent.next = ev;
2046: lastEvent = ev;
2047: charLocation = null;
2048: charBuf = null;
2049: }
2050: }
2051:
2052: public void characters(char[] ch, int start, int length)
2053: throws SAXException {
2054: if (charBuf == null) {
2055: charBuf = new StringBuffer(length);
2056: charLocation = LocationUtils.getLocation(locator,
2057: "[text]");
2058: }
2059: charBuf.append(ch, start, length);
2060: }
2061:
2062: public void endDocument() throws SAXException {
2063: StartDocument startDoc = (StartDocument) stack.pop();
2064: EndDocument endDoc = new EndDocument(LocationUtils
2065: .getLocation(locator, "template"));
2066: startDoc.endDocument = endDoc;
2067: addEvent(endDoc);
2068: }
2069:
2070: public void endElement(String namespaceURI, String localName,
2071: String raw) throws SAXException {
2072: Event start = (Event) stack.pop();
2073: Event newEvent = null;
2074: if (NS.equals(namespaceURI)) {
2075: StartInstruction startInstruction = (StartInstruction) start;
2076: EndInstruction endInstruction = new EndInstruction(
2077: LocationUtils.getLocation(locator, "<" + raw
2078: + ">"), startInstruction);
2079: newEvent = endInstruction;
2080: if (start instanceof StartWhen) {
2081: StartWhen startWhen = (StartWhen) start;
2082: StartChoose startChoose = (StartChoose) stack
2083: .peek();
2084: if (startChoose.firstChoice != null) {
2085: StartWhen w = startChoose.firstChoice;
2086: while (w.nextChoice != null) {
2087: w = w.nextChoice;
2088: }
2089: w.nextChoice = startWhen;
2090: } else {
2091: startChoose.firstChoice = startWhen;
2092: }
2093: } else if (start instanceof StartOtherwise) {
2094: StartOtherwise startOtherwise = (StartOtherwise) start;
2095: StartChoose startChoose = (StartChoose) stack
2096: .peek();
2097: startChoose.otherwise = startOtherwise;
2098: }
2099: } else {
2100: StartElement startElement = (StartElement) start;
2101: newEvent = startElement.endElement = new EndElement(
2102: LocationUtils.getLocation(locator, "<" + raw
2103: + ">"), startElement);
2104: }
2105: addEvent(newEvent);
2106: if (start instanceof StartDefine) {
2107: StartDefine startDefine = (StartDefine) start;
2108: startDefine.finish();
2109: }
2110: }
2111:
2112: public void endPrefixMapping(String prefix) throws SAXException {
2113: EndPrefixMapping endPrefixMapping = new EndPrefixMapping(
2114: LocationUtils.getLocation(locator, null), prefix);
2115: addEvent(endPrefixMapping);
2116: }
2117:
2118: public void ignorableWhitespace(char[] ch, int start, int length)
2119: throws SAXException {
2120: Event ev = new IgnorableWhitespace(LocationUtils
2121: .getLocation(locator, null), ch, start, length);
2122: addEvent(ev);
2123: }
2124:
2125: public void processingInstruction(String target, String data)
2126: throws SAXException {
2127: Event pi = new ProcessingInstruction(LocationUtils
2128: .getLocation(locator, null), target, data);
2129: addEvent(pi);
2130: }
2131:
2132: public void setDocumentLocator(Locator locator) {
2133: this .locator = locator;
2134: }
2135:
2136: public void skippedEntity(String name) throws SAXException {
2137: addEvent(new SkippedEntity(LocationUtils.getLocation(
2138: locator, null), name));
2139: }
2140:
2141: public void startDocument() {
2142: startEvent = new StartDocument(LocationUtils.getLocation(
2143: locator, null));
2144: lastEvent = startEvent;
2145: stack.push(lastEvent);
2146: }
2147:
2148: public void startElement(String namespaceURI, String localName,
2149: String qname, Attributes attrs) throws SAXException {
2150: Event newEvent = null;
2151: Location locator = LocationUtils.getLocation(this .locator,
2152: "<" + qname + ">");
2153: AttributesImpl elementAttributes = new AttributesImpl(attrs);
2154: int attributeCount = elementAttributes.getLength();
2155: for (int i = 0; i < attributeCount; i++) {
2156: String attributeURI = elementAttributes.getURI(i);
2157: if (StringUtils.equals(attributeURI, NS)) {
2158: getStartEvent().templateProperties.put(
2159: elementAttributes.getLocalName(i),
2160: compileExpr(elementAttributes.getValue(i),
2161: null, locator));
2162: elementAttributes.removeAttribute(i--);
2163: }
2164: }
2165: StartElement startElement = new StartElement(locator,
2166: namespaceURI, localName, qname, elementAttributes);
2167: if (NS.equals(namespaceURI)) {
2168: if (localName.equals(FOR_EACH)) {
2169: String items = attrs.getValue("items");
2170: String select = attrs.getValue("select");
2171: JXTExpression begin = compileInt(attrs
2172: .getValue("begin"), FOR_EACH, locator);
2173: JXTExpression end = compileInt(attrs
2174: .getValue("end"), FOR_EACH, locator);
2175: JXTExpression step = compileInt(attrs
2176: .getValue("step"), FOR_EACH, locator);
2177: JXTExpression var = compileExpr(attrs
2178: .getValue("var"), null, locator);
2179: JXTExpression varStatus = compileExpr(attrs
2180: .getValue("varStatus"), null, locator);
2181: if (items == null) {
2182: if (select == null
2183: && (begin == null || end == null)) {
2184: throw new JXTException(
2185: "forEach: \"select\", \"items\", or both \"begin\" and \"end\" must be specified",
2186: locator, null);
2187: }
2188: } else if (select != null) {
2189: throw new JXTException(
2190: "forEach: only one of \"select\" or \"items\" may be specified",
2191: locator, null);
2192: }
2193: JXTExpression expr = compileExpr(
2194: items == null ? select : items, null,
2195: locator);
2196: String lenientValue = attrs.getValue("lenient");
2197: Boolean lenient = (lenientValue == null) ? null
2198: : Boolean.valueOf(lenientValue);
2199: StartForEach startForEach = new StartForEach(
2200: startElement, expr, var, varStatus, begin,
2201: end, step, lenient);
2202: newEvent = startForEach;
2203: } else if (localName.equals(FORMAT_NUMBER)) {
2204: JXTExpression value = compileExpr(attrs
2205: .getValue("value"), null, locator);
2206: JXTExpression type = compileExpr(attrs
2207: .getValue("type"), null, locator);
2208: JXTExpression pattern = compileExpr(attrs
2209: .getValue("pattern"), null, locator);
2210: JXTExpression currencyCode = compileExpr(attrs
2211: .getValue("currencyCode"), null, locator);
2212: JXTExpression currencySymbol = compileExpr(attrs
2213: .getValue("currencySymbol"), null, locator);
2214: JXTExpression isGroupingUsed = compileBoolean(attrs
2215: .getValue("isGroupingUsed"), null, locator);
2216: JXTExpression maxIntegerDigits = compileInt(attrs
2217: .getValue("maxIntegerDigits"), null,
2218: locator);
2219: JXTExpression minIntegerDigits = compileInt(attrs
2220: .getValue("minIntegerDigits"), null,
2221: locator);
2222: JXTExpression maxFractionDigits = compileInt(attrs
2223: .getValue("maxFractionDigits"), null,
2224: locator);
2225: JXTExpression minFractionDigits = compileInt(attrs
2226: .getValue("minFractionDigits"), null,
2227: locator);
2228: JXTExpression var = compileExpr(attrs
2229: .getValue("var"), null, locator);
2230: JXTExpression locale = compileExpr(attrs
2231: .getValue("locale"), null, locator);
2232: StartFormatNumber startFormatNumber = new StartFormatNumber(
2233: startElement, var, value, type, pattern,
2234: currencyCode, currencySymbol,
2235: isGroupingUsed, maxIntegerDigits,
2236: minIntegerDigits, maxFractionDigits,
2237: minFractionDigits, locale);
2238: newEvent = startFormatNumber;
2239: } else if (localName.equals(FORMAT_DATE)) {
2240: JXTExpression var = compileExpr(attrs
2241: .getValue("var"), null, locator);
2242: JXTExpression value = compileExpr(attrs
2243: .getValue("value"), null, locator);
2244: JXTExpression type = compileExpr(attrs
2245: .getValue("type"), null, locator);
2246: JXTExpression pattern = compileExpr(attrs
2247: .getValue("pattern"), null, locator);
2248: JXTExpression timeZone = compileExpr(attrs
2249: .getValue("timeZone"), null, locator);
2250: JXTExpression dateStyle = compileExpr(attrs
2251: .getValue("dateStyle"), null, locator);
2252: JXTExpression timeStyle = compileExpr(attrs
2253: .getValue("timeStyle"), null, locator);
2254: JXTExpression locale = compileExpr(attrs
2255: .getValue("locale"), null, locator);
2256: StartFormatDate startFormatDate = new StartFormatDate(
2257: startElement, var, value, type, pattern,
2258: timeZone, dateStyle, timeStyle, locale);
2259: newEvent = startFormatDate;
2260: } else if (localName.equals(CHOOSE)) {
2261: StartChoose startChoose = new StartChoose(
2262: startElement);
2263: newEvent = startChoose;
2264: } else if (localName.equals(WHEN)) {
2265: if (stack.size() == 0
2266: || !(stack.peek() instanceof StartChoose)) {
2267: throw new JXTException(
2268: "<when> must be within <choose>",
2269: locator, null);
2270: }
2271: String test = attrs.getValue("test");
2272: if (test != null) {
2273: JXTExpression expr = compileExpr(test,
2274: "when: \"test\": ", locator);
2275: StartWhen startWhen = new StartWhen(
2276: startElement, expr);
2277: newEvent = startWhen;
2278: } else {
2279: throw new JXTException(
2280: "when: \"test\" is required", locator,
2281: null);
2282: }
2283: } else if (localName.equals(OUT)) {
2284: String value = attrs.getValue("value");
2285: if (value != null) {
2286: JXTExpression expr = compileExpr(value,
2287: "out: \"value\": ", locator);
2288: String lenientValue = attrs.getValue("lenient");
2289: Boolean lenient = lenientValue == null ? null
2290: : Boolean.valueOf(lenientValue);
2291: newEvent = new StartOut(startElement, expr,
2292: lenient);
2293: } else {
2294: throw new JXTException(
2295: "out: \"value\" is required", locator,
2296: null);
2297: }
2298: } else if (localName.equals(OTHERWISE)) {
2299: if (stack.size() != 0
2300: && (stack.peek() instanceof StartChoose)) {
2301: StartOtherwise startOtherwise = new StartOtherwise(
2302: startElement);
2303: newEvent = startOtherwise;
2304: } else {
2305: throw new JXTException(
2306: "<otherwise> must be within <choose>",
2307: locator, null);
2308: }
2309: } else if (localName.equals(IF)) {
2310: String test = attrs.getValue("test");
2311: if (test != null) {
2312: JXTExpression expr = compileExpr(test,
2313: "if: \"test\": ", locator);
2314: StartIf startIf = new StartIf(startElement,
2315: expr);
2316: newEvent = startIf;
2317: } else {
2318: throw new JXTException(
2319: "if: \"test\" is required", locator,
2320: null);
2321: }
2322: } else if (localName.equals(MACRO)) {
2323: // <macro name="myTag" targetNamespace="myNamespace">
2324: // <parameter name="paramName" required="Boolean" default="value"/>
2325: // body
2326: // </macro>
2327: String namespace = StringUtils.defaultString(attrs
2328: .getValue("targetNamespace"));
2329: String name = attrs.getValue("name");
2330: if (name != null) {
2331: StartDefine startDefine = new StartDefine(
2332: startElement, namespace, name);
2333: newEvent = startDefine;
2334: } else {
2335: throw new JXTException(
2336: "macro: \"name\" is required", locator,
2337: null);
2338: }
2339: } else if (localName.equals(PARAMETER)) {
2340: if (stack.size() == 0
2341: || !(stack.peek() instanceof StartDefine)) {
2342: throw new JXTException(
2343: "<parameter> not allowed here",
2344: locator, null);
2345: } else {
2346: String name = attrs.getValue("name");
2347: String optional = attrs.getValue("optional");
2348: String default_ = attrs.getValue("default");
2349: if (name != null) {
2350: StartParameter startParameter = new StartParameter(
2351: startElement, name, optional,
2352: default_);
2353: newEvent = startParameter;
2354: } else {
2355: throw new JXTException(
2356: "parameter: \"name\" is required",
2357: locator, null);
2358: }
2359: }
2360: } else if (localName.equals(EVALBODY)) {
2361: newEvent = new StartEvalBody(startElement);
2362: } else if (localName.equals(EVAL)) {
2363: String value = attrs.getValue("select");
2364: JXTExpression valueExpr = compileExpr(value,
2365: "eval: \"select\":", locator);
2366: newEvent = new StartEval(startElement, valueExpr);
2367: } else if (localName.equals(SET)) {
2368: String var = attrs.getValue("var");
2369: String value = attrs.getValue("value");
2370: JXTExpression varExpr = null;
2371: JXTExpression valueExpr = null;
2372: if (var != null) {
2373: varExpr = compileExpr(var, "set: \"var\":",
2374: locator);
2375: }
2376: if (value != null) {
2377: valueExpr = compileExpr(value,
2378: "set: \"value\":", locator);
2379: }
2380: StartSet startSet = new StartSet(startElement,
2381: varExpr, valueExpr);
2382: newEvent = startSet;
2383: } else if (localName.equals(IMPORT)) {
2384: // <import uri="${root}/foo/bar.xml" context="${foo}"/>
2385: AttributeEvent uri = null;
2386: Iterator iter = startElement.attributeEvents
2387: .iterator();
2388: while (iter.hasNext()) {
2389: AttributeEvent e = (AttributeEvent) iter.next();
2390: if (e.localName.equals("uri")) {
2391: uri = e;
2392: break;
2393: }
2394: }
2395: if (uri != null) {
2396: // If "context" is present then its value will be used
2397: // as the context object in the imported template
2398: String select = attrs.getValue("context");
2399: JXTExpression expr = null;
2400: if (select != null) {
2401: expr = compileExpr(select,
2402: "import: \"context\": ", locator);
2403: }
2404: StartImport startImport = new StartImport(
2405: startElement, uri, expr);
2406: newEvent = startImport;
2407: } else {
2408: throw new JXTException(
2409: "import: \"uri\" is required", locator,
2410: null);
2411: }
2412: } else if (localName.equals(TEMPLATE)) {
2413: StartTemplate startTemplate = new StartTemplate(
2414: startElement);
2415: newEvent = startTemplate;
2416: } else if (localName.equals(COMMENT)) {
2417: // <jx:comment>This will be parsed</jx:comment>
2418: StartComment startJXComment = new StartComment(
2419: startElement);
2420: newEvent = startJXComment;
2421: } else {
2422: throw new JXTException("unrecognized tag: "
2423: + localName, locator, null);
2424: }
2425: } else {
2426: newEvent = startElement;
2427: }
2428: stack.push(newEvent);
2429: addEvent(newEvent);
2430: }
2431:
2432: public void startPrefixMapping(String prefix, String uri)
2433: throws SAXException {
2434: addEvent(new StartPrefixMapping(LocationUtils.getLocation(
2435: locator, null), prefix, uri));
2436: }
2437:
2438: public void comment(char ch[], int start, int length)
2439: throws SAXException {
2440: // DO NOTHING
2441: }
2442:
2443: public void endCDATA() throws SAXException {
2444: addEvent(new EndCDATA(LocationUtils.getLocation(locator,
2445: null)));
2446: }
2447:
2448: public void endDTD() throws SAXException {
2449: addEvent(new EndDTD(LocationUtils
2450: .getLocation(locator, null)));
2451: }
2452:
2453: public void endEntity(String name) throws SAXException {
2454: addEvent(new EndEntity(LocationUtils.getLocation(locator,
2455: null), name));
2456: }
2457:
2458: public void startCDATA() throws SAXException {
2459: addEvent(new StartCDATA(LocationUtils.getLocation(locator,
2460: null)));
2461: }
2462:
2463: public void startDTD(String name, String publicId,
2464: String systemId) throws SAXException {
2465: addEvent(new StartDTD(LocationUtils.getLocation(locator,
2466: null), name, publicId, systemId));
2467: }
2468:
2469: public void startEntity(String name) throws SAXException {
2470: addEvent(new StartEntity(LocationUtils.getLocation(locator,
2471: null), name));
2472: }
2473: }
2474:
2475: /**
2476: * Adapter that makes this generator usable as a transformer
2477: * (Note there is a performance penalty for this however:
2478: * you effectively recompile the template for every instance document)
2479: */
2480:
2481: public static class TransformerAdapter extends
2482: ServiceableTransformer {
2483: static class TemplateConsumer extends Parser implements
2484: XMLConsumer {
2485:
2486: public TemplateConsumer() {
2487: this .gen = new JXTemplateGenerator();
2488: }
2489:
2490: public void setup(SourceResolver resolver, Map objectModel,
2491: String src, Parameters parameters)
2492: throws ProcessingException, SAXException,
2493: IOException {
2494: this .gen.setup(resolver, objectModel, null, parameters);
2495: }
2496:
2497: public void service(ServiceManager manager)
2498: throws ServiceException {
2499: this .gen.service(manager);
2500: }
2501:
2502: public void endDocument() throws SAXException {
2503: super .endDocument();
2504: gen.performGeneration(gen.getConsumer(), gen
2505: .getJexlContext(), gen.getJXPathContext(),
2506: null, getStartEvent(), null);
2507: }
2508:
2509: void setConsumer(XMLConsumer consumer) {
2510: gen.setConsumer(consumer);
2511: }
2512:
2513: void recycle() {
2514: super .recycle();
2515: gen.recycle();
2516: }
2517:
2518: JXTemplateGenerator gen;
2519: }
2520:
2521: TemplateConsumer templateConsumer = new TemplateConsumer();
2522:
2523: public void recycle() {
2524: super .recycle();
2525: templateConsumer.recycle();
2526: }
2527:
2528: public void setup(SourceResolver resolver, Map objectModel,
2529: String src, Parameters parameters)
2530: throws ProcessingException, SAXException, IOException {
2531: super .setup(resolver, objectModel, src, parameters);
2532: templateConsumer.setup(resolver, objectModel, src,
2533: parameters);
2534: }
2535:
2536: public void service(ServiceManager manager)
2537: throws ServiceException {
2538: super .service(manager);
2539: templateConsumer.service(manager);
2540: }
2541:
2542: public void setConsumer(XMLConsumer xmlConsumer) {
2543: super .setConsumer(templateConsumer);
2544: templateConsumer.setConsumer(xmlConsumer);
2545: }
2546: }
2547:
2548: private JXPathContext jxpathContext;
2549: private MyJexlContext globalJexlContext;
2550: private Variables variables;
2551: private static Map cache = new HashMap();
2552: private Source inputSource;
2553: private Map definitions;
2554: private Map cocoon;
2555:
2556: private JXPathContext getJXPathContext() {
2557: return jxpathContext;
2558: }
2559:
2560: private MyJexlContext getJexlContext() {
2561: return globalJexlContext;
2562: }
2563:
2564: /* (non-Javadoc)
2565: * @see org.apache.avalon.excalibur.pool.Recyclable#recycle()
2566: */
2567: public void recycle() {
2568: if (this .resolver != null) {
2569: this .resolver.release(this .inputSource);
2570: }
2571: this .inputSource = null;
2572: this .jxpathContext = null;
2573: this .globalJexlContext = null;
2574: this .variables = null;
2575: this .definitions = null;
2576: this .cocoon = null;
2577: this .namespaces.clear();
2578: super .recycle();
2579: }
2580:
2581: /* (non-Javadoc)
2582: * @see org.apache.cocoon.sitemap.SitemapModelComponent#setup(org.apache.cocoon.environment.SourceResolver, java.util.Map, java.lang.String, org.apache.avalon.framework.parameters.Parameters)
2583: */
2584: public void setup(SourceResolver resolver, Map objectModel,
2585: String src, Parameters parameters)
2586: throws ProcessingException, SAXException, IOException {
2587:
2588: super .setup(resolver, objectModel, src, parameters);
2589: if (src != null) {
2590: try {
2591: this .inputSource = resolver.resolveURI(src);
2592: } catch (SourceException se) {
2593: throw SourceUtil.handle("Error during resolving of '"
2594: + src + "'.", se);
2595: }
2596: final String uri = inputSource.getURI();
2597: boolean regenerate = false;
2598: StartDocument startEvent = null;
2599: synchronized (cache) {
2600: startEvent = (StartDocument) cache.get(uri);
2601: if (startEvent != null) {
2602: int valid = SourceValidity.UNKNOWN;
2603: if (startEvent.compileTime != null) {
2604: valid = startEvent.compileTime.isValid();
2605: }
2606: if (valid == SourceValidity.UNKNOWN
2607: && startEvent.compileTime != null) {
2608: SourceValidity validity = inputSource
2609: .getValidity();
2610: valid = startEvent.compileTime
2611: .isValid(validity);
2612: }
2613: if (valid != SourceValidity.VALID) {
2614: regenerate = true;
2615: }
2616: } else {
2617: regenerate = true;
2618: }
2619: }
2620: if (regenerate) {
2621: Parser parser = new Parser();
2622: SourceUtil
2623: .parse(this .manager, this .inputSource, parser);
2624: startEvent = parser.getStartEvent();
2625: startEvent.compileTime = this .inputSource.getValidity();
2626: synchronized (cache) {
2627: cache.put(uri, startEvent);
2628: }
2629: }
2630: }
2631: Object bean = FlowHelper.getContextObject(objectModel);
2632: WebContinuation kont = FlowHelper
2633: .getWebContinuation(objectModel);
2634: setContexts(bean, kont, parameters, objectModel);
2635: this .definitions = new HashMap();
2636: }
2637:
2638: private void fillContext(Object contextObject, Map map) {
2639: if (contextObject != null) {
2640: // Hack: I use jxpath to populate the context object's properties
2641: // in the jexl context
2642: final JXPathBeanInfo bi = JXPathIntrospector
2643: .getBeanInfo(contextObject.getClass());
2644: if (bi.isDynamic()) {
2645: Class cl = bi.getDynamicPropertyHandlerClass();
2646: try {
2647: DynamicPropertyHandler h = (DynamicPropertyHandler) cl
2648: .newInstance();
2649: String[] result = h.getPropertyNames(contextObject);
2650: int len = result.length;
2651: for (int i = 0; i < len; i++) {
2652: try {
2653: map.put(result[i], h.getProperty(
2654: contextObject, result[i]));
2655: } catch (Exception exc) {
2656: exc.printStackTrace();
2657: }
2658: }
2659: } catch (Exception ignored) {
2660: ignored.printStackTrace();
2661: }
2662: } else {
2663: PropertyDescriptor[] props = bi
2664: .getPropertyDescriptors();
2665: int len = props.length;
2666: for (int i = 0; i < len; i++) {
2667: try {
2668: Method read = props[i].getReadMethod();
2669: if (read != null) {
2670: map.put(props[i].getName(), read.invoke(
2671: contextObject, null));
2672: }
2673: } catch (Exception ignored) {
2674: ignored.printStackTrace();
2675: }
2676: }
2677: }
2678: }
2679: }
2680:
2681: private void setContexts(Object contextObject,
2682: WebContinuation kont, Parameters parameters, Map objectModel) {
2683: final Request request = ObjectModelHelper
2684: .getRequest(objectModel);
2685: final Object session = request.getSession(false);
2686: final Object app = ObjectModelHelper.getContext(objectModel);
2687: cocoon = new HashMap();
2688: Object fomRequest = FOM_JavaScriptFlowHelper
2689: .getFOM_Request(objectModel);
2690: cocoon
2691: .put("request", fomRequest != null ? fomRequest
2692: : request);
2693: if (session != null) {
2694: cocoon.put("session", FOM_JavaScriptFlowHelper
2695: .getFOM_Session(objectModel));
2696: }
2697: cocoon.put("context", FOM_JavaScriptFlowHelper
2698: .getFOM_Context(objectModel));
2699: cocoon.put("continuation", FOM_JavaScriptFlowHelper
2700: .getFOM_WebContinuation(objectModel));
2701: cocoon.put("parameters", parameters);
2702: this .variables = new MyVariables(cocoon, contextObject, kont,
2703: request, session, app, parameters);
2704: Map map;
2705: if (contextObject instanceof Map) {
2706: map = (Map) contextObject;
2707: } else {
2708: map = new HashMap();
2709: fillContext(contextObject, map);
2710: }
2711: jxpathContext = jxpathContextFactory.newContext(null,
2712: contextObject);
2713: jxpathContext
2714: .setNamespaceContextPointer(new NamespacesTablePointer(
2715: namespaces));
2716: jxpathContext.setVariables(variables);
2717: jxpathContext.setLenient(parameters.getParameterAsBoolean(
2718: "lenient-xpath", false));
2719: globalJexlContext = new MyJexlContext();
2720: globalJexlContext.setVars(map);
2721: map = globalJexlContext.getVars();
2722: map.put("cocoon", cocoon);
2723: if (contextObject != null) {
2724: map.put("flowContext", contextObject);
2725: // FIXME (VG): Is this required (what it's used for - examples)?
2726: // Here I use Rhino's live-connect objects to allow Jexl to call
2727: // java constructors
2728: Object javaPkg = FOM_JavaScriptFlowHelper
2729: .getJavaPackage(objectModel);
2730: Object pkgs = FOM_JavaScriptFlowHelper
2731: .getPackages(objectModel);
2732: map.put("java", javaPkg);
2733: map.put("Packages", pkgs);
2734: }
2735: if (kont != null) {
2736: map.put("continuation", kont);
2737: }
2738: map.put("request", request);
2739: map.put("context", app);
2740: map.put("parameters", parameters);
2741: if (session != null) {
2742: map.put("session", session);
2743: }
2744: }
2745:
2746: /* (non-Javadoc)
2747: * @see org.apache.cocoon.generation.Generator#generate()
2748: */
2749: public void generate() throws IOException, SAXException,
2750: ProcessingException {
2751: final String cacheKey = this .inputSource.getURI();
2752:
2753: StartDocument startEvent;
2754: synchronized (cache) {
2755: startEvent = (StartDocument) cache.get(cacheKey);
2756: }
2757: performGeneration(this .xmlConsumer, globalJexlContext,
2758: jxpathContext, null, startEvent, null);
2759: }
2760:
2761: private void performGeneration(final XMLConsumer consumer,
2762: MyJexlContext jexlContext, JXPathContext jxpathContext,
2763: StartElement macroCall, Event startEvent, Event endEvent)
2764: throws SAXException {
2765: cocoon.put("consumer", consumer);
2766: RedundantNamespacesFilter filter = new RedundantNamespacesFilter(
2767: this .xmlConsumer);
2768: // EventPrinterPipe log = new EventPrinterPipe();
2769: // log.setConsumer(filter);
2770: execute(filter, globalJexlContext, jxpathContext, null,
2771: startEvent, null);
2772: }
2773:
2774: interface CharHandler {
2775: public void characters(char[] ch, int offset, int length)
2776: throws SAXException;
2777: }
2778:
2779: private void characters(JexlContext jexlContext,
2780: JXPathContext jxpathContext, TextEvent event,
2781: CharHandler handler) throws SAXException {
2782: Iterator iter = event.substitutions.iterator();
2783: while (iter.hasNext()) {
2784: Object subst = iter.next();
2785: char[] chars;
2786: if (subst instanceof char[]) {
2787: chars = (char[]) subst;
2788: } else {
2789: JXTExpression expr = (JXTExpression) subst;
2790: try {
2791: Object val = getValue(expr, jexlContext,
2792: jxpathContext);
2793: chars = val != null ? val.toString().toCharArray()
2794: : ArrayUtils.EMPTY_CHAR_ARRAY;
2795: } catch (Exception e) {
2796: throw new JXTException(e.getMessage(),
2797: event.location, e);
2798: }
2799: }
2800: handler.characters(chars, 0, chars.length);
2801: }
2802: }
2803:
2804: /** dump a DOM document, using an IncludeXMLConsumer to filter out start/end document events */
2805: private void executeDOM(final XMLConsumer consumer,
2806: MyJexlContext jexlContext, JXPathContext jxpathContext,
2807: Node node) throws SAXException {
2808: IncludeXMLConsumer includer = new IncludeXMLConsumer(consumer);
2809: DOMStreamer streamer = new DOMStreamer(includer);
2810: streamer.stream(node);
2811: }
2812:
2813: private void call(Location location, StartElement macroCall,
2814: final XMLConsumer consumer, MyJexlContext jexlContext,
2815: JXPathContext jxpathContext, Event startEvent,
2816: Event endEvent) throws SAXException {
2817: try {
2818: execute(consumer, jexlContext, jxpathContext, macroCall,
2819: startEvent, endEvent);
2820: } catch (Exception exc) {
2821: throw new JXTException(macroCall.localName + ": "
2822: + exc.getMessage(), location, exc);
2823: }
2824: }
2825:
2826: public static class LoopTagStatus {
2827: Object current;
2828: int index;
2829: int count;
2830: boolean first;
2831: boolean last;
2832: int begin;
2833: int end;
2834: int step;
2835:
2836: public Object getCurrent() {
2837: return current;
2838: }
2839:
2840: public int getIndex() {
2841: return index;
2842: }
2843:
2844: public int getCount() {
2845: return count;
2846: }
2847:
2848: public boolean isFirst() {
2849: return first;
2850: }
2851:
2852: public boolean isLast() {
2853: return last;
2854: }
2855:
2856: public int getBegin() {
2857: return begin;
2858: }
2859:
2860: public int getEnd() {
2861: return end;
2862: }
2863:
2864: public int getStep() {
2865: return step;
2866: }
2867: }
2868:
2869: private void execute(final XMLConsumer consumer,
2870: MyJexlContext jexlContext, JXPathContext jxpathContext,
2871: StartElement macroCall, Event startEvent, Event endEvent)
2872: throws SAXException {
2873: Event ev = startEvent;
2874: LocationFacade loc = new LocationFacade(ev.location);
2875: consumer.setDocumentLocator(loc);
2876: while (ev != endEvent) {
2877: loc.setDocumentLocation(ev.location);
2878: if (ev instanceof Characters) {
2879: TextEvent text = (TextEvent) ev;
2880: Iterator iter = text.substitutions.iterator();
2881: while (iter.hasNext()) {
2882: Object subst = iter.next();
2883: char[] chars;
2884: if (subst instanceof char[]) {
2885: chars = (char[]) subst;
2886: } else {
2887: JXTExpression expr = (JXTExpression) subst;
2888: try {
2889: Object val = getNode(expr, jexlContext,
2890: jxpathContext);
2891: if (val instanceof Node) {
2892: executeDOM(consumer, jexlContext,
2893: jxpathContext, (Node) val);
2894: continue;
2895: } else if (val instanceof NodeList) {
2896: NodeList nodeList = (NodeList) val;
2897: int len = nodeList.getLength();
2898: for (int i = 0; i < len; i++) {
2899: Node n = nodeList.item(i);
2900: executeDOM(consumer, jexlContext,
2901: jxpathContext, n);
2902: }
2903: continue;
2904: } else if (val instanceof Node[]) {
2905: Node[] nodeList = (Node[]) val;
2906: int len = nodeList.length;
2907: for (int i = 0; i < len; i++) {
2908: Node n = nodeList[i];
2909: executeDOM(consumer, jexlContext,
2910: jxpathContext, n);
2911: }
2912: continue;
2913: } else if (val instanceof XMLizable) {
2914: ((XMLizable) val)
2915: .toSAX(new IncludeXMLConsumer(
2916: consumer));
2917: continue;
2918: }
2919: chars = val != null ? val.toString()
2920: .toCharArray()
2921: : ArrayUtils.EMPTY_CHAR_ARRAY;
2922: } catch (Exception e) {
2923: throw new JXTException(e.getMessage(),
2924: ev.location, e);
2925: }
2926: }
2927: consumer.characters(chars, 0, chars.length);
2928: }
2929: } else if (ev instanceof EndElement) {
2930: EndElement endElement = (EndElement) ev;
2931: StartElement startElement = endElement.startElement;
2932: StartDefine def = (StartDefine) definitions
2933: .get(startElement.qname);
2934: if (def == null) {
2935: consumer.endElement(startElement.namespaceURI,
2936: startElement.localName, startElement.raw);
2937: namespaces.leaveScope(consumer);
2938: }
2939: } else if (ev instanceof EndPrefixMapping) {
2940: EndPrefixMapping endPrefixMapping = (EndPrefixMapping) ev;
2941: namespaces.removeDeclaration(endPrefixMapping.prefix);
2942: } else if (ev instanceof IgnorableWhitespace) {
2943: TextEvent text = (TextEvent) ev;
2944: characters(jexlContext, jxpathContext, text,
2945: new CharHandler() {
2946: public void characters(char[] ch,
2947: int offset, int len)
2948: throws SAXException {
2949: consumer.ignorableWhitespace(ch,
2950: offset, len);
2951: }
2952: });
2953: } else if (ev instanceof SkippedEntity) {
2954: SkippedEntity skippedEntity = (SkippedEntity) ev;
2955: consumer.skippedEntity(skippedEntity.name);
2956: } else if (ev instanceof StartIf) {
2957: StartIf startIf = (StartIf) ev;
2958: Object val;
2959: try {
2960: val = getValue(startIf.test, jexlContext,
2961: jxpathContext, Boolean.TRUE);
2962: } catch (Exception e) {
2963: throw new JXTException(e.getMessage(), ev.location,
2964: e);
2965: }
2966: boolean result = false;
2967: if (val instanceof Boolean) {
2968: result = ((Boolean) val).booleanValue();
2969: } else {
2970: result = (val != null);
2971: }
2972: if (!result) {
2973: ev = startIf.endInstruction.next;
2974: continue;
2975: }
2976: } else if (ev instanceof StartForEach) {
2977: StartForEach startForEach = (StartForEach) ev;
2978: final Object items = startForEach.items;
2979: Iterator iter = null;
2980: int begin, end, step;
2981: String var, varStatus;
2982: try {
2983: if (items != null) {
2984: JXTExpression expr = (JXTExpression) items;
2985: if (expr.compiledExpression instanceof CompiledExpression) {
2986: CompiledExpression compiledExpression = (CompiledExpression) expr.compiledExpression;
2987: Object val = compiledExpression.getPointer(
2988: jxpathContext, expr.raw).getNode();
2989: // FIXME: workaround for JXPath bug
2990: iter = val instanceof NativeArray ? new JSIntrospector.NativeArrayIterator(
2991: (NativeArray) val)
2992: : compiledExpression
2993: .iteratePointers(jxpathContext);
2994: } else if (expr.compiledExpression instanceof Expression) {
2995: Expression e = (Expression) expr.compiledExpression;
2996: Object result = e.evaluate(jexlContext);
2997: if (result != null) {
2998: iter = Introspector
2999: .getUberspect()
3000: .getIterator(
3001: result,
3002: new Info(
3003: ev.location
3004: .getURI(),
3005: ev.location
3006: .getLineNumber(),
3007: ev.location
3008: .getColumnNumber()));
3009: }
3010: if (iter == null) {
3011: iter = EMPTY_ITER;
3012: }
3013: } else {
3014: // literal value
3015: iter = new Iterator() {
3016: Object val = items;
3017:
3018: public boolean hasNext() {
3019: return val != null;
3020: }
3021:
3022: public Object next() {
3023: Object res = val;
3024: val = null;
3025: return res;
3026: }
3027:
3028: public void remove() {
3029: // EMPTY
3030: }
3031: };
3032: }
3033: } else {
3034: iter = NULL_ITER;
3035: }
3036: begin = startForEach.begin == null ? 0
3037: : getIntValue(startForEach.begin,
3038: jexlContext, jxpathContext);
3039: end = startForEach.end == null ? Integer.MAX_VALUE
3040: : getIntValue(startForEach.end,
3041: jexlContext, jxpathContext);
3042: step = startForEach.step == null ? 1 : getIntValue(
3043: startForEach.step, jexlContext,
3044: jxpathContext);
3045: var = getStringValue(startForEach.var, jexlContext,
3046: jxpathContext);
3047: varStatus = getStringValue(startForEach.varStatus,
3048: jexlContext, jxpathContext);
3049: } catch (Exception exc) {
3050: throw new JXTException(exc.getMessage(),
3051: ev.location, exc);
3052: }
3053: MyJexlContext localJexlContext = new MyJexlContext(
3054: jexlContext);
3055: MyVariables localJXPathVariables = new MyVariables(
3056: (MyVariables) jxpathContext.getVariables());
3057: int i = 0;
3058: // Move to the begin row
3059: while (i < begin && iter.hasNext()) {
3060: iter.next();
3061: i++;
3062: }
3063: LoopTagStatus status = null;
3064: if (varStatus != null) {
3065: status = new LoopTagStatus();
3066: status.begin = begin;
3067: status.end = end;
3068: status.step = step;
3069: status.first = true;
3070: localJexlContext.put(varStatus, status);
3071: localJXPathVariables.declareVariable(varStatus,
3072: status);
3073: }
3074: int skipCounter, count = 1;
3075: JXPathContext localJXPathContext = null;
3076: while (i <= end && iter.hasNext()) {
3077: Object value = iter.next();
3078: if (value instanceof Pointer) {
3079: Pointer ptr = (Pointer) value;
3080: localJXPathContext = jxpathContext
3081: .getRelativeContext(ptr);
3082: localJXPathContext
3083: .setNamespaceContextPointer(new NamespacesTablePointer(
3084: namespaces));
3085: try {
3086: value = ptr.getNode();
3087: } catch (Exception exc) {
3088: throw new JXTException(exc.getMessage(),
3089: ev.location, null);
3090: }
3091: } else {
3092: localJXPathContext = jxpathContextFactory
3093: .newContext(jxpathContext, value);
3094: localJXPathContext
3095: .setNamespaceContextPointer(new NamespacesTablePointer(
3096: namespaces));
3097: }
3098: localJXPathContext
3099: .setVariables(localJXPathVariables);
3100: if (var != null) {
3101: localJexlContext.put(var, value);
3102: }
3103: if (status != null) {
3104: status.index = i;
3105: status.count = count;
3106: status.first = i == begin;
3107: status.current = value;
3108: status.last = (i == end || !iter.hasNext());
3109: }
3110: execute(consumer, localJexlContext,
3111: localJXPathContext, macroCall,
3112: startForEach.next,
3113: startForEach.endInstruction);
3114: // Skip rows
3115: skipCounter = step;
3116: while (--skipCounter > 0 && iter.hasNext()) {
3117: iter.next();
3118: }
3119: // Increase index
3120: i += step;
3121: count++;
3122: }
3123: ev = startForEach.endInstruction.next;
3124: continue;
3125: } else if (ev instanceof StartChoose) {
3126: StartChoose startChoose = (StartChoose) ev;
3127: StartWhen startWhen = startChoose.firstChoice;
3128: while (startWhen != null) {
3129: Object val;
3130: try {
3131: val = getValue(startWhen.test, jexlContext,
3132: jxpathContext, Boolean.TRUE);
3133: } catch (Exception e) {
3134: throw new JXTException(e.getMessage(),
3135: ev.location, e);
3136: }
3137: boolean result;
3138: if (val instanceof Boolean) {
3139: result = ((Boolean) val).booleanValue();
3140: } else {
3141: result = (val != null);
3142: }
3143: if (result) {
3144: execute(consumer, jexlContext, jxpathContext,
3145: macroCall, startWhen.next,
3146: startWhen.endInstruction);
3147: break;
3148: }
3149: startWhen = startWhen.nextChoice;
3150: }
3151: if (startWhen == null && startChoose.otherwise != null) {
3152: execute(consumer, jexlContext, jxpathContext,
3153: macroCall, startChoose.otherwise.next,
3154: startChoose.otherwise.endInstruction);
3155: }
3156: ev = startChoose.endInstruction.next;
3157: continue;
3158: } else if (ev instanceof StartSet) {
3159: StartSet startSet = (StartSet) ev;
3160: Object value = null;
3161: String var = null;
3162: try {
3163: if (startSet.var != null) {
3164: var = getStringValue(startSet.var, jexlContext,
3165: jxpathContext);
3166: }
3167: if (startSet.value != null) {
3168: value = getNode(startSet.value, jexlContext,
3169: jxpathContext);
3170: }
3171: } catch (Exception exc) {
3172: throw new JXTException(exc.getMessage(),
3173: ev.location, exc);
3174: }
3175: if (value == null) {
3176: NodeList nodeList = toDOMNodeList("set", startSet,
3177: jexlContext, macroCall);
3178: // JXPath doesn't handle NodeList, so convert it to an array
3179: int len = nodeList.getLength();
3180: Node[] nodeArr = new Node[len];
3181: for (int i = 0; i < len; i++) {
3182: nodeArr[i] = nodeList.item(i);
3183: }
3184: value = nodeArr;
3185: }
3186: if (var != null) {
3187: jxpathContext.getVariables().declareVariable(var,
3188: value);
3189: jexlContext.put(var, value);
3190: }
3191: ev = startSet.endInstruction.next;
3192: continue;
3193: } else if (ev instanceof StartElement) {
3194: StartElement startElement = (StartElement) ev;
3195: StartDefine def = (StartDefine) definitions
3196: .get(startElement.qname);
3197: if (def != null) {
3198: Map attributeMap = new HashMap();
3199: Iterator i = startElement.attributeEvents
3200: .iterator();
3201: while (i.hasNext()) {
3202: String attributeName;
3203: Object attributeValue;
3204: AttributeEvent attrEvent = (AttributeEvent) i
3205: .next();
3206: attributeName = attrEvent.localName;
3207: if (attrEvent instanceof CopyAttribute) {
3208: CopyAttribute copy = (CopyAttribute) attrEvent;
3209: attributeValue = copy.value;
3210: } else if (attrEvent instanceof SubstituteAttribute) {
3211: SubstituteAttribute substEvent = (SubstituteAttribute) attrEvent;
3212: if (substEvent.substitutions.size() == 1
3213: && substEvent.substitutions.get(0) instanceof JXTExpression) {
3214: JXTExpression expr = (JXTExpression) substEvent.substitutions
3215: .get(0);
3216: Object val;
3217: try {
3218: val = getNode(expr, jexlContext,
3219: jxpathContext);
3220: } catch (Exception e) {
3221: throw new JXTException(e
3222: .getMessage(), ev.location,
3223: e);
3224: }
3225: attributeValue = val != null ? val : "";
3226: } else {
3227: StringBuffer buf = new StringBuffer();
3228: Iterator iterSubst = substEvent.substitutions
3229: .iterator();
3230: while (iterSubst.hasNext()) {
3231: Subst subst = (Subst) iterSubst
3232: .next();
3233: if (subst instanceof Literal) {
3234: Literal lit = (Literal) subst;
3235: buf.append(lit.value);
3236: } else if (subst instanceof JXTExpression) {
3237: JXTExpression expr = (JXTExpression) subst;
3238: Object val;
3239: try {
3240: val = getValue(expr,
3241: jexlContext,
3242: jxpathContext);
3243: } catch (Exception e) {
3244: throw new JXTException(e
3245: .getMessage(),
3246: ev.location, e);
3247: }
3248: buf.append(val != null ? val
3249: .toString() : "");
3250: }
3251: }
3252: attributeValue = buf.toString();
3253: }
3254: } else {
3255: throw new Error(
3256: "this shouldn't have happened");
3257: }
3258: attributeMap.put(attributeName, attributeValue);
3259: }
3260: MyVariables parent = (MyVariables) jxpathContext
3261: .getVariables();
3262: MyVariables vars = new MyVariables(parent);
3263: MyJexlContext localJexlContext = new MyJexlContext(
3264: jexlContext);
3265: HashMap macro = new HashMap();
3266: macro.put("body", startElement);
3267: macro.put("arguments", attributeMap);
3268: localJexlContext.put("macro", macro);
3269: vars.declareVariable("macro", macro);
3270: Iterator iter = def.parameters.entrySet()
3271: .iterator();
3272: while (iter.hasNext()) {
3273: Map.Entry e = (Map.Entry) iter.next();
3274: String key = (String) e.getKey();
3275: StartParameter startParam = (StartParameter) e
3276: .getValue();
3277: Object default_ = startParam.default_;
3278: Object val = attributeMap.get(key);
3279: if (val == null) {
3280: val = default_;
3281: }
3282: localJexlContext.put(key, val);
3283: vars.declareVariable(key, val);
3284: }
3285: JXPathContext localJXPathContext = jxpathContextFactory
3286: .newContext(null, jxpathContext
3287: .getContextBean());
3288: localJXPathContext
3289: .setNamespaceContextPointer(new NamespacesTablePointer(
3290: namespaces));
3291: localJXPathContext.setVariables(vars);
3292: call(ev.location, startElement, consumer,
3293: localJexlContext, localJXPathContext,
3294: def.body, def.endInstruction);
3295: ev = startElement.endElement.next;
3296: continue;
3297: }
3298: Iterator i = startElement.attributeEvents.iterator();
3299: AttributesImpl attrs = new AttributesImpl();
3300: while (i.hasNext()) {
3301: AttributeEvent attrEvent = (AttributeEvent) i
3302: .next();
3303: if (attrEvent instanceof CopyAttribute) {
3304: CopyAttribute copy = (CopyAttribute) attrEvent;
3305: attrs.addAttribute(copy.namespaceURI,
3306: copy.localName, copy.raw, copy.type,
3307: copy.value);
3308: } else if (attrEvent instanceof SubstituteAttribute) {
3309: StringBuffer buf = new StringBuffer();
3310: SubstituteAttribute substEvent = (SubstituteAttribute) attrEvent;
3311: Iterator iterSubst = substEvent.substitutions
3312: .iterator();
3313: while (iterSubst.hasNext()) {
3314: Subst subst = (Subst) iterSubst.next();
3315: if (subst instanceof Literal) {
3316: Literal lit = (Literal) subst;
3317: buf.append(lit.value);
3318: } else if (subst instanceof JXTExpression) {
3319: JXTExpression expr = (JXTExpression) subst;
3320: Object val;
3321: try {
3322: val = getValue(expr, jexlContext,
3323: jxpathContext);
3324: } catch (Exception e) {
3325: throw new JXTException(e
3326: .getMessage(), ev.location,
3327: e);
3328: }
3329: buf.append(val != null ? val.toString()
3330: : "");
3331: }
3332: }
3333: attrs.addAttribute(attrEvent.namespaceURI,
3334: attrEvent.localName, attrEvent.raw,
3335: attrEvent.type, buf.toString());
3336: }
3337: }
3338: namespaces.enterScope(consumer);
3339: consumer
3340: .startElement(startElement.namespaceURI,
3341: startElement.localName,
3342: startElement.raw, attrs);
3343: } else if (ev instanceof StartFormatNumber) {
3344: StartFormatNumber startFormatNumber = (StartFormatNumber) ev;
3345: try {
3346: String result = startFormatNumber.format(
3347: jexlContext, jxpathContext);
3348: if (result != null) {
3349: char[] chars = result.toCharArray();
3350: consumer.characters(chars, 0, chars.length);
3351: }
3352: } catch (Exception e) {
3353: throw new JXTException(e.getMessage(), ev.location,
3354: e);
3355: }
3356: } else if (ev instanceof StartFormatDate) {
3357: StartFormatDate startFormatDate = (StartFormatDate) ev;
3358: try {
3359: String result = startFormatDate.format(jexlContext,
3360: jxpathContext);
3361: if (result != null) {
3362: char[] chars = result.toCharArray();
3363: consumer.characters(chars, 0, chars.length);
3364: }
3365: } catch (Exception e) {
3366: throw new JXTException(e.getMessage(), ev.location,
3367: e);
3368: }
3369: } else if (ev instanceof StartPrefixMapping) {
3370: StartPrefixMapping startPrefixMapping = (StartPrefixMapping) ev;
3371: namespaces.addDeclaration(startPrefixMapping.prefix,
3372: startPrefixMapping.uri);
3373: } else if (ev instanceof StartComment) {
3374: StartComment startJXComment = (StartComment) ev;
3375: // Parse the body of the comment
3376: NodeList nodeList = toDOMNodeList("comment",
3377: startJXComment, jexlContext, macroCall);
3378: // JXPath doesn't handle NodeList, so convert it to an array
3379: int len = nodeList.getLength();
3380: final StringBuffer buf = new StringBuffer();
3381: Properties omit = XMLUtils.createPropertiesForXML(true);
3382: for (int i = 0; i < len; i++) {
3383: try {
3384: buf.append(XMLUtils.serializeNode(nodeList
3385: .item(i), omit));
3386: } catch (Exception e) {
3387: throw new JXTException(e.getMessage(),
3388: startJXComment.location, e);
3389: }
3390: }
3391: char[] chars = new char[buf.length()];
3392: buf.getChars(0, chars.length, chars, 0);
3393: consumer.comment(chars, 0, chars.length);
3394: ev = startJXComment.endInstruction.next;
3395: continue;
3396: } else if (ev instanceof EndCDATA) {
3397: consumer.endCDATA();
3398: } else if (ev instanceof EndDTD) {
3399: consumer.endDTD();
3400: } else if (ev instanceof EndEntity) {
3401: consumer.endEntity(((EndEntity) ev).name);
3402: } else if (ev instanceof StartCDATA) {
3403: consumer.startCDATA();
3404: } else if (ev instanceof StartDTD) {
3405: StartDTD startDTD = (StartDTD) ev;
3406: consumer.startDTD(startDTD.name, startDTD.publicId,
3407: startDTD.systemId);
3408: } else if (ev instanceof StartEntity) {
3409: consumer.startEntity(((StartEntity) ev).name);
3410: } else if (ev instanceof StartOut) {
3411: StartOut startOut = (StartOut) ev;
3412: Object val;
3413: try {
3414: val = getNode(startOut.compiledExpression,
3415: jexlContext, jxpathContext,
3416: startOut.lenient);
3417: if (val instanceof Node) {
3418: executeDOM(consumer, jexlContext,
3419: jxpathContext, (Node) val);
3420: } else if (val instanceof NodeList) {
3421: NodeList nodeList = (NodeList) val;
3422: int len = nodeList.getLength();
3423: for (int i = 0; i < len; i++) {
3424: Node n = nodeList.item(i);
3425: executeDOM(consumer, jexlContext,
3426: jxpathContext, n);
3427: }
3428: } else if (val instanceof Node[]) {
3429: Node[] nodeList = (Node[]) val;
3430: int len = nodeList.length;
3431: for (int i = 0; i < len; i++) {
3432: Node n = nodeList[i];
3433: executeDOM(consumer, jexlContext,
3434: jxpathContext, n);
3435: }
3436: } else if (val instanceof XMLizable) {
3437: ((XMLizable) val).toSAX(new IncludeXMLConsumer(
3438: consumer));
3439: } else {
3440: char[] ch = val == null ? ArrayUtils.EMPTY_CHAR_ARRAY
3441: : val.toString().toCharArray();
3442: consumer.characters(ch, 0, ch.length);
3443: }
3444: } catch (Exception e) {
3445: throw new JXTException(e.getMessage(), ev.location,
3446: e);
3447: }
3448: } else if (ev instanceof StartTemplate) {
3449: // EMPTY
3450: } else if (ev instanceof StartEval) {
3451: StartEval startEval = (StartEval) ev;
3452: JXTExpression expr = startEval.value;
3453: try {
3454: Object val = getNode(expr, jexlContext,
3455: jxpathContext);
3456: if (!(val instanceof StartElement)) {
3457: throw new Exception(
3458: "macro invocation required instead of: "
3459: + val);
3460: }
3461: StartElement call = (StartElement) val;
3462: execute(consumer, jexlContext, jxpathContext, call,
3463: call.next, call.endElement);
3464: } catch (Exception exc) {
3465: throw new JXTException(exc.getMessage(),
3466: ev.location, exc);
3467: }
3468: ev = startEval.endInstruction.next;
3469: continue;
3470: } else if (ev instanceof StartEvalBody) {
3471: StartEvalBody startEval = (StartEvalBody) ev;
3472: try {
3473: execute(consumer, jexlContext, jxpathContext, null,
3474: macroCall.next, macroCall.endElement);
3475: } catch (Exception exc) {
3476: throw new JXTException(exc.getMessage(),
3477: ev.location, exc);
3478: }
3479: ev = startEval.endInstruction.next;
3480: continue;
3481: } else if (ev instanceof StartDefine) {
3482: StartDefine startDefine = (StartDefine) ev;
3483: definitions.put(startDefine.qname, startDefine);
3484: ev = startDefine.endInstruction.next;
3485: continue;
3486: } else if (ev instanceof StartImport) {
3487: StartImport startImport = (StartImport) ev;
3488: String uri;
3489: AttributeEvent e = startImport.uri;
3490: if (e instanceof CopyAttribute) {
3491: CopyAttribute copy = (CopyAttribute) e;
3492: uri = copy.value;
3493: } else {
3494: StringBuffer buf = new StringBuffer();
3495: SubstituteAttribute substAttr = (SubstituteAttribute) e;
3496: Iterator i = substAttr.substitutions.iterator();
3497: while (i.hasNext()) {
3498: Subst subst = (Subst) i.next();
3499: if (subst instanceof Literal) {
3500: Literal lit = (Literal) subst;
3501: buf.append(lit.value);
3502: } else if (subst instanceof JXTExpression) {
3503: JXTExpression expr = (JXTExpression) subst;
3504: Object val;
3505: try {
3506: val = getValue(expr, jexlContext,
3507: jxpathContext);
3508: } catch (Exception exc) {
3509: throw new JXTException(
3510: exc.getMessage(), ev.location,
3511: exc);
3512: }
3513: buf.append(val != null ? val.toString()
3514: : "");
3515: }
3516: }
3517: uri = buf.toString();
3518: }
3519: Source input = null;
3520: StartDocument doc;
3521: try {
3522: input = resolver.resolveURI(uri);
3523: SourceValidity validity = null;
3524: synchronized (cache) {
3525: doc = (StartDocument) cache.get(input.getURI());
3526: if (doc != null) {
3527: boolean recompile = false;
3528: if (doc.compileTime == null) {
3529: recompile = true;
3530: } else {
3531: int valid = doc.compileTime.isValid();
3532: if (valid == SourceValidity.UNKNOWN) {
3533: validity = input.getValidity();
3534: valid = doc.compileTime
3535: .isValid(validity);
3536: }
3537: if (valid != SourceValidity.VALID) {
3538: recompile = true;
3539: }
3540: }
3541: if (recompile) {
3542: doc = null; // recompile
3543: }
3544: }
3545: }
3546: if (doc == null) {
3547: Parser parser = new Parser();
3548: // call getValidity before using the stream is faster if the source is a SitemapSource
3549: if (validity == null) {
3550: validity = input.getValidity();
3551: }
3552: SourceUtil.parse(this .manager, input, parser);
3553: doc = parser.getStartEvent();
3554: doc.compileTime = validity;
3555: synchronized (cache) {
3556: cache.put(input.getURI(), doc);
3557: }
3558: }
3559: } catch (Exception exc) {
3560: throw new JXTException(exc.getMessage(),
3561: ev.location, exc);
3562: } finally {
3563: resolver.release(input);
3564: }
3565: JXPathContext selectJXPath = jxpathContext;
3566: MyJexlContext selectJexl = jexlContext;
3567: if (startImport.select != null) {
3568: try {
3569: Object obj = getValue(startImport.select,
3570: jexlContext, jxpathContext);
3571: selectJXPath = jxpathContextFactory.newContext(
3572: null, obj);
3573: selectJXPath
3574: .setNamespaceContextPointer(new NamespacesTablePointer(
3575: namespaces));
3576: selectJXPath.setVariables(variables);
3577: selectJexl = new MyJexlContext(jexlContext);
3578: fillContext(obj, selectJexl);
3579: } catch (Exception exc) {
3580: throw new JXTException(exc.getMessage(),
3581: ev.location, exc);
3582: }
3583: }
3584: try {
3585: execute(consumer, selectJexl, selectJXPath,
3586: macroCall, doc.next, doc.endDocument);
3587: } catch (Exception exc) {
3588: throw new JXTException(
3589: "Exception occurred in imported template "
3590: + uri + ": " + exc.getMessage(),
3591: ev.location, exc);
3592: }
3593: ev = startImport.endInstruction.next;
3594: continue;
3595: } else if (ev instanceof StartDocument) {
3596: if (((StartDocument) ev).endDocument != null) {
3597: // if this isn't a document fragment
3598: consumer.startDocument();
3599: }
3600: } else if (ev instanceof EndDocument) {
3601: consumer.endDocument();
3602: } else if (ev instanceof ProcessingInstruction) {
3603: ProcessingInstruction pi = (ProcessingInstruction) ev;
3604: consumer.processingInstruction(pi.target, pi.data);
3605: }
3606: ev = ev.next;
3607: }
3608: }
3609:
3610: /* (non-Javadoc)
3611: * @see org.apache.cocoon.caching.CacheableProcessingComponent#getKey()
3612: */
3613: public Serializable getKey() {
3614: JXTExpression cacheKeyExpr = (JXTExpression) getCurrentTemplateProperty(CACHE_KEY);
3615: try {
3616: final Serializable templateKey = (Serializable) getValue(
3617: cacheKeyExpr, globalJexlContext, jxpathContext);
3618: if (templateKey != null) {
3619: return new JXCacheKey(this .inputSource.getURI(),
3620: templateKey);
3621: }
3622: } catch (Exception e) {
3623: getLogger().error("error evaluating cache key", e);
3624: }
3625: return null;
3626: }
3627:
3628: /* (non-Javadoc)
3629: * @see org.apache.cocoon.caching.CacheableProcessingComponent#getValidity()
3630: */
3631: public SourceValidity getValidity() {
3632: JXTExpression validityExpr = (JXTExpression) getCurrentTemplateProperty(VALIDITY);
3633: try {
3634: final SourceValidity sourceValidity = this .inputSource
3635: .getValidity();
3636: final SourceValidity templateValidity = (SourceValidity) getValue(
3637: validityExpr, globalJexlContext, jxpathContext);
3638: if (sourceValidity != null && templateValidity != null) {
3639: return new JXSourceValidity(sourceValidity,
3640: templateValidity);
3641: }
3642: } catch (Exception e) {
3643: getLogger().error("error evaluating cache validity", e);
3644: }
3645: return null;
3646: }
3647:
3648: private Object getCurrentTemplateProperty(String propertyName) {
3649: final String uri = this .inputSource.getURI();
3650: StartDocument startEvent;
3651: synchronized (cache) {
3652: startEvent = (StartDocument) cache.get(uri);
3653: }
3654: return (startEvent != null) ? startEvent.templateProperties
3655: .get(propertyName) : null;
3656: }
3657:
3658: private NodeList toDOMNodeList(String elementName,
3659: StartInstruction si, MyJexlContext jexlContext,
3660: StartElement macroCall) throws SAXException {
3661: DOMBuilder builder = new DOMBuilder();
3662: builder.startDocument();
3663: builder.startElement(NS, elementName, elementName, EMPTY_ATTRS);
3664: execute(builder, jexlContext, jxpathContext, macroCall,
3665: si.next, si.endInstruction);
3666: builder.endElement(NS, elementName, elementName);
3667: builder.endDocument();
3668: Node node = builder.getDocument().getDocumentElement();
3669: return node.getChildNodes();
3670: }
3671:
3672: static final class JXCacheKey implements Serializable {
3673: private final String templateUri;
3674: private final Serializable templateKey;
3675:
3676: private JXCacheKey(String templateUri, Serializable templateKey) {
3677: this .templateUri = templateUri;
3678: this .templateKey = templateKey;
3679: }
3680:
3681: public int hashCode() {
3682: return templateUri.hashCode() + templateKey.hashCode();
3683: }
3684:
3685: public String toString() {
3686: return "TK:" + templateUri + "_" + templateKey;
3687: }
3688:
3689: public boolean equals(Object o) {
3690: if (o instanceof JXCacheKey) {
3691: JXCacheKey jxck = (JXCacheKey) o;
3692: return this .templateUri.equals(jxck.templateUri)
3693: && this .templateKey.equals(jxck.templateKey);
3694: }
3695: return false;
3696: }
3697: }
3698:
3699: static final class JXSourceValidity implements SourceValidity,
3700: Serializable {
3701: private final SourceValidity sourceValidity;
3702: private final SourceValidity templateValidity;
3703:
3704: private JXSourceValidity(SourceValidity sourceValidity,
3705: SourceValidity templateValidity) {
3706: this .sourceValidity = sourceValidity;
3707: this .templateValidity = templateValidity;
3708: }
3709:
3710: public int isValid() {
3711: switch (sourceValidity.isValid()) {
3712: case SourceValidity.INVALID:
3713: return SourceValidity.INVALID;
3714:
3715: case SourceValidity.UNKNOWN:
3716: if (templateValidity.isValid() == SourceValidity.INVALID) {
3717: return SourceValidity.INVALID;
3718: }
3719: return SourceValidity.UNKNOWN;
3720:
3721: case SourceValidity.VALID:
3722: return templateValidity.isValid();
3723: }
3724:
3725: return SourceValidity.UNKNOWN;
3726: }
3727:
3728: public int isValid(SourceValidity otherValidity) {
3729: if (otherValidity instanceof JXSourceValidity) {
3730: JXSourceValidity otherJXValidity = (JXSourceValidity) otherValidity;
3731: switch (sourceValidity
3732: .isValid(otherJXValidity.sourceValidity)) {
3733: case SourceValidity.INVALID:
3734: return SourceValidity.INVALID;
3735:
3736: case SourceValidity.UNKNOWN:
3737: if (templateValidity
3738: .isValid(otherJXValidity.templateValidity) == SourceValidity.INVALID) {
3739: return SourceValidity.INVALID;
3740: }
3741: return SourceValidity.UNKNOWN;
3742:
3743: case SourceValidity.VALID:
3744: return templateValidity
3745: .isValid(otherJXValidity.templateValidity);
3746: }
3747: }
3748: return SourceValidity.UNKNOWN;
3749: }
3750: }
3751: }
|