View Javadoc
1   /* -*-             c-basic-offset: 4; indent-tabs-mode: nil; -*-  //------100-columns-wide------>|*/
2   // for license please see accompanying LICENSE.txt file (available also at http://www.xmlpull.org/)
3   
4   package org.codehaus.plexus.metadata.merge;
5   
6   import java.io.InputStream;
7   import java.io.IOException;
8   import java.io.Reader;
9   
10  // not J2ME classes -- remove if you want to run in MIDP devices
11  import java.net.URL;
12  import java.net.MalformedURLException;
13  
14  
15  // not J2ME classes
16  import java.io.FileInputStream;
17  import java.io.FileNotFoundException;
18  
19  import org.codehaus.plexus.util.xml.pull.XmlPullParser;
20  import org.codehaus.plexus.util.xml.pull.XmlPullParserException;
21  import org.xml.sax.Attributes;
22  import org.xml.sax.DTDHandler;
23  import org.xml.sax.ContentHandler;
24  import org.xml.sax.EntityResolver;
25  import org.xml.sax.ErrorHandler;
26  import org.xml.sax.InputSource;
27  import org.xml.sax.Locator;
28  import org.xml.sax.SAXException;
29  import org.xml.sax.SAXParseException;
30  import org.xml.sax.SAXNotRecognizedException;
31  import org.xml.sax.SAXNotSupportedException;
32  import org.xml.sax.XMLReader;
33  import org.xml.sax.helpers.DefaultHandler;
34  
35  /**
36   * SAX2 Driver that pulls events from XmlPullParser
37   * and comverts them into SAX2 callbacks.
38   *
39   * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a>
40   */
41  
42  public class Driver implements Locator, XMLReader, Attributes
43  {
44  
45      protected static final String DECLARATION_HANDLER_PROPERTY =
46          "http://xml.org/sax/properties/declaration-handler";
47  
48      protected static final String LEXICAL_HANDLER_PROPERTY =
49          "http://xml.org/sax/properties/lexical-handler";
50  
51      protected static final String NAMESPACES_FEATURE =
52          "http://xml.org/sax/features/namespaces";
53  
54      protected static final String NAMESPACE_PREFIXES_FEATURE =
55          "http://xml.org/sax/features/namespace-prefixes";
56  
57      protected static final String VALIDATION_FEATURE =
58          "http://xml.org/sax/features/validation";
59  
60      protected static final String APACHE_SCHEMA_VALIDATION_FEATURE =
61          "http://apache.org/xml/features/validation/schema";
62  
63      protected static final String APACHE_DYNAMIC_VALIDATION_FEATURE =
64          "http://apache.org/xml/features/validation/dynamic";
65  
66      protected ContentHandler contentHandler = new DefaultHandler();
67      protected ErrorHandler errorHandler = new DefaultHandler();;
68  
69      protected String systemId;
70  
71      protected XmlPullParser pp;
72  
73      public Driver() throws XmlPullParserException
74      {
75          pp = new MXParser();
76          
77          try
78          {
79              setFeature( NAMESPACES_FEATURE, true );
80              setFeature( NAMESPACE_PREFIXES_FEATURE, true );
81              
82          }
83          catch ( Exception e )
84          {
85              e.printStackTrace();
86          }
87      }
88      
89      // -- Attributes interface
90  
91      public int getLength() { return pp.getAttributeCount(); }
92      public String getURI(int index) { return pp.getAttributeNamespace(index); }
93      public String getLocalName(int index) { return pp.getAttributeName(index); }
94      public String getQName(int index) {
95          final String prefix = pp.getAttributePrefix(index);
96          if(prefix != null) {
97              return prefix+':'+pp.getAttributeName(index);
98          } else {
99              return pp.getAttributeName(index);
100         }
101     }
102     public String getType(int index) { return pp.getAttributeType(index); }
103     public String getValue(int index) { return pp.getAttributeValue(index); }
104 
105     public int getIndex(String uri, String localName) {
106         for (int i = 0; i < pp.getAttributeCount(); i++)
107         {
108             if(pp.getAttributeNamespace(i).equals(uri)
109                && pp.getAttributeName(i).equals(localName))
110             {
111                 return i;
112             }
113 
114         }
115         return -1;
116     }
117 
118     public int getIndex(String qName) {
119         for (int i = 0; i < pp.getAttributeCount(); i++)
120         {
121             if(pp.getAttributeName(i).equals(qName))
122             {
123                 return i;
124             }
125 
126         }
127         return -1;
128     }
129 
130     public String getType(String uri, String localName) {
131         for (int i = 0; i < pp.getAttributeCount(); i++)
132         {
133             if(pp.getAttributeNamespace(i).equals(uri)
134                && pp.getAttributeName(i).equals(localName))
135             {
136                 return pp.getAttributeType(i);
137             }
138 
139         }
140         return null;
141     }
142     public String getType(String qName) {
143         for (int i = 0; i < pp.getAttributeCount(); i++)
144         {
145             if(pp.getAttributeName(i).equals(qName))
146             {
147                 return pp.getAttributeType(i);
148             }
149 
150         }
151         return null;
152     }
153     public String getValue(String uri, String localName) {
154         return pp.getAttributeValue(uri, localName);
155     }
156     public String getValue(String qName) {
157         return pp.getAttributeValue(null, qName);
158     }
159 
160     // -- Locator interface
161 
162     public String getPublicId() { return null; }
163     public String getSystemId() { return systemId; }
164     public int getLineNumber() { return pp.getLineNumber(); }
165     public int getColumnNumber() { return pp.getColumnNumber(); }
166 
167     // --- XMLReader interface
168 
169     //"http://xml.org/sax/features/namespaces",
170     //true
171     
172     public boolean getFeature(String name)
173         throws SAXNotRecognizedException, SAXNotSupportedException
174     {
175         if(NAMESPACES_FEATURE.equals(name)) {
176             return pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
177         } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) {
178             return pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES);
179         } else if(VALIDATION_FEATURE.equals(name)) {
180             return pp.getFeature(XmlPullParser.FEATURE_VALIDATION);
181             //        } else if(APACHE_SCHEMA_VALIDATION_FEATURE.equals(name)) {
182             //            return false;  //TODO
183             //        } else if(APACHE_DYNAMIC_VALIDATION_FEATURE.equals(name)) {
184             //            return false; //TODO
185         } else {
186             return pp.getFeature(name);
187             //throw new SAXNotRecognizedException("unrecognized feature "+name);
188         }
189     }
190 
191     public void setFeature (String name, boolean value)
192         throws SAXNotRecognizedException, SAXNotSupportedException
193     {
194         try {
195             if(NAMESPACES_FEATURE.equals(name)) {
196                 pp.setFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES, value);
197             } else if(NAMESPACE_PREFIXES_FEATURE.equals(name)) {
198                 if(pp.getFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES) != value) {
199                     pp.setFeature(XmlPullParser.FEATURE_REPORT_NAMESPACE_ATTRIBUTES, value);
200                 }
201             } else if(VALIDATION_FEATURE.equals(name)) {
202                 pp.setFeature(XmlPullParser.FEATURE_VALIDATION, value);
203             } else {
204                 pp.setFeature(name, value);
205                 //throw new SAXNotRecognizedException("unrecognized feature "+name);
206             }
207         } catch(XmlPullParserException ex) {
208             throw new SAXNotSupportedException("problem with setting feature "+name+": "+ex);
209         }
210     }
211 
212     public Object getProperty (String name)
213         throws SAXNotRecognizedException, SAXNotSupportedException
214     {
215         if(DECLARATION_HANDLER_PROPERTY.equals(name)) {
216             return null;
217         } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) {
218             return null;
219         } else {
220             return pp.getProperty(name);
221             //throw new SAXNotRecognizedException("not recognized get property "+name);
222         }
223     }
224 
225     public void setProperty (String name, Object value)
226         throws SAXNotRecognizedException, SAXNotSupportedException
227     {
228         //
229         if(DECLARATION_HANDLER_PROPERTY.equals(name)) {
230             throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value);
231         } else if(LEXICAL_HANDLER_PROPERTY.equals(name)) {
232             throw new SAXNotSupportedException("not supported setting property "+name);//+" to "+value);
233         } else {
234             try {
235                 pp.setProperty(name, value);
236             } catch(XmlPullParserException ex) {
237                 throw new SAXNotSupportedException("not supported set property "+name+": "+ ex);
238             }
239             //throw new SAXNotRecognizedException("not recognized set property "+name);
240         }
241     }
242 
243     public void setEntityResolver (EntityResolver resolver) {}
244 
245     public EntityResolver getEntityResolver () { return null; }
246 
247     public void setDTDHandler (DTDHandler handler) {}
248 
249     public DTDHandler getDTDHandler () { return null; }
250 
251     public void setContentHandler (ContentHandler handler)
252     {
253         this.contentHandler = handler;
254     }
255 
256     public ContentHandler getContentHandler() { return contentHandler; }
257 
258     public void setErrorHandler(ErrorHandler handler) {
259         this.errorHandler = handler;
260     }
261 
262     public ErrorHandler getErrorHandler() { return errorHandler; }
263 
264     public void parse(InputSource source) throws SAXException, IOException
265     {
266 
267         systemId = source.getSystemId();
268         contentHandler.setDocumentLocator(this);
269 
270         final Reader reader = source.getCharacterStream();
271         try {
272             if (reader == null) {
273                 InputStream stream = source.getByteStream();
274                 final String encoding = source.getEncoding();
275 
276                 if (stream == null) {
277                     systemId = source.getSystemId();
278                     if(systemId == null) {
279                         SAXParseException saxException = new SAXParseException(
280                             "null source systemId" , this);
281                         errorHandler.fatalError(saxException);
282                         return;
283                     }
284                     // NOTE: replace with Connection to run in J2ME environment
285                     try {
286                         final URL url = new URL(systemId);
287                         stream = url.openStream();
288                     } catch (MalformedURLException nue) {
289                         try {
290                             stream = new FileInputStream(systemId);
291                         } catch (FileNotFoundException fnfe) {
292                             final SAXParseException saxException = new SAXParseException(
293                                 "could not open file with systemId "+systemId, this, fnfe);
294                             errorHandler.fatalError(saxException);
295                             return;
296                         }
297                     }
298                 }
299                 pp.setInput(stream, encoding);
300             } else {
301                 pp.setInput(reader);
302             }
303         } catch (XmlPullParserException ex)  {
304             final SAXParseException saxException = new SAXParseException(
305                 "parsing initialization error: "+ex, this, ex);
306             //if(DEBUG) ex.printStackTrace();
307             errorHandler.fatalError(saxException);
308             return;
309         }
310 
311         // start parsing - move to first start tag
312         try {
313             contentHandler.startDocument();
314             // get first event
315             pp.next();
316             // it should be start tag...
317             if(pp.getEventType() != XmlPullParser.START_TAG) {
318                 final SAXParseException saxException = new SAXParseException(
319                     "expected start tag not"+pp.getPositionDescription(), this);
320                 //throw saxException;
321                 errorHandler.fatalError(saxException);
322                 return;
323             }
324         } catch (XmlPullParserException ex)  {
325             final SAXParseException saxException = new SAXParseException(
326                 "parsing initialization error: "+ex, this, ex);
327             //ex.printStackTrace();
328             errorHandler.fatalError(saxException);
329             return;
330         }
331 
332         // now real parsing can start!
333 
334         parseSubTree(pp);
335 
336         // and finished ...
337 
338         contentHandler.endDocument();
339     }
340 
341     public void parse(String systemId) throws SAXException, IOException {
342         parse(new InputSource(systemId));
343     }
344 
345 
346     public void parseSubTree(XmlPullParser pp) throws SAXException, IOException {
347         this.pp = pp;
348         final boolean namespaceAware = pp.getFeature(XmlPullParser.FEATURE_PROCESS_NAMESPACES);
349         try {
350             if(pp.getEventType() != XmlPullParser.START_TAG) {
351                 throw new SAXException(
352                     "start tag must be read before skiping subtree"+pp.getPositionDescription());
353             }
354             final int[] holderForStartAndLength = new int[2];
355             final StringBuilder rawName = new StringBuilder(16);
356             String prefix = null;
357             String name = null;
358             int level = pp.getDepth() - 1;
359             int type = XmlPullParser.START_TAG;
360 
361             LOOP:
362             do {
363                 switch(type) {
364                     case XmlPullParser.START_TAG:
365                         if(namespaceAware) {
366                             final int depth = pp.getDepth() - 1;
367                             final int countPrev =
368                                 (level > depth) ? pp.getNamespaceCount(depth) : 0;
369                             //int countPrev = pp.getNamespaceCount(pp.getDepth() - 1);
370                             final int count = pp.getNamespaceCount(depth + 1);
371                             for (int i = countPrev; i < count; i++)
372                             {
373                                 contentHandler.startPrefixMapping(
374                                     pp.getNamespacePrefix(i),
375                                     pp.getNamespaceUri(i)
376                                 );
377                             }
378                             name = pp.getName();
379                             prefix = pp.getPrefix();
380                             if(prefix != null) {
381                                 rawName.setLength(0);
382                                 rawName.append(prefix);
383                                 rawName.append(':');
384                                 rawName.append(name);
385                             }
386                             startElement(pp.getNamespace(),
387                                          name,
388                                          prefix != null ? name : rawName.toString());
389                         } else {
390                             startElement(pp.getNamespace(),
391                                          pp.getName(),
392                                          pp.getName());
393                         }
394                         //++level;
395 
396                         break;
397                     case XmlPullParser.TEXT:
398                         final char[] chars = pp.getTextCharacters(holderForStartAndLength);
399                         contentHandler.characters(chars,
400                                                   holderForStartAndLength[0], //start
401                                                   holderForStartAndLength[1] //len
402                                                  );
403                         break;
404                     case XmlPullParser.END_TAG:
405                         //--level;
406                         if(namespaceAware) {
407                             name = pp.getName();
408                             prefix = pp.getPrefix();
409                             if(prefix != null) {
410                                 rawName.setLength(0);
411                                 rawName.append(prefix);
412                                 rawName.append(':');
413                                 rawName.append(name);
414                             }
415                             contentHandler.endElement(pp.getNamespace(),
416                                                       name,
417                                                       prefix != null ? name : rawName.toString()
418                                                      );
419                             // when entering show prefixes for all levels!!!!
420                             final int depth = pp.getDepth();
421                             final int countPrev =
422                                 (level > depth) ? pp.getNamespaceCount(pp.getDepth()) : 0;
423                             int count = pp.getNamespaceCount(pp.getDepth() - 1);
424                             // undeclare them in reverse order
425                             for (int i = count - 1; i >= countPrev; i--)
426                             {
427                                 contentHandler.endPrefixMapping(
428                                     pp.getNamespacePrefix(i)
429                                 );
430                             }
431                         } else {
432                             contentHandler.endElement(pp.getNamespace(),
433                                                       pp.getName(),
434                                                       pp.getName()
435                                                      );
436 
437                         }
438                         break;
439                     case XmlPullParser.END_DOCUMENT:
440                         break LOOP;
441                 }
442                 type = pp.next();
443             } while(pp.getDepth() > level);
444         } catch (XmlPullParserException ex)  {
445             final SAXParseException saxException = new SAXParseException("parsing error: "+ex, this, ex);
446             ex.printStackTrace();
447             errorHandler.fatalError(saxException);
448         }
449     }
450 
451     /**
452      * Calls {@link ContentHandler.startElement(String, String, String, Attributes) startElement}
453      * on the <code>ContentHandler</code> with <code>this</code> driver object as the
454      * {@link Attributes} implementation. In default implementation
455      * {@link Attributes} object is valid only during this method call and may not
456      * be stored. Sub-classes can overwrite this method to cache attributes.
457      */
458     protected void startElement(String namespace, String localName, String qName) throws SAXException {
459         contentHandler.startElement(namespace, localName, qName, this);
460     }
461 
462 }