1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23 package org.talika.tarsis.command.factory.xml;
24
25 import java.util.LinkedList;
26 import java.util.List;
27 import java.util.Stack;
28 import java.util.Map;
29 import java.util.HashMap;
30 import java.io.InputStream;
31 import java.text.ParseException;
32
33 import org.xml.sax.Attributes;
34 import org.xml.sax.InputSource;
35 import org.xml.sax.SAXException;
36 import org.xml.sax.SAXParseException;
37 import org.xml.sax.helpers.DefaultHandler;
38 import org.xml.sax.helpers.AttributesImpl;
39
40 import org.talika.tarsis.command.Command;
41 import org.talika.tarsis.command.CommandImpl;
42 import org.talika.tarsis.command.CommandParameter;
43 import org.talika.tarsis.command.CommandParameterImpl;
44 import org.talika.tarsis.command.action.Action;
45 import org.talika.tarsis.command.action.ActionWrapper;
46 import org.talika.tarsis.command.action.DefaultAction;
47 import org.talika.tarsis.command.view.View;
48 import org.talika.tarsis.command.view.ViewImpl;
49 import org.talika.tarsis.command.view.InputView;
50 import org.talika.commons.util.ParseHelper;
51
52 /**
53 * Implementation of SAX handlers for Tarsis MVC Command Package XML files.
54 *
55 * @author Jose M. Palomar
56 * @version $Revision: 121 $
57 *
58 * @todo Change <duplicable> tag to <repeatable> tag.
59 */
60 public final class XmlCommandPackageHandler extends DefaultHandler {
61
62
63 /**
64 * <package> tag.
65 */
66 public static final String PACKAGE_TAG = "package";
67
68 /**
69 * <command> tag.
70 */
71 public static final String COMMAND_TAG = "command";
72
73 /**
74 * <action> tag.
75 */
76 public static final String ACTION_TAG = "action";
77
78 /**
79 * <view> tag.
80 */
81 public static final String VIEW_TAG = "view";
82
83 /**
84 * <input> tag.
85 */
86 public static final String INPUT_TAG = "input";
87
88 /**
89 * <parameter> tag.
90 */
91 public static final String PARAMETER_TAG = "parameter";
92
93 /**
94 * Name atribute.
95 */
96 public static final String NAME_ATTR = "name";
97
98 /**
99 * Cacheable atribute.
100 */
101 public static final String CACHEABLE_ATTR = "cacheable";
102
103 /**
104 * Duplicable atribute.<br>
105 */
106 public static final String DUPLICABLE_ATTR = "duplicable";
107
108 /**
109 * Validable atribute.
110 */
111 public static final String VALIDABLE_ATTR = "validable";
112
113 /**
114 * Secure atribute.
115 */
116 public static final String SECURE_ATTR = "secure";
117
118 /**
119 * Class name atribute.
120 */
121 public static final String CLASS_NAME_ATTR = "className";
122
123 /**
124 * Path atribute.
125 */
126 public static final String PATH_ATTR = "path";
127
128 /**
129 * Type atribute.
130 */
131 public static final String TYPE_ATTR = "type";
132
133 /**
134 * Required atribute.
135 */
136 public static final String REQUIRED_ATTR = "required";
137
138 /**
139 * Default atribute.
140 */
141 public static final String DEFAULT_ATTR = "default";
142
143 /**
144 * Multiple atribute.
145 */
146 public static final String MULTIPLE_ATTR = "multiple";
147
148 /**
149 * Stateless atribute.
150 */
151 public static final String STATELESS_VALUE = "stateless";
152
153 /**
154 * Statefull atribute.
155 */
156 public static final String STATEFULL_VALUE = "statefull";
157
158 /**
159 * Forward atribute.
160 */
161 public static final String FORWARD_VALUE = "forward";
162
163 /**
164 * Redirect atribute.
165 */
166 public static final String REDIRECT_VALUE = "redirect";
167
168 /**
169 * Include atribute.
170 */
171 public static final String INCLUDE_VALUE = "include";
172
173 /**
174 * True value.
175 */
176 public static final String TRUE_VALUE = "true";
177
178 /**
179 * False value.
180 */
181 public static final String FALSE_VALUE = "false";
182
183 /**
184 * DTD public id.
185 */
186 public static final String DTD_PUBLICID = "-//Talika Open Source Group//Command Package DTD 1.0//ES";
187
188 /**
189 * DTD class path.
190 */
191 public static final String DTD_CLASSPATH = "/org/talika/dtds/package_1_0.dtd";
192
193
194 /**
195 * Package name.
196 */
197 private String packageName;
198
199 /**
200 * Commands map.
201 */
202 private Map commands;
203
204 /**
205 * <code>ParseHelper</code> instance.
206 */
207 private ParseHelper parser;
208
209 /**
210 * Tag attributes stack.
211 */
212 private Stack stAtts;
213
214 /**
215 * Current command <code>Action</code> class.
216 */
217 private Class actionClass;
218
219 /**
220 * Current command views list.
221 */
222 private List views;
223
224 /**
225 * Current command input view.
226 */
227 private View inputView;
228
229 /**
230 * Current command parameters.
231 */
232 private List parameters;
233
234
235 /**
236 * Creates a new <code>XmlCommandPackageHandler</code> object.
237 */
238 public XmlCommandPackageHandler() {
239 this.commands = new HashMap();
240 this.parser = ParseHelper.getInstance();
241 this.stAtts = new Stack();
242 this.views = new LinkedList();
243 this.parameters = new LinkedList();
244 }
245
246
247
248 /**
249 * Receive notification of the beginning of a document.
250 *
251 * @throws SAXException Any SAX exception, possibly wrapping another exception.
252 * @see org.xml.sax.ContentHandler#startDocument()
253 */
254 public void startDocument() throws SAXException {
255
256 }
257
258 /**
259 * Receive notification of the end of a document.
260 *
261 * @throws SAXException Any SAX exception, possibly wrapping another exception.
262 * @see org.xml.sax.ContentHandler#endDocument()
263 */
264 public void endDocument() throws SAXException {
265
266 }
267
268 /**
269 * Receive notification of the beginning of an element.
270 *
271 * @param namespaceURI String The Namespace URI, or the empty string if the
272 * element has no Namespace URI or if Namespace processing is not being performed.
273 * @param localName String The local name (without prefix), or the empty string
274 * if Namespace processing is not being performed.
275 * @param name String The qualified name (with prefix), or the empty string if
276 * qualified names are not available.
277 * @param atts Attributes The attributes attached to the element. If there are no
278 * attributes, it shall be an empty Attributes object.
279 * @throws SAXException Any SAX exception, possibly wrapping another exception.
280 * @see org.xml.sax.ContentHandler#startElement(String, String, String, Attributes)
281 */
282 public void startElement(String namespaceURI, String localName, String name,
283 Attributes atts) throws SAXException {
284
285 stAtts.push(new AttributesImpl(atts));
286
287 if (name.equals(PACKAGE_TAG)) {
288 processPackageTag(atts);
289 }
290
291 }
292
293 /**
294 * Receive notification of the end of an element.
295 *
296 * @param namespaceURI String The Namespace URI, or the empty string if the
297 * element has no Namespace URI or if Namespace processing is not being performed.
298 * @param localName String The local name (without prefix), or the empty string
299 * if Namespace processing is not being performed.
300 * @param name String The qualified name (with prefix), or the empty string if
301 * qualified names are not available.
302 * @throws SAXException Any SAX exception, possibly wrapping another exception.
303 * @see org.xml.sax.ContentHandler#endElement(String, String, String)
304 */
305 public void endElement(String namespaceURI, String localName, String name)
306 throws SAXException {
307
308 Attributes atts = (Attributes) stAtts.pop();
309
310 if (name.equals(PACKAGE_TAG)) {
311
312 }
313 else if (name.equals(COMMAND_TAG)) {
314 processCommandTag(atts);
315 }
316 else if (name.equals(ACTION_TAG)) {
317 processActionTag(atts);
318 }
319 else if (name.equals(VIEW_TAG)) {
320 processViewTag(atts);
321 }
322 else if (name.equals(INPUT_TAG)) {
323 processInputTag(atts);
324 }
325 else if (name.equals(PARAMETER_TAG)) {
326 processParameterTag(atts);
327 }
328
329 }
330
331
332 /**
333 * Allow the application to resolve external entities.
334 *
335 * @param publicId String The public identifier of the external entity being
336 * referenced, or <code>null</code> if none was supplied.
337 * @param systemId String The system identifier of the external entity being
338 * referenced.
339 * @return InputSource A Java-specific IO exception, possibly the result of
340 * creating a new <code>InputStream</code> or <code>Reader</code> for the
341 * <code>InputSource</code>.
342 * @see org.xml.sax.EntityResolver#resolveEntity(String, String)
343 */
344 public InputSource resolveEntity (String publicId, String systemId) {
345
346 if (publicId.equals(DTD_PUBLICID)) {
347
348 InputStream is = this.getClass().getResourceAsStream(DTD_CLASSPATH);
349 if (is != null) {
350 return new InputSource(is);
351 }
352
353 }
354
355 return null;
356
357 }
358
359
360 /**
361 * Receive notification of a warning.
362 *
363 * @param spe SAXParseException The warning information encapsulated in a SAX
364 * parse exception.
365 * @throws SAXException Any SAX exception, possibly wrapping another exception.
366 * @see org.xml.sax.ErrorHandler#warning(SAXParseException)
367 */
368 public void warning(SAXParseException spe) throws SAXException {
369 }
370
371 /**
372 * Receive notification of a recoverable error.
373 *
374 * @param spe SAXParseException The error information encapsulated in a SAX parse
375 * exception.
376 * @throws SAXException Any SAX exception, possibly wrapping another exception.
377 * @see org.xml.sax.ErrorHandler#error(SAXParseException)
378 */
379 public void error(SAXParseException spe) throws SAXException {
380 throw new SAXException("** Package parse error\n" +
381 " at [" + spe.getLineNumber() +
382 ":" + spe.getColumnNumber() +
383 "] in " + spe.getSystemId(), spe);
384 }
385
386 /**
387 * Receive notification of a non-recoverable error.
388 *
389 * @param spe SAXParseException The error information encapsulated in a SAX parse
390 * exception.
391 * @throws SAXException Any SAX exception, possibly wrapping another exception.
392 * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException)
393 */
394 public void fatalError(SAXParseException spe) throws SAXException {
395 throw new SAXException("** Package parse error\n" +
396 " at [" + spe.getLineNumber() +
397 ":" + spe.getColumnNumber() +
398 "] in " + spe.getSystemId(), spe);
399 }
400
401
402 /**
403 * Returns parsed package name.
404 *
405 * @return String package name.
406 */
407 public String getPackage() {
408 return packageName;
409 }
410
411 /**
412 * Returns map of parsed commands.
413 *
414 * @return Map map of commands.
415 */
416 public Map getCommands() {
417 return commands;
418 }
419
420
421 /**
422 * Process <package> tag.
423 *
424 * @param atts Attributes tag attributes.
425 */
426 protected void processPackageTag(Attributes atts) {
427 this.packageName = atts.getValue(NAME_ATTR);
428 }
429
430 /**
431 * Process <command> tag.
432 *
433 * @param atts Attributes tag attributes.
434 * @throws SAXParseException if there is an error while processing tag.
435 */
436 protected void processCommandTag(Attributes atts) throws SAXParseException {
437
438 String commandName = atts.getValue(NAME_ATTR);
439 String duplicableStr = atts.getValue(DUPLICABLE_ATTR);
440 String cacheableStr = atts.getValue(CACHEABLE_ATTR);
441 String validableStr = atts.getValue(VALIDABLE_ATTR);
442 String secureStr = atts.getValue(SECURE_ATTR);
443 String typeStr = atts.getValue(TYPE_ATTR);
444
445 boolean duplicable = (duplicableStr != null && duplicableStr.equals(TRUE_VALUE));
446 boolean cacheable = (cacheableStr != null && cacheableStr.equals(TRUE_VALUE));
447 boolean validable = (validableStr != null && validableStr.equals(TRUE_VALUE));
448 boolean secure = (secureStr != null && secureStr.equals(TRUE_VALUE));
449 int type = (typeStr.equals("statefull") ? Command.STATEFULL : Command.STATELESS);
450
451 Action action = null;
452 if (type == Command.STATEFULL) {
453
454 action = new ActionWrapper(actionClass);
455 }
456 else {
457
458
459 if (actionClass == null) {
460 action = new DefaultAction();
461 }
462 else {
463
464 try {
465 action = (Action) actionClass.newInstance();
466 }
467 catch (InstantiationException ie) {
468 throw new SAXParseException("Invalid class name " + actionClass.getName(), null, ie);
469 }
470 catch (IllegalAccessException iae) {
471 throw new SAXParseException("Invalid class name " + actionClass.getName(), null, iae);
472 }
473
474 }
475
476 }
477
478 CommandParameter[] cmdParameters = (CommandParameter[])
479 parameters.toArray(new CommandParameter[parameters.size()]);
480 View[] cmdViews = (View[]) views.toArray(new View[views.size()]);
481
482 Command command = new CommandImpl(commandName, this.packageName, action,
483 cmdViews, inputView, cmdParameters, cacheable,
484 duplicable, validable, secure, type);
485
486 commands.put(command.getFullName(), command);
487
488 this.parameters.clear();
489 this.views.clear();
490 this.actionClass = null;
491 this.inputView = null;
492
493 }
494
495 /**
496 * Process <action> tag.
497 *
498 * @param atts Attributes tag attributes.
499 * @throws SAXParseException if there is an error while processing tag.
500 */
501 protected void processActionTag(Attributes atts) throws SAXParseException {
502
503 String className = atts.getValue(CLASS_NAME_ATTR);
504
505 try {
506 this.actionClass = Class.forName(className);
507 }
508 catch (ClassNotFoundException cnfe) {
509 throw new SAXParseException("Invalid class name " + className, null, cnfe);
510 }
511
512 }
513
514 /**
515 * Process <view> tag.
516 *
517 * @param atts Attributes tag attributes.
518 */
519 protected void processViewTag(Attributes atts) {
520
521 String name = atts.getValue(NAME_ATTR);
522 String path = atts.getValue(PATH_ATTR);
523 String typeStr = atts.getValue(TYPE_ATTR);
524 int type = -1;
525 if (typeStr.equalsIgnoreCase(FORWARD_VALUE)) {
526 type = View.FORWARD;
527 }
528 else if (typeStr.equalsIgnoreCase(REDIRECT_VALUE)) {
529 type = View.REDIRECT;
530 }
531 else if (typeStr.equalsIgnoreCase(INCLUDE_VALUE)) {
532 type = View.INCLUDE;
533 }
534
535 this.views.add(new ViewImpl(name, path, type));
536
537 }
538
539 /**
540 * Process <input> tag.
541 *
542 * @param atts Attributes tag attributes.
543 */
544 protected void processInputTag(Attributes atts) {
545
546 String path = atts.getValue(PATH_ATTR);
547
548 this.inputView = new InputView(path);
549
550 }
551
552 /**
553 * Process <parameter> tag.
554 *
555 * @param atts Attributes tag attributes.
556 * @throws SAXParseException if there is an error while processing tag.
557 */
558 protected void processParameterTag(Attributes atts) throws SAXParseException {
559
560 String name = atts.getValue(NAME_ATTR);
561 String typeStr = atts.getValue(TYPE_ATTR);
562 String requiredStr = atts.getValue(REQUIRED_ATTR);
563 String multipleStr = atts.getValue(MULTIPLE_ATTR);
564 String defaultValueStr = atts.getValue(DEFAULT_ATTR);
565
566 Class type = null;
567 try {
568 type = Class.forName(typeStr);
569 }
570 catch (ClassNotFoundException cnfe) {
571 throw new SAXParseException("Invalid type " + typeStr +
572 " for parameter " + name, null, cnfe);
573 }
574
575 boolean multiple = (multipleStr != null && multipleStr.equals(TRUE_VALUE));
576 boolean required = (requiredStr != null && requiredStr.equals(TRUE_VALUE));
577
578 Object defaultValue = null;
579 if (defaultValueStr != null) {
580
581 try {
582 defaultValue = parser.parseObject(type, defaultValueStr);
583 }
584 catch (ParseException pe) {
585 throw new SAXParseException("Invalid default value " + defaultValueStr +
586 " for parameter " + name, null, pe);
587 }
588
589 }
590
591 parameters.add(new CommandParameterImpl(name, type, required, multiple, defaultValue));
592
593 }
594
595 }