1 5 package xdoclet.template; 6 7 import java.io.BufferedWriter ; 8 import java.io.ByteArrayOutputStream ; 9 import java.io.File ; 10 import java.io.FileOutputStream ; 11 import java.io.FileWriter ; 12 import java.io.IOException ; 13 import java.io.OutputStreamWriter ; 14 import java.lang.reflect.InvocationTargetException ; 15 import java.lang.reflect.Method ; 16 import java.net.URL ; 17 import java.util.HashMap ; 18 import java.util.Iterator ; 19 import java.util.List ; 20 import java.util.Map ; 21 import java.util.Properties ; 22 import java.util.Set ; 23 24 import org.apache.commons.logging.Log; 25 import xjavadoc.XJavaDoc; 26 import xdoclet.loader.*; 27 28 import xdoclet.util.FileManager; 29 import xdoclet.util.LogUtil; 30 import xdoclet.util.Translator; 31 32 44 public class TemplateEngine 45 { 46 public final static String TAG_MAPPINGS_FILE = "/tagmappings.properties"; 47 protected final static String XDOCLET_PREFIX; 48 protected final static String XDOCLET_HEAD; 49 protected final static String XDOCLET_TAIL; 50 protected final static int XDOCLET_HEAD_LEN; 51 protected final static int XDOCLET_TAIL_LEN; 52 private static TemplateEngine instance = new TemplateEngine(); 53 54 60 protected transient PrettyPrintWriter out; 61 62 protected transient File output = null; 63 64 protected transient String docEncoding = null; 65 66 69 private transient URL templateURL = null; 70 71 74 private transient int currentLineNum = 0; 75 76 79 private Map tagMappings = new HashMap (); 80 81 private XJavaDoc _xJavaDoc; 82 83 static { 84 XDOCLET_PREFIX = "XD"; 85 86 88 XDOCLET_HEAD = "<" + XDOCLET_PREFIX; 89 XDOCLET_TAIL = "</" + XDOCLET_PREFIX; 90 XDOCLET_HEAD_LEN = XDOCLET_HEAD.length(); 91 XDOCLET_TAIL_LEN = XDOCLET_TAIL.length(); 92 } 93 94 protected TemplateEngine() 95 { 96 registerTagHandlers(); 97 } 98 99 104 public static TemplateEngine getEngineInstance() 105 { 106 return instance; 107 } 108 109 117 public static int skipWhitespace(String template, int i) 118 { 119 while (i < template.length() && Character.isWhitespace(template.charAt(i))) { 120 i++; 121 } 122 123 return i; 124 } 125 126 134 protected static int getLineNumber(String template, int tillIndex) 135 { 136 int NL_LEN = PrettyPrintWriter.LINE_SEPARATOR.length(); 137 int index = 0; 138 int lineNumber = 0; 139 140 do { 141 index = template.indexOf(PrettyPrintWriter.LINE_SEPARATOR, index); 142 143 if (index != -1) { 144 lineNumber++; 145 index += NL_LEN; 146 } 147 else { 148 break; 149 } 150 } while (index < tillIndex); 151 152 return lineNumber; 153 } 154 155 161 public URL getTemplateURL() 162 { 163 return templateURL; 164 } 165 166 171 public File getOutput() 172 { 173 return output; 174 } 175 176 181 public int getCurrentLineNum() 182 { 183 return currentLineNum; 184 } 185 186 193 public TemplateTagHandler getTagHandlerFor(String prefix) throws TemplateException 194 { 195 Log log = LogUtil.getLog(TemplateEngine.class, "getTagHandlerFor"); 196 197 TemplateTagHandler tagHandler = (TemplateTagHandler) tagMappings.get(prefix); 198 199 if (log.isDebugEnabled()) { 200 log.debug("prefix=" + prefix); 201 log.debug("tagHandler=" + tagHandler); 202 } 203 204 if (tagHandler == null) { 205 String msg = Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_NO_TAGHANDLER, new String []{"'XDt" + prefix + "'"}); 206 207 log.error(msg); 208 throw new TemplateException(msg); 209 } 210 211 return tagHandler; 212 } 213 214 219 public Set getNamespaces() 220 { 221 return tagMappings.keySet(); 222 } 223 224 public void setXJavaDoc(XJavaDoc xJavaDoc) 225 { 226 _xJavaDoc = xJavaDoc; 227 TemplateTagHandler.setXJavaDoc(_xJavaDoc); 228 } 229 230 235 public void setWriter(PrettyPrintWriter out) 236 { 237 this.out = out; 238 } 239 240 245 public void setCurrentLineNum(int currentLineNum) 246 { 247 this.currentLineNum = currentLineNum; 248 } 249 250 257 public void setTemplateURL(URL templateURL) 258 { 259 this.templateURL = templateURL; 260 } 261 262 267 public void setOutput(File output) 268 { 269 this.output = output; 270 } 271 272 279 public void setTagHandlerFor(String prefix, TemplateTagHandler tagHandler) throws TemplateException 280 { 281 Log log = LogUtil.getLog(TemplateEngine.class, "setTagHandlerFor"); 282 283 if (log.isDebugEnabled()) { 284 log.debug("prefix=" + prefix); 285 log.debug("tagHandler=" + tagHandler); 286 } 287 288 tagMappings.put(prefix, tagHandler); 289 } 290 291 296 public void setDocEncoding(String string) 297 { 298 docEncoding = string; 299 } 300 301 306 public final void print(String output) 307 { 308 if (out != null) { 309 out.print(output); 310 out.flush(); 311 } 312 } 313 314 331 public void generate(final String template) throws TemplateException 332 { 333 int index = 0; 334 int prevIndex = 0; 335 336 while (true) { 337 index = template.indexOf(XDOCLET_HEAD, prevIndex); 339 340 if (index == -1) { 341 print(template.substring(prevIndex)); 343 break; 344 } 345 else { 346 print(template.substring(prevIndex, index)); 348 prevIndex = handleTag(index, template); 349 } 350 } 351 } 352 353 365 public String outputOf(String template) throws TemplateException 366 { 367 PrettyPrintWriter oldOut = out; 368 ByteArrayOutputStream bout = new ByteArrayOutputStream (); 369 370 out = new PrettyPrintWriter(bout); 371 generate(template); 372 out.close(); 373 out = oldOut; 374 375 return new String (bout.toByteArray()); 376 } 377 378 383 public void start() throws TemplateException 384 { 385 Log log = LogUtil.getLog(TemplateEngine.class, "start"); 386 387 output.getParentFile().mkdirs(); 388 389 String content = FileManager.getURLContent(getTemplateURL()); 390 391 if (content != null) { 392 PrettyPrintWriter out = null; 393 394 try { 395 String encoding = docEncoding; 396 397 if (encoding == null) { 398 encoding = _xJavaDoc.getDocEncoding(); 399 } 400 401 if (encoding == null) { 402 out = new PrettyPrintWriter( 403 new BufferedWriter (new FileWriter (output))); 404 } 405 else { 406 out = new PrettyPrintWriter( 407 new BufferedWriter ( 408 new OutputStreamWriter ( 409 new FileOutputStream (output), 410 encoding))); 411 } 412 413 setWriter(out); 414 setCurrentLineNum(0); 415 generate(content); 416 setWriter(null); 417 } 418 catch (IOException ex) { 419 String msg = Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_ERROR_WRITING_OUTPUT, new String []{output.toString()}); 420 421 log.error(msg, ex); 422 throw new TemplateException(ex, msg); 423 } 424 finally { 425 if (out != null) { 426 out.close(); 427 } 428 } 429 } 430 else { 431 String msg = Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_NOT_FOUND, new String []{getTemplateURL().toString()}); 432 433 log.error(msg); 434 throw new TemplateException(msg); 435 } 436 } 437 438 446 protected int handleTag(int index, final String template) throws TemplateException 447 { 448 int i = index + XDOCLET_HEAD_LEN; 450 451 StringBuffer cmd = new StringBuffer (); 453 454 TagContext tagContext = new TagContext(); 456 457 Properties attributes = new Properties (); 459 460 i = extractTagName(template, i, cmd); 462 463 465 i = doInitialTagParse(template, i, tagContext); 467 468 i = extractAttributes(tagContext, template, i, attributes); 470 471 if (tagContext.isBlock()) { 472 i = handleBlockTag(i, template, cmd.toString(), attributes); 473 } 474 else { 475 invokeContentMethod(cmd.toString(), attributes, template, i); 476 } 477 478 return i; 479 } 480 481 497 protected Object invokeMethod(String cmd, Object [] params1, Object [] params2, String template, int i) throws TemplateException 498 { 499 Log log = LogUtil.getLog(TemplateEngine.class, "invokeMethod"); 500 501 508 int colon = cmd.indexOf(':'); 509 510 if (colon < 0) { 511 log.error("Invoking method failed: XD" + cmd + " is not valid because it does not contain ':' , line=" + getLineNumber(template, i) + " of template file: " + getTemplateURL()); 512 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_INVOKE_METHOD_FAILED, 513 new String []{"XD" + cmd, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString()})); 514 } 515 516 String prefix = cmd.substring(0, colon); 517 String methodName = cmd.substring(colon + 1); 518 519 TemplateTagHandler cmdImplProvider; 520 521 try { 522 cmdImplProvider = getTagHandlerFor(prefix.substring(1)); 523 } 524 catch (TemplateException e) { 525 log.error("Error occured at/around line " + getLineNumber(template, i) + ", offending template tag: XD" + cmd); 526 throw e; 527 } 528 529 String className = cmdImplProvider.getClass().getName(); 530 531 try { 532 Class [] paramTypes = new Class [params1.length]; 533 534 for (int j = 0; j < params1.length; j++) { 535 paramTypes[j] = params1[j].getClass(); 536 } 537 538 Method m = cmdImplProvider.getClass().getMethod(methodName, paramTypes); 539 540 return invoke(m, cmdImplProvider, params1); 541 } 542 catch (InvocationTargetException e) { 543 if (e.getTargetException() instanceof TemplateException) { 544 throw (TemplateException) e.getTargetException(); 545 } 546 else { 547 log.error("Invoking method failed: " + className + '.' + methodName + ", line=" + getLineNumber(template, i) + " of template file: " + getTemplateURL(), e); 548 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_INVOKE_METHOD_FAILED, 549 new String []{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e.getMessage()})); 550 } 551 } 552 catch (IllegalAccessException e) { 553 log.error("Invoking method failed: " + className + '.' + methodName + ", line=" + getLineNumber(template, i) + " of template file: " + getTemplateURL(), e); 554 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_INVOKE_METHOD_FAILED, 555 new String []{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e.getMessage()})); 556 } 557 catch (NoSuchMethodException e) { 558 Class [] paramTypes = new Class [params2.length]; 559 560 try { 561 for (int j = 0; j < params2.length; j++) { 562 paramTypes[j] = params2[j].getClass(); 563 } 564 565 Method m = cmdImplProvider.getClass().getMethod(methodName, paramTypes); 566 567 return invoke(m, cmdImplProvider, params2); 568 } 569 catch (NoSuchMethodException nsme) { 570 log.error("Could not find method " + className + '.' + methodName + " in class " + cmdImplProvider.getClass().getName()); 571 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_NO_SUCH_METHOD, 572 new String []{methodName, cmdImplProvider.getClass().getName(), nsme.getMessage()})); 573 } 574 catch (InvocationTargetException e2) { 575 if (e2.getTargetException() instanceof TemplateException) { 576 throw (TemplateException) e2.getTargetException(); 577 } 578 else { 579 log.error("Invoking method failed: " + className + '.' + methodName + ", line=" + getLineNumber(template, i) + " of template file: " + getTemplateURL(), e2); 580 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_INVOKE_METHOD_FAILED, 581 new String []{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e2.getMessage()})); 582 } 583 } 584 catch (IllegalAccessException e2) { 585 log.error("Invoking method failed: " + className + '.' + methodName + ", line=" + getLineNumber(template, i) + " of template file: " + getTemplateURL(), e2); 586 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_INVOKE_METHOD_FAILED, 587 new String []{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e2.getMessage()})); 588 } 589 } 590 } 591 592 604 protected void invokeContentMethod(String cmd, Properties attributes, String template, int i) throws TemplateException 605 { 606 Object [] params1 = null; 607 Object [] params2 = null; 608 609 611 if (attributes.size() > 0) { 612 params1 = new Object []{attributes}; 613 params2 = new Object []{}; 614 } 615 else { 616 params1 = new Object []{}; 617 params2 = new Object []{attributes}; 618 } 619 620 String result = (String ) invokeMethod(cmd, params1, params2, template, i); 621 622 if (result != null && out != null) { 623 print(result); 624 } 625 } 626 627 638 protected Object invoke(Method m, Object cmdImplProvider, Object [] params1) 639 throws InvocationTargetException , IllegalAccessException , TemplateException 640 { 641 return m.invoke(cmdImplProvider, params1); 642 } 643 644 private void registerTagHandlers() 645 { 646 Log log = LogUtil.getLog(TemplateEngine.class, "registerTagHandlers"); 647 List modules = ModuleFinder.findModules(); 648 Iterator i = modules.iterator(); 649 650 while (i.hasNext()) { 651 XDocletModule module = (XDocletModule) i.next(); 652 653 log.debug("Registering module " + module); 654 655 List tagHandlerDefinitions = module.getTagHandlerDefinitions(); 657 Iterator k = tagHandlerDefinitions.iterator(); 658 659 while (k.hasNext()) { 660 TagHandlerDefinition thd = (TagHandlerDefinition) k.next(); 661 662 log.debug("Registering tag handler " + thd.namespace); 663 664 try { 665 TemplateTagHandler handler = (TemplateTagHandler) Class.forName(thd.className).newInstance(); 666 667 671 log.debug("Add tagHandler " + thd.namespace + " (" + thd.className + ')'); 672 setTagHandlerFor(thd.namespace, handler); 673 677 } 678 catch (Exception e) { 679 log.error("Couldn't instantiate " + thd.className + " taghandler ", e); 680 } 681 } 682 } 683 } 684 685 695 private int extractAttributes(TagContext tagContext, final String template, int i, Properties attributes) throws TemplateException 696 { 697 while (tagContext.hasMoreAttributes()) { 698 i = extractNextAttribute(template, i, tagContext, attributes); 699 } 700 701 return i; 702 } 703 704 712 private int extractTagName(final String template, int index, StringBuffer cmd) 713 { 714 while ((!Character.isWhitespace(template.charAt(index))) && template.charAt(index) != '>' && template.charAt(index) != '/') { 715 cmd.append(template.charAt(index)); 716 index++; 717 } 718 719 return index; 720 } 721 722 732 private int extractNextAttribute(final String template, int index, TagContext tagContext, Properties attributes) throws TemplateException 733 { 734 StringBuffer attributeName = new StringBuffer (); 735 StringBuffer attributeValue = new StringBuffer (); 736 char quoteChar = '"'; 737 738 try { 739 while (template.charAt(index) != '=' && (!Character.isWhitespace(template.charAt(index)))) { 741 attributeName.append(template.charAt(index)); 742 index++; 743 } 744 745 index = skipWhitespace(template, index); 746 747 if (template.charAt(index) == '=') { 749 index++; 750 } 751 else { 752 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_EQUALS_EXPECTED, new String []{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()})); 753 } 754 755 index = skipWhitespace(template, index); 756 757 if (template.charAt(index) == '"') { 759 index++; 760 quoteChar = '"'; 761 } 762 else if (template.charAt(index) == '\'') { 763 index++; 764 quoteChar = '\''; 765 } 766 else { 767 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_QUOTE_EXPECTED, new String []{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()})); 768 } 769 770 while (template.charAt(index) != quoteChar) { 772 attributeValue.append(template.charAt(index)); 773 index++; 774 } 775 776 index++; 778 tagContext.setHasMoreAttributes(true); 779 780 if (attributeValue.toString().indexOf(XDOCLET_HEAD) != -1) { 781 attributeValue = new StringBuffer (outputOf(attributeValue.toString())); 782 } 783 784 index = skipWhitespace(template, index); 785 786 if (template.charAt(index) == '>') { 787 index++; 788 tagContext.setBlock(true); 789 tagContext.setHasMoreAttributes(false); 790 791 } 793 else if (template.charAt(index) == '/') { 794 index++; 795 796 if (template.charAt(index) == '>') { 797 index++; 798 tagContext.setBlock(false); 799 tagContext.setHasMoreAttributes(false); 800 801 } 803 else { 804 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_GT_EXPECTED, new String []{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()})); 805 } 806 } 807 } 808 catch (java.lang.StringIndexOutOfBoundsException ex) { 809 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_SYNTAX_ERROR, new String []{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString(), template})); 810 } 811 attributes.setProperty(attributeName.toString(), attributeValue.toString()); 812 return index; 813 } 814 815 825 private int doInitialTagParse(final String template, int index, TagContext tagContext) throws TemplateException 826 { 827 while (true) { 828 if (template.charAt(index) == '>') { 829 index++; 830 tagContext.setHasMoreAttributes(false); 831 tagContext.setBlock(true); 832 return index; 833 } 834 else if (template.charAt(index) == '/') { 835 index++; 836 837 if (template.charAt(index) == '>') { 838 index++; 839 tagContext.setHasMoreAttributes(false); 840 tagContext.setBlock(false); 841 return index; 842 } 843 else { 844 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_GT_EXPECTED, new String []{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()})); 845 } 846 } 847 else if (Character.isWhitespace(template.charAt(index))) { 848 index = skipWhitespace(template, index); 849 continue; 850 } 851 else { 852 tagContext.setHasMoreAttributes(true); 853 return index; 854 } 855 } 856 } 857 858 868 private int handleBlockTag(int index, String template, String cmd, Properties attributes) throws TemplateException 869 { 870 int openNestedElemCount = 1; 871 String newBody = null; 872 int bodyStartIndex = index; 873 int bodyEndIndex = -1; 874 875 while (index < template.length()) { 876 int fromIndex = index; 877 878 bodyEndIndex = template.indexOf(new StringBuffer (XDOCLET_TAIL).append(cmd).toString(), index); 879 880 if (bodyEndIndex == -1) { 881 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_CLOSE_TAG_MISSING, 882 new String []{new StringBuffer (XDOCLET_TAIL).append(cmd).append('>').toString(), Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()})); 883 } 884 else { 885 openNestedElemCount--; 886 } 887 888 index = bodyEndIndex + XDOCLET_TAIL_LEN + cmd.length(); 890 891 index = skipWhitespace(template, index); 893 894 index++; 896 897 StringBuffer xdocletPrefixPlusCmd = new StringBuffer (XDOCLET_PREFIX).append(cmd); 898 899 int nestedStartIndex = template.indexOf(xdocletPrefixPlusCmd.toString(), fromIndex); 902 903 while (nestedStartIndex != -1 && nestedStartIndex < bodyEndIndex) { 905 if (template.charAt(nestedStartIndex - 1) == '<') { 907 openNestedElemCount++; 908 } 909 else { 910 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_CORRESPONDING_TAG_MISSING, 911 new String []{new StringBuffer (XDOCLET_TAIL).append(cmd).append('>').toString(), Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()})); 912 } 913 914 nestedStartIndex = template.indexOf(xdocletPrefixPlusCmd.toString(), nestedStartIndex + 1); 915 } 916 917 if (openNestedElemCount == 0) { 918 break; 919 } 920 } 921 922 newBody = template.substring(bodyStartIndex, bodyEndIndex); 923 924 int previousLineNum = currentLineNum; 925 int localBodyLineNum = getLineNumber(template, bodyStartIndex); 926 927 currentLineNum += localBodyLineNum; 928 if (previousLineNum > 0) { 929 currentLineNum--; 930 } 931 932 invokeBlockMethod(cmd, newBody, attributes, template, index); 933 934 currentLineNum = previousLineNum; 935 return index; 936 } 937 938 949 private void invokeBlockMethod(String cmd, String block, Properties attributes, String template, int i) throws TemplateException 950 { 951 Object [] params1 = null; 952 Object [] params2 = null; 953 954 956 if (attributes.size() > 0) { 957 params1 = new Object []{block, attributes}; 958 params2 = new Object []{block}; 959 } 960 else { 961 params1 = new Object []{block}; 962 params2 = new Object []{block, attributes}; 963 } 964 965 invokeMethod(cmd, params1, params2, template, i); 966 } 967 968 974 private static class TagContext 975 { 976 private boolean hasMoreAttributes = false; 977 private boolean isBlock = false; 978 979 984 public boolean isBlock() 985 { 986 return isBlock; 987 } 988 989 994 public void setHasMoreAttributes(boolean attributes) 995 { 996 this.hasMoreAttributes = attributes; 997 } 998 999 1004 public void setBlock(boolean block) 1005 { 1006 isBlock = block; 1007 } 1008 1009 1014 public boolean hasMoreAttributes() 1015 { 1016 return hasMoreAttributes; 1017 } 1018 } 1019} 1020 | Popular Tags |