View Javadoc

1   /*
2    * $Id: XmlCommandPackageHandler.java 121 2004-11-01 13:05:23Z josem $
3    *
4    * Tarsis
5    * Copyright (C) 2002 Talika Open Source Group
6    *
7    * This program is free software; you can redistribute it and/or modify
8    * it under the terms of the GNU General Public License as published by
9    * the Free Software Foundation; either version 2 of the License, or
10   * (at your option) any later version.
11   *
12   * This program is distributed in the hope that it will be useful,
13   * but WITHOUT ANY WARRANTY; without even the implied warranty of
14   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15   * GNU General Public License for more details.
16   *
17   * You should have received a copy of the GNU General Public License
18   * along with this program; if not, write to the Free Software
19   * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
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      // Constants
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     // Fields
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     // Constructors
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     // Methods
247     // XML Handler
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         // Nothing to do
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         // Nothing to do
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             // Nothing to do
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     // Entity Resolver
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     // Error Handler
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     // Accesors
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     // Helper
421     /**
422      * Process &lt;package&gt; 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 &lt;command&gt; 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             // Statefull
454             action = new ActionWrapper(actionClass);
455         }
456         else {
457 
458             // Stateless
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 &lt;action&gt; 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 &lt;view&gt; 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 &lt;input&gt; 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 &lt;parameter&gt; 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 }