1 52 53 package freemarker.core; 54 55 import java.io.*; 56 import java.text.*; 57 import java.util.*; 58 59 import freemarker.ext.beans.BeansWrapper; 60 import freemarker.log.Logger; 61 import freemarker.template.*; 62 import freemarker.template.utility.UndeclaredThrowableException; 63 64 82 public final class Environment extends Configurable { 83 84 private static final ThreadLocal threadEnv = new ThreadLocal (); 85 86 private static final Logger logger = Logger.getLogger("freemarker.runtime"); 87 88 private static final Map localizedNumberFormats = new HashMap(); 89 private static final Map localizedDateFormats = new HashMap(); 90 91 private final TemplateHashModel rootDataModel; 92 private final ArrayList elementStack = new ArrayList(); 93 private final ArrayList recoveredErrorStack = new ArrayList(); 94 95 private NumberFormat numberFormat; 96 private Map numberFormats; 97 98 private DateFormat timeFormat, dateFormat, dateTimeFormat; 99 private Map[] dateFormats; 100 101 private Collator collator; 102 103 private Writer out; 104 private Macro.Context currentMacroContext; 105 private ArrayList localContextStack; 106 private Namespace mainNamespace, currentNamespace, globalNamespace; 107 private HashMap loadedLibs; 108 109 private Throwable lastThrowable; 110 111 private TemplateModel lastReturnValue; 112 private HashMap macroToNamespaceLookup = new HashMap(); 113 114 private TemplateNodeModel currentVisitorNode; 115 private TemplateSequenceModel nodeNamespaces; 116 private int nodeNamespaceIndex; 118 private String currentNodeName, currentNodeNS; 119 120 private String cachedURLEscapingCharset; 121 private boolean urlEscapingCharsetCached; 122 123 130 public static Environment getCurrentEnvironment() 131 { 132 return (Environment)threadEnv.get(); 133 } 134 135 public Environment(Template template, final TemplateHashModel rootDataModel, Writer out) 136 { 137 super(template); 138 this.globalNamespace = new Namespace(null); 139 this.currentNamespace = mainNamespace = new Namespace(template); 140 this.out = out; 141 this.rootDataModel = rootDataModel; 142 importMacros(template); 143 } 144 145 148 public Template getTemplate() 149 { 150 return (Template)getParent(); 151 } 152 153 157 private void clearCachedValues() { 158 numberFormats = null; 159 numberFormat = null; 160 dateFormats = null; 161 collator = null; 162 cachedURLEscapingCharset = null; 163 urlEscapingCharsetCached = false; 164 } 165 166 169 public void process() throws TemplateException, IOException { 170 Object savedEnv = threadEnv.get(); 171 threadEnv.set(this); 172 try { 173 clearCachedValues(); 175 try { 176 visit(getTemplate().getRootTreeNode()); 177 out.flush(); 179 } finally { 180 clearCachedValues(); 182 } 183 } finally { 184 threadEnv.set(savedEnv); 185 } 186 } 187 188 191 void visit(TemplateElement element) 192 throws TemplateException, IOException 193 { 194 pushElement(element); 195 try { 196 element.accept(this); 197 } 198 catch (TemplateException te) { 199 handleTemplateException(te); 200 } 201 finally { 202 popElement(); 203 } 204 } 205 206 214 void visit(TemplateElement element, 215 TemplateTransformModel transform, 216 Map args) 217 throws TemplateException, IOException 218 { 219 try { 220 Writer tw = transform.getWriter(out, args); 221 if (tw == null) tw = EMPTY_BODY_WRITER; 222 TransformControl tc = 223 tw instanceof TransformControl 224 ? (TransformControl)tw 225 : null; 226 227 Writer prevOut = out; 228 out = tw; 229 try { 230 if(tc == null || tc.onStart() != TransformControl.SKIP_BODY) { 231 do { 232 if(element != null) { 233 visit(element); 234 } 235 } while(tc != null && tc.afterBody() == TransformControl.REPEAT_EVALUATION); 236 } 237 } 238 catch(Throwable t) { 239 try { 240 if(tc != null) { 241 tc.onError(t); 242 } 243 else { 244 throw t; 245 } 246 } 247 catch(TemplateException e) { 248 throw e; 249 } 250 catch(IOException e) { 251 throw e; 252 } 253 catch(RuntimeException e) { 254 throw e; 255 } 256 catch(Error e) { 257 throw e; 258 } 259 catch(Throwable e) { 260 throw new UndeclaredThrowableException(e); 261 } 262 } 263 finally { 264 out = prevOut; 265 tw.close(); 266 } 267 } 268 catch(TemplateException te) { 269 handleTemplateException(te); 270 } 271 } 272 273 276 277 void visit(TemplateElement attemptBlock, TemplateElement recoveryBlock) 278 throws TemplateException, IOException { 279 Writer prevOut = this.out; 280 StringWriter sw = new StringWriter(); 281 this.out = sw; 282 TemplateException thrownException = null; 283 try { 284 visit(attemptBlock); 285 } catch (TemplateException te) { 286 thrownException = te; 287 } finally { 288 this.out = prevOut; 289 } 290 if (thrownException != null) { 291 if (logger.isErrorEnabled()) { 292 String msg = "Error in attempt block " + attemptBlock.getStartLocation(); 293 logger.error(msg, thrownException); 294 } 295 try { 296 recoveredErrorStack.add(thrownException.getMessage()); 297 visit(recoveryBlock); 298 } finally { 299 recoveredErrorStack.remove(recoveredErrorStack.size() -1); 300 } 301 } else { 302 out.write(sw.toString()); 303 } 304 } 305 306 String getCurrentRecoveredErrorMesssage() throws TemplateException { 307 if(recoveredErrorStack.isEmpty()) { 308 throw new TemplateException( 309 ".error is not available outside of a <#recover> block", this); 310 } 311 return (String ) recoveredErrorStack.get(recoveredErrorStack.size() -1); 312 } 313 314 315 void visit(BodyInstruction.Context bctxt) throws TemplateException, IOException { 316 Macro.Context invokingMacroContext = getCurrentMacroContext(); 317 ArrayList prevLocalContextStack = localContextStack; 318 TemplateElement body = invokingMacroContext.body; 319 if (body != null) { 320 this.currentMacroContext = invokingMacroContext.prevMacroContext; 321 currentNamespace = invokingMacroContext.bodyNamespace; 322 Configurable prevParent = getParent(); 323 setParent(currentNamespace.getTemplate()); 324 this.localContextStack = invokingMacroContext.prevLocalContextStack; 325 if (invokingMacroContext.bodyParameterNames != null) { 326 pushLocalContext(bctxt); 327 } 328 try { 329 visit(body); 330 } 331 finally { 332 if (invokingMacroContext.bodyParameterNames != null) { 333 popLocalContext(); 334 } 335 this.currentMacroContext = invokingMacroContext; 336 currentNamespace = getMacroNamespace(invokingMacroContext.getMacro()); 337 setParent(prevParent); 338 this.localContextStack = prevLocalContextStack; 339 } 340 } 341 } 342 343 346 void visit(IteratorBlock.Context ictxt) 347 throws TemplateException, IOException 348 { 349 pushLocalContext(ictxt); 350 try { 351 ictxt.runLoop(this); 352 } 353 catch (BreakInstruction.Break br) { 354 } 355 catch (TemplateException te) { 356 handleTemplateException(te); 357 } 358 finally { 359 popLocalContext(); 360 } 361 } 362 363 366 367 void visit(TemplateNodeModel node, TemplateSequenceModel namespaces) 368 throws TemplateException, IOException 369 { 370 if (nodeNamespaces == null) { 371 SimpleSequence ss = new SimpleSequence(1); 372 ss.add(currentNamespace); 373 nodeNamespaces = ss; 374 } 375 int prevNodeNamespaceIndex = this.nodeNamespaceIndex; 376 String prevNodeName = this.currentNodeName; 377 String prevNodeNS = this.currentNodeNS; 378 TemplateSequenceModel prevNodeNamespaces = nodeNamespaces; 379 TemplateNodeModel prevVisitorNode = currentVisitorNode; 380 currentVisitorNode = node; 381 if (namespaces != null) { 382 this.nodeNamespaces = namespaces; 383 } 384 try { 385 TemplateModel macroOrTransform = getNodeProcessor(node); 386 if (macroOrTransform instanceof Macro) { 387 visit((Macro) macroOrTransform, null, null, null, null); 388 } 389 else if (macroOrTransform instanceof TemplateTransformModel) { 390 visit(null, (TemplateTransformModel) macroOrTransform, null); 391 } 392 else { 393 String nodeType = node.getNodeType(); 394 if (nodeType != null) { 395 if ((nodeType.equals("text") && node instanceof TemplateScalarModel)) 397 { 398 out.write(((TemplateScalarModel) node).getAsString()); 399 } 400 else if (nodeType.equals("document")) { 401 recurse(node, namespaces); 402 } 403 else if (!nodeType.equals("pi") 406 && !nodeType.equals("comment") 407 && !nodeType.equals("document_type")) 408 { 409 String nsBit = ""; 410 String ns = node.getNodeNamespace(); 411 if (ns != null) { 412 if (ns.length() >0) { 413 nsBit = " and namespace " + ns; 414 } else { 415 nsBit = " and no namespace"; 416 } 417 } 418 throw new TemplateException("No macro or transform defined for node named " 419 + node.getNodeName() + nsBit 420 + ", and there is no fallback handler called @" + nodeType + " either.", 421 this); 422 } 423 } 424 else { 425 String nsBit = ""; 426 String ns = node.getNodeNamespace(); 427 if (ns != null) { 428 if (ns.length() >0) { 429 nsBit = " and namespace " + ns; 430 } else { 431 nsBit = " and no namespace"; 432 } 433 } 434 throw new TemplateException("No macro or transform defined for node with name " 435 + node.getNodeName() + nsBit 436 + ", and there is no macro or transform called @default either.", 437 this); 438 } 439 } 440 } 441 finally { 442 this.currentVisitorNode = prevVisitorNode; 443 this.nodeNamespaceIndex = prevNodeNamespaceIndex; 444 this.currentNodeName = prevNodeName; 445 this.currentNodeNS = prevNodeNS; 446 this.nodeNamespaces = prevNodeNamespaces; 447 } 448 } 449 450 void fallback() throws TemplateException, IOException { 451 TemplateModel macroOrTransform = getNodeProcessor(currentNodeName, currentNodeNS, nodeNamespaceIndex); 452 if (macroOrTransform instanceof Macro) { 453 visit((Macro) macroOrTransform, null, null, null, null); 454 } 455 else if (macroOrTransform instanceof TemplateTransformModel) { 456 visit(null, (TemplateTransformModel) macroOrTransform, null); 457 } 458 } 459 460 463 464 void visit(Macro macro, 465 Map namedArgs, 466 List positionalArgs, 467 List bodyParameterNames, 468 TemplateElement nestedBlock) 469 throws TemplateException, IOException 470 { 471 if (macro == Macro.DO_NOTHING_MACRO) { 472 return; 473 } 474 pushElement(macro); 475 try { 476 Macro.Context previousMacroContext = currentMacroContext; 477 Macro.Context mc = macro.new Context(this, nestedBlock, bodyParameterNames); 478 479 String catchAll = macro.getCatchAll(); 480 TemplateModel unknownVars = null; 481 482 if (namedArgs != null) { 483 if (catchAll != null) 484 unknownVars = new SimpleHash(); 485 for (Iterator it = namedArgs.entrySet().iterator(); it.hasNext();) { 486 Map.Entry entry = (Map.Entry) it.next(); 487 String varName = (String ) entry.getKey(); 488 boolean hasVar = macro.hasArgNamed(varName); 489 if (hasVar || catchAll != null) { 490 Expression arg = (Expression) entry.getValue(); 491 TemplateModel value = arg.getAsTemplateModel(this); 492 if (hasVar) { 493 mc.setLocalVar(varName, value); 494 } else { 495 ((SimpleHash)unknownVars).put(varName, value); 496 } 497 } else { 498 String msg = "Macro " + macro.getName() + " has no such argument: " + varName; 499 throw new TemplateException(msg, this); 500 } 501 } 502 } 503 else if (positionalArgs != null) { 504 if (catchAll != null) 505 unknownVars = new SimpleSequence(); 506 String [] argumentNames = macro.getArgumentNames(); 507 int size = positionalArgs.size(); 508 if (argumentNames.length < size && catchAll == null) { 509 throw new TemplateException("Macro " + macro.getName() 510 + " only accepts " + argumentNames.length + " parameters.", this); 511 } 512 for (int i = 0; i < size; i++) { 513 Expression argExp = (Expression) positionalArgs.get(i); 514 TemplateModel argModel = argExp.getAsTemplateModel(this); 515 try { 516 if (i < argumentNames.length) { 517 String argName = argumentNames[i]; 518 mc.setLocalVar(argName, argModel); 519 } else { 520 ((SimpleSequence)unknownVars).add(argModel); 521 } 522 } catch (RuntimeException re) { 523 throw new TemplateException(re, this); 524 } 525 } 526 } 527 if (catchAll != null) { 528 mc.setLocalVar(catchAll, unknownVars); 529 } 530 ArrayList prevLocalContextStack = localContextStack; 531 localContextStack = null; 532 Namespace prevNamespace = currentNamespace; 533 Configurable prevParent = getParent(); 534 currentNamespace = (Namespace) macroToNamespaceLookup.get(macro); 535 currentMacroContext = mc; 536 try { 537 mc.runMacro(this); 538 } 539 catch (ReturnInstruction.Return re) { 540 } 541 catch (TemplateException te) { 542 handleTemplateException(te); 543 } finally { 544 currentMacroContext = previousMacroContext; 545 localContextStack = prevLocalContextStack; 546 currentNamespace = prevNamespace; 547 setParent(prevParent); 548 } 549 } finally { 550 popElement(); 551 } 552 } 553 554 void visitMacroDef(Macro macro) { 555 macroToNamespaceLookup.put(macro, currentNamespace); 556 currentNamespace.put(macro.getName(), macro); 557 } 558 559 Namespace getMacroNamespace(Macro macro) { 560 return (Namespace) macroToNamespaceLookup.get(macro); 561 } 562 563 void recurse(TemplateNodeModel node, TemplateSequenceModel namespaces) 564 throws TemplateException, IOException 565 { 566 if (node == null) { 567 node = this.getCurrentVisitorNode(); 568 } 569 TemplateSequenceModel children = node.getChildNodes(); 570 if (children == null) return; 571 for (int i=0; i<children.size(); i++) { 572 TemplateNodeModel child = (TemplateNodeModel) children.get(i); 573 if (child != null) { 574 visit(child, namespaces); 575 } 576 } 577 } 578 579 Macro.Context getCurrentMacroContext() { 580 return currentMacroContext; 581 } 582 583 private void handleTemplateException(TemplateException te) 584 throws TemplateException 585 { 586 if(lastThrowable == te) { 589 throw te; 590 } 591 lastThrowable = te; 592 593 if(logger.isErrorEnabled()) { 595 logger.error("", te); 596 } 597 598 if(te instanceof StopException) { 601 throw te; 602 } 603 604 getTemplateExceptionHandler().handleTemplateException(te, this, out); 606 } 607 608 public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) { 609 super.setTemplateExceptionHandler(templateExceptionHandler); 610 lastThrowable = null; 611 } 612 613 public void setLocale(Locale locale) { 614 super.setLocale(locale); 615 numberFormats = null; 617 numberFormat = null; 618 619 dateFormats = null; 620 timeFormat = dateFormat = dateTimeFormat = null; 621 622 collator = null; 623 } 624 625 public void setTimeZone(TimeZone timeZone) { 626 super.setTimeZone(timeZone); 627 dateFormats = null; 629 timeFormat = dateFormat = dateTimeFormat = null; 630 } 631 632 public void setURLEscapingCharset(String urlEscapingCharset) { 633 urlEscapingCharsetCached = false; 634 super.setURLEscapingCharset(urlEscapingCharset); 635 } 636 637 643 public void setOutputEncoding(String outputEncoding) { 644 urlEscapingCharsetCached = false; 645 super.setOutputEncoding(outputEncoding); 646 } 647 648 654 String getEffectiveURLEscapingCharset() { 655 if (!urlEscapingCharsetCached) { 656 cachedURLEscapingCharset = getURLEscapingCharset(); 657 if (cachedURLEscapingCharset == null) { 658 cachedURLEscapingCharset = getOutputEncoding(); 659 } 660 urlEscapingCharsetCached = true; 661 } 662 return cachedURLEscapingCharset; 663 } 664 665 Collator getCollator() { 666 if(collator == null) { 667 collator = Collator.getInstance(getLocale()); 668 } 669 return collator; 670 } 671 672 public void setOut(Writer out) { 673 this.out = out; 674 } 675 676 public Writer getOut() { 677 return out; 678 } 679 680 String formatNumber(Number number) { 681 if(numberFormat == null) { 682 numberFormat = getNumberFormatObject(getNumberFormat()); 683 } 684 return numberFormat.format(number); 685 } 686 687 public void setNumberFormat(String formatName) { 688 super.setNumberFormat(formatName); 689 numberFormat = null; 690 } 691 692 String formatDate(Date date, int type) throws TemplateModelException { 693 DateFormat df = getDateFormatObject(type); 694 if(df == null) { 695 throw new TemplateModelException("Can't convert the date to string, because it is not known which parts of the date variable are in use. Use ?date, ?time or ?datetime built-in, or ?string.<format> or ?string(format) built-in with this date."); 696 } 697 return df.format(date); 698 } 699 700 public void setTimeFormat(String formatName) { 701 super.setTimeFormat(formatName); 702 timeFormat = null; 703 } 704 705 public void setDateFormat(String formatName) { 706 super.setDateFormat(formatName); 707 dateFormat = null; 708 } 709 710 public void setDateTimeFormat(String formatName) { 711 super.setDateTimeFormat(formatName); 712 dateTimeFormat = null; 713 } 714 715 public Configuration getConfiguration() { 716 return getTemplate().getConfiguration(); 717 } 718 719 TemplateModel getLastReturnValue() { 720 return lastReturnValue; 721 } 722 723 void setLastReturnValue(TemplateModel lastReturnValue) { 724 this.lastReturnValue = lastReturnValue; 725 } 726 727 void clearLastReturnValue() { 728 this.lastReturnValue = null; 729 } 730 731 NumberFormat getNumberFormatObject(String pattern) 732 { 733 if(numberFormats == null) { 734 numberFormats = new HashMap(); 735 } 736 737 NumberFormat format = (NumberFormat) numberFormats.get(pattern); 738 if(format != null) 739 { 740 return format; 741 } 742 743 synchronized(localizedNumberFormats) 745 { 746 Locale locale = getLocale(); 747 NumberFormatKey fk = new NumberFormatKey(pattern, locale); 748 format = (NumberFormat)localizedNumberFormats.get(fk); 749 if(format == null) 750 { 751 if("number".equals(pattern)) 754 { 755 format = NumberFormat.getNumberInstance(locale); 756 } 757 else if("currency".equals(pattern)) 758 { 759 format = NumberFormat.getCurrencyInstance(locale); 760 } 761 else if("percent".equals(pattern)) 762 { 763 format = NumberFormat.getPercentInstance(locale); 764 } 765 else 766 { 767 format = new DecimalFormat(pattern, new DecimalFormatSymbols(getLocale())); 768 } 769 localizedNumberFormats.put(fk, format); 770 } 771 } 772 773 format = (NumberFormat)format.clone(); 775 numberFormats.put(pattern, format); 776 return format; 777 } 778 779 DateFormat getDateFormatObject(int dateType) 780 throws 781 TemplateModelException 782 { 783 switch(dateType) { 784 case TemplateDateModel.UNKNOWN: { 785 return null; 786 } 787 case TemplateDateModel.TIME: { 788 if(timeFormat == null) { 789 timeFormat = getDateFormatObject(dateType, getTimeFormat()); 790 } 791 return timeFormat; 792 } 793 case TemplateDateModel.DATE: { 794 if(dateFormat == null) { 795 dateFormat = getDateFormatObject(dateType, getDateFormat()); 796 } 797 return dateFormat; 798 } 799 case TemplateDateModel.DATETIME: { 800 if(dateTimeFormat == null) { 801 dateTimeFormat = getDateFormatObject(dateType, getDateTimeFormat()); 802 } 803 return dateTimeFormat; 804 } 805 default: { 806 throw new TemplateModelException("Unrecognized date type " + dateType); 807 } 808 } 809 } 810 811 DateFormat getDateFormatObject(int dateType, String pattern) 812 throws 813 TemplateModelException 814 { 815 if(dateFormats == null) { 816 dateFormats = new Map[4]; 817 dateFormats[TemplateDateModel.UNKNOWN] = new HashMap(); 818 dateFormats[TemplateDateModel.TIME] = new HashMap(); 819 dateFormats[TemplateDateModel.DATE] = new HashMap(); 820 dateFormats[TemplateDateModel.DATETIME] = new HashMap(); 821 } 822 Map typedDateFormat = dateFormats[dateType]; 823 824 DateFormat format = (DateFormat) typedDateFormat.get(pattern); 825 if(format != null) { 826 return format; 827 } 828 829 synchronized(localizedDateFormats) { 831 Locale locale = getLocale(); 832 TimeZone timeZone = getTimeZone(); 833 DateFormatKey fk = new DateFormatKey(dateType, pattern, locale, timeZone); 834 format = (DateFormat)localizedDateFormats.get(fk); 835 if(format == null) { 836 StringTokenizer tok = new StringTokenizer(pattern, "_"); 839 int style = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : DateFormat.DEFAULT; 840 if(style != -1) { 841 switch(dateType) { 842 case TemplateDateModel.UNKNOWN: { 843 throw new TemplateModelException( 844 "Can't convert the date to string using a " + 845 "built-in format, because it is not known which " + 846 "parts of the date variable are in use. Use " + 847 "?date, ?time or ?datetime built-in, or " + 848 "?string.<format> or ?string(<format>) built-in "+ 849 "with explicit formatting pattern with this date."); 850 } 851 case TemplateDateModel.TIME: { 852 format = DateFormat.getTimeInstance(style, locale); 853 break; 854 } 855 case TemplateDateModel.DATE: { 856 format = DateFormat.getDateInstance(style, locale); 857 break; 858 } 859 case TemplateDateModel.DATETIME: { 860 int timestyle = tok.hasMoreTokens() ? parseDateStyleToken(tok.nextToken()) : style; 861 if(timestyle != -1) { 862 format = DateFormat.getDateTimeInstance(style, timestyle, locale); 863 } 864 break; 865 } 866 } 867 } 868 if(format == null) { 869 try { 870 format = new SimpleDateFormat(pattern, locale); 871 } 872 catch(IllegalArgumentException e) { 873 throw new TemplateModelException("Can't parse " + pattern + " to a date format.", e); 874 } 875 } 876 format.setTimeZone(timeZone); 877 localizedDateFormats.put(fk, format); 878 } 879 } 880 881 format = (DateFormat)format.clone(); 883 typedDateFormat.put(pattern, format); 884 return format; 885 } 886 887 int parseDateStyleToken(String token) { 888 if("short".equals(token)) { 889 return DateFormat.SHORT; 890 } 891 if("medium".equals(token)) { 892 return DateFormat.MEDIUM; 893 } 894 if("long".equals(token)) { 895 return DateFormat.LONG; 896 } 897 return -1; 898 } 899 900 TemplateTransformModel getTransform(Expression exp) throws TemplateException { 901 TemplateTransformModel ttm = null; 902 TemplateModel tm = exp.getAsTemplateModel(this); 903 if (tm instanceof TemplateTransformModel) { 904 ttm = (TemplateTransformModel) tm; 905 } 906 else if (exp instanceof Identifier) { 907 tm = getConfiguration().getSharedVariable(exp.toString()); 908 if (tm instanceof TemplateTransformModel) { 909 ttm = (TemplateTransformModel) tm; 910 } 911 } 912 return ttm; 913 } 914 915 921 public TemplateModel getLocalVariable(String name) throws TemplateModelException { 922 if (localContextStack != null) { 923 for (int i = localContextStack.size()-1; i>=0; i--) { 924 LocalContext lc = (LocalContext) localContextStack.get(i); 925 TemplateModel tm = lc.getLocalVariable(name); 926 if (tm != null) { 927 return tm; 928 } 929 } 930 } 931 return currentMacroContext == null ? null : currentMacroContext.getLocalVariable(name); 932 } 933 934 952 public TemplateModel getVariable(String name) throws TemplateModelException { 953 TemplateModel result = getLocalVariable(name); 954 if (result == null) { 955 result = currentNamespace.get(name); 956 } 957 if (result == null) { 958 result = getGlobalVariable(name); 959 } 960 return result; 961 } 962 963 969 public TemplateModel getGlobalVariable(String name) throws TemplateModelException { 970 TemplateModel result = globalNamespace.get(name); 971 if (result == null) { 972 result = rootDataModel.get(name); 973 } 974 if (result == null) { 975 result = getConfiguration().getSharedVariable(name); 976 } 977 return result; 978 } 979 980 986 public void setGlobalVariable(String name, TemplateModel model) { 987 globalNamespace.put(name, model); 988 } 989 990 996 public void setVariable(String name, TemplateModel model) { 997 currentNamespace.put(name, model); 998 } 999 1000 1008 public void setLocalVariable(String name, TemplateModel model) { 1009 if(currentMacroContext == null) { 1010 throw new IllegalStateException ("Not executing macro body"); 1011 } 1012 currentMacroContext.setLocalVar(name, model); 1013 } 1014 1015 1027 public Set getKnownVariableNames() throws TemplateModelException { 1028 Set set = getConfiguration().getSharedVariableNames(); 1030 1031 if (rootDataModel instanceof TemplateHashModelEx) { 1033 TemplateModelIterator rootNames = 1034 ((TemplateHashModelEx) rootDataModel).keys().iterator(); 1035 while(rootNames.hasNext()) { 1036 set.add(((TemplateScalarModel)rootNames.next()).getAsString()); 1037 } 1038 } 1039 1040 for (TemplateModelIterator tmi = globalNamespace.keys().iterator(); tmi.hasNext();) { 1042 set.add(((TemplateScalarModel) tmi.next()).getAsString()); 1043 } 1044 1045 for (TemplateModelIterator tmi = currentNamespace.keys().iterator(); tmi.hasNext();) { 1047 set.add(((TemplateScalarModel) tmi.next()).getAsString()); 1048 } 1049 1050 if(currentMacroContext != null) { 1052 set.addAll(currentMacroContext.getLocalVariableNames()); 1053 } 1054 if (localContextStack != null) { 1055 for (int i = localContextStack.size()-1; i>=0; i--) { 1056 LocalContext lc = (LocalContext) localContextStack.get(i); 1057 set.addAll(lc.getLocalVariableNames()); 1058 } 1059 } 1060 return set; 1061 } 1062 1063 1068 public void outputInstructionStack(PrintWriter pw) { 1069 pw.println("----------"); 1070 ListIterator iter = elementStack.listIterator(elementStack.size()); 1071 if(iter.hasPrevious()) { 1072 pw.print("==> "); 1073 TemplateElement prev = (TemplateElement) iter.previous(); 1074 pw.print(prev.getDescription()); 1075 pw.print(" ["); 1076 pw.print(prev.getStartLocation()); 1077 pw.println("]"); 1078 } 1079 while(iter.hasPrevious()) { 1080 TemplateElement prev = (TemplateElement) iter.previous(); 1081 if (prev instanceof UnifiedCall || prev instanceof Include) { 1082 String location = prev.getDescription() + " [" + prev.getStartLocation() + "]"; 1083 if(location != null && location.length() > 0) { 1084 pw.print(" in "); 1085 pw.println(location); 1086 } 1087 } 1088 } 1089 pw.println("----------"); 1090 pw.flush(); 1091 } 1092 1093 private void pushLocalContext(LocalContext localContext) { 1094 if (localContextStack == null) { 1095 localContextStack = new ArrayList(); 1096 } 1097 localContextStack.add(localContext); 1098 } 1099 1100 private void popLocalContext() { 1101 localContextStack.remove(localContextStack.size() - 1); 1102 } 1103 1104 ArrayList getLocalContextStack() { 1105 return localContextStack; 1106 } 1107 1108 1114 public Namespace getNamespace(String name) { 1115 if (name.startsWith("/")) name = name.substring(1); 1116 if (loadedLibs != null) { 1117 return (Namespace) loadedLibs.get(name); 1118 } else { 1119 return null; 1120 } 1121 } 1122 1123 1127 public Namespace getMainNamespace() { 1128 return mainNamespace; 1129 } 1130 1131 1135 public Namespace getCurrentNamespace() { 1136 return currentNamespace; 1137 } 1138 1139 1146 public Namespace getGlobalNamespace() { 1147 return globalNamespace; 1148 } 1149 1150 1157 public TemplateHashModel getDataModel() { 1158 return new TemplateHashModel() { 1159 public boolean isEmpty() { 1160 return false; 1161 } 1162 public TemplateModel get(String key) throws TemplateModelException { 1163 TemplateModel result = rootDataModel.get(key); 1164 if (result == null) { 1165 result = getConfiguration().getSharedVariable(key); 1166 } 1167 return result; 1168 } 1169 }; 1170 } 1171 1172 1179 public TemplateHashModel getGlobalVariables() { 1180 return new TemplateHashModel() { 1181 public boolean isEmpty() { 1182 return false; 1183 } 1184 public TemplateModel get(String key) throws TemplateModelException { 1185 TemplateModel result = globalNamespace.get(key); 1186 if (result == null) { 1187 result = rootDataModel.get(key); 1188 } 1189 if (result == null) { 1190 result = getConfiguration().getSharedVariable(key); 1191 } 1192 return result; 1193 } 1194 }; 1195 } 1196 1197 private void pushElement(TemplateElement element) { 1198 elementStack.add(element); 1199 } 1200 1201 private void popElement() { 1202 elementStack.remove(elementStack.size() - 1); 1203 } 1204 1205 public TemplateNodeModel getCurrentVisitorNode() { 1206 return currentVisitorNode; 1207 } 1208 1209 1212 public void setCurrentVisitorNode(TemplateNodeModel node) { 1213 currentVisitorNode = node; 1214 } 1215 1216 TemplateModel getNodeProcessor(TemplateNodeModel node) throws TemplateException { 1217 String nodeName = node.getNodeName(); 1218 if (nodeName == null) { 1219 throw new TemplateException("Node name is null.", this); 1220 } 1221 TemplateModel result = getNodeProcessor(nodeName, node.getNodeNamespace(), 0); 1222 if (result == null) { 1223 String type = node.getNodeType(); 1224 if (type == null) { 1225 type = "default"; 1226 } 1227 result = getNodeProcessor("@" + type, null, 0); 1228 } 1229 return result; 1230 } 1231 1232 private TemplateModel getNodeProcessor(final String nodeName, final String nsURI, int startIndex) 1233 throws TemplateException 1234 { 1235 TemplateModel result = null; 1236 int i; 1237 for (i = startIndex; i<nodeNamespaces.size(); i++) { 1238 Namespace ns = null; 1239 try { 1240 ns = (Namespace) nodeNamespaces.get(i); 1241 } catch (ClassCastException cce) { 1242 throw new InvalidReferenceException("A using clause should contain a sequence of namespaces or strings that indicate the location of importable macro libraries.", this); 1243 } 1244 result = getNodeProcessor(ns, nodeName, nsURI); 1245 if (result != null) 1246 break; 1247 } 1248 if (result != null) { 1249 this.nodeNamespaceIndex = i+1; 1250 this.currentNodeName = nodeName; 1251 this.currentNodeNS = nsURI; 1252 } 1253 return result; 1254 } 1255 1256 private TemplateModel getNodeProcessor(Namespace ns, String localName, String nsURI) throws TemplateException { 1257 TemplateModel result = null; 1258 if (nsURI == null) { 1259 result = ns.get(localName); 1260 if (!(result instanceof Macro) && !(result instanceof TemplateTransformModel)) { 1261 result = null; 1262 } 1263 } else { 1264 Template template = ns.getTemplate(); 1265 String prefix = template.getPrefixForNamespace(nsURI); 1266 if (prefix == null) { 1267 return null; 1270 } 1271 if (prefix.length() >0) { 1272 result = ns.get(prefix + ":" + localName); 1273 if (!(result instanceof Macro) && !(result instanceof TemplateTransformModel)) { 1274 result = null; 1275 } 1276 } else { 1277 if (nsURI.length() == 0) { 1278 result = ns.get(Template.NO_NS_PREFIX + ":" + localName); 1279 if (!(result instanceof Macro) && !(result instanceof TemplateTransformModel)) { 1280 result = null; 1281 } 1282 } 1283 if (nsURI.equals(template.getDefaultNS())) { 1284 result = ns.get(Template.DEFAULT_NAMESPACE_PREFIX + ":" + localName); 1285 if (!(result instanceof Macro) && !(result instanceof TemplateTransformModel)) { 1286 result = null; 1287 } 1288 } 1289 if (result == null) { 1290 result = ns.get(localName); 1291 if (!(result instanceof Macro) && !(result instanceof TemplateTransformModel)) { 1292 result = null; 1293 } 1294 } 1295 } 1296 } 1297 return result; 1298 } 1299 1300 1311 public void include(String name, String encoding, boolean parse) 1312 throws IOException, TemplateException 1313 { 1314 include(getTemplateForInclusion(name, encoding, parse)); 1315 } 1316 1317 1332 public Template getTemplateForInclusion(String name, String encoding, boolean parse) 1333 throws IOException 1334 { 1335 if (encoding == null) { 1336 encoding = getTemplate().getEncoding(); 1337 } 1338 if (encoding == null) { 1339 encoding = getConfiguration().getEncoding(this.getLocale()); 1340 } 1341 return getConfiguration().getTemplate(name, getLocale(), encoding, parse); 1342 } 1343 1344 1352 public void include(Template includedTemplate) 1353 throws TemplateException, IOException 1354 { 1355 Template prevTemplate = getTemplate(); 1356 setParent(includedTemplate); 1357 importMacros(includedTemplate); 1358 try { 1359 visit(includedTemplate.getRootTreeNode()); 1360 } 1361 finally { 1362 setParent(prevTemplate); 1363 } 1364 } 1365 1366 1377 public Namespace importLib(String name, String namespace) 1378 throws IOException, TemplateException 1379 { 1380 return importLib(getTemplateForImporting(name), namespace); 1381 } 1382 1383 1394 public Template getTemplateForImporting(String name) throws IOException { 1395 return getTemplateForInclusion(name, null, true); 1396 } 1397 1398 1404 public Namespace importLib(Template loadedTemplate, String namespace) 1405 throws IOException, TemplateException 1406 { 1407 if (loadedLibs == null) { 1408 loadedLibs = new HashMap(); 1409 } 1410 String templateName = loadedTemplate.getName(); 1411 Namespace existingNamespace = (Namespace) loadedLibs.get(templateName); 1412 if (existingNamespace != null) { 1413 if (namespace != null) { 1414 setVariable(namespace, existingNamespace); 1415 } 1416 } 1417 else { 1418 Namespace newNamespace = new Namespace(loadedTemplate); 1419 if (namespace != null) { 1420 currentNamespace.put(namespace, newNamespace); 1421 if (currentNamespace == mainNamespace) { 1422 globalNamespace.put(namespace, newNamespace); 1423 } 1424 } 1425 Namespace prevNamespace = this.currentNamespace; 1426 this.currentNamespace = newNamespace; 1427 loadedLibs.put(templateName, currentNamespace); 1428 Writer prevOut = out; 1429 this.out = NULL_WRITER; 1430 try { 1431 include(loadedTemplate); 1432 } finally { 1433 this.out = prevOut; 1434 this.currentNamespace = prevNamespace; 1435 } 1436 } 1437 return (Namespace) loadedLibs.get(templateName); 1438 } 1439 1440 String renderElementToString(TemplateElement te) throws IOException, TemplateException { 1441 Writer prevOut = out; 1442 try { 1443 StringWriter sw = new StringWriter(); 1444 this.out = sw; 1445 visit(te); 1446 return sw.toString(); 1447 } 1448 finally { 1449 this.out = prevOut; 1450 } 1451 } 1452 1453 void importMacros(Template template) { 1454 for (Iterator it = template.getMacros().values().iterator(); it.hasNext();) { 1455 visitMacroDef((Macro) it.next()); 1456 } 1457 } 1458 1459 1463 public String getNamespaceForPrefix(String prefix) { 1464 return currentNamespace.getTemplate().getNamespaceForPrefix(prefix); 1465 } 1466 1467 public String getPrefixForNamespace(String nsURI) { 1468 return currentNamespace.getTemplate().getPrefixForNamespace(nsURI); 1469 } 1470 1471 1474 public String getDefaultNS() { 1475 return currentNamespace.getTemplate().getDefaultNS(); 1476 } 1477 1478 1481 public Object __getitem__(String key) throws TemplateModelException { 1482 return BeansWrapper.getDefaultInstance().unwrap(getVariable(key)); 1483 } 1484 1485 1488 public void __setitem__(String key, Object o) throws TemplateException { 1489 setGlobalVariable(key, getObjectWrapper().wrap(o)); 1490 } 1491 1492 private static final class NumberFormatKey 1493 { 1494 private final String pattern; 1495 private final Locale locale; 1496 1497 NumberFormatKey(String pattern, Locale locale) 1498 { 1499 this.pattern = pattern; 1500 this.locale = locale; 1501 } 1502 1503 public boolean equals(Object o) 1504 { 1505 if(o instanceof NumberFormatKey) 1506 { 1507 NumberFormatKey fk = (NumberFormatKey)o; 1508 return fk.pattern.equals(pattern) && fk.locale.equals(locale); 1509 } 1510 return false; 1511 } 1512 1513 public int hashCode() 1514 { 1515 return pattern.hashCode() ^ locale.hashCode(); 1516 } 1517 } 1518 1519 private static final class DateFormatKey 1520 { 1521 private final int dateType; 1522 private final String pattern; 1523 private final Locale locale; 1524 private final TimeZone timeZone; 1525 1526 DateFormatKey(int dateType, String pattern, Locale locale, TimeZone timeZone) 1527 { 1528 this.dateType = dateType; 1529 this.pattern = pattern; 1530 this.locale = locale; 1531 this.timeZone = timeZone; 1532 } 1533 1534 public boolean equals(Object o) 1535 { 1536 if(o instanceof DateFormatKey) 1537 { 1538 DateFormatKey fk = (DateFormatKey)o; 1539 return dateType == fk.dateType && fk.pattern.equals(pattern) && fk.locale.equals(locale) && fk.timeZone.equals(timeZone); 1540 } 1541 return false; 1542 } 1543 1544 public int hashCode() 1545 { 1546 return dateType ^ pattern.hashCode() ^ locale.hashCode() ^ timeZone.hashCode(); 1547 } 1548 } 1549 1550 public class Namespace extends SimpleHash { 1551 1552 private Template template; 1553 1554 Namespace() { 1555 this.template = Environment.this.getTemplate(); 1556 } 1557 1558 Namespace(Template template) { 1559 this.template = template; 1560 } 1561 1562 1565 public Template getTemplate() { 1566 return template == null ? Environment.this.getTemplate() : template; 1567 } 1568 } 1569 1570 static final Writer NULL_WRITER = new Writer() { 1571 public void write(char cbuf[], int off, int len) {} 1572 public void flush() {} 1573 public void close() {} 1574 }; 1575 1576 private static final Writer EMPTY_BODY_WRITER = new Writer() { 1577 1578 public void write(char[] cbuf, int off, int len) throws IOException { 1579 if (len > 0) { 1580 throw new IOException( 1581 "This transform does not allow nested content."); 1582 } 1583 } 1584 1585 public void flush() { 1586 } 1587 1588 public void close() { 1589 } 1590 }; 1591 1592} 1593 | Popular Tags |