| 1 52 53 package freemarker.core; 54 55 import freemarker.template.*; 56 57 import java.io.UnsupportedEncodingException ; 58 import java.text.DateFormat ; 59 import java.text.NumberFormat ; 60 import java.util.Date ; 61 import java.util.HashMap ; 62 import java.util.Iterator ; 63 import java.util.List ; 64 65 import freemarker.template.utility.ClassUtil; 66 import freemarker.template.utility.StringUtil; 67 import freemarker.core.NodeBuiltins.*; 68 import freemarker.core.NumericalBuiltins.*; 69 import freemarker.core.SequenceBuiltins.*; 70 import freemarker.core.StringBuiltins.*; 71 72 77 abstract class BuiltIn extends Expression implements Cloneable { 78 Expression target; 79 String key; 80 81 static final HashMap builtins = new HashMap (); 82 83 static { 84 builtins.put("ancestors", new ancestorsBI()); 87 builtins.put("byte", new byteBI()); 88 builtins.put("cap_first", new cap_firstBI()); 89 builtins.put("capitalize", new capitalizeBI()); 90 builtins.put("children", new childrenBI()); 91 builtins.put("chop_linebreak", new chop_linebreakBI()); 92 builtins.put("contains", new containsBI()); 93 builtins.put("date", new dateBI(TemplateDateModel.DATE)); 94 builtins.put("datetime", new dateBI(TemplateDateModel.DATETIME)); 95 builtins.put("default", new defaultBI()); 96 builtins.put("double", new doubleBI()); 97 builtins.put("ends_with", new ends_withBI()); 98 builtins.put("eval", new evalBI()); 99 builtins.put("exists", new existsBI()); 100 builtins.put("first", new firstBI()); 101 builtins.put("float", new floatBI()); 102 builtins.put("has_content", new has_contentBI()); 103 builtins.put("html", new htmlBI()); 104 builtins.put("if_exists", new if_existsBI()); 105 builtins.put("index_of", new index_ofBI()); 106 builtins.put("int", new intBI()); 107 builtins.put("interpret", new Interpret()); 108 builtins.put("is_boolean", new is_booleanBI()); 109 builtins.put("is_collection", new is_collectionBI()); 110 builtins.put("is_date", new is_dateBI()); 111 builtins.put("is_directive", new is_directiveBI()); 112 builtins.put("is_enumerable", new is_enumerableBI()); 113 builtins.put("is_hash_ex", new is_hash_exBI()); 114 builtins.put("is_hash", new is_hashBI()); 115 builtins.put("is_indexable", new is_indexableBI()); 116 builtins.put("is_macro", new is_macroBI()); 117 builtins.put("is_method", new is_methodBI()); 118 builtins.put("is_node", new is_nodeBI()); 119 builtins.put("is_number", new is_numberBI()); 120 builtins.put("is_sequence", new is_sequenceBI()); 121 builtins.put("is_string", new is_stringBI()); 122 builtins.put("is_transform", new is_transformBI()); 123 builtins.put("j_string", new j_stringBI()); 124 builtins.put("js_string", new js_stringBI()); 125 builtins.put("keys", new keysBI()); 126 builtins.put("last_index_of", new last_index_ofBI()); 127 builtins.put("last", new lastBI()); 128 builtins.put("left_pad", new left_padBI()); 129 builtins.put("length", new lengthBI()); 130 builtins.put("long", new longBI()); 131 builtins.put("lower_case", new lower_caseBI()); 132 builtins.put("namespace", new namespaceBI()); 133 builtins.put("new", new NewBI()); 134 builtins.put("node_name", new node_nameBI()); 135 builtins.put("node_namespace", new node_namespaceBI()); 136 builtins.put("node_type", new node_typeBI()); 137 builtins.put("number", new numberBI()); 138 builtins.put("parent", new parentBI()); 139 builtins.put("replace", new replaceBI()); 140 builtins.put("reverse", new reverseBI()); 141 builtins.put("right_pad", new right_padBI()); 142 builtins.put("root", new rootBI()); 143 builtins.put("rtf", new rtfBI()); 144 builtins.put("seq_contains", new seq_containsBI()); 145 builtins.put("seq_index_of", new seq_index_ofBI(1)); 146 builtins.put("seq_last_index_of", new seq_index_ofBI(-1)); 147 builtins.put("short", new shortBI()); 148 builtins.put("size", new sizeBI()); 149 builtins.put("sort_by", new sort_byBI()); 150 builtins.put("sort", new sortBI()); 151 builtins.put("split", new splitBI()); 152 builtins.put("starts_with", new starts_withBI()); 153 builtins.put("string", new stringBI()); 154 builtins.put("time", new dateBI(TemplateDateModel.TIME)); 155 builtins.put("trim", new trimBI()); 156 builtins.put("uncap_first", new uncap_firstBI()); 157 builtins.put("upper_case", new upper_caseBI()); 158 builtins.put("url", new urlBI()); 159 builtins.put("values", new valuesBI()); 160 builtins.put("web_safe", builtins.get("html")); builtins.put("word_list", new word_listBI()); 162 builtins.put("xml", new xmlBI()); 163 try { 164 Class.forName("java.util.regex.Pattern"); 165 builtins.put("matches", instantiate("freemarker.core.RegexBuiltins$matchesBI")); 166 builtins.put("groups", instantiate("freemarker.core.RegexBuiltins$groupsBI")); 167 builtins.put("replace", instantiate("freemarker.core.RegexBuiltins$replace_reBI")); 168 builtins.put("split", instantiate("freemarker.core.RegexBuiltins$split_reBI")); 169 } catch (Exception e) {} 170 } 171 172 private static Object instantiate(String className) throws Exception  173 { 174 return ClassUtil.forName(className).newInstance(); 175 } 176 177 static BuiltIn newBuiltIn(Expression target, String key, Token tok, String templateName) throws ParseException { 178 BuiltIn bi = (BuiltIn) builtins.get(key); 179 if (bi == null) { 180 String locationInfo = "Error on line " + tok.beginLine + ", column " + tok.beginColumn + ", in template " + templateName + "\n"; 181 StringBuffer buf = new StringBuffer ("Found " + key + ", expecting one of: "); 182 for (Iterator it= builtins.keySet().iterator(); it.hasNext();) { 183 if (it.hasNext()) { 184 buf.append(" "); 185 } else { 186 buf.append( " or "); 187 } 188 buf.append(it.next()); 189 if (it.hasNext()) { 190 buf.append(", "); 191 } 192 } 193 throw new ParseException(locationInfo + buf, target); 194 } 195 try { 196 bi = (BuiltIn) bi.clone(); 197 } 198 catch (CloneNotSupportedException e) { 199 throw new InternalError (); 200 } 201 bi.target = target; 202 bi.key = key; 203 return bi; 204 } 205 206 public String getCanonicalForm() { 207 return target.getCanonicalForm() + "?" + key; 208 } 209 210 boolean isLiteral() { 211 return false; } 213 214 Expression _deepClone(String name, Expression subst) { 215 try { 216 BuiltIn clone = (BuiltIn)clone(); 217 clone.target = target.deepClone(name, subst); 218 return clone; 219 } 220 catch (CloneNotSupportedException e) { 221 throw new InternalError (); 222 } 223 } 224 225 226 static class lengthBI extends BuiltIn { 227 TemplateModel _getAsTemplateModel(Environment env) 228 throws TemplateException 229 { 230 return new SimpleNumber(target.getStringValue(env).length()); 231 } 232 } 233 234 static class dateBI extends BuiltIn { 235 private final int dateType; 236 237 dateBI(int dateType) { 238 this.dateType = dateType; 239 } 240 241 TemplateModel _getAsTemplateModel(Environment env) 242 throws TemplateException 243 { 244 TemplateModel model = target.getAsTemplateModel(env); 245 if (model instanceof TemplateDateModel) { 246 TemplateDateModel dmodel = (TemplateDateModel)model; 247 int dtype = dmodel.getDateType(); 248 if(dateType == dtype) { 250 return model; 251 } 252 if(dtype == TemplateDateModel.UNKNOWN || dtype == TemplateDateModel.DATETIME) { 254 return new SimpleDate(dmodel.getAsDate(), dateType); 255 } 256 throw new TemplateException( 257 "Cannot convert " + TemplateDateModel.TYPE_NAMES.get(dtype) 258 + " into " + TemplateDateModel.TYPE_NAMES.get(dateType), env); 259 } 260 String s = target.getStringValue(env); 263 return new DateParser(s, env); 264 } 265 266 private class DateParser 267 implements 268 TemplateDateModel, 269 TemplateMethodModel, 270 TemplateHashModel 271 { 272 private final String text; 273 private final Environment env; 274 private final DateFormat defaultFormat; 275 private Date cachedValue; 276 277 DateParser(String text, Environment env) 278 throws 279 TemplateModelException 280 { 281 this.text = text; 282 this.env = env; 283 this.defaultFormat = env.getDateFormatObject(dateType); 284 } 285 286 public Date getAsDate() throws TemplateModelException { 287 if(cachedValue == null) { 288 cachedValue = parse(defaultFormat); 289 } 290 return cachedValue; 291 } 292 293 public int getDateType() { 294 return dateType; 295 } 296 297 public TemplateModel get(String pattern) throws TemplateModelException { 298 return new SimpleDate( 299 parse(env.getDateFormatObject(dateType, pattern)), 300 dateType); 301 } 302 303 public Object exec(List arguments) 304 throws TemplateModelException { 305 if (arguments.size() != 1) { 306 throw new TemplateModelException( 307 "string?" + key + "(...) requires exactly 1 argument."); 308 } 309 return get((String ) arguments.get(0)); 310 } 311 312 public boolean isEmpty() 313 { 314 return false; 315 } 316 317 private Date parse(DateFormat df) 318 throws 319 TemplateModelException 320 { 321 try { 322 return df.parse(text); 323 } 324 catch(java.text.ParseException e) { 325 String mess = "Error: " + getStartLocation() 326 + "\nExpecting a date here, found: " + text; 327 throw new TemplateModelException(mess); 328 } 329 } 330 } 331 } 332 333 static class stringBI extends BuiltIn { 334 TemplateModel _getAsTemplateModel(Environment env) 335 throws TemplateException 336 { 337 TemplateModel model = target.getAsTemplateModel(env); 338 if (model instanceof TemplateNumberModel) { 339 return new NumberFormatter(EvaluationUtil.getNumber((TemplateNumberModel)model, target, env), env); 340 } 341 if (model instanceof TemplateDateModel) { 342 TemplateDateModel dm = (TemplateDateModel)model; 343 int dateType = dm.getDateType(); 344 return new DateFormatter(EvaluationUtil.getDate(dm, target, env), dateType, env); 345 } 346 if (model instanceof SimpleScalar) { 347 return model; 348 } 349 if (model instanceof TemplateBooleanModel) { 350 return new BooleanFormatter((TemplateBooleanModel) model, env); 351 } 352 if (model instanceof TemplateScalarModel) { 353 return new SimpleScalar(((TemplateScalarModel) model).getAsString()); 354 } 355 throw invalidTypeException(model, target, env, "number, date, or string"); 356 } 357 358 private static class NumberFormatter 359 implements 360 TemplateScalarModel, 361 TemplateHashModel, 362 TemplateMethodModel 363 { 364 private final Number number; 365 private final Environment env; 366 private final NumberFormat defaultFormat; 367 private String cachedValue; 368 369 NumberFormatter(Number number, Environment env) 370 { 371 this.number = number; 372 this.env = env; 373 defaultFormat = env.getNumberFormatObject(env.getNumberFormat()); 374 } 375 376 public String getAsString() 377 { 378 if(cachedValue == null) { 379 cachedValue = defaultFormat.format(number); 380 } 381 return cachedValue; 382 } 383 384 public TemplateModel get(String key) 385 { 386 return new SimpleScalar(env.getNumberFormatObject(key).format(number)); 387 } 388 389 public Object exec(List arguments) 390 throws TemplateModelException { 391 if (arguments.size() != 1) { 392 throw new TemplateModelException( 393 "number?string(...) requires exactly 1 argument."); 394 } 395 return get((String ) arguments.get(0)); 396 } 397 398 public boolean isEmpty() 399 { 400 return false; 401 } 402 } 403 404 private static class DateFormatter 405 implements 406 TemplateScalarModel, 407 TemplateHashModel, 408 TemplateMethodModel 409 { 410 private final Date date; 411 private final int dateType; 412 private final Environment env; 413 private final DateFormat defaultFormat; 414 private String cachedValue; 415 416 DateFormatter(Date date, int dateType, Environment env) 417 throws 418 TemplateModelException 419 { 420 this.date = date; 421 this.dateType = dateType; 422 this.env = env; 423 defaultFormat = env.getDateFormatObject(dateType); 424 } 425 426 public String getAsString() 427 throws 428 TemplateModelException 429 { 430 if(dateType == TemplateDateModel.UNKNOWN) { 431 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."); 432 } 433 if(cachedValue == null) { 434 cachedValue = defaultFormat.format(date); 435 } 436 return cachedValue; 437 } 438 439 public TemplateModel get(String key) 440 throws 441 TemplateModelException 442 { 443 return new SimpleScalar(env.getDateFormatObject(dateType, key).format(date)); 444 } 445 446 public Object exec(List arguments) 447 throws TemplateModelException { 448 if (arguments.size() != 1) { 449 throw new TemplateModelException( 450 "date?string(...) requires exactly 1 argument."); 451 } 452 return get((String ) arguments.get(0)); 453 } 454 455 public boolean isEmpty() 456 { 457 return false; 458 } 459 } 460 461 private static class BooleanFormatter 462 implements 463 TemplateScalarModel, 464 TemplateMethodModel 465 { 466 private final TemplateBooleanModel bool; 467 private final Environment env; 468 469 BooleanFormatter(TemplateBooleanModel bool, Environment env) { 470 this.bool = bool; 471 this.env = env; 472 } 473 474 public String getAsString() throws TemplateModelException { 475 if (bool instanceof TemplateScalarModel) { 476 return ((TemplateScalarModel) bool).getAsString(); 477 } else { 478 return env.getBooleanFormat(bool.getAsBoolean()); 479 } 480 } 481 482 public Object exec(List arguments) 483 throws TemplateModelException { 484 if (arguments.size() != 2) { 485 throw new TemplateModelException( 486 "boolean?string(...) requires exactly " 487 + "2 arguments."); 488 } 489 return new SimpleScalar( 490 (String ) arguments.get(bool.getAsBoolean() ? 0 : 1)); 491 } 492 } 493 } 494 495 static class trimBI extends StringBuiltIn { 496 TemplateModel calculateResult(String s, Environment env) { 497 return new SimpleScalar(s.trim()); 498 } 499 } 500 501 static class htmlBI extends StringBuiltIn { 502 TemplateModel calculateResult(String s, Environment env) { 503 return new SimpleScalar(StringUtil.HTMLEnc(s)); 504 } 505 } 506 507 static class xmlBI extends StringBuiltIn { 508 TemplateModel calculateResult(String s, Environment env) { 509 return new SimpleScalar(StringUtil.XMLEnc(s)); 510 } 511 } 512 513 static class rtfBI extends StringBuiltIn { 514 TemplateModel calculateResult(String s, Environment env) { 515 return new SimpleScalar(StringUtil.RTFEnc(s)); 516 } 517 } 518 519 static class urlBI extends StringBuiltIn { 520 521 TemplateModel calculateResult(String s, Environment env) { 522 return new urlBIResult(s, env); 523 } 524 525 static class urlBIResult implements 526 TemplateScalarModel, TemplateMethodModel { 527 528 private final String target; 529 private final Environment env; 530 private String cachedResult; 531 532 private urlBIResult(String target, Environment env) { 533 this.target = target; 534 this.env = env; 535 } 536 537 public String getAsString() throws TemplateModelException { 538 if (cachedResult == null) { 539 String cs = env.getEffectiveURLEscapingCharset(); 540 if (cs == null) { 541 throw new TemplateModelException( 542 "To do URL encoding, the framework that encloses " 543 + "FreeMarker must specify the output encoding " 544 + "or the URL encoding charset, so ask the " 545 + "programmers to fix it. Or, as a last chance, " 546 + "you can set the url_encoding_charset setting in " 547 + "the template, e.g. " 548 + "<#setting url_escaping_charset='ISO-8859-1'>, or " 549 + "give the charset explicitly to the buit-in, e.g. " 550 + "foo?url('ISO-8859-1')."); 551 } 552 try { 553 cachedResult = StringUtil.URLEnc(target, cs); 554 } catch (UnsupportedEncodingException e) { 555 throw new TemplateModelException( 556 "Failed to execute URL encoding.", e); 557 } 558 } 559 return cachedResult; 560 } 561 562 public Object exec(List args) throws TemplateModelException { 563 if (args.size() != 1) { 564 throw new TemplateModelException("The \"url\" built-in " 565 + "needs exactly 1 parameter, the charset."); 566 } 567 try { 568 return new SimpleScalar( 569 StringUtil.URLEnc(target, (String ) args.get(0))); 570 } catch (UnsupportedEncodingException e) { 571 throw new TemplateModelException( 572 "Failed to execute URL encoding.", e); 573 } 574 } 575 576 } 577 } 578 579 static class keysBI extends BuiltIn { 580 TemplateModel _getAsTemplateModel(Environment env) 581 throws TemplateException 582 { 583 TemplateModel model = target.getAsTemplateModel(env); 584 if (model instanceof TemplateHashModelEx) { 585 TemplateCollectionModel keys = ((TemplateHashModelEx) model).keys(); 586 assertNonNull(keys, this, env); 587 if (!(keys instanceof TemplateSequenceModel)) 588 keys = new CollectionAndSequence(keys); 589 return keys; 590 } 591 throw invalidTypeException(model, target, env, "extended hash"); 592 } 593 } 594 595 static class valuesBI extends BuiltIn { 596 TemplateModel _getAsTemplateModel(Environment env) 597 throws TemplateException 598 { 599 TemplateModel model = target.getAsTemplateModel(env); 600 if (model instanceof TemplateHashModelEx) { 601 TemplateCollectionModel values = ((TemplateHashModelEx) model).values(); 602 assertNonNull(values, this, env); 603 if (!(values instanceof TemplateSequenceModel)) 604 values = new CollectionAndSequence(values); 605 return values; 606 } 607 throw invalidTypeException(model, target, env, "extended hash"); 608 } 609 } 610 611 static class sizeBI extends BuiltIn { 612 TemplateModel _getAsTemplateModel(Environment env) 613 throws TemplateException 614 { 615 TemplateModel model = target.getAsTemplateModel(env); 616 if (model instanceof TemplateSequenceModel) { 617 int size = ((TemplateSequenceModel) model).size(); 618 return new SimpleNumber(size); 619 } 620 if (model instanceof TemplateHashModelEx) { 621 int size = ((TemplateHashModelEx) model).size(); 622 return new SimpleNumber(size); 623 } 624 throw invalidTypeException(model, target, env, "extended hash or sequence"); 625 } 626 } 627 628 static class existsBI extends BuiltIn { 629 TemplateModel _getAsTemplateModel(Environment env) 630 throws TemplateException 631 { 632 try { 633 TemplateModel model = target.getAsTemplateModel(env); 634 return model==null ? TemplateBooleanModel.FALSE : TemplateBooleanModel.TRUE; 635 } catch (InvalidReferenceException ire) { 636 if (target instanceof ParentheticalExpression) { 637 return TemplateBooleanModel.FALSE; 638 } 639 throw ire; 640 } 641 } 642 643 boolean isTrue(Environment env) throws TemplateException { 644 return _getAsTemplateModel(env) == TemplateBooleanModel.TRUE; 645 } 646 } 647 648 static class has_contentBI extends BuiltIn { 649 TemplateModel _getAsTemplateModel(Environment env) 650 throws TemplateException 651 { 652 try { 653 TemplateModel model = target.getAsTemplateModel(env); 654 return Expression.isEmpty(model) ? 655 TemplateBooleanModel.FALSE : TemplateBooleanModel.TRUE; 656 } catch (InvalidReferenceException ire) { 657 if (target instanceof ParentheticalExpression) { 658 return TemplateBooleanModel.FALSE; 659 } 660 throw ire; 661 } 662 } 663 664 boolean isTrue(Environment env) throws TemplateException { 665 return _getAsTemplateModel(env) == TemplateBooleanModel.TRUE; 666 } 667 } 668 669 static class if_existsBI extends BuiltIn { 670 TemplateModel _getAsTemplateModel(Environment env) 671 throws TemplateException 672 { 673 try { 674 TemplateModel model = target.getAsTemplateModel(env); 675 return model == null ? TemplateModel.NOTHING : model; 676 } catch (InvalidReferenceException ire) { 677 if (target instanceof ParentheticalExpression) { 678 return TemplateModel.NOTHING; 679 } 680 throw ire; 681 } 682 } 683 } 684 685 static class is_stringBI extends BuiltIn { 686 TemplateModel _getAsTemplateModel(Environment env) throws TemplateException { 687 TemplateModel tm = target.getAsTemplateModel(env); 688 assertNonNull(tm, target, env); 689 return (tm instanceof TemplateScalarModel) ? 690 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; 691 } 692 } 693 694 static class is_numberBI extends BuiltIn { 695 TemplateModel _getAsTemplateModel(Environment env) throws TemplateException { 696 TemplateModel tm = target.getAsTemplateModel(env); 697 assertNonNull(tm, target, env); 698 return (tm instanceof TemplateNumberModel) ? 699 TemplateBooleanModel.TRUE : TemplateBooleanModel.FALSE; 700 } 701 } 702 703 static class is_nodeBI extends BuiltIn { 704 TemplateModel _getAsTemplateModel(Environm
|