KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > net > sf > invicta > template > TemplateProcessor


1 package net.sf.invicta.template;
2
3 import java.util.ArrayList JavaDoc;
4 import java.util.HashMap JavaDoc;
5 import java.util.Iterator JavaDoc;
6 import java.util.List JavaDoc;
7 import java.util.Map JavaDoc;
8 import java.util.StringTokenizer JavaDoc;
9
10 import net.sf.invicta.InvictaException;
11 import net.sf.invicta.handler.InvictaHandler;
12
13 import sun.misc.Service;
14
15 /**
16  * TemplateProcessor facility.
17  * Based on TLA generic Template processing mechanism.
18  * TODO: Add support for an escape char (\).
19  */

20 public class TemplateProcessor {
21     protected static final char HANDLER_SEPERATOR = ';';
22     protected static final String JavaDoc EXTRA_SEPERATOR = ";";
23     protected static final String JavaDoc EXTRA_MAP_SEPERATOR = "=";
24     protected static final char ESCAPE = '%';
25     protected static final char PLACEHOLDER_START = '{';
26     protected static final char PLACEHOLDER_END = '}';
27
28     protected static Map JavaDoc handlers = new HashMap JavaDoc();
29
30     static {
31         Iterator JavaDoc handlersIt =
32             Service.providers(InvictaHandler.class);
33         while (handlersIt.hasNext()) {
34             InvictaHandler handler = (InvictaHandler) handlersIt.next();
35             handlers.put(handler.getName(), handler);
36         }
37     }
38
39     /**
40      * Retrieve TemplateHandler object for the given handler name.
41      */

42     protected static InvictaHandler getHandler(String JavaDoc handler) {
43         return (InvictaHandler) handlers.get(handler);
44     }
45
46     /**
47      * Generic mechanism for creating part of the templates result. The template
48      * is compiled into an array (list) of instructions that are executed
49      * sequentially. The object retuned by each instruction is appended to the
50      * result. Can throw an exception in case of error.
51      * @author tla
52      */

53     protected static abstract class Instruction {
54         public abstract Object JavaDoc handle(Object JavaDoc[][] parameters, Object JavaDoc context) throws InvictaException, InvictaException ;
55     }
56
57     /**
58      * Static instruction that is consturcted with a text string that should be
59      * added to the result in this specific location.
60      * @author tla
61      */

62     protected static class StaticInstruction extends Instruction {
63         private String JavaDoc text;
64         public StaticInstruction(String JavaDoc text) {
65             this.text = text;
66         }
67         public Object JavaDoc handle(Object JavaDoc[][] parameters, Object JavaDoc context) throws InvictaException {
68             return this.text;
69         }
70     }
71
72     /**
73      * Dynamic instruction that is resolved using the supplied parameters.
74      * The key is used to find the parameter to use in this instruction (can
75      * be null, when no parameter is needed). TemplateHandler, if supplied is called
76      * with the parameter matched by the key (or null if there is no key). The
77      * extra information is passed as a parameter to the handler. The format if
78      * given is converted into a MessageFormat formating instruction and called
79      * on the result of the handler/key.
80      * @author tla
81      */

82     protected static class DynamicInstruction extends Instruction {
83         protected String JavaDoc key = null;
84         protected int position = -1;
85         protected List JavaDoc extra = null;
86         
87
88         public DynamicInstruction(
89             String JavaDoc key,
90             List JavaDoc extra) {
91             this.key = key;
92             // If the key is an int convert it and use it for optimized a
93
// access in case of position based placeholder.
94
try {
95                 this.position = Integer.parseInt(key);
96             } catch (NumberFormatException JavaDoc e) {
97                 // Ignore!
98
}
99             this.extra = extra;
100         }
101
102         public Object JavaDoc handle(Object JavaDoc[][] parameters, Object JavaDoc context) throws InvictaException {
103             Object JavaDoc object = null;
104
105             boolean found = false;
106             // If a key is not empty, find the parameter matching the key.
107
if (!"".equals(key)) {
108                 if (parameters == null)
109                     throw new InvictaTemplateException(
110                         "Parameter "
111                             + key
112                             + " is needed, but no parameters were given");
113                 if (position >= 0 && position < parameters.length) {
114                     // If the key is a number, position will contain it in
115
// number form. If it is a valid index into parameters use
116
// it as a hint of where to start looking for. This is an
117
// optimization of position based parameters.
118
int index = position;
119                     for (int i = 0; i < parameters.length; i++) {
120                         Object JavaDoc[] pair = parameters[index];
121                         if (key.equals(pair[0])) {
122                             object = pair[1];
123                             found = true;
124                             break;
125                         }
126                         index = (index + 1) % parameters.length;
127                     }
128                 } else {
129                     // Find the parameter by sequentially comparing the keys
130
// of the parameters.
131
for (int i = 0; i < parameters.length; i++) {
132                         Object JavaDoc[] pair = parameters[i];
133                         if (key.equals(pair[0])) {
134                             object = pair[1];
135                             found = true;
136                             break;
137                         }
138                     }
139                 }
140             }
141
142             if (!found) {
143             
144                 // Find the handler for this name.
145
InvictaHandler handler = (InvictaHandler)getHandler(key);
146             
147                 if (handler == null)
148                     throw new InvictaTemplateException(
149                         "Can't find parameter or handler named " + key);
150                 
151                 Map JavaDoc extraMap = lookForExtraMap(extra);
152                                 
153                 if (extraMap == null)
154                     object = handler.handle(context, extra);
155                 else
156                     object = handler.handle(context, extraMap);
157                 
158             }
159
160             // Nulls are not allowed. If the handler wants to allow nulls, they
161
// must be converted to something else (e.g. empty string).
162
if (object == null)
163                 throw new InvictaTemplateException(
164                     "Got null result for '" + key + "'");
165
166             return object;
167         }
168
169         /**
170          * @param extra
171          * @return
172          */

173         private Map JavaDoc lookForExtraMap(List JavaDoc extra) {
174             Map JavaDoc extraMap = new HashMap JavaDoc();
175             for (Iterator JavaDoc iter = extra.iterator(); iter.hasNext();) {
176                 String JavaDoc param = (String JavaDoc) iter.next();
177                 int equal = param.indexOf(EXTRA_MAP_SEPERATOR);
178                 if (equal == -1)
179                     return null;
180                 extraMap.put(param.substring(0, equal), param.substring(equal+1));
181             }
182             
183             return extraMap;
184         }
185
186     }
187
188     /**
189      * The template compiled into an sequence of Instruction objects.
190      */

191     protected Instruction[] compiledFormat;
192
193     /**
194      * The original template supplied.
195      */

196     protected String JavaDoc template;
197     
198     /**
199      * Context for the current template format
200      */

201     protected Object JavaDoc context;
202     
203     /**
204      * TemplateProcessor constructor using the given template.
205      * @param template
206      * @throws InvictaTemplateExceptioneption A runtime exception throws in case the
207      * template parsing fails.
208      */

209     public TemplateProcessor(String JavaDoc template) throws InvictaTemplateException {
210         this(template, null);
211     }
212
213
214
215     /**
216      * TemplateProcessor constructor using the given template.
217      * @param template
218      * @throws InvictaTemplateExceptioneption A runtime exception throws in case the
219      * template parsing fails.
220      */

221     public TemplateProcessor(String JavaDoc template, Object JavaDoc context) throws InvictaTemplateException {
222         this.template = template;
223         this.context = context;
224
225         ArrayList JavaDoc compiledFormat = new ArrayList JavaDoc();
226
227         int length = template.length();
228         int position = 0; // Current position in the template.
229
try {
230             while (position < length) {
231                 // Find the next escape sequence.
232
int beginIndex = template.indexOf(ESCAPE, position);
233                 // If not found or found as the last character of the string.
234
// The rest of the string should be added as is.
235
if ((beginIndex == -1) || (beginIndex == length - 1)) {
236                     compiledFormat.add(
237                         new StaticInstruction(template.substring(position)));
238                     break;
239                 }
240
241                 // If the next character is not the openning character, this
242
// is not really a placeholder, the string up to this character
243
// is added as is.
244
int nextIndex = beginIndex + 1;
245                 char nextChar = template.charAt(nextIndex);
246                 if (nextChar != PLACEHOLDER_START) {
247                     if (nextChar == ESCAPE) {
248                         // This is an escape sequence. Skip the second escape
249
// character.
250

251                         // Do Nothing.
252
} else {
253                         // Otherwise skip the second character too.
254
nextIndex++;
255                     }
256
257                     compiledFormat.add(
258                         new StaticInstruction(
259                             template.substring(position, nextIndex)));
260
261                     position = beginIndex + 2;
262                     continue;
263                 }
264
265                 // Find the end of the placeholder. Scream if not found.
266
int endIndex = template.indexOf(PLACEHOLDER_END, beginIndex);
267                 if (endIndex == -1) {
268                     throw new InvictaTemplateException("Can't find end of placeholder");
269                 }
270
271                 // If there are characters before the beginning of the placeholder
272
// add them now.
273
if (position != beginIndex) {
274                     compiledFormat.add(
275                         new StaticInstruction(
276                             template.substring(position, beginIndex)));
277                 }
278
279                 String JavaDoc key = template.substring(beginIndex + 2, endIndex);
280
281                 List JavaDoc extra = new ArrayList JavaDoc();
282                                                 
283                 // find possible extra information for handler.
284
int semicolon = key.indexOf(HANDLER_SEPERATOR);
285                 if (semicolon != -1) {
286                     String JavaDoc extraString = key.substring(semicolon + 1);
287                     StringTokenizer JavaDoc st = new StringTokenizer JavaDoc(extraString, EXTRA_SEPERATOR);
288                     while (st.hasMoreTokens()) {
289                         String JavaDoc extraParam = st.nextToken().trim();
290                         extra.add(extraParam);
291                     }
292                     
293                     key = key.substring(0, semicolon);
294                 }
295
296
297                 DynamicInstruction instruction =
298                     new DynamicInstruction(key, extra);
299                 
300                 compiledFormat.add(instruction);
301                 
302                 // Jump to the end of the placeholder.
303
position = endIndex + 1;
304             }
305
306             // We save the compiled instruction list as an array for speed.
307
this.compiledFormat =
308                 (Instruction[]) compiledFormat.toArray(
309                     new Instruction[compiledFormat.size()]);
310         } catch (Throwable JavaDoc problem) {
311             // A problem was found when parsing the template. Wrap with an
312
// appropriate exception. The format attempt is the given template
313
// since we couldn't even parse it.
314
throw new InvictaTemplateException(
315                 "Can't parse template: " + problem.getMessage(), problem, template);
316
317         }
318     }
319
320     /**
321      * Format the template represented by this object.
322      * @param parameters The parameters to use when formatting the template.
323      * Should be an array of arrays. The first element of each inner array is
324      * the key name and the second is the value to use for that key.
325      * @param buffer The formatted string is appended to this buffer.
326      * @throws InvictaTemplateExceptioneption A runtime exception throws in case the
327      * formatting fails.
328      */

329     public void format(Object JavaDoc[][] parameters, StringBuffer JavaDoc buffer)
330         throws InvictaTemplateException {
331         Throwable JavaDoc problem = null;
332         for (int i = 0; i < compiledFormat.length; i++) {
333             Instruction instruction = compiledFormat[i];
334             try {
335                 buffer.append(instruction.handle(parameters, context));
336             } catch (Throwable JavaDoc t) {
337                 problem = t;
338             }
339         }
340         if (problem != null) {
341             throw new InvictaTemplateException(
342                 "Can't format template: " + problem.getMessage(),
343                 problem,
344                 buffer.toString());
345         }
346     }
347
348     /**
349      * Format the template represented by this object.
350      * @param parameters The parameters to use when formatting the template.
351      * Should be an array of arrays. The first element of each inner array is
352      * the key name and the second is the value to use for that key.
353      * @return String The formatted String.
354      * @throws InvictaTemplateExceptioneption A runtime exception throws in case the
355      * formatting fails.
356      */

357     public String JavaDoc format(Object JavaDoc[][] parameters) throws InvictaTemplateException {
358         StringBuffer JavaDoc buffer = new StringBuffer JavaDoc();
359         format(parameters, buffer);
360         return buffer.toString();
361     }
362
363     static protected String JavaDoc[] conversion =
364         new String JavaDoc[] {
365             "0",
366             "1",
367             "2",
368             "3",
369             "4",
370             "5",
371             "6",
372             "7",
373             "8",
374             "9",
375             "10",
376             "11",
377             "12",
378             "13",
379             "14",
380             "15",
381             "16",
382             "17",
383             "18",
384             "19" };
385     /**
386      * Transform position based parameters to key based parameters by making
387      * each position into a key of the same value.
388      * @param array The array to transform
389      * @return Object[][] key based parameters in the format expected by
390      * TemplateProcessor
391      */

392     public static Object JavaDoc[][] positionToKey(Object JavaDoc[] array) {
393         if (array == null)
394             return null;
395         Object JavaDoc[][] parameters = new Object JavaDoc[array.length][2];
396         for (int i = 0; i < array.length; i++) {
397             parameters[i][0] = conversion[i];
398             parameters[i][1] = array[i];
399         }
400         return parameters;
401     }
402
403     /**
404      * Utility method for constructing a method and formatting together.
405      * @param template TemplateProcessor format to use.
406      * @param parameters Parameters used to format the template, can be null.
407      * @param buffer The buffer to append the formatted String to.
408      * @throws InvictaTemplateExceptioneption A runtime exception throws in case the
409      * formatting fails.
410      */

411     public static void format(
412         String JavaDoc template,
413         Object JavaDoc[][] parameters,
414         StringBuffer JavaDoc buffer) throws InvictaTemplateException {
415         new TemplateProcessor(template).format(parameters, buffer);
416     }
417
418     /**
419      * Utility method for constructing a method and formatting together.
420      * @param template TemplateProcessor format to use.
421      * @param parameters Parameters used to format the template, can be null.
422      * @return String The formatted String.
423      * @throws InvictaTemplateExceptioneption A runtime exception throws in case the
424      * formatting fails.
425      */

426     public static String JavaDoc format(
427         String JavaDoc template,
428         Object JavaDoc[][] parameters) throws InvictaTemplateException {
429         return new TemplateProcessor(template).format(parameters);
430     }
431     
432     /**
433      * Returns the context.
434      * @return Object
435      */

436     public Object JavaDoc getContext() {
437         return context;
438     }
439
440     /**
441      * Sets the context.
442      * @param context The context to set
443      */

444     public void setContext(Object JavaDoc context) {
445         this.context = context;
446     }
447
448 }
449
Popular Tags