1 52 53 package freemarker.core; 54 55 import java.io.IOException ; 56 import java.io.InputStream ; 57 import java.util.*; 58 59 import freemarker.template.*; 60 import freemarker.template.utility.ClassUtil; 61 import freemarker.template.utility.StringUtil; 62 63 78 public class Configurable 79 { 80 public static final String LOCALE_KEY = "locale"; 81 public static final String NUMBER_FORMAT_KEY = "number_format"; 82 public static final String TIME_FORMAT_KEY = "time_format"; 83 public static final String DATE_FORMAT_KEY = "date_format"; 84 public static final String DATETIME_FORMAT_KEY = "datetime_format"; 85 public static final String TIME_ZONE_KEY = "time_zone"; 86 public static final String CLASSIC_COMPATIBLE_KEY = "classic_compatible"; 87 public static final String TEMPLATE_EXCEPTION_HANDLER_KEY = "template_exception_handler"; 88 public static final String ARITHMETIC_ENGINE_KEY = "arithmetic_engine"; 89 public static final String OBJECT_WRAPPER_KEY = "object_wrapper"; 90 public static final String BOOLEAN_FORMAT_KEY = "boolean_format"; 91 public static final String OUTPUT_ENCODING_KEY = "output_encoding"; 92 public static final String URL_ESCAPING_CHARSET_KEY = "url_escaping_charset"; 93 94 private static final char COMMA = ','; 95 96 private Configurable parent; 97 private Properties properties; 98 private HashMap customAttributes; 99 100 private Locale locale; 101 private String numberFormat; 102 private String timeFormat; 103 private String dateFormat; 104 private String dateTimeFormat; 105 private TimeZone timeZone; 106 private String trueFormat; 107 private String falseFormat; 108 private Boolean classicCompatible; 109 private TemplateExceptionHandler templateExceptionHandler; 110 private ArithmeticEngine arithmeticEngine; 111 private ObjectWrapper objectWrapper; 112 private String outputEncoding; 113 private String urlEscapingCharset; 114 115 public Configurable() { 116 parent = null; 117 locale = Locale.getDefault(); 118 timeZone = TimeZone.getDefault(); 119 numberFormat = "number"; 120 timeFormat = ""; 121 dateFormat = ""; 122 dateTimeFormat = ""; 123 trueFormat = "true"; 124 falseFormat = "false"; 125 classicCompatible = Boolean.FALSE; 126 templateExceptionHandler = TemplateExceptionHandler.DEBUG_HANDLER; 127 arithmeticEngine = ArithmeticEngine.BIGDECIMAL_ENGINE; 128 objectWrapper = ObjectWrapper.DEFAULT_WRAPPER; 129 132 properties = new Properties(); 133 properties.setProperty(LOCALE_KEY, locale.toString()); 134 properties.setProperty(TIME_FORMAT_KEY, timeFormat); 135 properties.setProperty(DATE_FORMAT_KEY, dateFormat); 136 properties.setProperty(DATETIME_FORMAT_KEY, dateTimeFormat); 137 properties.setProperty(TIME_ZONE_KEY, timeZone.getID()); 138 properties.setProperty(NUMBER_FORMAT_KEY, numberFormat); 139 properties.setProperty(CLASSIC_COMPATIBLE_KEY, classicCompatible.toString()); 140 properties.setProperty(TEMPLATE_EXCEPTION_HANDLER_KEY, templateExceptionHandler.getClass().getName()); 141 properties.setProperty(ARITHMETIC_ENGINE_KEY, arithmeticEngine.getClass().getName()); 142 properties.setProperty(BOOLEAN_FORMAT_KEY, "true,false"); 143 146 customAttributes = new HashMap(); 147 } 148 149 153 public Configurable(Configurable parent) { 154 this.parent = parent; 155 locale = null; 156 numberFormat = null; 157 trueFormat = null; 158 falseFormat = null; 159 classicCompatible = null; 160 templateExceptionHandler = null; 161 properties = new Properties(parent.properties); 162 customAttributes = new HashMap(); 163 } 164 165 protected Object clone() throws CloneNotSupportedException { 166 Configurable copy = (Configurable)super.clone(); 167 copy.properties = new Properties(properties); 168 copy.customAttributes = (HashMap)customAttributes.clone(); 169 return copy; 170 } 171 172 182 public final Configurable getParent() { 183 return parent; 184 } 185 186 191 final void setParent(Configurable parent) { 192 this.parent = parent; 193 } 194 195 199 public void setClassicCompatible(boolean classicCompatibility) { 200 this.classicCompatible = classicCompatibility ? Boolean.TRUE : Boolean.FALSE; 201 properties.setProperty(CLASSIC_COMPATIBLE_KEY, classicCompatible.toString()); 202 } 203 204 247 public boolean isClassicCompatible() { 248 return classicCompatible != null ? classicCompatible.booleanValue() : parent.isClassicCompatible(); 249 } 250 251 255 public void setLocale(Locale locale) { 256 if (locale == null) throw new IllegalArgumentException ("Setting \"locale\" can't be null"); 257 this.locale = locale; 258 properties.setProperty(LOCALE_KEY, locale.toString()); 259 } 260 261 265 public TimeZone getTimeZone() { 266 return timeZone != null ? timeZone : parent.getTimeZone(); 267 } 268 269 272 public void setTimeZone(TimeZone timeZone) { 273 if (timeZone == null) throw new IllegalArgumentException ("Setting \"time_zone\" can't be null"); 274 this.timeZone = timeZone; 275 properties.setProperty(TIME_ZONE_KEY, timeZone.getID()); 276 } 277 278 282 public Locale getLocale() { 283 return locale != null ? locale : parent.getLocale(); 284 } 285 286 289 public void setNumberFormat(String numberFormat) { 290 if (numberFormat == null) throw new IllegalArgumentException ("Setting \"number_format\" can't be null"); 291 this.numberFormat = numberFormat; 292 properties.setProperty(NUMBER_FORMAT_KEY, numberFormat); 293 } 294 295 299 public String getNumberFormat() { 300 return numberFormat != null ? numberFormat : parent.getNumberFormat(); 301 } 302 303 public void setBooleanFormat(String booleanFormat) { 304 if (booleanFormat == null) { 305 throw new IllegalArgumentException ("Setting \"boolean_format\" can't be null"); 306 } 307 int comma = booleanFormat.indexOf(COMMA); 308 if(comma == -1) { 309 throw new IllegalArgumentException ("Setting \"boolean_format\" must consist of two comma-separated values for true and false respectively"); 310 } 311 trueFormat = booleanFormat.substring(0, comma); 312 falseFormat = booleanFormat.substring(comma + 1); 313 properties.setProperty(BOOLEAN_FORMAT_KEY, booleanFormat); 314 } 315 316 public String getBooleanFormat() { 317 if(trueFormat == null) { 318 return parent.getBooleanFormat(); 319 } 320 return trueFormat + COMMA + falseFormat; 321 } 322 323 String getBooleanFormat(boolean value) { 324 return value ? getTrueFormat() : getFalseFormat(); 325 } 326 327 private String getTrueFormat() { 328 return trueFormat != null ? trueFormat : parent.getTrueFormat(); 329 } 330 331 private String getFalseFormat() { 332 return falseFormat != null ? falseFormat : parent.getFalseFormat(); 333 } 334 335 339 public void setTimeFormat(String timeFormat) { 340 if (timeFormat == null) throw new IllegalArgumentException ("Setting \"time_format\" can't be null"); 341 this.timeFormat = timeFormat; 342 properties.setProperty(TIME_FORMAT_KEY, timeFormat); 343 } 344 345 350 public String getTimeFormat() { 351 return timeFormat != null ? timeFormat : parent.getTimeFormat(); 352 } 353 354 358 public void setDateFormat(String dateFormat) { 359 if (dateFormat == null) throw new IllegalArgumentException ("Setting \"date_format\" can't be null"); 360 this.dateFormat = dateFormat; 361 properties.setProperty(DATE_FORMAT_KEY, dateFormat); 362 } 363 364 369 public String getDateFormat() { 370 return dateFormat != null ? dateFormat : parent.getDateFormat(); 371 } 372 373 377 public void setDateTimeFormat(String dateTimeFormat) { 378 if (dateTimeFormat == null) throw new IllegalArgumentException ("Setting \"datetime_format\" can't be null"); 379 this.dateTimeFormat = dateTimeFormat; 380 properties.setProperty(DATETIME_FORMAT_KEY, dateTimeFormat); 381 } 382 383 388 public String getDateTimeFormat() { 389 return dateTimeFormat != null ? dateTimeFormat : parent.getDateTimeFormat(); 390 } 391 392 399 public void setTemplateExceptionHandler(TemplateExceptionHandler templateExceptionHandler) { 400 if (templateExceptionHandler == null) throw new IllegalArgumentException ("Setting \"template_exception_handler\" can't be null"); 401 this.templateExceptionHandler = templateExceptionHandler; 402 properties.setProperty(TEMPLATE_EXCEPTION_HANDLER_KEY, templateExceptionHandler.getClass().getName()); 403 } 404 405 408 public TemplateExceptionHandler getTemplateExceptionHandler() { 409 return templateExceptionHandler != null 410 ? templateExceptionHandler : parent.getTemplateExceptionHandler(); 411 } 412 413 420 public void setArithmeticEngine(ArithmeticEngine arithmeticEngine) { 421 if (arithmeticEngine == null) throw new IllegalArgumentException ("Setting \"arithmetic_engine\" can't be null"); 422 this.arithmeticEngine = arithmeticEngine; 423 properties.setProperty(ARITHMETIC_ENGINE_KEY, arithmeticEngine.getClass().getName()); 424 } 425 426 429 public ArithmeticEngine getArithmeticEngine() { 430 return arithmeticEngine != null 431 ? arithmeticEngine : parent.getArithmeticEngine(); 432 } 433 434 440 public void setObjectWrapper(ObjectWrapper objectWrapper) { 441 if (objectWrapper == null) throw new IllegalArgumentException ("Setting \"object_wrapper\" can't be null"); 442 this.objectWrapper = objectWrapper; 443 properties.setProperty(OBJECT_WRAPPER_KEY, objectWrapper.getClass().getName()); 444 } 445 446 449 public ObjectWrapper getObjectWrapper() { 450 return objectWrapper != null 451 ? objectWrapper : parent.getObjectWrapper(); 452 } 453 454 public void setOutputEncoding(String outputEncoding) { 455 this.outputEncoding = outputEncoding; 456 properties.setProperty(OUTPUT_ENCODING_KEY, outputEncoding); 457 } 458 459 public String getOutputEncoding() { 460 return outputEncoding != null 461 ? outputEncoding 462 : (parent != null ? parent.getOutputEncoding() : null); 463 } 464 465 public void setURLEscapingCharset(String urlEscapingCharset) { 466 this.urlEscapingCharset = urlEscapingCharset; 467 properties.setProperty(URL_ESCAPING_CHARSET_KEY, urlEscapingCharset); 468 } 469 470 public String getURLEscapingCharset() { 471 return urlEscapingCharset != null 472 ? urlEscapingCharset 473 : (parent != null ? parent.getURLEscapingCharset() : null); 474 } 475 476 527 public void setSetting(String key, String value) throws TemplateException { 528 try { 529 if (LOCALE_KEY.equals(key)) { 530 setLocale(StringUtil.deduceLocale(value)); 531 } else if (NUMBER_FORMAT_KEY.equals(key)) { 532 setNumberFormat(value); 533 } else if (TIME_FORMAT_KEY.equals(key)) { 534 setTimeFormat(value); 535 } else if (DATE_FORMAT_KEY.equals(key)) { 536 setDateFormat(value); 537 } else if (DATETIME_FORMAT_KEY.equals(key)) { 538 setDateTimeFormat(value); 539 } else if (TIME_ZONE_KEY.equals(key)) { 540 setTimeZone(TimeZone.getTimeZone(value)); 541 } else if (CLASSIC_COMPATIBLE_KEY.equals(key)) { 542 setClassicCompatible(StringUtil.getYesNo(value)); 543 } else if (TEMPLATE_EXCEPTION_HANDLER_KEY.equals(key)) { 544 if (value.indexOf('.') == -1) { 545 if ("debug".equalsIgnoreCase(value)) { 546 setTemplateExceptionHandler( 547 TemplateExceptionHandler.DEBUG_HANDLER); 548 } else if ("html_debug".equalsIgnoreCase(value)) { 549 setTemplateExceptionHandler( 550 TemplateExceptionHandler.HTML_DEBUG_HANDLER); 551 } else if ("ignore".equalsIgnoreCase(value)) { 552 setTemplateExceptionHandler( 553 TemplateExceptionHandler.IGNORE_HANDLER); 554 } else if ("rethrow".equalsIgnoreCase(value)) { 555 setTemplateExceptionHandler( 556 TemplateExceptionHandler.RETHROW_HANDLER); 557 } else { 558 throw invalidSettingValueException(key, value); 559 } 560 } else { 561 setTemplateExceptionHandler( 562 (TemplateExceptionHandler) ClassUtil.forName(value) 563 .newInstance()); 564 } 565 } else if (ARITHMETIC_ENGINE_KEY.equals(key)) { 566 if (value.indexOf('.') == -1) { 567 if ("bigdecimal".equalsIgnoreCase(value)) { 568 setArithmeticEngine(ArithmeticEngine.BIGDECIMAL_ENGINE); 569 } else if ("conservative".equalsIgnoreCase(value)) { 570 setArithmeticEngine(ArithmeticEngine.CONSERVATIVE_ENGINE); 571 } else { 572 throw invalidSettingValueException(key, value); 573 } 574 } else { 575 setArithmeticEngine( 576 (ArithmeticEngine) ClassUtil.forName(value) 577 .newInstance()); 578 } 579 } else if (OBJECT_WRAPPER_KEY.equals(key)) { 580 if (value.indexOf('.') == -1) { 581 if ("default".equalsIgnoreCase(value)) { 582 setObjectWrapper(ObjectWrapper.DEFAULT_WRAPPER); 583 } else if ("simple".equalsIgnoreCase(value)) { 584 setObjectWrapper(ObjectWrapper.SIMPLE_WRAPPER); 585 } else if ("beans".equalsIgnoreCase(value)) { 586 setObjectWrapper(ObjectWrapper.BEANS_WRAPPER); 587 } else if ("jython".equalsIgnoreCase(value)) { 588 Class clazz = Class.forName( 589 "freemarker.ext.jython.JythonWrapper"); 590 setObjectWrapper( 591 (ObjectWrapper) clazz.getField("INSTANCE").get(null)); 592 } else { 593 throw invalidSettingValueException(key, value); 594 } 595 596 } else { 597 setObjectWrapper((ObjectWrapper) ClassUtil.forName(value) 598 .newInstance()); 599 } 600 } else if (BOOLEAN_FORMAT_KEY.equals(key)) { 601 setBooleanFormat(value); 602 } else if (OUTPUT_ENCODING_KEY.equals(key)) { 603 setOutputEncoding(value); 604 } else if (URL_ESCAPING_CHARSET_KEY.equals(key)) { 605 setURLEscapingCharset(value); 606 } else { 607 throw unknownSettingException(key); 608 } 609 } catch(TemplateException e) { 610 throw e; 611 } catch(Exception e) { 612 throw new TemplateException( 613 "Failed to set setting " + key + " to value " + value, 614 e, getEnvironment()); 615 } 616 } 617 618 628 public String getSetting(String key) { 629 return properties.getProperty(key); 630 } 631 632 643 public Map getSettings() { 644 return Collections.unmodifiableMap(properties); 645 } 646 647 protected Environment getEnvironment() { 648 return this instanceof Environment 649 ? (Environment) this 650 : Environment.getCurrentEnvironment(); 651 } 652 653 protected TemplateException unknownSettingException(String name) { 654 return new UnknownSettingException(name, getEnvironment()); 655 } 656 657 protected TemplateException invalidSettingValueException(String name, String value) { 658 return new TemplateException("Invalid value for setting " + name + ": " + value, getEnvironment()); 659 } 660 661 public class UnknownSettingException extends TemplateException { 662 private UnknownSettingException(String name, Environment env) { 663 super("Unknown setting: " + name, env); 664 } 665 } 666 667 674 public void setSettings(Properties props) throws TemplateException { 675 Iterator it = props.keySet().iterator(); 676 while (it.hasNext()) { 677 String key = (String ) it.next(); 678 setSetting(key, props.getProperty(key).trim()); 679 } 680 } 681 682 691 public void setSettings(InputStream propsIn) throws TemplateException, IOException { 692 Properties p = new Properties(); 693 p.load(propsIn); 694 setSettings(p); 695 } 696 697 700 void setCustomAttribute(Object key, Object value) { 701 synchronized(customAttributes) { 702 customAttributes.put(key, value); 703 } 704 } 705 706 709 Object getCustomAttribute(Object key, CustomAttribute attr) { 710 synchronized(customAttributes) { 711 Object o = customAttributes.get(key); 712 if(o == null && !customAttributes.containsKey(key)) { 713 o = attr.create(); 714 customAttributes.put(key, o); 715 } 716 return o; 717 } 718 } 719 720 729 public void setCustomAttribute(String name, Object value) { 730 synchronized(customAttributes) { 731 customAttributes.put(name, value); 732 } 733 } 734 735 743 public String [] getCustomAttributeNames() { 744 synchronized(customAttributes) { 745 Collection names = new LinkedList(customAttributes.keySet()); 746 for (Iterator iter = names.iterator(); iter.hasNext();) { 747 if(!(iter.next() instanceof String )) { 748 iter.remove(); 749 } 750 } 751 return (String [])names.toArray(new String [names.size()]); 752 } 753 } 754 755 767 public void removeCustomAttribute(String name) { 768 synchronized(customAttributes) { 769 customAttributes.remove(name); 770 } 771 } 772 773 785 public Object getCustomAttribute(String name) { 786 Object retval; 787 synchronized(customAttributes) { 788 retval = customAttributes.get(name); 789 if(retval == null && customAttributes.containsKey(name)) { 790 return null; 791 } 792 } 793 if(retval == null && parent != null) { 794 return parent.getCustomAttribute(name); 795 } 796 return retval; 797 } 798 } 799 | Popular Tags |