1 52 53 package freemarker.template; 54 55 import java.io.*; 56 import java.util.*; 57 import javax.swing.tree.TreePath ; 58 import freemarker.core.*; 59 60 import freemarker.debug.impl.DebuggerService; 61 62 93 94 public class Template extends Configurable { 95 public static final String DEFAULT_NAMESPACE_PREFIX = "D"; 96 public static final String NO_NS_PREFIX = "N"; 97 98 private Map macros = new HashMap(); 99 private List imports = new Vector(); 100 private TemplateElement rootElement; 101 private String encoding, defaultNS; 102 private final String name; 103 private final ArrayList lines = new ArrayList(); 104 private Map prefixToNamespaceURILookup = new HashMap(); 105 private Map namespaceURIToPrefixLookup = new HashMap(); 106 107 111 private Template(String name, Configuration cfg) 112 { 113 super(cfg != null ? cfg : Configuration.getDefaultConfiguration()); 114 this.name = name; 115 } 116 117 133 public Template(String name, Reader reader, Configuration cfg, String encoding) 134 throws IOException 135 { 136 this(name, cfg); 137 this.encoding = encoding; 138 139 if (!(reader instanceof BufferedReader)) { 140 reader = new BufferedReader(reader, 0x1000); 141 } 142 LineTableBuilder ltb = new LineTableBuilder(reader); 143 try { 144 FMParser parser = new FMParser(this, 145 ltb, 146 getConfiguration().getStrictSyntaxMode(), 147 getConfiguration().getWhitespaceStripping()); 148 this.rootElement = parser.Root(); 149 } 150 catch (TokenMgrError exc) { 151 throw new ParseException("Token manager error: " + exc, 0, 0); 152 } 153 finally { 154 ltb.close(); 155 } 156 DebuggerService.registerTemplate(this); 157 namespaceURIToPrefixLookup = Collections.unmodifiableMap(namespaceURIToPrefixLookup); 158 prefixToNamespaceURILookup = Collections.unmodifiableMap(prefixToNamespaceURILookup); 159 } 160 161 164 165 public Template(String name, Reader reader, Configuration cfg) throws IOException { 166 this(name, reader, cfg, null); 167 } 168 169 170 180 public Template(String name, Reader reader) throws IOException { 181 this(name, reader, null); 182 } 183 184 187 Template(String name, TemplateElement root, Configuration config) { 188 this(name, config); 189 this.rootElement = root; 190 DebuggerService.registerTemplate(this); 191 } 192 193 202 static public Template getPlainTextTemplate(String name, String content, Configuration config) { 203 Template template = new Template(name, config); 204 TextBlock block = new TextBlock(content); 205 template.rootElement = block; 206 DebuggerService.registerTemplate(template); 207 return template; 208 } 209 210 228 public void process(Object rootMap, Writer out) 229 throws TemplateException, IOException 230 { 231 createProcessingEnvironment(rootMap, out, null).process(); 232 } 233 234 255 public void process(Object rootMap, Writer out, ObjectWrapper wrapper, TemplateNodeModel rootNode) 256 throws TemplateException, IOException 257 { 258 Environment env = createProcessingEnvironment(rootMap, out, wrapper); 259 if (rootNode != null) { 260 env.setCurrentVisitorNode(rootNode); 261 } 262 env.process(); 263 } 264 265 285 public void process(Object rootMap, Writer out, ObjectWrapper wrapper) 286 throws TemplateException, IOException 287 { 288 process(rootMap, out, wrapper, null); 289 } 290 291 337 public Environment createProcessingEnvironment(Object rootMap, Writer out, ObjectWrapper wrapper) 338 throws TemplateException, IOException 339 { 340 TemplateHashModel root = null; 341 if(rootMap instanceof TemplateHashModel) { 342 root = (TemplateHashModel)rootMap; 343 } 344 else { 345 if(wrapper == null) { 346 wrapper = getObjectWrapper(); 347 } 348 349 try { 350 root = rootMap != null 351 ? (TemplateHashModel)wrapper.wrap(rootMap) 352 : new SimpleHash(wrapper); 353 if(root == null) { 354 throw new IllegalArgumentException (wrapper.getClass().getName() + " converted " + rootMap.getClass().getName() + " to null."); 355 } 356 } 357 catch(ClassCastException e) { 358 throw new IllegalArgumentException (wrapper.getClass().getName() + " could not convert " + rootMap.getClass().getName() + " to a TemplateHashModel."); 359 } 360 } 361 Environment env = new Environment(this, root, out); 362 getConfiguration().doAutoImports(env); 363 getConfiguration().doAutoIncludes(env); 364 return env; 365 } 366 367 371 public Environment createProcessingEnvironment(Object rootMap, Writer out) 372 throws TemplateException, IOException 373 { 374 return createProcessingEnvironment(rootMap, out, null); 375 } 376 377 381 public String toString() { 382 StringWriter sw = new StringWriter(); 383 try { 384 dump(sw); 385 } catch (IOException ioe) { 386 throw new RuntimeException (ioe.getMessage()); 387 } 388 return sw.toString(); 389 } 390 391 392 401 public String getName() { 402 return name; 403 } 404 405 408 public Configuration getConfiguration() { 409 return (Configuration) getParent(); 410 } 411 412 417 418 public void setEncoding(String encoding) { 419 this.encoding = encoding; 420 } 421 422 425 public String getEncoding() { 426 return this.encoding; 427 } 428 429 432 public void dump(PrintStream ps) { 433 ps.print(rootElement.getCanonicalForm()); 434 } 435 436 439 public void dump(Writer out) throws IOException { 440 out.write(rootElement.getCanonicalForm()); 441 } 442 443 447 public void addMacro(Macro macro) { 448 macros.put(macro.getName(), macro); 449 } 450 451 455 public void addImport(LibraryLoad ll) { 456 imports.add(ll); 457 } 458 459 468 public String getSource(int beginColumn, 469 int beginLine, 470 int endColumn, 471 int endLine) 472 { 473 --beginLine; 475 --beginColumn; 476 --endColumn; 477 --endLine; 478 StringBuffer buf = new StringBuffer (); 479 for (int i = beginLine ; i<=endLine; i++) { 480 if (i < lines.size()) { 481 buf.append(lines.get(i)); 482 } 483 } 484 int lastLineLength = lines.get(endLine).toString().length(); 485 int trailingCharsToDelete = lastLineLength - endColumn -1; 486 buf.delete(0, beginColumn); 487 buf.delete(buf.length() - trailingCharsToDelete, buf.length()); 488 return buf.toString(); 489 } 490 491 495 private class LineTableBuilder extends FilterReader { 496 497 StringBuffer lineBuf = new StringBuffer (); 498 int lastChar; 499 500 503 LineTableBuilder(Reader r) { 504 super(r); 505 } 506 507 public int read() throws IOException { 508 int c = in.read(); 509 handleChar(c); 510 return c; 511 } 512 513 public int read(char cbuf[], int off, int len) throws IOException { 514 int numchars = in.read(cbuf, off, len); 515 for (int i=off; i < off+numchars; i++) { 516 char c = cbuf[i]; 517 handleChar(c); 518 } 519 return numchars; 520 } 521 522 public void close() throws IOException { 523 if (lineBuf.length() >0) { 524 lines.add(lineBuf.toString()); 525 lineBuf.setLength(0); 526 } 527 super.close(); 528 } 529 530 private void handleChar(int c) { 531 if (c == '\n' || c == '\r') { 532 if (lastChar == '\r' && c == '\n') { int lastIndex = lines.size() -1; 534 String lastLine = (String ) lines.get(lastIndex); 535 lines.set(lastIndex, lastLine + '\n'); 536 } else { 537 lineBuf.append((char) c); 538 lines.add(lineBuf.toString()); 539 lineBuf.setLength(0); 540 } 541 } 542 else if (c == '\t') { 543 int numSpaces = 8 - (lineBuf.length() %8); 544 for (int i=0; i<numSpaces; i++) { 545 lineBuf.append(' '); 546 } 547 } 548 else { 549 lineBuf.append((char) c); 550 } 551 lastChar = c; 552 } 553 } 554 555 558 public TemplateElement getRootTreeNode() { 559 return rootElement; 560 } 561 562 public Map getMacros() { 563 return macros; 564 } 565 566 public List getImports() { 567 return imports; 568 } 569 570 573 public void addPrefixNSMapping(String prefix, String nsURI) { 574 if (nsURI.length() == 0) { 575 throw new IllegalArgumentException ("Cannot map empty string URI"); 576 } 577 if (prefix.length() == 0) { 578 throw new IllegalArgumentException ("Cannot map empty string prefix"); 579 } 580 if (prefix.equals(NO_NS_PREFIX)) { 581 throw new IllegalArgumentException ("The prefix: " + prefix + " cannot be registered, it is reserved for special internal use."); 582 } 583 if (prefixToNamespaceURILookup.containsKey(prefix)) { 584 throw new IllegalArgumentException ("The prefix: '" + prefix + "' was repeated. This is illegal."); 585 } 586 if (namespaceURIToPrefixLookup.containsKey(nsURI)) { 587 throw new IllegalArgumentException ("The namespace URI: " + nsURI + " cannot be mapped to 2 different prefixes."); 588 } 589 if (prefix.equals(DEFAULT_NAMESPACE_PREFIX)) { 590 this.defaultNS = nsURI; 591 } else { 592 prefixToNamespaceURILookup.put(prefix, nsURI); 593 namespaceURIToPrefixLookup.put(nsURI, prefix); 594 } 595 } 596 597 public String getDefaultNS() { 598 return this.defaultNS; 599 } 600 601 604 public String getNamespaceForPrefix(String prefix) { 605 if (prefix.equals("")) { 606 return defaultNS == null ? "" : defaultNS; 607 } 608 return (String ) prefixToNamespaceURILookup.get(prefix); 609 } 610 611 614 public String getPrefixForNamespace(String nsURI) { 615 if (nsURI == null) { 616 return null; 617 } 618 if (nsURI.length() == 0) { 619 return defaultNS == null ? "" : NO_NS_PREFIX; 620 } 621 if (nsURI.equals(defaultNS)) { 622 return ""; 623 } 624 return (String ) namespaceURIToPrefixLookup.get(nsURI); 625 } 626 627 632 public String getPrefixedName(String localName, String nsURI) { 633 if (nsURI == null || nsURI.length() == 0) { 634 if (defaultNS != null) { 635 return NO_NS_PREFIX + ":" + localName; 636 } else { 637 return localName; 638 } 639 } 640 if (nsURI.equals(defaultNS)) { 641 return localName; 642 } 643 String prefix = getPrefixForNamespace(nsURI); 644 if (prefix == null) { 645 return null; 646 } 647 return prefix + ":" + localName; 648 } 649 650 655 public TreePath containingElements(int column, int line) { 656 ArrayList elements = new ArrayList(); 657 TemplateElement element = rootElement; 658 mainloop: 659 while (element.contains(column, line)) { 660 elements.add(element); 661 for (Enumeration enumeration = element.children(); enumeration.hasMoreElements();) { 662 TemplateElement elem = (TemplateElement) enumeration.nextElement(); 663 if (elem.contains(column, line)) { 664 element = elem; 665 continue mainloop; 666 } 667 } 668 break; 669 } 670 if (elements == null || elements.isEmpty()) { 671 return null; 672 } 673 return new TreePath (elements.toArray()); 674 } 675 676 static public class WrongEncodingException extends ParseException { 677 678 public String specifiedEncoding; 679 680 public WrongEncodingException(String specifiedEncoding) { 681 this.specifiedEncoding = specifiedEncoding; 682 } 683 684 } 685 } 686 687 | Popular Tags |