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