1 16 package org.apache.cocoon.generation; 17 18 import java.beans.PropertyDescriptor ; 19 import java.io.CharArrayReader ; 20 import java.io.IOException ; 21 import java.io.PrintStream ; 22 import java.io.PrintWriter ; 23 import java.io.Serializable ; 24 import java.io.StringReader ; 25 import java.lang.reflect.Field ; 26 import java.lang.reflect.InvocationTargetException ; 27 import java.lang.reflect.Method ; 28 import java.text.DateFormat ; 29 import java.text.DecimalFormat ; 30 import java.text.DecimalFormatSymbols ; 31 import java.text.NumberFormat ; 32 import java.text.SimpleDateFormat ; 33 import java.util.Enumeration ; 34 import java.util.HashMap ; 35 import java.util.Iterator ; 36 import java.util.LinkedList ; 37 import java.util.List ; 38 import java.util.Locale ; 39 import java.util.Map ; 40 import java.util.Properties ; 41 import java.util.Stack ; 42 import java.util.TimeZone ; 43 44 import org.apache.avalon.framework.parameters.Parameters; 45 import org.apache.avalon.framework.service.ServiceException; 46 import org.apache.avalon.framework.service.ServiceManager; 47 import org.apache.cocoon.ProcessingException; 48 import org.apache.cocoon.caching.CacheableProcessingComponent; 49 import org.apache.cocoon.components.flow.FlowHelper; 50 import org.apache.cocoon.components.flow.WebContinuation; 51 import org.apache.cocoon.components.flow.javascript.fom.FOM_JavaScriptFlowHelper; 52 import org.apache.cocoon.components.source.SourceUtil; 53 import org.apache.cocoon.environment.ObjectModelHelper; 54 import org.apache.cocoon.environment.Request; 55 import org.apache.cocoon.environment.SourceResolver; 56 import org.apache.cocoon.transformation.ServiceableTransformer; 57 import org.apache.cocoon.util.jxpath.NamespacesTablePointer; 58 import org.apache.cocoon.util.location.LocatedRuntimeException; 59 import org.apache.cocoon.util.location.Location; 60 import org.apache.cocoon.util.location.LocationUtils; 61 import org.apache.cocoon.xml.IncludeXMLConsumer; 62 import org.apache.cocoon.xml.NamespacesTable; 63 import org.apache.cocoon.xml.RedundantNamespacesFilter; 64 import org.apache.cocoon.xml.XMLConsumer; 65 import org.apache.cocoon.xml.XMLUtils; 66 import org.apache.cocoon.xml.dom.DOMBuilder; 67 import org.apache.cocoon.xml.dom.DOMStreamer; 68 import org.apache.commons.jexl.Expression; 69 import org.apache.commons.jexl.ExpressionFactory; 70 import org.apache.commons.jexl.JexlContext; 71 import org.apache.commons.jexl.util.Introspector; 72 import org.apache.commons.jexl.util.introspection.Info; 73 import org.apache.commons.jexl.util.introspection.UberspectImpl; 74 import org.apache.commons.jexl.util.introspection.VelMethod; 75 import org.apache.commons.jexl.util.introspection.VelPropertyGet; 76 import org.apache.commons.jexl.util.introspection.VelPropertySet; 77 import org.apache.commons.jxpath.*; 78 import org.apache.commons.lang.ArrayUtils; 79 import org.apache.commons.lang.StringUtils; 80 import org.apache.excalibur.source.Source; 81 import org.apache.excalibur.source.SourceException; 82 import org.apache.excalibur.source.SourceValidity; 83 import org.apache.excalibur.xml.sax.XMLizable; 84 import org.mozilla.javascript.Context; 85 import org.mozilla.javascript.Function; 86 import org.mozilla.javascript.JavaScriptException; 87 import org.mozilla.javascript.NativeArray; 88 import org.mozilla.javascript.NativeJavaClass; 89 import org.mozilla.javascript.ScriptRuntime; 90 import org.mozilla.javascript.Scriptable; 91 import org.mozilla.javascript.ScriptableObject; 92 import org.mozilla.javascript.Undefined; 93 import org.mozilla.javascript.Wrapper; 94 import org.w3c.dom.Node ; 95 import org.w3c.dom.NodeList ; 96 import org.xml.sax.Attributes ; 97 import org.xml.sax.ContentHandler ; 98 import org.xml.sax.Locator ; 99 import org.xml.sax.SAXException ; 100 import org.xml.sax.ext.LexicalHandler ; 101 import org.xml.sax.helpers.AttributesImpl ; 102 103 116 public class JXTemplateGenerator extends ServiceableGenerator implements CacheableProcessingComponent { 117 118 private static final class JXTException extends LocatedRuntimeException { 120 JXTException(String message, Location loc, Throwable thr) { 121 super(message, thr, loc); 122 } 123 } 124 125 private static final JXPathContextFactory jxpathContextFactory = JXPathContextFactory.newInstance(); 126 127 private static final Attributes EMPTY_ATTRS = XMLUtils.EMPTY_ATTRIBUTES; 128 129 private final NamespacesTable namespaces = new NamespacesTable(); 130 131 private static final Iterator EMPTY_ITER = new Iterator () { 132 public boolean hasNext() { 133 return false; 134 } 135 136 public Object next() { 137 return null; 138 } 139 140 public void remove() { 141 } 143 }; 144 145 private static final Iterator NULL_ITER = new Iterator () { 146 public boolean hasNext() { 147 return true; 148 } 149 150 public Object next() { 151 return null; 152 } 153 154 public void remove() { 155 } 157 }; 158 159 private XMLConsumer getConsumer() { 160 return this.xmlConsumer; 161 } 162 163 public static class ErrorHolder extends Exception { 164 165 private Error err; 166 167 public ErrorHolder(Error err) { 168 super(err.getMessage()); 169 this.err = err; 170 } 171 172 public void printStackTrace(PrintStream ps) { 173 err.printStackTrace(ps); 174 } 175 176 public void printStackTrace(PrintWriter pw) { 177 err.printStackTrace(pw); 178 } 179 180 public void printStackTrace() { 181 err.printStackTrace(); 182 } 183 184 public Error getError() { 185 return err; 186 } 187 188 } 189 190 194 public static class LocationFacade implements Locator { 195 private Location locator; 196 197 public LocationFacade(Location initialLocation) { 198 this.locator = initialLocation; 199 } 200 201 public void setDocumentLocation(Location newLocation) { 202 this.locator = newLocation; 203 } 204 205 public int getColumnNumber() { 206 return this.locator.getColumnNumber(); 207 } 208 209 public int getLineNumber() { 210 return this.locator.getLineNumber(); 211 } 212 213 public String getPublicId() { 214 return null; 215 } 216 217 public String getSystemId() { 218 return this.locator.getURI(); 219 } 220 } 221 222 226 static class JSIntrospector extends UberspectImpl { 227 228 static class JSMethod implements VelMethod { 229 230 Scriptable scope; 231 String name; 232 233 public JSMethod(Scriptable scope, String name) { 234 this.scope = scope; 235 this.name = name; 236 } 237 238 public Object invoke(Object thisArg, Object [] args) throws Exception { 239 Context cx = Context.enter(); 240 try { 241 Object result; 242 Scriptable thisObj = !(thisArg instanceof Scriptable) ? 243 Context.toObject(thisArg, scope) : (Scriptable)thisArg; 244 result = ScriptableObject.getProperty(thisObj, name); 245 Object [] newArgs = null; 246 if (args != null) { 247 newArgs = new Object [args.length]; 248 int len = args.length; 249 for (int i = 0; i < len; i++) { 250 newArgs[i] = args[i]; 251 if (args[i] != null && 252 !(args[i] instanceof Number ) && 253 !(args[i] instanceof Boolean ) && 254 !(args[i] instanceof String ) && 255 !(args[i] instanceof Scriptable)) { 256 newArgs[i] = Context.toObject(args[i], scope); 257 } 258 } 259 } 260 result = ScriptRuntime.call(cx, result, thisObj, newArgs, scope); 261 if (result == Undefined.instance || result == Scriptable.NOT_FOUND) { 262 result = null; 263 } else if (!(result instanceof NativeJavaClass)) { 264 while (result instanceof Wrapper) { 265 result = ((Wrapper)result).unwrap(); 266 } 267 } 268 return result; 269 } catch (JavaScriptException e) { 270 throw new java.lang.reflect.InvocationTargetException (e); 271 } finally { 272 Context.exit(); 273 } 274 } 275 276 public boolean isCacheable() { 277 return false; 278 } 279 280 public String getMethodName() { 281 return name; 282 } 283 284 public Class getReturnType() { 285 return Object .class; 286 } 287 288 } 289 290 static class JSPropertyGet implements VelPropertyGet { 291 292 Scriptable scope; 293 String name; 294 295 public JSPropertyGet(Scriptable scope, String name) { 296 this.scope = scope; 297 this.name = name; 298 } 299 300 public Object invoke(Object thisArg) throws Exception { 301 Context cx = Context.enter(); 302 try { 303 Scriptable thisObj = !(thisArg instanceof Scriptable) ? 304 Context.toObject(thisArg, scope) : (Scriptable)thisArg; 305 Object result = ScriptableObject.getProperty(thisObj, name); 306 if (result == Scriptable.NOT_FOUND) { 307 result = ScriptableObject.getProperty(thisObj, "get" + StringUtils.capitalize(name)); 308 if (result != Scriptable.NOT_FOUND && result instanceof Function) { 309 try { 310 result = ((Function)result).call( 311 cx, ScriptableObject.getTopLevelScope(thisObj), thisObj, new Object [] {}); 312 } catch (JavaScriptException exc) { 313 exc.printStackTrace(); 314 result = null; 315 } 316 } 317 } 318 if (result == Scriptable.NOT_FOUND || result == Undefined.instance) { 319 result = null; 320 } else if (result instanceof Wrapper && !(result instanceof NativeJavaClass)) { 321 result = ((Wrapper)result).unwrap(); 322 } 323 return result; 324 } finally { 325 Context.exit(); 326 } 327 } 328 329 public boolean isCacheable() { 330 return false; 331 } 332 333 public String getMethodName() { 334 return name; 335 } 336 } 337 338 static class JSPropertySet implements VelPropertySet { 339 340 Scriptable scope; 341 String name; 342 343 public JSPropertySet(Scriptable scope, String name) { 344 this.scope = scope; 345 this.name = name; 346 } 347 348 public Object invoke(Object thisArg, Object rhs) throws Exception { 349 Context.enter(); 350 try { 351 Scriptable thisObj; 352 Object arg = rhs; 353 if (!(thisArg instanceof Scriptable)) { 354 thisObj = Context.toObject(thisArg, scope); 355 } else { 356 thisObj = (Scriptable)thisArg; 357 } 358 if (arg != null && 359 !(arg instanceof Number ) && 360 !(arg instanceof Boolean ) && 361 !(arg instanceof String ) && 362 !(arg instanceof Scriptable)) { 363 arg = Context.toObject(arg, scope); 364 } 365 ScriptableObject.putProperty(thisObj, name, arg); 366 return rhs; 367 } finally { 368 Context.exit(); 369 } 370 } 371 372 public boolean isCacheable() { 373 return false; 374 } 375 376 public String getMethodName() { 377 return name; 378 } 379 } 380 381 static class NativeArrayIterator implements Iterator { 382 383 NativeArray arr; 384 int index; 385 386 public NativeArrayIterator(NativeArray arr) { 387 this.arr = arr; 388 this.index = 0; 389 } 390 391 public boolean hasNext() { 392 return index < (int)arr.jsGet_length(); 393 } 394 395 public Object next() { 396 Context.enter(); 397 try { 398 Object result = arr.get(index++, arr); 399 if (result == Undefined.instance || 400 result == Scriptable.NOT_FOUND) { 401 result = null; 402 } else { 403 if (!(result instanceof NativeJavaClass)) { 404 while (result instanceof Wrapper) { 405 result = ((Wrapper)result).unwrap(); 406 } 407 } 408 } 409 return result; 410 } finally { 411 Context.exit(); 412 } 413 } 414 415 public void remove() { 416 arr.delete(index); 417 } 418 } 419 420 static class ScriptableIterator implements Iterator { 421 422 Scriptable scope; 423 Object [] ids; 424 int index; 425 426 public ScriptableIterator(Scriptable scope) { 427 this.scope = scope; 428 this.ids = scope.getIds(); 429 this.index = 0; 430 } 431 432 public boolean hasNext() { 433 return index < ids.length; 434 } 435 436 public Object next() { 437 Context.enter(); 438 try { 439 Object result = ScriptableObject.getProperty(scope, ids[index++].toString()); 440 if (result == Undefined.instance || result == Scriptable.NOT_FOUND) { 441 result = null; 442 } else if (!(result instanceof NativeJavaClass)) { 443 while (result instanceof Wrapper) { 444 result = ((Wrapper)result).unwrap(); 445 } 446 } 447 return result; 448 } finally { 449 Context.exit(); 450 } 451 } 452 453 public void remove() { 454 Context.enter(); 455 try { 456 scope.delete(ids[index].toString()); 457 } finally { 458 Context.exit(); 459 } 460 } 461 } 462 463 public Iterator getIterator(Object obj, Info i) throws Exception { 464 if (!(obj instanceof Scriptable)) { 465 if (obj instanceof Enumeration ) { 467 final Enumeration e = (Enumeration )obj; 468 return new Iterator () { 469 470 public boolean hasNext() { 471 return e.hasMoreElements(); 472 } 473 474 public Object next() { 475 return e.nextElement(); 476 } 477 478 public void remove() { 479 } 481 482 }; 483 } 484 if (obj instanceof Iterator ) { 485 return (Iterator )obj; 487 } 488 return super.getIterator(obj, i); 489 } 490 if (obj instanceof NativeArray) { 491 return new NativeArrayIterator((NativeArray)obj); 492 } 493 return new ScriptableIterator((Scriptable)obj); 494 } 495 496 public VelMethod getMethod(Object obj, String methodName, Object [] args, Info i) throws Exception { 497 return !(obj instanceof Scriptable) ? 498 super.getMethod(obj, methodName, args, i) : new JSMethod((Scriptable)obj, methodName); 499 } 500 501 public VelPropertyGet getPropertyGet(Object obj, String identifier, Info i) throws Exception { 502 return !(obj instanceof Scriptable) ? 503 super.getPropertyGet(obj, identifier, i) : new JSPropertyGet((Scriptable)obj, identifier); 504 } 505 506 public VelPropertySet getPropertySet(Object obj, String identifier, Object arg, Info i) throws Exception { 507 return !(obj instanceof Scriptable) ? 508 super.getPropertySet(obj, identifier, arg, i) : new JSPropertySet((Scriptable)obj, identifier); 509 } 510 } 511 512 static class MyJexlContext extends HashMap implements JexlContext { 513 514 private MyJexlContext closure; 515 516 MyJexlContext() { 517 this(null); 518 } 519 520 MyJexlContext(MyJexlContext closure) { 521 this.closure = closure; 522 } 523 524 public Map getVars() { 525 return this; 526 } 527 528 public void setVars(Map map) { 529 putAll(map); 530 } 531 532 public boolean containsKey(Object key) { 533 return this.get(key) !=null; 534 } 535 536 public Object get(Object key) { 537 if (key.equals("this")) { 538 return this; 539 } 540 Object result = super.get(key); 541 if (result == null && closure != null) { 542 result = closure.get(key); 543 } 544 return result; 545 } 546 } 547 548 static class MyVariables implements Variables { 549 550 MyVariables closure; 551 552 Map localVariables = new HashMap (); 553 554 static final String [] VARIABLES = new String [] { 555 "cocoon", 556 "continuation", 557 "flowContext", 558 "request", 559 "response", 560 "context", 561 "session", 562 "parameters" 563 }; 564 565 Object cocoon; 566 567 Object bean, kont, request, response, 569 session, context, parameters; 570 571 MyVariables(Object cocoon, 572 Object bean, 573 WebContinuation kont, 574 Object request, 575 Object session, 576 Object context, 577 Object parameters) { 578 this.cocoon = cocoon; 579 this.bean = bean; 580 this.kont = kont; 581 this.request = request; 582 this.session = session; 583 this.context = context; 584 this.parameters = parameters; 585 } 586 587 public MyVariables(MyVariables parent) { 588 this.closure = parent; 589 } 590 591 public boolean isDeclaredVariable(String varName) { 592 int len = VARIABLES.length; 593 for (int i = 0; i < len; i++) { 594 if (varName.equals(VARIABLES[i])) { 595 return true; 596 } 597 } 598 if (localVariables.containsKey(varName)) { 599 return true; 600 } 601 if (closure != null) { 602 return closure.isDeclaredVariable(varName); 603 } 604 return false; 605 } 606 607 public Object getVariable(String varName) { 608 Object result = localVariables.get(varName); 609 if (result != null) { 610 return result; 611 } 612 if (closure != null) { 613 return closure.getVariable(varName); 614 } 615 if (varName.equals("cocoon")) { 616 return cocoon; 617 } 618 if (varName.equals("continuation")) { 620 return kont; 621 } else if (varName.equals("flowContext")) { 622 return bean; 623 } else if (varName.equals("request")) { 624 return request; 625 } else if (varName.equals("session")) { 626 return session; 627 } else if (varName.equals("context")) { 628 return context; 629 } else if (varName.equals("parameters")) { 630 return parameters; 631 } 632 return null; 633 } 634 635 public void declareVariable(String varName, Object value) { 636 localVariables.put(varName, value); 637 } 638 639 public void undeclareVariable(String varName) { 640 localVariables.remove(varName); 641 } 642 } 643 644 static { 645 try { 647 Field field = Introspector.class.getDeclaredField("uberSpect"); 648 field.setAccessible(true); 649 field.set(null, new JSIntrospector()); 650 } catch (Exception e) { 651 e.printStackTrace(); 652 } 653 } 654 655 656 657 public final static String NS = "http://apache.org/cocoon/templates/jx/1.0"; 658 659 final static String TEMPLATE = "template"; 660 final static String FOR_EACH = "forEach"; 661 final static String IF = "if"; 662 final static String CHOOSE = "choose"; 663 final static String WHEN = "when"; 664 final static String OTHERWISE = "otherwise"; 665 final static String OUT = "out"; 666 final static String IMPORT = "import"; 667 final static String SET = "set"; 668 final static String MACRO = "macro"; 669 final static String EVALBODY = "evalBody"; 670 final static String EVAL = "eval"; 671 final static String PARAMETER = "parameter"; 672 final static String FORMAT_NUMBER = "formatNumber"; 673 final static String FORMAT_DATE = "formatDate"; 674 final static String COMMENT = "comment"; 675 final static String CACHE_KEY = "cache-key"; 676 final static String VALIDITY = "cache-validity"; 677 678 682 683 private static JXTExpression compileExpr(String expr, String errorPrefix, Location location) throws JXTException { 684 try { 685 return compileExpr(expr); 686 } catch (Exception exc) { 687 throw new JXTException(errorPrefix + exc.getMessage(), location, exc); 688 } 689 } 690 691 private static JXTExpression compileExpr(String inStr) throws Exception { 692 try { 693 if (inStr == null) { 694 return null; 695 } 696 StringReader in = new StringReader (inStr.trim()); 697 int ch; 698 boolean xpath = false; 699 boolean inExpr = false; 700 StringBuffer expr = new StringBuffer (); 701 while ((ch = in.read()) != -1) { 702 char c = (char)ch; 703 if (inExpr) { 704 if (c == '\\') { 705 ch = in.read(); 706 expr.append((ch == -1) ? '\\' : (char)ch); 707 } else if (c == '}') { 708 return compile(expr.toString(), xpath); 709 } else { 710 expr.append(c); 711 } 712 } else { 713 if (c == '$' || c == '#') { 714 ch = in.read(); 715 if (ch == '{') { 716 inExpr = true; 717 xpath = c == '#'; 718 continue; 719 } 720 } 721 return new JXTExpression(inStr, null); 724 } 725 } 726 if (inExpr) { 727 throw new Exception ("Unterminated " + (xpath ? "#" : "$") + "{"); 729 } 730 } catch (IOException ignored) { 731 ignored.printStackTrace(); 732 } 733 return new JXTExpression(inStr, null); 734 } 735 736 740 private static JXTExpression compileInt(String val, String msg, Location location) throws SAXException { 741 JXTExpression res = compileExpr(val, msg, location); 742 if (res != null) { 743 if (res.compiledExpression == null) { 744 res.compiledExpression = Integer.valueOf(res.raw); 745 } 746 return res; 747 } 748 return null; 749 } 750 751 private static JXTExpression compileBoolean(String val, String msg, Location location) throws SAXException { 752 JXTExpression res = compileExpr(val, msg, location); 753 if (res != null) { 754 if (res.compiledExpression == null) { 755 res.compiledExpression = Boolean.valueOf(res.raw); 756 } 757 return res; 758 } 759 return null; 760 } 761 762 private static JXTExpression compile(final String variable, boolean xpath) throws Exception { 763 Object compiled; 764 if (xpath) { 765 compiled = JXPathContext.compile(variable); 766 } else { 767 compiled = ExpressionFactory.createExpression(variable); 768 } 769 return new JXTExpression(variable, compiled); 770 } 771 772 static private Object getValue(JXTExpression expr, JexlContext jexlContext, 773 JXPathContext jxpathContext, Boolean lenient) throws Exception { 774 if (expr != null) { 775 Object compiled = expr.compiledExpression; 776 try { 777 if (compiled instanceof CompiledExpression) { 778 CompiledExpression e = (CompiledExpression)compiled; 779 boolean oldLenient = jxpathContext.isLenient(); 780 if (lenient != null) { 781 jxpathContext.setLenient(lenient.booleanValue()); 782 } 783 try { 784 return e.getValue(jxpathContext); 785 } finally { 786 jxpathContext.setLenient(oldLenient); 787 } 788 } else if (compiled instanceof Expression) { 789 Expression e = (Expression)compiled; 790 return e.evaluate(jexlContext); 791 } 792 return compiled; 793 } catch (InvocationTargetException e) { 794 Throwable t = e.getTargetException(); 795 if (t instanceof Exception ) { 796 throw (Exception )t; 797 } 798 throw (Error )t; 799 } 800 } else { 801 return null; 802 } 803 } 804 805 static private Object getValue(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception { 806 return getValue(expr, jexlContext, jxpathContext, null); 807 } 808 809 static private int getIntValue(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception { 810 Object res = getValue(expr, jexlContext, jxpathContext); 811 return res instanceof Number ? ((Number )res).intValue() : 0; 812 } 813 814 static private Number getNumberValue(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception { 815 Object res = getValue(expr, jexlContext, jxpathContext); 816 if (res instanceof Number ) { 817 return (Number )res; 818 } 819 if (res != null) { 820 return Double.valueOf(res.toString()); 821 } 822 return null; 823 } 824 825 static private String getStringValue(JXTExpression expr, JexlContext jexlContext, 826 JXPathContext jxpathContext) throws Exception { 827 Object res = getValue(expr, jexlContext, jxpathContext); 828 if (res != null) { 829 return res.toString(); 830 } 831 if (expr != null && expr.compiledExpression == null) { 832 return expr.raw; 833 } 834 return null; 835 } 836 837 static private Boolean getBooleanValue(JXTExpression expr, JexlContext jexlContext, 838 JXPathContext jxpathContext) throws Exception { 839 Object res = getValue(expr, jexlContext, jxpathContext); 840 return res instanceof Boolean ? (Boolean )res : null; 841 } 842 843 private Object getNode(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext) throws Exception { 844 return getNode(expr, jexlContext, jxpathContext, null); 845 } 846 847 private Object getNode(JXTExpression expr, JexlContext jexlContext, JXPathContext jxpathContext, Boolean lenient) 849 throws Exception { 850 try { 851 Object compiled = expr.compiledExpression; 852 if (compiled instanceof CompiledExpression) { 853 CompiledExpression e = (CompiledExpression)compiled; 854 boolean oldLenient = jxpathContext.isLenient(); 855 if (lenient != null) jxpathContext.setLenient(lenient.booleanValue()); 856 try { 857 Iterator iter = e.iteratePointers(jxpathContext); 858 if (iter.hasNext()) { 859 Pointer first = (Pointer)iter.next(); 860 if (iter.hasNext()) { 861 List result = new LinkedList (); 862 result.add(first.getNode()); 863 boolean dom = (first.getNode() instanceof Node ); 864 while (iter.hasNext()) { 865 Object obj = ((Pointer)iter.next()).getNode(); 866 dom = dom && (obj instanceof Node ); 867 result.add(obj); 868 } 869 Object [] arr; 870 if (dom) { 871 arr = new Node [result.size()]; 872 } else { 873 arr = new Object [result.size()]; 874 } 875 result.toArray(arr); 876 return arr; 877 } 878 return first.getNode(); 879 } 880 return null; 881 } finally { 882 jxpathContext.setLenient(oldLenient); 883 } 884 } else if (compiled instanceof Expression) { 885 Expression e = (Expression)compiled; 886 return e.evaluate(jexlContext); 887 } 888 return expr.raw; 889 } catch (InvocationTargetException e) { 890 Throwable t = e.getTargetException(); 891 if (t instanceof Exception ) { 892 throw (Exception )t; 893 } 894 throw (Error )t; 895 } 896 } 897 898 static class Event { 899 final Location location; 900 Event next; Event(Location locator) { 902 this.location = locator != null ? locator : Location.UNKNOWN; 903 } 904 905 public String locationString() { 906 return location.toString(); 907 } 908 } 909 910 static class TextEvent extends Event { 911 TextEvent(Location location, char[] chars, int start, int length) 912 throws SAXException { 913 super(location); 914 StringBuffer buf = new StringBuffer (); 915 this.raw = new char[length]; 916 System.arraycopy(chars, start, this.raw, 0, length); 917 CharArrayReader in = new CharArrayReader (chars, start, length); 918 int ch; 919 boolean inExpr = false; 920 boolean xpath = false; 921 try { 922 top: while ((ch = in.read()) != -1) { 923 char c = (char)ch; 925 processChar: while (true) { 926 if (inExpr) { 927 if (c == '\\') { 928 ch = in.read(); 929 buf.append(ch == -1 ? '\\' : (char)ch); 930 } else if (c == '}') { 931 String str = buf.toString(); 932 Object compiledExpression; 933 try { 934 if (xpath) { 935 compiledExpression = JXPathContext.compile(str); 936 } else { 937 compiledExpression = ExpressionFactory.createExpression(str); 938 } 939 } catch (Exception exc) { 940 throw new JXTException(exc.getMessage(), this.location, exc); 941 } 942 substitutions.add(new JXTExpression(str, compiledExpression)); 943 buf.setLength(0); 944 inExpr = false; 945 } else { 946 buf.append(c); 947 } 948 } else if (c == '$' || c == '#') { 949 ch = in.read(); 950 if (ch == '{') { 951 xpath = c == '#'; 952 inExpr = true; 953 if (buf.length() > 0) { 954 char[] charArray = new char[buf.length()]; 955 956 buf.getChars(0, buf.length(), charArray, 0); 957 substitutions.add(charArray); 958 buf.setLength(0); 959 } 960 continue top; 961 } 962 buf.append(c); 963 if (ch != -1) { 964 c = (char)ch; 965 continue processChar; 966 } 967 } else { 968 buf.append(c); 969 } 970 break; 971 } 972 } 973 } catch (IOException ignored) { 974 ignored.printStackTrace(); 976 } 977 if (inExpr) { 978 buf.insert(0, (xpath ? "#" : "$") + "{"); 980 } 981 if (buf.length() > 0) { 982 char[] charArray = new char[buf.length()]; 983 984 buf.getChars(0, buf.length(), charArray, 0); 985 substitutions.add(charArray); 986 } else if (substitutions.isEmpty()) { 987 substitutions.add(ArrayUtils.EMPTY_CHAR_ARRAY); 988 } 989 } 990 final List substitutions = new LinkedList (); 991 final char[] raw; 992 } 993 994 static class Characters extends TextEvent { 995 Characters(Location location, char[] chars, int start, int length) throws SAXException { 996 super(location, chars, start, length); 997 } 998 } 999 1000 static class StartDocument extends Event { 1001 StartDocument(Location location) { 1002 super(location); 1003 templateProperties = new HashMap (); 1004 } 1005 SourceValidity compileTime; 1006 EndDocument endDocument; Map templateProperties; 1008 } 1009 1010 static class EndDocument extends Event { 1011 EndDocument(Location location) { 1012 super(location); 1013 } 1014 } 1015 1016 static class EndElement extends Event { 1017 EndElement(Location location, StartElement startElement) { 1018 super(location); 1019 this.startElement = startElement; 1020 } 1021 final StartElement startElement; 1022 } 1023 1024 static class EndPrefixMapping extends Event { 1025 EndPrefixMapping(Location location, String prefix) { 1026 super(location); 1027 this.prefix = prefix; 1028 } 1029 final String prefix; 1030 } 1031 1032 static class IgnorableWhitespace extends TextEvent { 1033 IgnorableWhitespace(Location location, char[] chars, int start, int length) throws SAXException { 1034 super(location, chars, start, length); 1035 } 1036 } 1037 1038 static class ProcessingInstruction extends Event { 1039 ProcessingInstruction(Location location, String target, String data) { 1040 super(location); 1041 this.target = target; 1042 this.data = data; 1043 } 1044 final String target; 1045 final String data; 1046 } 1047 1048 static class SkippedEntity extends Event { 1049 SkippedEntity(Location location, String name) { 1050 super(location); 1051 this.name = name; 1052 } 1053 final String name; 1054 } 1055 1056 abstract static class AttributeEvent { 1057 AttributeEvent(String namespaceURI, String localName, String raw, String type) { 1058 this.namespaceURI = namespaceURI; 1059 this.localName = localName; 1060 this.raw = raw; 1061 this.type = type; 1062 } 1063 final String namespaceURI; 1064 final String localName; 1065 final String raw; 1066 final String type; 1067 } 1068 1069 static class CopyAttribute extends AttributeEvent { 1070 CopyAttribute(String namespaceURI, String localName, String raw, String type, String value) { 1071 super(namespaceURI, localName, raw, type); 1072 this.value = value; 1073 } 1074 final String value; 1075 } 1076 1077 static class Subst { 1078 } 1080 1081 static class Literal extends Subst { 1082 Literal(String val) { 1083 this.value = val; 1084 } 1085 final String value; 1086 } 1087 1088 static class JXTExpression extends Subst { 1089 JXTExpression(String raw, Object expr) { 1090 this.raw = raw; 1091 this.compiledExpression = expr; 1092 } 1093 String raw; 1094 Object compiledExpression; 1095 } 1096 1097 static class SubstituteAttribute extends AttributeEvent { 1098 SubstituteAttribute(String namespaceURI, String localName, String raw, String type, List substs) { 1099 super(namespaceURI, localName, raw, type); 1100 this.substitutions = substs; 1101 } 1102 final List substitutions; 1103 } 1104 1105 static class StartElement extends Event { 1106 StartElement(Location location, String namespaceURI, String localName, 1107 String raw, Attributes attrs) 1108 throws SAXException { 1109 super(location); 1110 1111 this.namespaceURI = namespaceURI; 1112 this.localName = localName; 1113 this.raw = raw; 1114 this.qname = "{" + namespaceURI + "}" + localName; 1115 1116 StringBuffer buf = new StringBuffer (); 1117 int len = attrs.getLength(); 1118 for (int i = 0; i < len; i++) { 1119 String uri = attrs.getURI(i); 1120 String local = attrs.getLocalName(i); 1121 String qname = attrs.getQName(i); 1122 String type = attrs.getType(i); 1123 String value = attrs.getValue(i); 1124 StringReader in = new StringReader (value); 1125 1126 int ch; 1127 buf.setLength(0); 1128 boolean inExpr = false; 1129 List substEvents = new LinkedList (); 1130 boolean xpath = false; 1131 try { 1132 top: while ((ch = in.read()) != -1) { 1133 char c = (char)ch; 1134 processChar: while (true) { 1135 if (inExpr) { 1136 if (c == '\\') { 1137 ch = in.read(); 1138 buf.append(ch == -1 ? '\\' : (char)ch); 1139 } else if (c == '}') { 1140 String str = buf.toString(); 1141 JXTExpression compiledExpression; 1142 try { 1143 compiledExpression = compile(str, xpath); 1144 } catch (Exception exc) { 1145 throw new JXTException(exc.getMessage(), location, exc); 1146 } 1147 substEvents.add(compiledExpression); 1148 buf.setLength(0); 1149 inExpr = false; 1150 } else { 1151 buf.append(c); 1152 } 1153 } else if (c == '$' || c == '#') { 1154 ch = in.read(); 1155 if (ch == '{') { 1156 if (buf.length() > 0) { 1157 substEvents.add(new Literal(buf.toString())); 1158 buf.setLength(0); 1159 } 1160 inExpr = true; 1161 xpath = c == '#'; 1162 continue top; 1163 } 1164 buf.append(c); 1165 if (ch != -1) { 1166 c = (char)ch; 1167 continue processChar; 1168 } 1169 } else { 1170 buf.append(c); 1171 } 1172 break; 1173 } 1174 } 1175 } catch (IOException ignored) { 1176 ignored.printStackTrace(); 1177 } 1178 1179 if (inExpr) { 1180 String msg = "Unterminated " + (xpath ? "#" : "$") + "{"; 1182 throw new JXTException(msg, location, null); 1183 } 1184 1185 if (buf.length() > 0) { 1186 if (substEvents.size() == 0) { 1187 attributeEvents.add(new CopyAttribute(uri, local, qname, type, buf.toString())); 1188 } else { 1189 substEvents.add(new Literal(buf.toString())); 1190 attributeEvents.add(new SubstituteAttribute(uri, local, qname, type, substEvents)); 1191 } 1192 } else { 1193 if (substEvents.size() > 0) { 1194 attributeEvents.add(new SubstituteAttribute(uri, local, qname, type, substEvents)); 1195 } else { 1196 attributeEvents.add(new CopyAttribute(uri, local, qname, type, "")); 1197 } 1198 } 1199 } 1200 this.attributes = new AttributesImpl (attrs); 1201 } 1202 final String namespaceURI; 1203 final String localName; 1204 final String raw; 1205 final String qname; 1206 final List attributeEvents = new LinkedList (); 1207 final Attributes attributes; 1208 EndElement endElement; 1209 } 1210 1211 static class StartPrefixMapping extends Event { 1212 StartPrefixMapping(Location location, String prefix, String uri) { 1213 super(location); 1214 this.prefix = prefix; 1215 this.uri = uri; 1216 } 1217 final String prefix; 1218 final String uri; 1219 } 1220 1221 static class EndCDATA extends Event { 1222 EndCDATA(Location location) { 1223 super(location); 1224 } 1225 } 1226 1227 static class EndDTD extends Event { 1228 EndDTD(Location location) { 1229 super(location); 1230 } 1231 } 1232 1233 static class EndEntity extends Event { 1234 EndEntity(Location location, String name) { 1235 super(location); 1236 this.name = name; 1237 } 1238 final String name; 1239 } 1240 1241 static class StartCDATA extends Event { 1242 StartCDATA(Location location) { 1243 super(location); 1244 } 1245 } 1246 1247 static class StartDTD extends Event { 1248 StartDTD(Location location, String name, String publicId, String systemId) { 1249 super(location); 1250 this.name = name; 1251 this.publicId = publicId; 1252 this.systemId = systemId; 1253 } 1254 final String name; 1255 final String publicId; 1256 final String systemId; 1257 } 1258 1259 static class StartEntity extends Event { 1260 public StartEntity(Location location, String name) { 1261 super(location); 1262 this.name = name; 1263 } 1264 final String name; 1265 } 1266 1267 static class StartInstruction extends Event { 1268 StartInstruction(StartElement startElement) { 1269 super(startElement.location); 1270 this.startElement = startElement; 1271 } 1272 final StartElement startElement; 1273 EndInstruction endInstruction; 1274 } 1275 1276 static class EndInstruction extends Event { 1277 EndInstruction(Location locator, StartInstruction startInstruction) { 1278 super(locator); 1279 this.startInstruction = startInstruction; 1280 startInstruction.endInstruction = this; 1281 } 1282 final StartInstruction startInstruction; 1283 } 1284 1285 static class StartForEach extends StartInstruction { 1286 StartForEach(StartElement raw, JXTExpression items, JXTExpression var, 1287 JXTExpression varStatus, JXTExpression begin, JXTExpression end, 1288 JXTExpression step, Boolean lenient) { 1289 super(raw); 1290 this.items = items; 1291 this.var = var; 1292 this.varStatus = varStatus; 1293 this.begin = begin; 1294 this.end = end; 1295 this.step = step; 1296 this.lenient = lenient; 1297 } 1298 final JXTExpression items; 1299 final JXTExpression var; 1300 final JXTExpression varStatus; 1301 final JXTExpression begin; 1302 final JXTExpression end; 1303 final JXTExpression step; 1304 final Boolean lenient; 1305 } 1306 1307 static class StartIf extends StartInstruction { 1308 StartIf(StartElement raw, JXTExpression test) { 1309 super(raw); 1310 this.test = test; 1311 } 1312 final JXTExpression test; 1313 } 1314 1315 static class StartChoose extends StartInstruction { 1316 StartChoose(StartElement raw) { 1317 super(raw); 1318 } 1319 StartWhen firstChoice; 1320 StartOtherwise otherwise; 1321 } 1322 1323 static class StartWhen extends StartInstruction { 1324 StartWhen(StartElement raw, JXTExpression test) { 1325 super(raw); 1326 this.test = test; 1327 } 1328 final JXTExpression test; 1329 StartWhen nextChoice; 1330 } 1331 1332 static class StartOtherwise extends StartInstruction { 1333 StartOtherwise(StartElement raw) { 1334 super(raw); 1335 } 1336 } 1337 1338 static class StartOut extends StartInstruction { 1339 StartOut(StartElement raw, JXTExpression expr, Boolean lenient) { 1340 super(raw); 1341 this.compiledExpression = expr; 1342 this.lenient = lenient; 1343 } 1344 final JXTExpression compiledExpression; 1345 final Boolean lenient; 1346 } 1347 1348 static class StartImport extends StartInstruction { 1349 StartImport(StartElement raw, AttributeEvent uri, JXTExpression select) { 1350 super(raw); 1351 this.uri = uri; 1352 this.select = select; 1353 } 1354 final AttributeEvent uri; 1355 final JXTExpression select; 1356 } 1357 1358 static class StartTemplate extends StartInstruction { 1359 StartTemplate(StartElement raw) { 1360 super(raw); 1361 } 1362 } 1363 1364 static class StartEvalBody extends StartInstruction { 1365 StartEvalBody(StartElement raw) { 1366 super(raw); 1367 } 1368 } 1369 1370 static class StartEval extends StartInstruction { 1371 StartEval(StartElement raw, JXTExpression value) { 1372 super(raw); 1373 this.value = value; 1374 } 1375 final JXTExpression value; 1376 } 1377 1378 static class StartDefine extends StartInstruction { 1379 StartDefine(StartElement raw, String namespace, String name) { 1380 super(raw); 1381 this.namespace = namespace; 1382 this.name = name; 1383 this.qname = "{" + namespace + "}" + name; 1384 this.parameters = new HashMap (); 1385 } 1386 final String name; 1387 final String namespace; 1388 final String qname; 1389 final Map parameters; 1390 Event body; 1391 void finish() throws SAXException { 1392 Event e = next; 1393 boolean params = true; 1394 while (e != this.endInstruction) { 1395 if (e instanceof StartParameter) { 1396 StartParameter startParam = (StartParameter)e; 1397 if (!params) { 1398 throw new JXTException("<parameter> not allowed here: \"" + startParam.name + "\"", 1399 startParam.location, null); 1400 } 1401 Object prev = parameters.put(startParam.name, startParam); 1402 if (prev != null) { 1403 throw new JXTException("duplicate parameter: \"" + startParam.name + "\"", location, null); 1404 } 1405 e = startParam.endInstruction; 1406 } else if (e instanceof IgnorableWhitespace) { 1407 } else if (e instanceof Characters) { 1409 char[] ch = ((TextEvent)e).raw; 1411 int len = ch.length; 1412 for (int i = 0; i < len; i++) { 1413 if (!Character.isWhitespace(ch[i])) { 1414 if (params) { 1415 params = false; 1416 body = e; 1417 } 1418 break; 1419 } 1420 } 1421 } else { 1422 if (params) { 1423 params = false; 1424 body = e; 1425 } 1426 } 1427 e = e.next; 1428 } 1429 if (this.body == null) { 1430 this.body = this.endInstruction; 1431 } 1432 } 1433 } 1434 1435 static class StartParameter extends StartInstruction { 1436 StartParameter(StartElement raw, String name, String optional, String default_) { 1437 super(raw); 1438 this.name = name; 1439 this.optional = optional; 1440 this.default_ = default_; 1441 } 1442 final String name; 1443 final String optional; 1444 final String default_; 1445 } 1446 1447 static class StartSet extends StartInstruction { 1448 StartSet(StartElement raw, JXTExpression var, JXTExpression value) { 1449 super(raw); 1450 this.var = var; 1451 this.value = value; 1452 } 1453 final JXTExpression var; 1454 final JXTExpression value; 1455 } 1456 1457 1458 static class StartComment extends StartInstruction { 1459 StartComment(StartElement raw) { 1460 super(raw); 1461 } 1462 } 1463 1464 1466 private static Locale parseLocale(String locale, String variant) { 1467 Locale ret = null; 1468 String language = locale; 1469 String country = null; 1470 int index = StringUtils.indexOfAny(locale, "-_"); 1471 1472 if (index > -1) { 1473 language = locale.substring(0, index); 1474 country = locale.substring(index + 1); 1475 } 1476 if (StringUtils.isEmpty(language)) { 1477 throw new IllegalArgumentException ("No language in locale"); 1478 } 1479 if (country == null) { 1480 ret = variant != null ? new Locale (language, "", variant) : new Locale (language, ""); 1481 } else if (country.length() > 0) { 1482 ret = variant != null ? new Locale (language, country, variant) : new Locale (language, country); 1483 } else { 1484 throw new IllegalArgumentException ("Empty country in locale"); 1485 } 1486 return ret; 1487 } 1488 1489 private static final String NUMBER = "number"; 1490 private static final String CURRENCY = "currency"; 1491 private static final String PERCENT = "percent"; 1492 1493 static class LocaleAwareInstruction extends StartInstruction { 1494 private JXTExpression locale; 1495 1496 LocaleAwareInstruction(StartElement startElement, JXTExpression locale) { 1497 super(startElement); 1498 this.locale = locale; 1499 } 1500 1501 protected Locale getLocale(JexlContext jexl, JXPathContext jxp) throws Exception { 1502 Object locVal = getValue(this.locale, jexl, jxp); 1503 if (locVal == null) 1504 locVal = getStringValue(this.locale, jexl, jxp); 1505 1506 if (locVal != null) { 1507 return locVal instanceof Locale ? (Locale ) locVal : parseLocale(locVal.toString(), null); 1508 } else { 1509 return Locale.getDefault(); 1510 } 1511 } 1512 } 1513 1514 static class StartFormatNumber extends LocaleAwareInstruction { 1515 1516 JXTExpression value; 1517 JXTExpression type; 1518 JXTExpression pattern; 1519 JXTExpression currencyCode; 1520 JXTExpression currencySymbol; 1521 JXTExpression isGroupingUsed; 1522 JXTExpression maxIntegerDigits; 1523 JXTExpression minIntegerDigits; 1524 JXTExpression maxFractionDigits; 1525 JXTExpression minFractionDigits; 1526 1527 JXTExpression var; 1528 1529 private static Class currencyClass; 1530 1531 static { 1532 try { 1533 currencyClass = Class.forName("java.util.Currency"); 1534 } catch (Exception cnfe) { 1536 } 1538 } 1539 1540 public StartFormatNumber(StartElement raw, 1541 JXTExpression var, 1542 JXTExpression value, 1543 JXTExpression type, 1544 JXTExpression pattern, 1545 JXTExpression currencyCode, 1546 JXTExpression currencySymbol, 1547 JXTExpression isGroupingUsed, 1548 JXTExpression maxIntegerDigits, 1549 JXTExpression minIntegerDigits, 1550 JXTExpression maxFractionDigits, 1551 JXTExpression minFractionDigits, 1552 JXTExpression locale) { 1553 super(raw, locale); 1554 this.var = var; 1555 this.value = value; 1556 this.type = type; 1557 this.pattern = pattern; 1558 this.currencyCode = currencyCode; 1559 this.currencySymbol = currencySymbol; 1560 this.isGroupingUsed = isGroupingUsed; 1561 this.maxIntegerDigits = maxIntegerDigits; 1562 this.minIntegerDigits = minIntegerDigits; 1563 this.maxFractionDigits = maxFractionDigits; 1564 this.minFractionDigits = minFractionDigits; 1565 } 1566 1567 String format(JexlContext jexl, JXPathContext jxp) throws Exception { 1568 String var = getStringValue(this.var, jexl, jxp); 1570 Number input = getNumberValue(this.value, jexl, jxp); 1571 String type = getStringValue(this.type, jexl, jxp); 1572 String pattern = getStringValue(this.pattern, jexl, jxp); 1573 String currencyCode = getStringValue(this.currencyCode, jexl, jxp); 1574 String currencySymbol = getStringValue(this.currencySymbol, jexl, jxp); 1575 Boolean isGroupingUsed = getBooleanValue(this.isGroupingUsed, jexl, jxp); 1576 Number maxIntegerDigits = getNumberValue(this.maxIntegerDigits, jexl, jxp); 1577 Number minIntegerDigits = getNumberValue(this.minIntegerDigits, jexl, jxp); 1578 Number maxFractionDigits = getNumberValue(this.maxFractionDigits, jexl, jxp); 1579 Number minFractionDigits = getNumberValue(this.minFractionDigits, jexl, jxp); 1580 Locale loc = getLocale(jexl,jxp); 1581 String formatted; 1582 if (loc != null) { 1583 NumberFormat formatter = null; 1585 if (StringUtils.isNotEmpty(pattern)) { 1586 DecimalFormatSymbols symbols = new DecimalFormatSymbols (loc); 1588 formatter = new DecimalFormat (pattern, symbols); 1589 } else { 1590 formatter = createFormatter(loc, type); 1591 } 1592 if (StringUtils.isNotEmpty(pattern) || CURRENCY.equalsIgnoreCase(type)) { 1593 setCurrency(formatter, currencyCode, currencySymbol); 1594 } 1595 configureFormatter(formatter, 1596 isGroupingUsed, 1597 maxIntegerDigits, 1598 minIntegerDigits, 1599 maxFractionDigits, 1600 minFractionDigits); 1601 formatted = formatter.format(input); 1602 } else { 1603 formatted = input.toString(); 1605 } 1606 if (var != null) { 1607 jexl.getVars().put(var, formatted); 1608 jxp.getVariables().declareVariable(var, formatted); 1609 return null; 1610 } 1611 return formatted; 1612 } 1613 1614 private NumberFormat createFormatter(Locale loc, String type) throws Exception { 1615 NumberFormat formatter = null; 1616 if ((type == null) || NUMBER.equalsIgnoreCase(type)) { 1617 formatter = NumberFormat.getNumberInstance(loc); 1618 } else if (CURRENCY.equalsIgnoreCase(type)) { 1619 formatter = NumberFormat.getCurrencyInstance(loc); 1620 } else if (PERCENT.equalsIgnoreCase(type)) { 1621 formatter = NumberFormat.getPercentInstance(loc); 1622 } else { 1623 throw new IllegalArgumentException ("Invalid type: \"" + type + "\": should be \"number\" or \"currency\" or \"percent\""); 1624 } 1625 return formatter; 1626 } 1627 1628 1633 private void configureFormatter(NumberFormat formatter, 1634 Boolean isGroupingUsed, 1635 Number maxIntegerDigits, 1636 Number minIntegerDigits, 1637 Number maxFractionDigits, 1638 Number minFractionDigits) { 1639 if (isGroupingUsed != null) 1640 formatter.setGroupingUsed(isGroupingUsed.booleanValue()); 1641 if (maxIntegerDigits != null) 1642 formatter.setMaximumIntegerDigits(maxIntegerDigits.intValue()); 1643 if (minIntegerDigits != null) 1644 formatter.setMinimumIntegerDigits(minIntegerDigits.intValue()); 1645 if (maxFractionDigits != null) 1646 formatter.setMaximumFractionDigits(maxFractionDigits.intValue()); 1647 if (minFractionDigits != null) 1648 formatter.setMinimumFractionDigits(minFractionDigits.intValue()); 1649 } 1650 1651 1679 private void setCurrency(NumberFormat formatter, String currencyCode, String currencySymbol) throws Exception { 1680 String code = null; 1681 String symbol = null; 1682 1683 if (currencyCode == null) { 1684 if (currencySymbol == null) { 1685 return; 1686 } 1687 symbol = currencySymbol; 1688 } else if (currencySymbol != null) { 1689 if (currencyClass != null) { 1690 code = currencyCode; 1691 } else { 1692 symbol = currencySymbol; 1693 } 1694 } else if (currencyClass != null) { 1695 code = currencyCode; 1696 } else { 1697 symbol = currencyCode; 1698 } 1699 if (code != null) { 1700 Object [] methodArgs = new Object [1]; 1701 1702 1705 Method m = currencyClass.getMethod("getInstance", new Class [] {String .class}); 1706 1707 methodArgs[0] = code; 1708 Object currency = m.invoke(null, methodArgs); 1709 1710 1713 Class [] paramTypes = new Class [1]; 1714 paramTypes[0] = currencyClass; 1715 Class numberFormatClass = Class.forName("java.text.NumberFormat"); 1716 m = numberFormatClass.getMethod("setCurrency", paramTypes); 1717 methodArgs[0] = currency; 1718 m.invoke(formatter, methodArgs); 1719 } else { 1720 1724 DecimalFormat df = (DecimalFormat ) formatter; 1725 DecimalFormatSymbols dfs = df.getDecimalFormatSymbols(); 1726 dfs.setCurrencySymbol(symbol); 1727 df.setDecimalFormatSymbols(dfs); 1728 } 1729 } 1730 } 1731 1732 1734 static class StartFormatDate extends LocaleAwareInstruction { 1735 1736 private static final String DATE = "date"; 1737 private static final String TIME = "time"; 1738 private static final String DATETIME = "both"; 1739 1740 JXTExpression var; 1741 JXTExpression value; 1742 JXTExpression type; 1743 JXTExpression pattern; 1744 JXTExpression timeZone; 1745 JXTExpression dateStyle; 1746 JXTExpression timeStyle; 1747 JXTExpression locale; 1748 1749 StartFormatDate(StartElement raw, 1750 JXTExpression var, 1751 JXTExpression value, 1752 JXTExpression type, 1753 JXTExpression pattern, 1754 JXTExpression timeZone, 1755 JXTExpression dateStyle, 1756 JXTExpression timeStyle, 1757 JXTExpression locale) { 1758 super(raw, locale); 1759 this.var = var; 1760 this.value = value; 1761 this.type = type; 1762 this.pattern = pattern; 1763 this.timeZone = timeZone; 1764 this.dateStyle = dateStyle; 1765 this.timeStyle = timeStyle; 1766 } 1767 1768 String format(JexlContext jexl, JXPathContext jxp) 1769 throws Exception { 1770 String var = getStringValue(this.var, jexl, jxp); 1771 Object value = getValue(this.value, jexl, jxp); 1772 String pattern = getStringValue(this.pattern, jexl, jxp); 1773 Object timeZone = getValue(this.timeZone, jexl, jxp); 1774 1775 String type = getStringValue(this.type, jexl, jxp); 1776 String timeStyle = getStringValue(this.timeStyle, jexl, jxp); 1777 String dateStyle = getStringValue(this.dateStyle, jexl, jxp); 1778 1779 String formatted = null; 1780 1781 Locale locale = getLocale(jexl, jxp); 1783 DateFormat formatter = createFormatter(locale, type, dateStyle, timeStyle); 1784 if (pattern != null) { 1786 try { 1787 ((SimpleDateFormat ) formatter).applyPattern(pattern); 1788 } catch (ClassCastException cce) { 1789 formatter = new SimpleDateFormat (pattern, locale); 1790 } 1791 } 1792 TimeZone tz = null; 1794 if ((timeZone instanceof String ) && timeZone.equals("")) { 1795 timeZone = null; 1796 } 1797 if (timeZone != null) { 1798 if (timeZone instanceof String ) { 1799 tz = TimeZone.getTimeZone((String ) timeZone); 1800 } else if (timeZone instanceof TimeZone ) { 1801 tz = (TimeZone ) timeZone; 1802 } else { 1803 throw new IllegalArgumentException ("Illegal timeZone value: \""+timeZone+"\""); 1804 } 1805 } 1806 if (tz != null) { 1807 formatter.setTimeZone(tz); 1808 } 1809 formatted = formatter.format(value); 1810 if (var != null) { 1811 jexl.getVars().put(var, formatted); 1812 jxp.getVariables().declareVariable(var, formatted); 1813 return null; 1814 } 1815 return formatted; 1816 } 1817 1818 private DateFormat createFormatter(Locale loc, String type, String dateStyle, String timeStyle) 1819 throws Exception { 1820 DateFormat formatter = null; 1821 if ((type == null) || DATE.equalsIgnoreCase(type)) { 1822 formatter = DateFormat.getDateInstance(getStyle(dateStyle), loc); 1823 } else if (TIME.equalsIgnoreCase(type)) { 1824 formatter = DateFormat.getTimeInstance(getStyle(timeStyle), loc); 1825 } else if (DATETIME.equalsIgnoreCase(type)) { 1826 formatter = DateFormat.getDateTimeInstance(getStyle(dateStyle), getStyle(timeStyle), loc); 1827 } else { 1828 throw new IllegalArgumentException ("Invalid type: \""+ type+"\""); 1829 } 1830 return formatter; 1831 } 1832 1833 private static final String DEFAULT = "default"; 1834 private static final String SHORT = "short"; 1835 private static final String MEDIUM = "medium"; 1836 private static final String LONG = "long"; 1837 private static final String FULL = "full"; 1838 1839 private int getStyle(String style) { 1840 int ret = DateFormat.DEFAULT; 1841 if (style != null) { 1842 if (DEFAULT.equalsIgnoreCase(style)) { 1843 ret = DateFormat.DEFAULT; 1844 } else if (SHORT.equalsIgnoreCase(style)) { 1845 ret = DateFormat.SHORT; 1846 } else if (MEDIUM.equalsIgnoreCase(style)) { 1847 ret = DateFormat.MEDIUM; 1848 } else if (LONG.equalsIgnoreCase(style)) { 1849 ret = DateFormat.LONG; 1850 } else if (FULL.equalsIgnoreCase(style)) { 1851 ret = DateFormat.FULL; 1852 } else { 1853 throw new IllegalArgumentException ("Invalid style: \"" + style + "\": should be \"default\" or \"short\" or \"medium\" or \"long\" or \"full\""); 1854 } 1855 } 1856 return ret; 1857 } 1858 } 1859 1860 1861 static class Parser implements ContentHandler , LexicalHandler { 1862 1863 StartDocument startEvent; 1864 Event lastEvent; 1865 Stack stack = new Stack (); 1866 Locator locator; 1867 Location charLocation; 1868 StringBuffer charBuf; 1869 1870 public Parser() { 1871 } 1873 1874 StartDocument getStartEvent() { 1875 return this.startEvent; 1876 } 1877 1878 1879 void recycle() { 1880 startEvent = null; 1881 lastEvent = null; 1882 stack.clear(); 1883 locator = null; 1884 charLocation = null; 1885 charBuf = null; 1886 } 1887 1888 private void addEvent(Event ev) throws SAXException { 1889 if (ev != null) { 1890 if (lastEvent == null) { 1891 lastEvent = startEvent = new StartDocument(LocationUtils.getLocation(locator, "template")); 1892 } else { 1893 flushChars(); 1894 } 1895 lastEvent.next = ev; 1896 lastEvent = ev; 1897 } else { 1898 throw new NullPointerException ("null event"); 1899 } 1900 } 1901 1902 void flushChars() throws SAXException { 1903 if (charBuf != null) { 1904 char[] chars = new char[charBuf.length()]; 1905 charBuf.getChars(0, charBuf.length(), chars, 0); 1906 Characters ev = new Characters(charLocation, chars, 0, chars.length); 1907 lastEvent.next = ev; 1908 lastEvent = ev; 1909 charLocation = null; 1910 charBuf = null; 1911 } 1912 } 1913 1914 public void characters(char[] ch, int start, int length) 1915 throws SAXException { 1916 if (charBuf == null) { 1917 charBuf = new StringBuffer (length); 1918 charLocation = LocationUtils.getLocation(locator, "[text]"); 1919 } 1920 charBuf.append(ch, start, length); 1921 } 1922 1923 public void endDocument() throws SAXException { 1924 StartDocument startDoc = (StartDocument)stack.pop(); 1925 EndDocument endDoc = new EndDocument(LocationUtils.getLocation(locator, "template")); 1926 startDoc.endDocument = endDoc; 1927 addEvent(endDoc); 1928 } 1929 1930 public void endElement(String namespaceURI, String localName, String raw) throws SAXException { 1931 Event start = (Event)stack.pop(); 1932 Event newEvent = null; 1933 if (NS.equals(namespaceURI)) { 1934 StartInstruction startInstruction = (StartInstruction)start; 1935 EndInstruction endInstruction = new EndInstruction(LocationUtils.getLocation(locator, "<"+raw+">"), startInstruction); 1936 newEvent = endInstruction; 1937 if (start instanceof StartWhen) { 1938 StartWhen startWhen = (StartWhen)start; 1939 StartChoose startChoose = (StartChoose)stack.peek(); 1940 if (startChoose.firstChoice != null) { 1941 StartWhen w = startChoose.firstChoice; 1942 while (w.nextChoice != null) { 1943 w = w.nextChoice; 1944 } 1945 w.nextChoice = startWhen; 1946 } else { 1947 startChoose.firstChoice = startWhen; 1948 } 1949 } else if (start instanceof StartOtherwise) { 1950 StartOtherwise startOtherwise = (StartOtherwise)start; 1951 StartChoose startChoose = (StartChoose)stack.peek(); 1952 startChoose.otherwise = startOtherwise; 1953 } 1954 } else { 1955 StartElement startElement = (StartElement)start; 1956 newEvent = startElement.endElement = new EndElement(LocationUtils.getLocation(locator, "<"+raw+">"), startElement); 1957 } 1958 addEvent(newEvent); 1959 if (start instanceof StartDefine) { 1960 StartDefine startDefine = (StartDefine)start; 1961 startDefine.finish(); 1962 } 1963 } 1964 1965 public void endPrefixMapping(String prefix) throws SAXException { 1966 EndPrefixMapping endPrefixMapping = new EndPrefixMapping(LocationUtils.getLocation(locator, null), prefix); 1967 addEvent(endPrefixMapping); 1968 } 1969 1970 public void ignorableWhitespace(char[] ch, int start, int length) throws SAXException { 1971 Event ev = new IgnorableWhitespace(LocationUtils.getLocation(locator, null), ch, start, length); 1972 addEvent(ev); 1973 } 1974 1975 public void processingInstruction(String target, String data) throws SAXException { 1976 Event pi = new ProcessingInstruction(LocationUtils.getLocation(locator, null), target, data); 1977 addEvent(pi); 1978 } 1979 1980 public void setDocumentLocator(Locator locator) { 1981 this.locator = locator; 1982 } 1983 1984 public void skippedEntity(String name) throws SAXException { 1985 addEvent(new SkippedEntity(LocationUtils.getLocation(locator, null), name)); 1986 } 1987 1988 public void startDocument() { 1989 startEvent = new StartDocument(LocationUtils.getLocation(locator, null)); 1990 lastEvent = startEvent; 1991 stack.push(lastEvent); 1992 } 1993 1994 public void startElement(String namespaceURI, String localName, String qname, Attributes attrs) throws SAXException { 1995 Event newEvent = null; 1996 Location locator = LocationUtils.getLocation(this.locator, "<"+qname+">"); 1997 AttributesImpl elementAttributes = new AttributesImpl (attrs); 1998 int attributeCount = elementAttributes.getLength(); 1999 for (int i = 0; i < attributeCount; i++) { 2000 String attributeURI = elementAttributes.getURI(i); 2001 if (StringUtils.equals(attributeURI, NS)) { 2002 getStartEvent().templateProperties.put(elementAttributes.getLocalName(i), 2003 compileExpr(elementAttributes.getValue(i), null, locator)); 2004 elementAttributes.removeAttribute(i--); 2005 } 2006 } 2007 StartElement startElement = new StartElement(locator, namespaceURI, 2008 localName, qname, elementAttributes); 2009 if (NS.equals(namespaceURI)) { 2010 if (localName.equals(FOR_EACH)) { 2011 String items = attrs.getValue("items"); 2012 String select = attrs.getValue("select"); 2013 JXTExpression begin = compileInt(attrs.getValue("begin"), FOR_EACH, locator); 2014 JXTExpression end = compileInt(attrs.getValue("end"), FOR_EACH, locator); 2015 JXTExpression step = compileInt(attrs.getValue("step"), FOR_EACH, locator); 2016 JXTExpression var = compileExpr(attrs.getValue("var"), null, locator); 2017 JXTExpression varStatus = compileExpr(attrs.getValue("varStatus"), null, locator); 2018 if (items == null) { 2019 if (select == null && (begin == null || end == null)) { 2020 throw new JXTException("forEach: \"select\", \"items\", or both \"begin\" and \"end\" must be specified", locator, null); 2021 } 2022 } else if (select != null) { 2023 throw new JXTException("forEach: only one of \"select\" or \"items\" may be specified", locator, null); 2024 } 2025 JXTExpression expr = compileExpr(items == null ? select : items, null, locator); 2026 String lenientValue = attrs.getValue("lenient"); 2027 Boolean lenient = (lenientValue == null) ? null : Boolean.valueOf(lenientValue); 2028 StartForEach startForEach = new StartForEach(startElement, expr, var, varStatus, begin, end, step,lenient); 2029 newEvent = startForEach; 2030 } else if (localName.equals(FORMAT_NUMBER)) { 2031 JXTExpression value = compileExpr(attrs.getValue("value"), null, locator); 2032 JXTExpression type = compileExpr(attrs.getValue("type"), null, locator); 2033 JXTExpression pattern = compileExpr(attrs.getValue("pattern"), null, locator); 2034 JXTExpression currencyCode = compileExpr(attrs.getValue("currencyCode"), null, locator); 2035 JXTExpression currencySymbol = compileExpr(attrs.getValue("currencySymbol"), null, locator); 2036 JXTExpression isGroupingUsed = compileBoolean(attrs.getValue("isGroupingUsed"), null, locator); 2037 JXTExpression maxIntegerDigits = compileInt(attrs.getValue("maxIntegerDigits"), null, locator); 2038 JXTExpression minIntegerDigits = compileInt(attrs.getValue("minIntegerDigits"), null, locator); 2039 JXTExpression maxFractionDigits = compileInt(attrs.getValue("maxFractionDigits"), null, locator); 2040 JXTExpression minFractionDigits = compileInt(attrs.getValue("minFractionDigits"), null, locator); 2041 JXTExpression var = compileExpr(attrs.getValue("var"), null, locator); 2042 JXTExpression locale = compileExpr(attrs.getValue("locale"), null, locator); 2043 StartFormatNumber startFormatNumber = 2044 new StartFormatNumber(startElement, 2045 var, 2046 value, 2047 type, 2048 pattern, 2049 currencyCode, 2050 currencySymbol, 2051 isGroupingUsed, 2052 maxIntegerDigits, 2053 minIntegerDigits, 2054 maxFractionDigits, 2055 minFractionDigits, 2056 locale); 2057 newEvent = startFormatNumber; 2058 } else if (localName.equals(FORMAT_DATE)) { 2059 JXTExpression var = compileExpr(attrs.getValue("var"), null, locator); 2060 JXTExpression value = compileExpr(attrs.getValue("value"), null, locator); 2061 JXTExpression type = compileExpr(attrs.getValue("type"), null, locator); 2062 JXTExpression pattern = compileExpr(attrs.getValue("pattern"), null, locator); 2063 JXTExpression timeZone = compileExpr(attrs.getValue("timeZone"), null, locator); 2064 JXTExpression dateStyle = compileExpr(attrs.getValue("dateStyle"), null, locator); 2065 JXTExpression timeStyle = compileExpr(attrs.getValue("timeStyle"), null, locator); 2066 JXTExpression locale = compileExpr(attrs.getValue("locale"), null, locator); 2067 StartFormatDate startFormatDate = 2068 new StartFormatDate(startElement, 2069 var, 2070 value, 2071 type, 2072 pattern, 2073 timeZone, 2074 dateStyle, 2075 timeStyle, 2076 locale); 2077 newEvent = startFormatDate; 2078 } else if (localName.equals(CHOOSE)) { 2079 StartChoose startChoose = new StartChoose(startElement); 2080 newEvent = startChoose; 2081 } else if (localName.equals(WHEN)) { 2082 if (stack.size() == 0 || !(stack.peek() instanceof StartChoose)) { 2083 throw new JXTException("<when> must be within <choose>", locator, null); 2084 } 2085 String test = attrs.getValue("test"); 2086 if (test != null) { 2087 JXTExpression expr = compileExpr(test, "when: \"test\": ", locator); 2088 StartWhen startWhen = new StartWhen(startElement, expr); 2089 newEvent = startWhen; 2090 } else { 2091 throw new JXTException("when: \"test\" is required", locator, null); 2092 } 2093 } else if (localName.equals(OUT)) { 2094 String value = attrs.getValue("value"); 2095 if (value != null) { 2096 JXTExpression expr = compileExpr(value, "out: \"value\": ", locator); 2097 String lenientValue = attrs.getValue("lenient"); 2098 Boolean lenient = lenientValue == null ? null : Boolean.valueOf(lenientValue); 2099 newEvent = new StartOut(startElement, expr, lenient); 2100 } else { 2101 throw new JXTException("out: \"value\" is required", locator, null); 2102 } 2103 } else if (localName.equals(OTHERWISE)) { 2104 if (stack.size() != 0 && (stack.peek() instanceof StartChoose)) { 2105 StartOtherwise startOtherwise = new StartOtherwise(startElement); 2106 newEvent = startOtherwise; 2107 } else { 2108 throw new JXTException( "<otherwise> must be within <choose>", locator, null); 2109 } 2110 } else if (localName.equals(IF)) { 2111 String test = attrs.getValue("test"); 2112 if (test != null) { 2113 JXTExpression expr = compileExpr(test, "if: \"test\": ", locator); 2114 StartIf startIf = new StartIf(startElement, expr); 2115 newEvent = startIf; 2116 } else { 2117 throw new JXTException("if: \"test\" is required", locator, null); 2118 } 2119 } else if (localName.equals(MACRO)) { 2120 String namespace = StringUtils.defaultString(attrs.getValue("targetNamespace")); 2125 String name = attrs.getValue("name"); 2126 if (name != null) { 2127 StartDefine startDefine = new StartDefine(startElement, namespace, name); 2128 newEvent = startDefine; 2129 } else { 2130 throw new JXTException("macro: \"name\" is required", locator, null); 2131 } 2132 } else if (localName.equals(PARAMETER)) { 2133 if (stack.size() == 0 || !(stack.peek() instanceof StartDefine)) { 2134 throw new JXTException("<parameter> not allowed here", locator, null); 2135 } else { 2136 String name = attrs.getValue("name"); 2137 String optional = attrs.getValue("optional"); 2138 String default_ = attrs.getValue("default"); 2139 if (name != null) { 2140 StartParameter startParameter = new StartParameter(startElement, name, optional, default_); 2141 newEvent = startParameter; 2142 } else { 2143 throw new JXTException("parameter: \"name\" is required", locator, null); 2144 } 2145 } 2146 } else if (localName.equals(EVALBODY)) { 2147 newEvent = new StartEvalBody(startElement); 2148 } else if (localName.equals(EVAL)) { 2149 String value = attrs.getValue("select"); 2150 JXTExpression valueExpr = compileExpr(value, "eval: \"select\":", locator); 2151 newEvent = new StartEval(startElement, valueExpr); 2152 } else if (localName.equals(SET)) { 2153 String var = attrs.getValue("var"); 2154 String value = attrs.getValue("value"); 2155 JXTExpression varExpr = null; 2156 JXTExpression valueExpr = null; 2157 if (var != null) { 2158 varExpr = compileExpr(var, "set: \"var\":", locator); 2159 } 2160 if (value != null) { 2161 valueExpr = compileExpr(value, "set: \"value\":", locator); 2162 } 2163 StartSet startSet = new StartSet(startElement, varExpr, valueExpr); 2164 newEvent = startSet; 2165 } else if (localName.equals(IMPORT)) { 2166 AttributeEvent uri = null; 2168 Iterator iter = startElement.attributeEvents.iterator(); 2169 while (iter.hasNext()) { 2170 AttributeEvent e = (AttributeEvent)iter.next(); 2171 if (e.localName.equals("uri")) { 2172 uri = e; 2173 break; 2174 } 2175 } 2176 if (uri != null) { 2177 String select = attrs.getValue("context"); 2180 JXTExpression expr = null; 2181 if (select != null) { 2182 expr = compileExpr(select, "import: \"context\": ", locator); 2183 } 2184 StartImport startImport = new StartImport(startElement, uri, expr); 2185 newEvent = startImport; 2186 } else { 2187 throw new JXTException("import: \"uri\" is required", locator, null); 2188 } 2189 } else if (localName.equals(TEMPLATE)) { 2190 StartTemplate startTemplate = new StartTemplate(startElement); 2191 newEvent = startTemplate; 2192 } else if (localName.equals(COMMENT)) { 2193 StartComment startJXComment = new StartComment(startElement); 2195 newEvent = startJXComment; 2196 } else { 2197 throw new JXTException("unrecognized tag: " + localName, locator, null); 2198 } 2199 } else { 2200 newEvent = startElement; 2201 } 2202 stack.push(newEvent); 2203 addEvent(newEvent); 2204 } 2205 2206 public void startPrefixMapping(String prefix, String uri) throws SAXException { 2207 addEvent(new StartPrefixMapping(LocationUtils.getLocation(locator, null), prefix, uri)); 2208 } 2209 2210 public void comment(char ch[], int start, int length) throws SAXException { 2211 } 2213 2214 public void endCDATA() throws SAXException { 2215 addEvent(new EndCDATA(LocationUtils.getLocation(locator, null))); 2216 } 2217 2218 public void endDTD() throws SAXException { 2219 addEvent(new EndDTD(LocationUtils.getLocation(locator, null))); 2220 } 2221 2222 public void endEntity(String name) throws SAXException { 2223 addEvent(new EndEntity(LocationUtils.getLocation(locator, null), name)); 2224 } 2225 2226 public void startCDATA() throws SAXException { 2227 addEvent(new StartCDATA(LocationUtils.getLocation(locator, null))); 2228 } 2229 2230 public void startDTD(String name, String publicId, String systemId) throws SAXException { 2231 addEvent(new StartDTD(LocationUtils.getLocation(locator, null), name, publicId, systemId)); 2232 } 2233 2234 public void startEntity(String name) throws SAXException { 2235 addEvent(new StartEntity(LocationUtils.getLocation(locator, null), name)); 2236 } 2237 } 2238 2239 2244 2245 public static class TransformerAdapter extends ServiceableTransformer { 2246 static class TemplateConsumer extends Parser implements XMLConsumer { 2247 2248 public TemplateConsumer() { 2249 this.gen = new JXTemplateGenerator(); 2250 } 2251 2252 public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) 2253 throws ProcessingException, SAXException , IOException { 2254 this.gen.setup(resolver, objectModel, null, parameters); 2255 } 2256 2257 public void service(ServiceManager manager) 2258 throws ServiceException { 2259 this.gen.service(manager); 2260 } 2261 2262 public void endDocument() throws SAXException { 2263 super.endDocument(); 2264 gen.performGeneration(gen.getConsumer(), gen.getJexlContext(), gen.getJXPathContext(), null, getStartEvent(), null); 2265 } 2266 2267 void setConsumer(XMLConsumer consumer) { 2268 gen.setConsumer(consumer); 2269 } 2270 2271 void recycle() { 2272 super.recycle(); 2273 gen.recycle(); 2274 } 2275 2276 JXTemplateGenerator gen; 2277 } 2278 2279 TemplateConsumer templateConsumer = new TemplateConsumer(); 2280 2281 public void recycle() { 2282 super.recycle(); 2283 templateConsumer.recycle(); 2284 } 2285 2286 public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) 2287 throws ProcessingException, SAXException , IOException { 2288 super.setup(resolver, objectModel, src, parameters); 2289 templateConsumer.setup(resolver, objectModel, src, parameters); 2290 } 2291 2292 public void service(ServiceManager manager) 2293 throws ServiceException { 2294 super.service(manager); 2295 templateConsumer.service(manager); 2296 } 2297 2298 public void setConsumer(XMLConsumer xmlConsumer) { 2299 super.setConsumer(templateConsumer); 2300 templateConsumer.setConsumer(xmlConsumer); 2301 } 2302 } 2303 2304 private JXPathContext jxpathContext; 2305 private MyJexlContext globalJexlContext; 2306 private Variables variables; 2307 private static Map cache = new HashMap (); 2308 private Source inputSource; 2309 private Map definitions; 2310 private Map cocoon; 2311 2312 private JXPathContext getJXPathContext() { 2313 return jxpathContext; 2314 } 2315 2316 private MyJexlContext getJexlContext() { 2317 return globalJexlContext; 2318 } 2319 2320 2323 public void recycle() { 2324 if (this.resolver != null) { 2325 this.resolver.release(this.inputSource); 2326 } 2327 this.inputSource = null; 2328 this.jxpathContext = null; 2329 this.globalJexlContext = null; 2330 this.variables = null; 2331 this.definitions = null; 2332 this.cocoon = null; 2333 this.namespaces.clear(); 2334 super.recycle(); 2335 } 2336 2337 2340 public void setup(SourceResolver resolver, Map objectModel, String src, Parameters parameters) 2341 throws ProcessingException, SAXException , IOException { 2342 2343 super.setup(resolver, objectModel, src, parameters); 2344 if (src != null) { 2345 try { 2346 this.inputSource = resolver.resolveURI(src); 2347 } catch (SourceException se) { 2348 throw SourceUtil.handle("Error during resolving of '" + src + "'.", se); 2349 } 2350 final String uri = inputSource.getURI(); 2351 boolean regenerate = false; 2352 StartDocument startEvent = null; 2353 synchronized (cache) { 2354 startEvent = (StartDocument)cache.get(uri); 2355 if (startEvent != null) { 2356 int valid = SourceValidity.UNKNOWN; 2357 if (startEvent.compileTime != null) { 2358 valid = startEvent.compileTime.isValid(); 2359 } 2360 if (valid == SourceValidity.UNKNOWN && startEvent.compileTime != null) { 2361 SourceValidity validity = inputSource.getValidity(); 2362 valid = startEvent.compileTime.isValid(validity); 2363 } 2364 if (valid != SourceValidity.VALID) { 2365 regenerate = true; 2366 } 2367 } else { 2368 regenerate = true; 2369 } 2370 } 2371 if (regenerate) { 2372 Parser parser = new Parser(); 2373 SourceUtil.parse(this.manager, this.inputSource, parser); 2374 startEvent = parser.getStartEvent(); 2375 startEvent.compileTime = this.inputSource.getValidity(); 2376 synchronized (cache) { 2377 cache.put(uri, startEvent); 2378 } 2379 } 2380 } 2381 Object bean = FlowHelper.getContextObject(objectModel); 2382 WebContinuation kont = FlowHelper.getWebContinuation(objectModel); 2383 setContexts(bean, kont, parameters, objectModel); 2384 this.definitions = new HashMap (); 2385 } 2386 2387 private void fillContext(Object contextObject, Map map) { 2388 if (contextObject != null) { 2389 final JXPathBeanInfo bi = JXPathIntrospector.getBeanInfo(contextObject.getClass()); 2392 if (bi.isDynamic()) { 2393 Class cl = bi.getDynamicPropertyHandlerClass(); 2394 try { 2395 DynamicPropertyHandler h = (DynamicPropertyHandler)cl.newInstance(); 2396 String [] result = h.getPropertyNames(contextObject); 2397 int len = result.length; 2398 for (int i = 0; i < len; i++) { 2399 try { 2400 map.put(result[i], h.getProperty(contextObject, result[i])); 2401 } catch (Exception exc) { 2402 exc.printStackTrace(); 2403 } 2404 } 2405 } catch (Exception ignored) { 2406 ignored.printStackTrace(); 2407 } 2408 } else { 2409 PropertyDescriptor [] props = bi.getPropertyDescriptors(); 2410 int len = props.length; 2411 for (int i = 0; i < len; i++) { 2412 try { 2413 Method read = props[i].getReadMethod(); 2414 if (read != null) { 2415 map.put(props[i].getName(), read.invoke(contextObject, null)); 2416 } 2417 } catch (Exception ignored) { 2418 ignored.printStackTrace(); 2419 } 2420 } 2421 } 2422 } 2423 } 2424 2425 private void setContexts(Object contextObject, WebContinuation kont, Parameters parameters, Map objectModel) { 2426 final Request request = ObjectModelHelper.getRequest(objectModel); 2427 final Object session = request.getSession(false); 2428 final Object app = ObjectModelHelper.getContext(objectModel); 2429 cocoon = new HashMap (); 2430 Object fomRequest = FOM_JavaScriptFlowHelper.getFOM_Request(objectModel); 2431 cocoon.put("request", fomRequest != null ? fomRequest : request); 2432 if (session != null) { 2433 cocoon.put("session", FOM_JavaScriptFlowHelper.getFOM_Session(objectModel)); 2434 } 2435 cocoon.put("context", FOM_JavaScriptFlowHelper.getFOM_Context(objectModel)); 2436 cocoon.put("continuation", FOM_JavaScriptFlowHelper.getFOM_WebContinuation(objectModel)); 2437 cocoon.put("parameters", Parameters.toProperties(parameters)); 2438 this.variables = new MyVariables(cocoon, contextObject, kont, request, session, app, parameters); 2439 Map map; 2440 if (contextObject instanceof Map ) { 2441 map = (Map )contextObject; 2442 } else { 2443 map = new HashMap (); 2444 fillContext(contextObject, map); 2445 } 2446 jxpathContext = jxpathContextFactory.newContext(null, contextObject); 2447 jxpathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces)); 2448 jxpathContext.setVariables(variables); 2449 jxpathContext.setLenient(parameters.getParameterAsBoolean("lenient-xpath", false)); 2450 globalJexlContext = new MyJexlContext(); 2451 globalJexlContext.setVars(map); 2452 map = globalJexlContext.getVars(); 2453 map.put("cocoon", cocoon); 2454 if (contextObject != null) { 2455 map.put("flowContext", contextObject); 2456 Object javaPkg = FOM_JavaScriptFlowHelper.getJavaPackage(objectModel); 2460 Object pkgs = FOM_JavaScriptFlowHelper.getPackages(objectModel); 2461 map.put("java", javaPkg); 2462 map.put("Packages", pkgs); 2463 } 2464 if (kont != null) { 2465 map.put("continuation", kont); 2466 } 2467 map.put("request", request); 2468 map.put("context", app); 2469 map.put("parameters", parameters); 2470 if (session != null) { 2471 map.put("session", session); 2472 } 2473 } 2474 2475 2478 public void generate() 2479 throws IOException , SAXException , ProcessingException { 2480 final String cacheKey = this.inputSource.getURI(); 2481 2482 StartDocument startEvent; 2483 synchronized (cache) { 2484 startEvent = (StartDocument)cache.get(cacheKey); 2485 } 2486 performGeneration(this.xmlConsumer, globalJexlContext, jxpathContext, null, startEvent, null); 2487 } 2488 2489 private void performGeneration(final XMLConsumer consumer, MyJexlContext jexlContext, JXPathContext jxpathContext, 2490 StartElement macroCall, Event startEvent, Event endEvent) throws SAXException { 2491 cocoon.put("consumer", consumer); 2492 RedundantNamespacesFilter filter = new RedundantNamespacesFilter(this.xmlConsumer); 2493 execute(filter, globalJexlContext, jxpathContext, null, startEvent, null); 2496 } 2497 2498 interface CharHandler { 2499 public void characters(char[] ch, int offset, int length) 2500 throws SAXException ; 2501 } 2502 2503 private void characters(JexlContext jexlContext, JXPathContext jxpathContext, TextEvent event, CharHandler handler) 2504 throws SAXException { 2505 Iterator iter = event.substitutions.iterator(); 2506 while (iter.hasNext()) { 2507 Object subst = iter.next(); 2508 char[] chars; 2509 if (subst instanceof char[]) { 2510 chars = (char[])subst; 2511 } else { 2512 JXTExpression expr = (JXTExpression)subst; 2513 try { 2514 Object val = getValue(expr, jexlContext, jxpathContext); 2515 chars = val != null ? val.toString().toCharArray() : ArrayUtils.EMPTY_CHAR_ARRAY; 2516 } catch (Exception e) { 2517 throw new JXTException(e.getMessage(), event.location, e); 2518 } 2519 } 2520 handler.characters(chars, 0, chars.length); 2521 } 2522 } 2523 2524 2525 private void executeDOM(final XMLConsumer consumer, MyJexlContext jexlContext, JXPathContext jxpathContext, Node node) 2526 throws SAXException { 2527 IncludeXMLConsumer includer = new IncludeXMLConsumer(consumer); 2528 DOMStreamer streamer = new DOMStreamer(includer); 2529 streamer.stream(node); 2530 } 2531 2532 private void call(Location location, StartElement macroCall, final XMLConsumer consumer, MyJexlContext jexlContext, 2533 JXPathContext jxpathContext, Event startEvent, Event endEvent) throws SAXException { 2534 try { 2535 execute(consumer, jexlContext, jxpathContext, macroCall, startEvent, endEvent); 2536 } catch (Exception exc) { 2537 throw new JXTException(macroCall.localName + ": " + exc.getMessage(), location, exc); 2538 } 2539 } 2540 2541 public static class LoopTagStatus { 2542 Object current; 2543 int index; 2544 int count; 2545 boolean first; 2546 boolean last; 2547 int begin; 2548 int end; 2549 int step; 2550 2551 public Object getCurrent() { 2552 return current; 2553 } 2554 public int getIndex() { 2555 return index; 2556 } 2557 public int getCount() { 2558 return count; 2559 } 2560 public boolean isFirst() { 2561 return first; 2562 } 2563 public boolean isLast() { 2564 return last; 2565 } 2566 public int getBegin() { 2567 return begin; 2568 } 2569 public int getEnd() { 2570 return end; 2571 } 2572 public int getStep() { 2573 return step; 2574 } 2575 } 2576 2577 private void execute(final XMLConsumer consumer, MyJexlContext jexlContext, JXPathContext jxpathContext, 2578 StartElement macroCall, Event startEvent, Event endEvent) throws SAXException { 2579 Event ev = startEvent; 2580 LocationFacade loc = new LocationFacade(ev.location); 2581 consumer.setDocumentLocator(loc); 2582 while (ev != endEvent) { 2583 loc.setDocumentLocation(ev.location); 2584 if (ev instanceof Characters) { 2585 TextEvent text = (TextEvent)ev; 2586 Iterator iter = text.substitutions.iterator(); 2587 while (iter.hasNext()) { 2588 Object subst = iter.next(); 2589 char[] chars; 2590 if (subst instanceof char[]) { 2591 chars = (char[])subst; 2592 } else { 2593 JXTExpression expr = (JXTExpression)subst; 2594 try { 2595 Object val = getNode(expr, jexlContext, jxpathContext); 2596 if (val instanceof Node ) { 2597 executeDOM(consumer, jexlContext, jxpathContext, (Node )val); 2598 continue; 2599 } else if (val instanceof NodeList ) { 2600 NodeList nodeList = (NodeList )val; 2601 int len = nodeList.getLength(); 2602 for (int i = 0; i < len; i++) { 2603 Node n = nodeList.item(i); 2604 executeDOM(consumer, jexlContext, jxpathContext, n); 2605 } 2606 continue; 2607 } else if (val instanceof Node []) { 2608 Node [] nodeList = (Node [])val; 2609 int len = nodeList.length; 2610 for (int i = 0; i < len; i++) { 2611 Node n = nodeList[i]; 2612 executeDOM(consumer, jexlContext, jxpathContext, n); 2613 } 2614 continue; 2615 } else if (val instanceof XMLizable) { 2616 ((XMLizable)val).toSAX(new IncludeXMLConsumer(consumer)); 2617 continue; 2618 } 2619 chars = val != null ? val.toString().toCharArray() : ArrayUtils.EMPTY_CHAR_ARRAY; 2620 } catch (Exception e) { 2621 throw new JXTException(e.getMessage(), ev.location, e); 2622 } 2623 } 2624 consumer.characters(chars, 0, chars.length); 2625 } 2626 } else if (ev instanceof EndElement) { 2627 EndElement endElement = (EndElement)ev; 2628 StartElement startElement = endElement.startElement; 2629 StartDefine def = 2630 (StartDefine)definitions.get(startElement.qname); 2631 if (def == null) { 2632 consumer.endElement(startElement.namespaceURI, startElement.localName, startElement.raw); 2633 namespaces.leaveScope(consumer); 2634 } 2635 } else if (ev instanceof EndPrefixMapping) { 2636 EndPrefixMapping endPrefixMapping = (EndPrefixMapping)ev; 2637 namespaces.removeDeclaration(endPrefixMapping.prefix); 2638 } else if (ev instanceof IgnorableWhitespace) { 2639 TextEvent text = (TextEvent)ev; 2640 characters(jexlContext, jxpathContext, text, new CharHandler() { 2641 public void characters(char[] ch, int offset, int len) throws SAXException { 2642 consumer.ignorableWhitespace(ch, offset, len); 2643 } 2644 }); 2645 } else if (ev instanceof SkippedEntity) { 2646 SkippedEntity skippedEntity = (SkippedEntity)ev; 2647 consumer.skippedEntity(skippedEntity.name); 2648 } else if (ev instanceof StartIf) { 2649 StartIf startIf = (StartIf)ev; 2650 Object val; 2651 try { 2652 val = getValue(startIf.test, jexlContext, jxpathContext, Boolean.TRUE); 2653 } catch (Exception e) { 2654 throw new JXTException(e.getMessage(), ev.location, e); 2655 } 2656 boolean result = false; 2657 if (val instanceof Boolean ) { 2658 result = ((Boolean )val).booleanValue(); 2659 } else { 2660 result = (val != null); 2661 } 2662 if (!result) { 2663 ev = startIf.endInstruction.next; 2664 continue; 2665 } 2666 } else if (ev instanceof StartForEach) { 2667 StartForEach startForEach = (StartForEach)ev; 2668 final Object items = startForEach.items; 2669 Iterator iter = null; 2670 int begin, end, step; 2671 String var, varStatus; 2672 try { 2673 if (items != null) { 2674 JXTExpression expr = (JXTExpression)items; 2675 if (expr.compiledExpression instanceof CompiledExpression) { 2676 CompiledExpression compiledExpression = (CompiledExpression)expr.compiledExpression; 2677 Object val = 2678 compiledExpression.getPointer(jxpathContext, expr.raw).getNode(); 2679 iter = val instanceof NativeArray ? new JSIntrospector.NativeArrayIterator((NativeArray)val) : 2681 compiledExpression.iteratePointers(jxpathContext); 2682 } else if (expr.compiledExpression instanceof Expression) { 2683 Expression e = (Expression)expr.compiledExpression; 2684 Object result = e.evaluate(jexlContext); 2685 if (result != null) { 2686 iter = Introspector.getUberspect().getIterator(result, 2687 new Info(ev.location.getURI(), ev.location.getLineNumber(), ev.location.getColumnNumber())); 2688 } 2689 if (iter == null) { 2690 iter = EMPTY_ITER; 2691 } 2692 } else { 2693 iter = new Iterator () { 2695 Object val = items; 2696 2697 public boolean hasNext() { 2698 return val != null; 2699 } 2700 2701 public Object next() { 2702 Object res = val; 2703 val = null; 2704 return res; 2705 } 2706 2707 public void remove() { 2708 } 2710 }; 2711 } 2712 } else { 2713 iter = NULL_ITER; 2714 } 2715 begin = startForEach.begin == null ? 0 : 2716 getIntValue(startForEach.begin, jexlContext, jxpathContext); 2717 end = startForEach.end == null ? Integer.MAX_VALUE : 2718 getIntValue(startForEach.end, jexlContext, jxpathContext); 2719 step = startForEach.step == null ? 1 : 2720 getIntValue(startForEach.step, jexlContext, jxpathContext); 2721 var = getStringValue(startForEach.var, jexlContext, jxpathContext); 2722 varStatus = getStringValue(startForEach.varStatus, jexlContext, jxpathContext); 2723 } catch (Exception exc) { 2724 throw new JXTException(exc.getMessage(), ev.location, exc); 2725 } 2726 MyJexlContext localJexlContext = new MyJexlContext(jexlContext); 2727 MyVariables localJXPathVariables = new MyVariables((MyVariables)jxpathContext.getVariables()); 2728 int i = 0; 2729 while (i < begin && iter.hasNext()) { 2731 iter.next(); 2732 i++; 2733 } 2734 LoopTagStatus status = null; 2735 if (varStatus != null) { 2736 status = new LoopTagStatus(); 2737 status.begin = begin; 2738 status.end = end; 2739 status.step = step; 2740 status.first = true; 2741 localJexlContext.put(varStatus, status); 2742 localJXPathVariables.declareVariable(varStatus, status); 2743 } 2744 int skipCounter, count = 1; 2745 JXPathContext localJXPathContext = null; 2746 while (i <= end && iter.hasNext()) { 2747 Object value = iter.next(); 2748 if (value instanceof Pointer) { 2749 Pointer ptr = (Pointer)value; 2750 localJXPathContext = jxpathContext.getRelativeContext(ptr); 2751 localJXPathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces)); 2752 try { 2753 value = ptr.getNode(); 2754 } catch (Exception exc) { 2755 throw new JXTException(exc.getMessage(), ev.location, null); 2756 } 2757 } else { 2758 localJXPathContext = jxpathContextFactory.newContext(jxpathContext, value); 2759 localJXPathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces)); 2760 } 2761 localJXPathContext.setVariables(localJXPathVariables); 2762 if (var != null) { 2763 localJexlContext.put(var, value); 2764 } 2765 if (status != null) { 2766 status.index = i; 2767 status.count = count; 2768 status.first = i == begin; 2769 status.current = value; 2770 status.last = (i == end || !iter.hasNext()); 2771 } 2772 execute(consumer, localJexlContext, localJXPathContext, macroCall, startForEach.next, startForEach.endInstruction); 2773 skipCounter = step; 2775 while (--skipCounter > 0 && iter.hasNext()) { 2776 iter.next(); 2777 } 2778 i += step; 2780 count++; 2781 } 2782 ev = startForEach.endInstruction.next; 2783 continue; 2784 } else if (ev instanceof StartChoose) { 2785 StartChoose startChoose = (StartChoose)ev; 2786 StartWhen startWhen = startChoose.firstChoice; 2787 while (startWhen != null) { 2788 Object val; 2789 try { 2790 val = getValue(startWhen.test, jexlContext, jxpathContext, Boolean.TRUE); 2791 } catch (Exception e) { 2792 throw new JXTException(e.getMessage(), ev.location, e); 2793 } 2794 boolean result; 2795 if (val instanceof Boolean ) { 2796 result = ((Boolean )val).booleanValue(); 2797 } else { 2798 result = (val != null); 2799 } 2800 if (result) { 2801 execute(consumer, jexlContext, jxpathContext, macroCall, startWhen.next, startWhen.endInstruction); 2802 break; 2803 } 2804 startWhen = startWhen.nextChoice; 2805 } 2806 if (startWhen == null && startChoose.otherwise != null) { 2807 execute(consumer, jexlContext, jxpathContext, macroCall, startChoose.otherwise.next, startChoose.otherwise.endInstruction); 2808 } 2809 ev = startChoose.endInstruction.next; 2810 continue; 2811 } else if (ev instanceof StartSet) { 2812 StartSet startSet = (StartSet)ev; 2813 Object value = null; 2814 String var = null; 2815 try { 2816 if (startSet.var != null) { 2817 var = getStringValue(startSet.var, jexlContext, jxpathContext); 2818 } 2819 if (startSet.value != null) { 2820 value = getNode(startSet.value, jexlContext, jxpathContext); 2821 } 2822 } catch (Exception exc) { 2823 throw new JXTException(exc.getMessage(), ev.location, exc); 2824 } 2825 if (value == null) { 2826 NodeList nodeList = toDOMNodeList("set", startSet, jexlContext, macroCall); 2827 int len = nodeList.getLength(); 2829 Node [] nodeArr = new Node [len]; 2830 for (int i = 0; i < len; i++) { 2831 nodeArr[i] = nodeList.item(i); 2832 } 2833 value = nodeArr; 2834 } 2835 if (var != null) { 2836 jxpathContext.getVariables().declareVariable(var, value); 2837 jexlContext.put(var, value); 2838 } 2839 ev = startSet.endInstruction.next; 2840 continue; 2841 } else if (ev instanceof StartElement) { 2842 StartElement startElement = (StartElement)ev; 2843 StartDefine def = 2844 (StartDefine)definitions.get(startElement.qname); 2845 if (def != null) { 2846 Map attributeMap = new HashMap (); 2847 Iterator i = startElement.attributeEvents.iterator(); 2848 while (i.hasNext()) { 2849 String attributeName; 2850 Object attributeValue; 2851 AttributeEvent attrEvent = (AttributeEvent) i.next(); 2852 attributeName = attrEvent.localName; 2853 if (attrEvent instanceof CopyAttribute) { 2854 CopyAttribute copy = (CopyAttribute)attrEvent; 2855 attributeValue = copy.value; 2856 } else if (attrEvent instanceof SubstituteAttribute) { 2857 SubstituteAttribute substEvent = (SubstituteAttribute)attrEvent; 2858 if (substEvent.substitutions.size() == 1 && substEvent.substitutions.get(0) instanceof JXTExpression) { 2859 JXTExpression expr = (JXTExpression)substEvent.substitutions.get(0); 2860 Object val; 2861 try { 2862 val = getNode(expr, jexlContext, jxpathContext); 2863 } catch (Exception e) { 2864 throw new JXTException(e.getMessage(), ev.location, e); 2865 } 2866 attributeValue = val != null ? val : ""; 2867 } else { 2868 StringBuffer buf = new StringBuffer (); 2869 Iterator iterSubst = substEvent.substitutions.iterator(); 2870 while (iterSubst.hasNext()) { 2871 Subst subst = (Subst)iterSubst.next(); 2872 if (subst instanceof Literal) { 2873 Literal lit = (Literal)subst; 2874 buf.append(lit.value); 2875 } else if (subst instanceof JXTExpression) { 2876 JXTExpression expr = (JXTExpression)subst; 2877 Object val; 2878 try { 2879 val = getValue(expr, jexlContext, jxpathContext); 2880 } catch (Exception e) { 2881 throw new JXTException(e.getMessage(), ev.location, e); 2882 } 2883 buf.append(val != null ? val.toString() : ""); 2884 } 2885 } 2886 attributeValue = buf.toString(); 2887 } 2888 } else { 2889 throw new Error ("this shouldn't have happened"); 2890 } 2891 attributeMap.put(attributeName, attributeValue); 2892 } 2893 MyVariables parent =(MyVariables)jxpathContext.getVariables(); 2894 MyVariables vars = new MyVariables(parent); 2895 MyJexlContext localJexlContext = new MyJexlContext(jexlContext); 2896 HashMap macro = new HashMap (); 2897 macro.put("body", startElement); 2898 macro.put("arguments", attributeMap); 2899 localJexlContext.put("macro", macro); 2900 vars.declareVariable("macro", macro); 2901 Iterator iter = def.parameters.entrySet().iterator(); 2902 while (iter.hasNext()) { 2903 Map.Entry e = (Map.Entry )iter.next(); 2904 String key = (String )e.getKey(); 2905 StartParameter startParam = (StartParameter)e.getValue(); 2906 Object default_ = startParam.default_; 2907 Object val = attributeMap.get(key); 2908 if (val == null) { 2909 val = default_; 2910 } 2911 localJexlContext.put(key, val); 2912 vars.declareVariable(key, val); 2913 } 2914 JXPathContext localJXPathContext = jxpathContextFactory.newContext(null, jxpathContext.getContextBean()); 2915 localJXPathContext.setNamespaceContextPointer(new NamespacesTablePointer(namespaces)); 2916 localJXPathContext.setVariables(vars); 2917 call(ev.location, startElement, consumer, localJexlContext, localJXPathContext, def.body, def.endInstruction); 2918 ev = startElement.endElement.next; 2919 continue; 2920 } 2921 Iterator i = startElement.attributeEvents.iterator(); 2922 AttributesImpl attrs = new AttributesImpl (); 2923 while (i.hasNext()) { 2924 AttributeEvent attrEvent = (AttributeEvent)i.next(); 2925 if (attrEvent instanceof CopyAttribute) { 2926 CopyAttribute copy = (CopyAttribute)attrEvent; 2927 attrs.addAttribute(copy.namespaceURI, copy.localName, copy.raw, copy.type, copy.value); 2928 } else if (attrEvent instanceof SubstituteAttribute) { 2929 StringBuffer buf = new StringBuffer (); 2930 SubstituteAttribute substEvent = (SubstituteAttribute)attrEvent; 2931 Iterator iterSubst = substEvent.substitutions.iterator(); 2932 while (iterSubst.hasNext()) { 2933 Subst subst = (Subst)iterSubst.next(); 2934 if (subst instanceof Literal) { 2935 Literal lit = (Literal)subst; 2936 buf.append(lit.value); 2937 } else if (subst instanceof JXTExpression) { 2938 JXTExpression expr = (JXTExpression)subst; 2939 Object val; 2940 try { 2941 val = getValue(expr, jexlContext, jxpathContext); 2942 } catch (Exception e) { 2943 throw new JXTException(e.getMessage(), ev.location, e); 2944 } 2945 buf.append(val != null ? val.toString() : ""); 2946 } 2947 } 2948 attrs.addAttribute(attrEvent.namespaceURI, attrEvent.localName, attrEvent.raw, attrEvent.type, buf.toString()); 2949 } 2950 } 2951 namespaces.enterScope(consumer); 2952 consumer.startElement(startElement.namespaceURI, startElement.localName, startElement.raw, attrs); 2953 } else if (ev instanceof StartFormatNumber) { 2954 StartFormatNumber startFormatNumber = (StartFormatNumber)ev; 2955 try { 2956 String result = startFormatNumber.format(jexlContext, jxpathContext); 2957 if (result != null) { 2958 char[] chars = result.toCharArray(); 2959 consumer.characters(chars, 0, chars.length); 2960 } 2961 } catch (Exception e) { 2962 throw new JXTException(e.getMessage(), ev.location, e); 2963 } 2964 } else if (ev instanceof StartFormatDate) { 2965 StartFormatDate startFormatDate = (StartFormatDate)ev; 2966 try { 2967 String result = startFormatDate.format(jexlContext, jxpathContext); 2968 if (result != null) { 2969 char[] chars = result.toCharArray(); 2970 consumer.characters(chars, 0, chars.length); 2971 } 2972 } catch (Exception e) { 2973 throw new JXTException(e.getMessage(), ev.location, e); 2974 } 2975 } else if (ev instanceof StartPrefixMapping) { 2976 StartPrefixMapping startPrefixMapping = (StartPrefixMapping)ev; 2977 namespaces.addDeclaration(startPrefixMapping.prefix, startPrefixMapping.uri); 2978 } else if (ev instanceof StartComment) { 2979 StartComment startJXComment = (StartComment)ev; 2980 NodeList nodeList = toDOMNodeList("comment", startJXComment, jexlContext, macroCall); 2982 int len = nodeList.getLength(); 2984 final StringBuffer buf = new StringBuffer (); 2985 Properties omit = XMLUtils.createPropertiesForXML(true); 2986 for (int i = 0; i < len; i++) { 2987 try { 2988 String str = XMLUtils.serializeNode(nodeList.item(i), omit); 2989 buf.append(StringUtils.substringAfter(str, ">")); } catch (Exception e) { 2991 throw new JXTException(e.getMessage(), startJXComment.location, e); 2992 } 2993 } 2994 char[] chars = new char[buf.length()]; 2995 buf.getChars(0, chars.length, chars, 0); 2996 consumer.comment(chars, 0, chars.length); 2997 ev = startJXComment.endInstruction.next; 2998 continue; 2999 } else if (ev instanceof EndCDATA) { 3000 consumer.endCDATA(); 3001 } else if (ev instanceof EndDTD) { 3002 consumer.endDTD(); 3003 } else if (ev instanceof EndEntity) { 3004 consumer.endEntity(((EndEntity)ev).name); 3005 } else if (ev instanceof StartCDATA) { 3006 consumer.startCDATA(); 3007 } else if (ev instanceof StartDTD) { 3008 StartDTD startDTD = (StartDTD)ev; 3009 consumer.startDTD(startDTD.name, startDTD.publicId, startDTD.systemId); 3010 } else if (ev instanceof StartEntity) { 3011 consumer.startEntity(((StartEntity)ev).name); 3012 } else if (ev instanceof StartOut) { 3013 StartOut startOut = (StartOut)ev; 3014 Object val; 3015 try { 3016 val = getNode(startOut.compiledExpression, jexlContext, jxpathContext, startOut.lenient); 3017 if (val instanceof Node ) { 3018 executeDOM(consumer, jexlContext, jxpathContext, (Node )val); 3019 } else if (val instanceof NodeList ) { 3020 NodeList nodeList = (NodeList )val; 3021 int len = nodeList.getLength(); 3022 for (int i = 0; i < len; i++) { 3023 Node n = nodeList.item(i); 3024 executeDOM(consumer, jexlContext, jxpathContext, n); 3025 } 3026 } else if (val instanceof Node []) { 3027 Node [] nodeList = (Node [])val; 3028 int len = nodeList.length; 3029 for (int i = 0;i < len; i++) { 3030 Node n = nodeList[i]; 3031 executeDOM(consumer, jexlContext, jxpathContext, n); 3032 } 3033 } else if (val instanceof XMLizable) { 3034 ((XMLizable)val).toSAX(new IncludeXMLConsumer(consumer)); 3035 } else { 3036 char[] ch = val == null ? ArrayUtils.EMPTY_CHAR_ARRAY : val.toString().toCharArray(); 3037 consumer.characters(ch, 0, ch.length); 3038 } 3039 } catch (Exception e) { 3040 throw new JXTException(e.getMessage(), ev.location, e); 3041 } 3042 } else if (ev instanceof StartTemplate) { 3043 } else if (ev instanceof StartEval) { 3045 StartEval startEval = (StartEval)ev; 3046 JXTExpression expr = startEval.value; 3047 try { 3048 Object val = getNode(expr, jexlContext, jxpathContext); 3049 if (!(val instanceof StartElement)) { 3050 throw new Exception ("macro invocation required instead of: " + val); 3051 } 3052 StartElement call = (StartElement)val; 3053 execute(consumer, jexlContext, jxpathContext, call, call.next, call.endElement); 3054 } catch (Exception exc) { 3055 throw new JXTException(exc.getMessage(), ev.location, exc); 3056 } 3057 ev = startEval.endInstruction.next; 3058 continue; 3059 } else if (ev instanceof StartEvalBody) { 3060 StartEvalBody startEval = (StartEvalBody)ev; 3061 try { 3062 execute(consumer, jexlContext, jxpathContext, null, macroCall.next, macroCall.endElement); 3063 } catch (Exception exc) { 3064 throw new JXTException(exc.getMessage(), ev.location, exc); 3065 } 3066 ev = startEval.endInstruction.next; 3067 continue; 3068 } else if (ev instanceof StartDefine) { 3069 StartDefine startDefine = (StartDefine)ev; 3070 definitions.put(startDefine.qname, startDefine); 3071 ev = startDefine.endInstruction.next; 3072 continue; 3073 } else if (ev instanceof StartImport) { 3074 StartImport startImport = (StartImport)ev; 3075 String uri; 3076 AttributeEvent e = startImport.uri; 3077 if (e instanceof CopyAttribute) { 3078 CopyAttribute copy = (CopyAttribute)e; 3079 uri = copy.value; 3080 } else { 3081 StringBuffer buf = new StringBuffer (); 3082 SubstituteAttribute substAttr = (SubstituteAttribute)e; 3083 Iterator i = substAttr.substitutions.iterator(); 3084 while (i.hasNext()) { 3085 Subst subst = (Subst)i.next(); 3086 if (subst instanceof Literal) { 3087 Literal lit = (Literal)subst; 3088 buf.append(lit.value); 3089 } else if (subst instanceof JXTExpression) { 3090 JXTExpression expr = (JXTExpression)subst; 3091 Object val; 3092 try { 3093 val = getValue(expr, jexlContext, jxpathContext); 3094 } catch (Exception exc) { 3095 throw new JXTException(exc.getMessage(), ev.location, exc); 3096 } 3097 buf.append(val != null ? val.toString() : ""); 3098 } 3099 } 3100 uri = buf.toString(); 3101 } 3102 Source input = null; 3103 StartDocument doc; 3104 try { 3105 input = resolver.resolveURI(uri); 3106 SourceValidity validity = null; 3107 synchronized (cache) { 3108 doc = (StartDocument)cache.get(input.getURI()); 3109 if (doc != null) { 3110 boolean recompile = false; 3111 if (doc.compileTime == null) { 3112 recompile = true; 3113 } else { 3114 int valid = doc.compileTime.isValid(); 3115 if (valid == SourceValidity.UNKNOWN) { 3116 validity = input.getValidity(); 3117 valid = doc.compileTime.isValid(validity); 3118 } 3119 if (valid != SourceValidity.VALID) { 3120 recompile = true; 3121 } 3122 } 3123 if (recompile) { 3124 doc = null; } 3126 } 3127 } 3128 if (doc == null) { 3129 Parser parser = new Parser(); 3130 if (validity == null) { 3132 validity = input.getValidity(); 3133 } 3134 SourceUtil.parse(this.manager, input, parser); 3135 doc = parser.getStartEvent(); 3136 doc.compileTime = validity; 3137 synchronized (cache) { 3138 cache.put(input.getURI(), doc); 3139 } 3140 } 3141 } catch (Exception exc) { 3142 throw new JXTException(exc.getMessage(), ev.location, exc); 3143 } 3144 finally { 3145 resolver.release(input); 3146 } 3147 JXPathContext selectJXPath = jxpathContext; 3148 MyJexlContext selectJexl = jexlContext; 3149 if (startImport.select != null) { 3150 try { 3151 Object obj = getValue(startImport.select, jexlContext, jxpathContext); 3152 selectJXPath = jxpathContextFactory.newContext(null, obj); 3153 selectJXPath.setNamespaceContextPointer(new NamespacesTablePointer(namespaces)); 3154 selectJXPath.setVariables(variables); 3155 selectJexl = new MyJexlContext(jexlContext); 3156 fillContext(obj, selectJexl); 3157 } catch (Exception exc) { 3158 throw new JXTException(exc.getMessage(), ev.location, exc); 3159 } 3160 } 3161 try { 3162 execute(consumer, selectJexl, selectJXPath, macroCall, doc.next, doc.endDocument); 3163 } catch (Exception exc) { 3164 throw new JXTException("Exception occurred in imported template " + uri + ": "+ exc.getMessage(), ev.location, exc); 3165 } 3166 ev = startImport.endInstruction.next; 3167 continue; 3168 } else if (ev instanceof StartDocument) { 3169 if (((StartDocument)ev).endDocument != null) { 3170 consumer.startDocument(); 3172 } 3173 } else if (ev instanceof EndDocument) { 3174 consumer.endDocument(); 3175 } else if (ev instanceof ProcessingInstruction) { 3176 ProcessingInstruction pi = (ProcessingInstruction)ev; 3177 consumer.processingInstruction(pi.target, pi.data); 3178 } 3179 ev = ev.next; 3180 } 3181 } 3182 3183 3186 public Serializable getKey() { 3187 JXTExpression cacheKeyExpr = (JXTExpression)getCurrentTemplateProperty(CACHE_KEY); 3188 try { 3189 final Serializable templateKey = (Serializable ) getValue(cacheKeyExpr, globalJexlContext, jxpathContext); 3190 if (templateKey != null) { 3191 return new JXCacheKey(this.inputSource.getURI(), templateKey); 3192 } 3193 } catch (Exception e) { 3194 getLogger().error("error evaluating cache key", e); 3195 } 3196 return null; 3197 } 3198 3199 3202 public SourceValidity getValidity() { 3203 JXTExpression validityExpr = (JXTExpression)getCurrentTemplateProperty(VALIDITY); 3204 try { 3205 final SourceValidity sourceValidity = this.inputSource.getValidity(); 3206 final SourceValidity templateValidity = (SourceValidity) getValue(validityExpr, globalJexlContext, jxpathContext); 3207 if (sourceValidity != null && templateValidity != null) { 3208 return new JXSourceValidity(sourceValidity, templateValidity); 3209 } 3210 } catch (Exception e) { 3211 getLogger().error("error evaluating cache validity", e); 3212 } 3213 return null; 3214 } 3215 3216 private Object getCurrentTemplateProperty(String propertyName) { 3217 final String uri = this.inputSource.getURI(); 3218 StartDocument startEvent; 3219 synchronized (cache) { 3220 startEvent = (StartDocument)cache.get(uri); 3221 } 3222 return (startEvent != null) ? startEvent.templateProperties.get(propertyName) : null; 3223 } 3224 3225 private NodeList toDOMNodeList(String elementName, StartInstruction si, 3226 MyJexlContext jexlContext, StartElement macroCall) throws SAXException { 3227 DOMBuilder builder = new DOMBuilder(); 3228 builder.startDocument(); 3229 builder.startElement(NS, elementName, elementName, EMPTY_ATTRS); 3230 execute(builder, jexlContext, jxpathContext, macroCall, si.next, si.endInstruction); 3231 builder.endElement(NS, elementName, elementName); 3232 builder.endDocument(); 3233 Node node = builder.getDocument().getDocumentElement(); 3234 return node.getChildNodes(); 3235 } 3236 3237 static final class JXCacheKey implements Serializable { 3238 private final String templateUri; 3239 private final Serializable templateKey; 3240 private JXCacheKey(String templateUri, Serializable templateKey) { 3241 this.templateUri = templateUri; 3242 this.templateKey = templateKey; 3243 } 3244 public int hashCode() { 3245 return templateUri.hashCode() + templateKey.hashCode(); 3246 } 3247 public String toString() { 3248 return "TK:" + templateUri + "_" + templateKey; 3249 } 3250 public boolean equals(Object o) { 3251 if (o instanceof JXCacheKey) { 3252 JXCacheKey jxck = (JXCacheKey)o; 3253 return this.templateUri.equals(jxck.templateUri) 3254 && this.templateKey.equals(jxck.templateKey); 3255 } 3256 return false; 3257 } 3258 } 3259 3260 static final class JXSourceValidity implements SourceValidity, Serializable { 3261 private final SourceValidity sourceValidity; 3262 private final SourceValidity templateValidity; 3263 3264 private JXSourceValidity(SourceValidity sourceValidity, SourceValidity templateValidity) { 3265 this.sourceValidity = sourceValidity; 3266 this.templateValidity = templateValidity; 3267 } 3268 3269 public int isValid() { 3270 switch (sourceValidity.isValid()) { 3271 case SourceValidity.INVALID: 3272 return SourceValidity.INVALID; 3273 3274 case SourceValidity.UNKNOWN: 3275 if (templateValidity.isValid() == SourceValidity.INVALID) { 3276 return SourceValidity.INVALID; 3277 } 3278 return SourceValidity.UNKNOWN; 3279 3280 case SourceValidity.VALID: 3281 return templateValidity.isValid(); 3282 } 3283 3284 return SourceValidity.UNKNOWN; 3285 } 3286 3287 public int isValid(SourceValidity otherValidity) { 3288 if (otherValidity instanceof JXSourceValidity) { 3289 JXSourceValidity otherJXValidity = (JXSourceValidity) otherValidity; 3290 switch (sourceValidity.isValid(otherJXValidity.sourceValidity)) { 3291 case SourceValidity.INVALID: 3292 return SourceValidity.INVALID; 3293 3294 case SourceValidity.UNKNOWN: 3295 if (templateValidity.isValid(otherJXValidity.templateValidity) == SourceValidity.INVALID) { 3296 return SourceValidity.INVALID; 3297 } 3298 return SourceValidity.UNKNOWN; 3299 3300 case SourceValidity.VALID: 3301 return templateValidity.isValid(otherJXValidity.templateValidity); 3302 } 3303 } 3304 return SourceValidity.UNKNOWN; 3305 } 3306 } 3307} 3308 | Popular Tags |