KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > xdoclet > template > TemplateEngine


1 /*
2  * Copyright (c) 2001, 2002 The XDoclet team
3  * All rights reserved.
4  */

5 package xdoclet.template;
6
7 import java.io.BufferedWriter JavaDoc;
8 import java.io.ByteArrayOutputStream JavaDoc;
9 import java.io.File JavaDoc;
10 import java.io.FileOutputStream JavaDoc;
11 import java.io.FileWriter JavaDoc;
12 import java.io.IOException JavaDoc;
13 import java.io.OutputStreamWriter JavaDoc;
14 import java.lang.reflect.InvocationTargetException JavaDoc;
15 import java.lang.reflect.Method JavaDoc;
16 import java.net.URL JavaDoc;
17 import java.util.HashMap JavaDoc;
18 import java.util.Iterator JavaDoc;
19 import java.util.List JavaDoc;
20 import java.util.Map JavaDoc;
21 import java.util.Properties JavaDoc;
22 import java.util.Set JavaDoc;
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 /**
33  * The default template engine used by derived SubTasks. It looks for XML-ish <XDoclet:blabla> strings, just like JSP
34  * tag libraries. There's no support for scriptlets, because it's cleaner to use and implementing the XML-ish tags is
35  * easy, they are not heavyweight like jsp taglib implementations and there's no need for something like taglib.tld.
36  *
37  * @author Rickard Oberg (rickard@dreambean.com)
38  * @author Ara Abrahamian (ara_e@email.com)
39  * @author Dmitri Colebatch (dim@bigpond.net.au)
40  * @created July 14, 2001
41  * @version $Revision: 1.39 $
42  * @see #generate(java.lang.String)
43  */

44 public class TemplateEngine
45 {
46     public final static String JavaDoc TAG_MAPPINGS_FILE = "/tagmappings.properties";
47     protected final static String JavaDoc XDOCLET_PREFIX;
48     protected final static String JavaDoc XDOCLET_HEAD;
49     protected final static String JavaDoc 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     /**
55      * The PrintWriter used for outputing the generated stuff. {@link xdoclet.template.PrettyPrintWriter} tries to
56      * pretty format the generated file by removing redundant spaces/lines.
57      *
58      * @see xdoclet.template.PrettyPrintWriter
59      */

60     protected transient PrettyPrintWriter out;
61
62     protected transient File JavaDoc output = null;
63
64     protected transient String JavaDoc docEncoding = null;
65
66     /**
67      * The template file currently being processed.
68      */

69     private transient URL JavaDoc templateURL = null;
70
71     /**
72      * Where we are in the template file. Used for reporting errors.
73      */

74     private transient int currentLineNum = 0;
75
76     /**
77      * The map of tag mappings. String => TemplateTagHandler
78      */

79     private Map JavaDoc tagMappings = new HashMap JavaDoc();
80
81     private XJavaDoc _xJavaDoc;
82
83     static {
84         XDOCLET_PREFIX = "XD";
85
86         // migration path from "XDoclet:" to "XDt"
87

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     /**
100      * Gets the EngineInstance attribute of the TemplateEngine class
101      *
102      * @return The EngineInstance value
103      */

104     public static TemplateEngine getEngineInstance()
105     {
106         return instance;
107     }
108
109     /**
110      * Skips whitespaces, starting from index <code>i</code> till the first non-whitespace character or end of <code>template</code>
111      * and returns the new index.
112      *
113      * @param template Description of Parameter
114      * @param i Description of Parameter
115      * @return Description of the Returned Value
116      */

117     public static int skipWhitespace(String JavaDoc template, int i)
118     {
119         while (i < template.length() && Character.isWhitespace(template.charAt(i))) {
120             i++;
121         }
122
123         return i;
124     }
125
126     /**
127      * Loops over the <code>template</code> content till reaching <code>tillIndex</code> index and returns the number of
128      * lines it has encountered.
129      *
130      * @param template Description of Parameter
131      * @param tillIndex Description of Parameter
132      * @return The LineNumber value
133      */

134     protected static int getLineNumber(String JavaDoc 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     /**
156      * Returns current template URL.
157      *
158      * @return The TemplateURL value
159      * @see #setTemplateURL(java.net.URL)
160      */

161     public URL JavaDoc getTemplateURL()
162     {
163         return templateURL;
164     }
165
166     /**
167      * Gets the Output attribute of the TemplateEngine object
168      *
169      * @return The Output value
170      */

171     public File JavaDoc getOutput()
172     {
173         return output;
174     }
175
176     /**
177      * Gets the CurrentLineNum attribute of the TemplateEngine object
178      *
179      * @return The CurrentLineNum value
180      */

181     public int getCurrentLineNum()
182     {
183         return currentLineNum;
184     }
185
186     /**
187      * Get the tag handler for the prefix.
188      *
189      * @param prefix The prefix that the tag handler is mapped to
190      * @return The TemplateTagHandler for the specified prefix. ALways non-null.
191      * @throws TemplateException If there is no tag handler class for the prefix specified.
192      */

193     public TemplateTagHandler getTagHandlerFor(String JavaDoc 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 JavaDoc msg = Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_NO_TAGHANDLER, new String JavaDoc[]{"'XDt" + prefix + "'"});
206
207             log.error(msg);
208             throw new TemplateException(msg);
209         }
210
211         return tagHandler;
212     }
213
214     /**
215      * Returns a Set of Strings that represent the registered namespaces
216      *
217      * @return
218      */

219     public Set JavaDoc 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     /**
231      * Sets the Writer attribute of the TemplateEngine object
232      *
233      * @param out The new Writer value
234      */

235     public void setWriter(PrettyPrintWriter out)
236     {
237         this.out = out;
238     }
239
240     /**
241      * Sets the CurrentLineNum attribute of the TemplateEngine object
242      *
243      * @param currentLineNum The new CurrentLineNum value
244      */

245     public void setCurrentLineNum(int currentLineNum)
246     {
247         this.currentLineNum = currentLineNum;
248     }
249
250     /**
251      * A config parameter settable from Ant build file. It sets the current template file to <code>templateURL</code>,
252      * so thereafter the new template file is used.
253      *
254      * @param templateURL The new TemplateFile value
255      * @see #getTemplateURL()
256      */

257     public void setTemplateURL(URL JavaDoc templateURL)
258     {
259         this.templateURL = templateURL;
260     }
261
262     /**
263      * Sets the Output attribute of the TemplateEngine object
264      *
265      * @param output The new Output value
266      */

267     public void setOutput(File JavaDoc output)
268     {
269         this.output = output;
270     }
271
272     /**
273      * Sets the TagHandlerFor attribute of the TemplateEngine object
274      *
275      * @param prefix The new TagHandlerFor value
276      * @param tagHandler The new TagHandlerFor value
277      * @exception TemplateException Describe the exception
278      */

279     public void setTagHandlerFor(String JavaDoc 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     /**
292      * set output charset;
293      *
294      * @param string
295      */

296     public void setDocEncoding(String JavaDoc string)
297     {
298         docEncoding = string;
299     }
300
301     /**
302      * Describe what the method does
303      *
304      * @param output Describe what the parameter does
305      */

306     public final void print(String JavaDoc output)
307     {
308         if (out != null) {
309             out.print(output);
310             out.flush();
311         }
312     }
313
314     /**
315      * The main template parsing/processing/running logic. It searches for &lt;XDoclet: string, parses what is found
316      * after it till a matching &lt;/XDoclet: is found in case of a block tag, or till a /&gt; is found in case of a
317      * content tag. It automatically calls the relevent tag implementation method with the correct parameters. If a
318      * block tag, then the tag implementation accepts two parameters, the body of the block tag as a string and a
319      * Properties object containing all attributes. Note that if the tag doesn't have any attributes the corresponding
320      * tag implementation typically only accepts a single string value denoting the block body, though it can also
321      * accept a Properties as the second parameter. Tags that may or may not have attributes can safely accept the
322      * second Properties object, which will be filled either by nothing or by all the given attributes. Content tag
323      * implementation methods have no parameter but should return a String containing the result that should be printed
324      * to the generated file. XTag implementation methods should define and throw org.apache.tools.ant.TemplateException
325      * if any serious error occurs.
326      *
327      * @param template Description of Parameter
328      * @exception TemplateException Description of Exception
329      * @see #outputOf(java.lang.String)
330      */

331     public void generate(final String JavaDoc template) throws TemplateException
332     {
333         int index = 0;
334         int prevIndex = 0;
335
336         while (true) {
337             // Look for the next tag that we haven't yet handled.
338
index = template.indexOf(XDOCLET_HEAD, prevIndex);
339
340             if (index == -1) {
341                 // we didn't find a tag! print the rest of the template and finish
342
print(template.substring(prevIndex));
343                 break;
344             }
345             else {
346                 // we found a tag! print the template up to the tag start, and then handle the tag
347
print(template.substring(prevIndex, index));
348                 prevIndex = handleTag(index, template);
349             }
350         }
351     }
352
353     /**
354      * Calls <code>generate()</code> of the specified template content but instead of outputing it to the generated
355      * file, it returns the generated content. It's useful for cases where you want to synthesize the result but use it
356      * instead of roughly outputing it, for example it's used for the content tags nested inside an attribute value such
357      * as: <code>&lt;XDoclet:blabla param1="&lt;XDoclet:aContentTag/&gt;"/&gt;</code> where we obviously don't want to
358      * output the result of <code>aContentTag</code> but use it as the value of the <code>param1</code> parameter.
359      *
360      * @param template Description of Parameter
361      * @return Description of the Returned Value
362      * @exception TemplateException Description of Exception
363      * @see #generate(java.lang.String)
364      */

365     public String JavaDoc outputOf(String JavaDoc template) throws TemplateException
366     {
367         PrettyPrintWriter oldOut = out;
368         ByteArrayOutputStream JavaDoc bout = new ByteArrayOutputStream JavaDoc();
369
370         out = new PrettyPrintWriter(bout);
371         generate(template);
372         out.close();
373         out = oldOut;
374
375         return new String JavaDoc(bout.toByteArray());
376     }
377
378     /**
379      * A utility method used for generating the dest_file based on template_file template file.
380      *
381      * @exception TemplateException Description of Exception
382      */

383     public void start() throws TemplateException
384     {
385         Log log = LogUtil.getLog(TemplateEngine.class, "start");
386
387         output.getParentFile().mkdirs();
388
389         String JavaDoc content = FileManager.getURLContent(getTemplateURL());
390
391         if (content != null) {
392             PrettyPrintWriter out = null;
393
394             try {
395                 String JavaDoc encoding = docEncoding;
396
397                 if (encoding == null) {
398                     encoding = _xJavaDoc.getDocEncoding();
399                 }
400
401                 if (encoding == null) {
402                     out = new PrettyPrintWriter(
403                         new BufferedWriter JavaDoc(new FileWriter JavaDoc(output)));
404                 }
405                 else {
406                     out = new PrettyPrintWriter(
407                         new BufferedWriter JavaDoc(
408                         new OutputStreamWriter JavaDoc(
409                         new FileOutputStream JavaDoc(output),
410                         encoding)));
411                 }
412
413                 setWriter(out);
414                 setCurrentLineNum(0);
415                 generate(content);
416                 setWriter(null);
417             }
418             catch (IOException JavaDoc ex) {
419                 String JavaDoc msg = Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_ERROR_WRITING_OUTPUT, new String JavaDoc[]{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 JavaDoc msg = Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_NOT_FOUND, new String JavaDoc[]{getTemplateURL().toString()});
432
433             log.error(msg);
434             throw new TemplateException(msg);
435         }
436     }
437
438     /**
439      * Handle the tag that starts at <code>index</code> in the <code>template</code> provided.
440      *
441      * @param index The index that the tag to handle starts at.
442      * @param template The template the tag is in.
443      * @return The index where the tag finished.
444      * @exception TemplateException Description of Exception
445      */

446     protected int handleTag(int index, final String JavaDoc template) throws TemplateException
447     {
448         // the point in the template where we are. starts at the end of the tag head.
449
int i = index + XDOCLET_HEAD_LEN;
450
451         // the command name (tag name)
452
StringBuffer JavaDoc cmd = new StringBuffer JavaDoc();
453
454         // tag context (are there more attributes? is it a block or content tag?)
455
TagContext tagContext = new TagContext();
456
457         // any attributes the tag has)
458
Properties JavaDoc attributes = new Properties JavaDoc();
459
460         // extract the tag name, keeping track of where we are in the template
461
i = extractTagName(template, i, cmd);
462
463         // log.debug( "Found " + cmd.toString() + ", line=" + getLineNumber( template, i ) + " of template file: " + getTemplateURL() );
464

465         // do an initial parse of the tag
466
i = doInitialTagParse(template, i, tagContext);
467
468         // extract the attributes from the tag, and determine if it is a block or content tag
469
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     /**
482      * Invokes content tag implementation method named <code>cmd</code>. It first tries with parameters <code>params1</code>
483      * then if not successful tries <code>param2</code>. This is used for cases where it's not obvious whether the tag
484      * implementation method expects a Properties object or no parameter at all (the tag takes no attributes).
485      *
486      * @param cmd The command to be executed. Everything after the <code>&lt;XDoclet:</code> in the
487      * template.
488      * @param params1 Description of Parameter
489      * @param params2 Description of Parameter
490      * @param template Description of Parameter
491      * @param i Description of Parameter
492      * @return Description of the Returned Value
493      * @exception TemplateException Description of Exception
494      * @see #invokeBlockMethod(java.lang.String,java.lang.String,java.util.Properties,java.lang.String,int)
495      * @see #invokeContentMethod(java.lang.String,java.util.Properties,java.lang.String,int)
496      */

497     protected Object JavaDoc invokeMethod(String JavaDoc cmd, Object JavaDoc[] params1, Object JavaDoc[] params2, String JavaDoc template, int i) throws TemplateException
498     {
499         Log log = LogUtil.getLog(TemplateEngine.class, "invokeMethod");
500
501         /*
502          * During refactoring accept two states:
503          * <XDoclet:foo> will look for the method "foo" in the current class
504          * in this case cmd == "oclet:foo"
505          * <XDtBar:foo> will look for the method "foo" in the class mapped as "Bar"
506          * in this case cmd == "tBar:foo"
507          */

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 JavaDoc[]{"XD" + cmd, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString()}));
514         }
515
516         String JavaDoc prefix = cmd.substring(0, colon);
517         String JavaDoc 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 JavaDoc className = cmdImplProvider.getClass().getName();
530
531         try {
532             Class JavaDoc[] paramTypes = new Class JavaDoc[params1.length];
533
534             for (int j = 0; j < params1.length; j++) {
535                 paramTypes[j] = params1[j].getClass();
536             }
537
538             Method JavaDoc m = cmdImplProvider.getClass().getMethod(methodName, paramTypes);
539
540             return invoke(m, cmdImplProvider, params1);
541         }
542         catch (InvocationTargetException JavaDoc 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 JavaDoc[]{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e.getMessage()}));
550             }
551         }
552         catch (IllegalAccessException JavaDoc 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 JavaDoc[]{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e.getMessage()}));
556         }
557         catch (NoSuchMethodException JavaDoc e) {
558             Class JavaDoc[] paramTypes = new Class JavaDoc[params2.length];
559
560             try {
561                 for (int j = 0; j < params2.length; j++) {
562                     paramTypes[j] = params2[j].getClass();
563                 }
564
565                 Method JavaDoc m = cmdImplProvider.getClass().getMethod(methodName, paramTypes);
566
567                 return invoke(m, cmdImplProvider, params2);
568             }
569             catch (NoSuchMethodException JavaDoc 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 JavaDoc[]{methodName, cmdImplProvider.getClass().getName(), nsme.getMessage()}));
573             }
574             catch (InvocationTargetException JavaDoc 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 JavaDoc[]{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e2.getMessage()}));
582                 }
583             }
584             catch (IllegalAccessException JavaDoc 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 JavaDoc[]{className, methodName, Integer.toString(getLineNumber(template, i)), getTemplateURL().toString(), e2.getMessage()}));
588             }
589         }
590     }
591
592     /**
593      * Invokes content tag implementation method named <code>cmd</code> with the specified set of attributes. If <code>attributes</code>
594      * Properties object is not empty it tries to find a method taking one parameter (Properties attributes), otherwise
595      * a method with no parameter.
596      *
597      * @param cmd Description of Parameter
598      * @param attributes Description of Parameter
599      * @param template Description of Parameter
600      * @param i Description of Parameter
601      * @exception TemplateException Description of Exception
602      * @see #invokeMethod(java.lang.String,java.lang.Object[],java.lang.Object[],java.lang.String,int)
603      */

604     protected void invokeContentMethod(String JavaDoc cmd, Properties JavaDoc attributes, String JavaDoc template, int i) throws TemplateException
605     {
606         Object JavaDoc[] params1 = null;
607         Object JavaDoc[] params2 = null;
608
609         // probable conditions
610

611         if (attributes.size() > 0) {
612             params1 = new Object JavaDoc[]{attributes};
613             params2 = new Object JavaDoc[]{};
614         }
615         else {
616             params1 = new Object JavaDoc[]{};
617             params2 = new Object JavaDoc[]{attributes};
618         }
619
620         String JavaDoc result = (String JavaDoc) invokeMethod(cmd, params1, params2, template, i);
621
622         if (result != null && out != null) {
623             print(result);
624         }
625     }
626
627     /**
628      * Describe what the method does
629      *
630      * @param m Describe what the parameter does
631      * @param cmdImplProvider Describe what the parameter does
632      * @param params1 Describe what the parameter does
633      * @return Describe the return value
634      * @exception InvocationTargetException Describe the exception
635      * @exception IllegalAccessException Describe the exception
636      * @exception TemplateException Describe the exception
637      */

638     protected Object JavaDoc invoke(Method JavaDoc m, Object JavaDoc cmdImplProvider, Object JavaDoc[] params1)
639          throws InvocationTargetException JavaDoc, IllegalAccessException JavaDoc, 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 JavaDoc modules = ModuleFinder.findModules();
648         Iterator JavaDoc i = modules.iterator();
649
650         while (i.hasNext()) {
651             XDocletModule module = (XDocletModule) i.next();
652
653             log.debug("Registering module " + module);
654
655             // Register tag handlers defined in the module
656
List JavaDoc tagHandlerDefinitions = module.getTagHandlerDefinitions();
657             Iterator JavaDoc 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                     // get ANOTHER instance for template parser. So it would not overwrite
668
// template engine settings.
669
// TemplateTagHandler handlerForParser = (TemplateTagHandler) Class.forName(thd.className).newInstance();
670

671                     log.debug("Add tagHandler " + thd.namespace + " (" + thd.className + ')');
672                     setTagHandlerFor(thd.namespace, handler);
673                     // add it also to template parser, or it will barf
674
// while ettempting to parse files...
675
//TemplateParser.getParserInstance().setTagHandlerFor(thd.namespace, handlerForParser);
676

677                 }
678                 catch (Exception JavaDoc e) {
679                     log.error("Couldn't instantiate " + thd.className + " taghandler ", e);
680                 }
681             }
682         }
683     }
684
685     /**
686      * Extract the attributes from the tag and return the index that the tag
687      *
688      * @param tagContext Description of Parameter
689      * @param template Description of Parameter
690      * @param i Description of Parameter
691      * @param attributes Description of Parameter
692      * @return Description of the Returned Value
693      * @exception TemplateException Description of Exception
694      */

695     private int extractAttributes(TagContext tagContext, final String JavaDoc template, int i, Properties JavaDoc attributes) throws TemplateException
696     {
697         while (tagContext.hasMoreAttributes()) {
698             i = extractNextAttribute(template, i, tagContext, attributes);
699         }
700
701         return i;
702     }
703
704     /**
705      * Extract the name of the tag starting at index <code>i</code> from the specified <code>template</code>.
706      *
707      * @param template The template containing the tag.
708      * @param index The index that the tag is at in the template.
709      * @param cmd The StringBuffer to put the tag name in.
710      * @return The index that the tag name finished at in the template.
711      */

712     private int extractTagName(final String JavaDoc template, int index, StringBuffer JavaDoc 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     /**
723      * Extract the next attribute from the <code>template</code> provided, starting at the <code>index</code> specified.
724      *
725      * @param template The template to extract the attribute from.
726      * @param index The index to start looking for the attribute.
727      * @param tagContext The TagContext - is the tag block/content, are there any more attributes?
728      * @param attributes The attributes collection to add the next attribute to.
729      * @return The index that the next attribute finished at.
730      * @exception TemplateException Description of Exception
731      */

732     private int extractNextAttribute(final String JavaDoc template, int index, TagContext tagContext, Properties JavaDoc attributes) throws TemplateException
733     {
734         StringBuffer JavaDoc attributeName = new StringBuffer JavaDoc();
735         StringBuffer JavaDoc attributeValue = new StringBuffer JavaDoc();
736         char quoteChar = '"';
737
738         try {
739             // read attribute name
740
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             // skip = sign
748
if (template.charAt(index) == '=') {
749                 index++;
750             }
751             else {
752                 throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_EQUALS_EXPECTED, new String JavaDoc[]{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()}));
753             }
754
755             index = skipWhitespace(template, index);
756
757             // skip " sign
758
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 JavaDoc[]{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()}));
768             }
769
770             // read attribute value
771
while (template.charAt(index) != quoteChar) {
772                 attributeValue.append(template.charAt(index));
773                 index++;
774             }
775
776             // skip " sign
777
index++;
778             tagContext.setHasMoreAttributes(true);
779
780             if (attributeValue.toString().indexOf(XDOCLET_HEAD) != -1) {
781                 attributeValue = new StringBuffer JavaDoc(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                 // no more attributes
792
}
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                     // no more attributes
802
}
803                 else {
804                     throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_GT_EXPECTED, new String JavaDoc[]{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()}));
805                 }
806             }
807         }
808         catch (java.lang.StringIndexOutOfBoundsException JavaDoc ex) {
809             throw new TemplateException(Translator.getString(XDocletTemplateMessages.class, XDocletTemplateMessages.TEMPLATE_SYNTAX_ERROR, new String JavaDoc[]{Integer.toString(getLineNumber(template, index)), getTemplateURL().toString(), template}));
810         }
811         attributes.setProperty(attributeName.toString(), attributeValue.toString());
812         return index;
813     }
814
815     /**
816      * Do an initial parse of the tag at the <code>index</code> specified in the <code>template</code> provided. Look to
817      * see if the tag has attributes, and/or is block or content.
818      *
819      * @param template The template the tag is contained in.
820      * @param index The index the tag starts at.
821      * @param tagContext The tag context.
822      * @return Description of the Returned Value
823      * @exception TemplateException Description of Exception
824      */

825     private int doInitialTagParse(final String JavaDoc 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 JavaDoc[]{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     /**
859      * Handle the block tag starting at the <code>index</code> specified in the <code>template</code> provided.
860      *
861      * @param index The start of the block tag's contents.
862      * @param template The template the block tag is contained in.
863      * @param cmd The name of the block tag, without XDOCLET_HEAD
864      * @param attributes The attributes in the tag.
865      * @return The index that the block tag finishes at.
866      * @exception TemplateException Description of Exception
867      */

868     private int handleBlockTag(int index, String JavaDoc template, String JavaDoc cmd, Properties JavaDoc attributes) throws TemplateException
869     {
870         int openNestedElemCount = 1;
871         String JavaDoc 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 JavaDoc(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 JavaDoc[]{new StringBuffer JavaDoc(XDOCLET_TAIL).append(cmd).append('>').toString(), Integer.toString(getLineNumber(template, index)), getTemplateURL().toString()}));
883             }
884             else {
885                 openNestedElemCount--;
886             }
887
888             // </XDoclet:cmd
889
index = bodyEndIndex + XDOCLET_TAIL_LEN + cmd.length();
890
891             // skip spaces in </XDoclet:cmd >
892
index = skipWhitespace(template, index);
893
894             // trailing >
895
index++;
896
897             StringBuffer JavaDoc xdocletPrefixPlusCmd = new StringBuffer JavaDoc(XDOCLET_PREFIX).append(cmd);
898
899             // XXX: I cant remember why I did this like that (using prefix instead of head), it could
900
// probably be looked at, but there are other things to do atm, and it works. dim 13-Oct-01
901
int nestedStartIndex = template.indexOf(xdocletPrefixPlusCmd.toString(), fromIndex);
902
903             // has nested elements with the same name, need to loop here for multiple nests.
904
while (nestedStartIndex != -1 && nestedStartIndex < bodyEndIndex) {
905                 // <XDoclet:blabla ...>
906
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 JavaDoc[]{new StringBuffer JavaDoc(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     /**
939      * Invokes block tag implementation method named cmd with the specified body block and set of attributes.
940      *
941      * @param cmd Description of Parameter
942      * @param block Description of Parameter
943      * @param attributes Description of Parameter
944      * @param template Description of Parameter
945      * @param i Description of Parameter
946      * @exception TemplateException Description of Exception
947      * @see #invokeMethod(java.lang.String,java.lang.Object[],java.lang.Object[],java.lang.String,int)
948      */

949     private void invokeBlockMethod(String JavaDoc cmd, String JavaDoc block, Properties JavaDoc attributes, String JavaDoc template, int i) throws TemplateException
950     {
951         Object JavaDoc[] params1 = null;
952         Object JavaDoc[] params2 = null;
953
954         // probable conditions
955

956         if (attributes.size() > 0) {
957             params1 = new Object JavaDoc[]{block, attributes};
958             params2 = new Object JavaDoc[]{block};
959         }
960         else {
961             params1 = new Object JavaDoc[]{block};
962             params2 = new Object JavaDoc[]{block, attributes};
963         }
964
965         invokeMethod(cmd, params1, params2, template, i);
966     }
967
968     /**
969      * A wrapper around the hasMoreAttributes and isBlock status used in {@link TemplateEngine#generate}.
970      *
971      * @author Aslak Hellesøy
972      * @created October 14, 2001
973      */

974     private static class TagContext
975     {
976         private boolean hasMoreAttributes = false;
977         private boolean isBlock = false;
978
979         /**
980          * Gets the Block attribute of the TagContext object
981          *
982          * @return The Block value
983          */

984         public boolean isBlock()
985         {
986             return isBlock;
987         }
988
989         /**
990          * Sets the HasMoreAttributes attribute of the TagContext object
991          *
992          * @param attributes The new HasMoreAttributes value
993          */

994         public void setHasMoreAttributes(boolean attributes)
995         {
996             this.hasMoreAttributes = attributes;
997         }
998
999         /**
1000         * Sets the Block attribute of the TagContext object
1001         *
1002         * @param block The new Block value
1003         */

1004        public void setBlock(boolean block)
1005        {
1006            isBlock = block;
1007        }
1008
1009        /**
1010         * Describe what the method does
1011         *
1012         * @return Describe the return value
1013         */

1014        public boolean hasMoreAttributes()
1015        {
1016            return hasMoreAttributes;
1017        }
1018    }
1019}
1020
Popular Tags