/Users/lyon/j4p/src/classUtils/pack/util/xml/configuration/DispatcherHandler.java

1    package classUtils.pack.util.xml.configuration; 
2     
3    import org.xml.sax.*; 
4    import org.xml.sax.helpers.DefaultHandler; 
5     
6    import java.io.IOException; 
7    import java.util.*; 
8     
9    /** 
10    * <a name="desc"> 
11    * A SAX handler which dispatches events to subclasses implementing the 
12    * {@link Configurator Configurator} interface.  
13    * <p> 
14    * Dispatching can be set by tree depth level (using {@link #setDispatchLevel(int)  
15    * setDispatchLevel()}) or by namespaceURI/tag combination (using  
16    * {@link #setToDispatch(java.lang.String, java.lang.String)}. 
17    * <p> 
18    * {@link Configurator Configurator} classes are located automatically in a package 
19    * whose name is set at {@link #DispatcherHandler(java.lang.String) construction}  
20    * and their name must match a transformation the element tag obtained by the  
21    * following rules: 
22    * <p> 
23    * <ul> 
24    * <li> The tag name is altered so that  
25    *      <p> 
26    *      <ul> 
27    *      <li>the first letter is capitalized; 
28    *      <li>dashes  are eliminated and the following letter  
29    *          capitalized. 
30    *      </ul> 
31    *      <p> 
32    *      For example <tt><b>&lt;my-tag&gt;</b></tt> becomes <b>MyTag</b>. 
33    *      <p> 
34    * <li> If the so transformed tag ends with <b>Configuration</b>, this part is stripped. 
35    *      <p> 
36    *      For example, <tt><b>MyTagConfiguration</b></tt> becomes also <b>MyTag</b> 
37    *      <p> 
38    * <li> If the {@link #DispatcherHandler(java.lang.String, java.lang.String) two-parameters  
39    *      constructor} is used to create the DispatcherHandler, the given postfix  
40    *      is concatenated to the name. 
41    *      <p> 
42    *      For example, if the postfix is <b>Configurator</b>, the final result for 
43    *      a tag <tt><b>&lt;my-tag&gt;</b></tt> will be the class name <b>MyTagConfigurator</b>. 
44    * </ul> 
45    * <p> 
46    * The class works via a <i>current configurator</i> (initially null). 
47    * <p> 
48    * Whenever a dispatching occurs, the  class found in the package name given at constructor is instantiated and 
49    * control is sent to that instance, which becomes the current configurator. 
50    * <p> 
51    * When the level/element for which the dispatch was enabled is terminated, the method {@link 
52    * ) configuratorReady()} 
53    * (which must be implemented by the class extending this one) is invoked, and the previous configurator 
54    * is reinstated as "current". 
55    * <p> 
56    * Otherwise, the current configurator (if any) keeps receiving the SAX events. 
57    *  
58    * @author Cristiano Sadun 
59    * 
60    */ 
61   public abstract class DispatcherHandler extends DefaultHandler { 
62    
63       private String packageName; 
64       private String postfix; 
65       private Stack elementsStack = new Stack(); 
66       private Configurator currentConfigurator = null; 
67    
68       private Map dispatcherMap = new HashMap(); 
69       private Set dispatchLevels = new HashSet(); 
70    
71       private Stack configuratorsStack = new Stack(); 
72    
73       /** 
74        * Constructor for DispatcherHandler. Indicates that the {@link Configurator Configurator} 
75        * classes will be searched for in the given package (see <a href="#desc">class description</a>). 
76        * @param packageName the name of the package where to attempt to locate the 
77        *         necessary {@link Configurator Configurator} classes. 
78        * @param postfix a fixed postfix which will be added to the {@link Configurator Configurator}  
79        *         class name (see <a href="#desc">class description</a>). 
80        */ 
81       protected DispatcherHandler(String packageName, String postfix) { 
82           this.packageName = packageName; 
83           this.postfix = postfix; 
84       } 
85        
86       /** 
87        * Constructor for DispatcherHandler. Indicates that the {@link Configurator Configurator} 
88        * classes will be searched for in the given package (see <a href="#desc">class description</a>). 
89        * @param packageName the name of the package where to attempt to locate the 
90        *         necessary {@link Configurator Configurator} classes. 
91        */ 
92       protected DispatcherHandler(String packageName) { 
93           this(packageName, ""); 
94       } 
95    
96       /** 
97        * Indicate that the given namespace URI/local name combination 
98        * is associated to a Configurator. 
99        */ 
100      protected void setToDispatch(String namespaceURI, String localName) { 
101          dispatcherMap.put( 
102              namespaceURI + ":" + localName, 
103              makeConfiguratorClassNameFromQName(localName)); 
104      } 
105   
106      /** 
107       * Sets wether or not every element at a given depth level in the XML 
108       * tree is associated to a Configurator. 
109       * @param level the depth level in the XML tree 
110   
111       */ 
112      protected void setDispatchLevel(int level, boolean toDispatch) { 
113          if (toDispatch) 
114              dispatchLevels.add(new Integer(level)); 
115          else 
116              dispatchLevels.remove(new Integer(level)); 
117      } 
118       
119      /** 
120       * Indicates that every element at a given depth level in the XML 
121       * tree is associated to a Configurator. 
122       * @param level the depth level in the XML tree 
123       */ 
124      protected void setDispatchLevel(int level) { 
125          setDispatchLevel(level, true); 
126      } 
127   
128      private String makeConfiguratorClassNameFromQName(String qName) { 
129          String tag = getTagFromQName(qName); 
130          StringBuffer sb = new StringBuffer(); 
131          boolean doChange = true; 
132          for (int i = 0; i < qName.length(); i++) { 
133              char c = tag.charAt(i); 
134              if (doChange) { 
135                  sb.append(Character.toUpperCase(c)); 
136                  doChange = false; 
137              } else if (c == '-') 
138                  doChange = true; 
139              else 
140                  sb.append(c); 
141          } 
142          tag = sb.toString(); 
143          if (tag.endsWith("Configuration")) 
144              tag = tag.substring(0, tag.length() - 13); 
145          return packageName + "." + tag + postfix; 
146      } 
147   
148      private String getTagFromQName(String qName) { 
149          int i = qName.indexOf(":"); 
150          String tag; 
151          if (i == -1) 
152              tag = qName; 
153          else 
154              tag = qName.substring(i + 1); 
155          return tag; 
156      } 
157       
158      /** 
159       * Check whether the namespace/tag are associated to a configurator 
160       * or we're at a dispatch level.  
161       * <p>  
162       * If yes, instantiate a new associated {@link Configurator Configurator}.  
163       * and invoke its {@link Configurator#startElement(java.lang.String, 
164       * java.lang.String, java.lang.String, org.xml.sax.Attributes)  
165       * startElement()} method. 
166       * <p> 
167       * If no, invoke the 
168       * {@link Configurator#startElement(java.lang.String, java.lang.String, 
169       * java.lang.String, org.xml.sax.Attributes) startElement()} method 
170       * of the current {@link Configurator Configurator} (if any). 
171       */ 
172      public final void startElement( 
173          String namespaceURI, 
174          String localName, 
175          String qName, 
176          Attributes atts) 
177          throws SAXException { 
178   
179          elementsStack.push(qName); 
180           
181          if (dispatchLevels.contains(new Integer(elementsStack.size())) 
182              || hasConfigurator(namespaceURI, localName)) { 
183              String clsName = makeConfiguratorClassNameFromQName(qName); 
184               
185              try { 
186   
187                  Class configuratorCls = Class.forName(clsName); 
188                  try { 
189                      currentConfigurator = 
190                          (Configurator) configuratorCls.newInstance(); 
191                      configuratorsStack.push(currentConfigurator); 
192                  } catch (InstantiationException e) { 
193                      throw new SAXException( 
194                          "Could not instantiate XSpace configurator object", 
195                          e); 
196                  } catch (IllegalAccessException e) { 
197                      throw new SAXException( 
198                          "Could not access XSpace configurator object", 
199                          e); 
200                  } 
201              } catch (ClassNotFoundException e) { 
202                  throw new SAXException( 
203                      qName 
204                          + " configuration element not handled (class " 
205                          + clsName 
206                          + " not found"); 
207              } 
208   
209          }  
210           
211          if (currentConfigurator != null) 
212              currentConfigurator.startElement( 
213                  namespaceURI, 
214                  localName, 
215                  qName, 
216                  atts); 
217   
218      } 
219   
220      public final void characters(char[] ch, int start, int length) 
221          throws SAXException { 
222          if (currentConfigurator != null) 
223              currentConfigurator.characters(ch, start, length); 
224   
225      } 
226   
227      public final void endElement( 
228          String namespaceURI, 
229          String localName, 
230          String qName) 
231          throws SAXException { 
232          elementsStack.pop(); 
233          if (currentConfigurator == null) 
234              return; 
235          currentConfigurator.endElement( 
236              namespaceURI, 
237              localName, 
238              qName); 
239               
240           
241   
242          if (dispatchLevels.contains(new Integer(elementsStack.size()+1)) 
243              || hasConfigurator(namespaceURI, localName)) { 
244              configuratorReady( 
245                  namespaceURI, 
246                  localName, 
247                  qName, 
248                  currentConfigurator); 
249              currentConfigurator = (Configurator)configuratorsStack.pop(); 
250          } 
251      }  
252       
253      /** 
254       * Method hasConfigurator. 
255       * @param namespaceURI 
256       * @param localName 
257       * @return boolean 
258       */ 
259      private boolean hasConfigurator(String namespaceURI, String localName) { 
260          return dispatcherMap.containsKey(namespaceURI + ":" + localName); 
261      } 
262   
263      protected abstract void configuratorReady( 
264          String namespaceURI, 
265          String localName, 
266          String qName, 
267          Configurator configurator) 
268          throws SAXException; 
269      /** 
270       * @see org.xml.sax.ContentHandler#endDocument() 
271       */ 
272      public void endDocument() throws SAXException { 
273          if (currentConfigurator!=null)  currentConfigurator.endDocument(); 
274      } 
275   
276      /** 
277       * @see org.xml.sax.ContentHandler#endPrefixMapping(String) 
278       */ 
279      public void endPrefixMapping(String prefix) throws SAXException { 
280          if (currentConfigurator!=null) currentConfigurator.endPrefixMapping(prefix); 
281      } 
282   
283      /** 
284       * @see org.xml.sax.ErrorHandler#error(SAXParseException) 
285       */ 
286      public void error(SAXParseException exception) throws SAXException { 
287          if (currentConfigurator!=null && currentConfigurator instanceof ErrorHandler) ((ErrorHandler)currentConfigurator).error(exception); 
288      } 
289   
290      /** 
291       * @see org.xml.sax.ErrorHandler#fatalError(SAXParseException) 
292       */ 
293      public void fatalError(SAXParseException exception) throws SAXException { 
294          if (currentConfigurator!=null && currentConfigurator instanceof ErrorHandler) ((ErrorHandler)currentConfigurator).fatalError(exception); 
295      } 
296   
297      /** 
298       * @see org.xml.sax.ContentHandler#ignorableWhitespace(char[], int, int) 
299       */ 
300      public void ignorableWhitespace(char[] ch, int start, int length) 
301          throws SAXException { 
302          if (currentConfigurator!=null)  currentConfigurator.ignorableWhitespace(ch, start, length); 
303      } 
304   
305      /** 
306       * @see org.xml.sax.DTDHandler#notationDecl(String, String, String) 
307       */ 
308      public void notationDecl(String name, String publicId, String systemId) 
309          throws SAXException { 
310          if (currentConfigurator!=null && currentConfigurator instanceof DTDHandler) ((DTDHandler)currentConfigurator).notationDecl(name, publicId, systemId); 
311      } 
312   
313      /** 
314       * @see org.xml.sax.ContentHandler#processingInstruction(String, String) 
315       */ 
316      public void processingInstruction(String target, String data) 
317          throws SAXException { 
318          if (currentConfigurator!=null)  currentConfigurator.processingInstruction(target, data); 
319      } 
320   
321      /** 
322       * @see org.xml.sax.EntityResolver#resolveEntity(String, String) 
323       */ 
324      public InputSource resolveEntity(String publicId, String systemId) 
325              throws SAXException { 
326          if (currentConfigurator!=null && currentConfigurator instanceof EntityResolver) try { 
327              return ((EntityResolver)currentConfigurator).resolveEntity(publicId, systemId); 
328          } catch (IOException e) { 
329              e.printStackTrace(); 
330              return super.resolveEntity(publicId, systemId); 
331          } 
332          else return super.resolveEntity(publicId, systemId); 
333      } 
334   
335      /** 
336       * @see org.xml.sax.ContentHandler#setDocumentLocator(Locator) 
337       */ 
338      public void setDocumentLocator(Locator locator) { 
339          if (currentConfigurator!=null)  currentConfigurator.setDocumentLocator(locator); 
340      } 
341   
342      /** 
343       * @see org.xml.sax.ContentHandler#skippedEntity(String) 
344       */ 
345      public void skippedEntity(String name) throws SAXException { 
346          if (currentConfigurator!=null)  currentConfigurator.skippedEntity(name); 
347      } 
348   
349      /** 
350       * @see org.xml.sax.ContentHandler#startDocument() 
351       */ 
352      public void startDocument() throws SAXException { 
353          if (currentConfigurator!=null)  currentConfigurator.startDocument(); 
354      } 
355   
356      /** 
357       * @see org.xml.sax.ContentHandler#startPrefixMapping(String, String) 
358       */ 
359      public void startPrefixMapping(String prefix, String uri) 
360          throws SAXException { 
361          if (currentConfigurator!=null)  currentConfigurator.startPrefixMapping(prefix, uri); 
362      } 
363   
364      /** 
365       * @see org.xml.sax.DTDHandler#unparsedEntityDecl(String, String, String, String) 
366       */ 
367      public void unparsedEntityDecl( 
368          String name, 
369          String publicId, 
370          String systemId, 
371          String notationName) 
372          throws SAXException { 
373          if (currentConfigurator!=null && currentConfigurator instanceof DTDHandler) 
374              ((DTDHandler)currentConfigurator).unparsedEntityDecl(name, publicId, systemId, notationName); 
375      } 
376   
377      /** 
378       * @see org.xml.sax.ErrorHandler#warning(SAXParseException) 
379       */ 
380      public void warning(SAXParseException exception) throws SAXException { 
381          if (currentConfigurator!=null&& currentConfigurator instanceof ErrorHandler) 
382              ((ErrorHandler)currentConfigurator).warning(exception); 
383      } 
384   
385  } 
386