View Javadoc

1   /*
2    * $Id: XmlCommandsHandler.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 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      // Constants
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     // Fields
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     // Constructors
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     // Methods
265     // XML Handler
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         // Nothing to do
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         // Nothing to do
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             // Nothing to do
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     // Entity Resolver
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     // Error Handler
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     // Accesors
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     // Helper
428     /**
429      * Process &lt;package&gt; 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 &lt;command&gt; 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             // Statefull
479             action = new ActionWrapper(actionClass);
480         }
481         else {
482 
483             // Stateless
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 &lt;action&gt; 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 &lt;view&gt; 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 &lt;input&gt; 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 &lt;parameter&gt; 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 }