Home | History | Annotate | Download | only in v1
      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.xmlpull.v1;
      5 
      6 import java.io.InputStream;
      7 import java.util.ArrayList;
      8 import java.util.HashMap;
      9 import java.util.Iterator;
     10 
     11 /**
     12  * This class is used to create implementations of XML Pull Parser defined in XMPULL V1 API.
     13  * The name of actual factory class will be determined based on several parameters.
     14  * It works similar to JAXP but tailored to work in J2ME environments
     15  * (no access to system properties or file system) so name of parser class factory to use
     16  * and its class used for loading (no class loader - on J2ME no access to context class loaders)
     17  * must be passed explicitly. If no name of parser factory was passed (or is null)
     18  * it will try to find name by searching in CLASSPATH for
     19  * META-INF/services/org.xmlpull.v1.XmlPullParserFactory resource that should contain
     20  * a comma separated list of class names of factories or parsers to try (in order from
     21  * left to the right). If none found, it will throw an exception.
     22  *
     23  * <br /><strong>NOTE:</strong>In J2SE or J2EE environments, you may want to use
     24  * <code>newInstance(property, classLoaderCtx)</code>
     25  * where first argument is
     26  * <code>System.getProperty(XmlPullParserFactory.PROPERTY_NAME)</code>
     27  * and second is <code>Thread.getContextClassLoader().getClass()</code> .
     28  *
     29  * @see XmlPullParser
     30  *
     31  * @author <a href="http://www.extreme.indiana.edu/~aslom/">Aleksander Slominski</a>
     32  * @author Stefan Haustein
     33  */
     34 
     35 public class XmlPullParserFactory {
     36     /** used as default class to server as context class in newInstance() */
     37     final static Class referenceContextClass;
     38 
     39     static {
     40         XmlPullParserFactory f = new XmlPullParserFactory();
     41         referenceContextClass = f.getClass();
     42     }
     43 
     44     /** Name of the system or midlet property that should be used for
     45      a system property containing a comma separated list of factory
     46      or parser class names (value:
     47      org.xmlpull.v1.XmlPullParserFactory). */
     48 
     49 
     50     public static final String PROPERTY_NAME =
     51         "org.xmlpull.v1.XmlPullParserFactory";
     52 
     53     private static final String RESOURCE_NAME =
     54         "/META-INF/services/" + PROPERTY_NAME;
     55 
     56 
     57     // public static final String DEFAULT_PROPERTY =
     58     //    "org.xmlpull.xpp3.XmlPullParser,org.kxml2.io.KXmlParser";
     59 
     60 
     61     protected ArrayList parserClasses;
     62     protected String classNamesLocation;
     63 
     64     protected ArrayList serializerClasses;
     65 
     66 
     67     // features are kept there
     68     protected HashMap features = new HashMap();
     69 
     70 
     71     /**
     72      * Protected constructor to be called by factory implementations.
     73      */
     74 
     75     protected XmlPullParserFactory() {
     76     }
     77 
     78 
     79 
     80     /**
     81      * Set the features to be set when XML Pull Parser is created by this factory.
     82      * <p><b>NOTE:</b> factory features are not used for XML Serializer.
     83      *
     84      * @param name string with URI identifying feature
     85      * @param state if true feature will be set; if false will be ignored
     86      */
     87 
     88     public void setFeature(String name,
     89                            boolean state) throws XmlPullParserException {
     90 
     91         features.put(name, new Boolean(state));
     92     }
     93 
     94 
     95     /**
     96      * Return the current value of the feature with given name.
     97      * <p><b>NOTE:</b> factory features are not used for XML Serializer.
     98      *
     99      * @param name The name of feature to be retrieved.
    100      * @return The value of named feature.
    101      *     Unknown features are <string>always</strong> returned as false
    102      */
    103 
    104     public boolean getFeature (String name) {
    105         Boolean value = (Boolean) features.get(name);
    106         return value != null ? value.booleanValue() : false;
    107     }
    108 
    109     /**
    110      * Specifies that the parser produced by this factory will provide
    111      * support for XML namespaces.
    112      * By default the value of this is set to false.
    113      *
    114      * @param awareness true if the parser produced by this code
    115      *    will provide support for XML namespaces;  false otherwise.
    116      */
    117 
    118     public void setNamespaceAware(boolean awareness) {
    119         features.put (XmlPullParser.FEATURE_PROCESS_NAMESPACES, new Boolean (awareness));
    120     }
    121 
    122     /**
    123      * Indicates whether or not the factory is configured to produce
    124      * parsers which are namespace aware
    125      * (it simply set feature XmlPullParser.FEATURE_PROCESS_NAMESPACES to true or false).
    126      *
    127      * @return  true if the factory is configured to produce parsers
    128      *    which are namespace aware; false otherwise.
    129      */
    130 
    131     public boolean isNamespaceAware() {
    132         return getFeature (XmlPullParser.FEATURE_PROCESS_NAMESPACES);
    133     }
    134 
    135 
    136     /**
    137      * Specifies that the parser produced by this factory will be validating
    138      * (it simply set feature XmlPullParser.FEATURE_VALIDATION to true or false).
    139      *
    140      * By default the value of this is set to false.
    141      *
    142      * @param validating - if true the parsers created by this factory  must be validating.
    143      */
    144 
    145     public void setValidating(boolean validating) {
    146         features.put (XmlPullParser.FEATURE_VALIDATION, new Boolean (validating));
    147     }
    148 
    149     /**
    150      * Indicates whether or not the factory is configured to produce parsers
    151      * which validate the XML content during parse.
    152      *
    153      * @return   true if the factory is configured to produce parsers
    154      * which validate the XML content during parse; false otherwise.
    155      */
    156 
    157     public boolean isValidating() {
    158         return getFeature (XmlPullParser.FEATURE_VALIDATION);
    159     }
    160 
    161     /**
    162      * Creates a new instance of a XML Pull Parser
    163      * using the currently configured factory features.
    164      *
    165      * @return A new instance of a XML Pull Parser.
    166      * @throws XmlPullParserException if a parser cannot be created which satisfies the
    167      * requested configuration.
    168      */
    169 
    170     public XmlPullParser newPullParser() throws XmlPullParserException {
    171 
    172         if (parserClasses == null) throw new XmlPullParserException
    173                 ("Factory initialization was incomplete - has not tried "+classNamesLocation);
    174 
    175         if (parserClasses.size() == 0) throw new XmlPullParserException
    176                 ("No valid parser classes found in "+classNamesLocation);
    177 
    178         final StringBuffer issues = new StringBuffer ();
    179 
    180         for (int i = 0; i < parserClasses.size(); i++) {
    181             final Class ppClass = (Class) parserClasses.get(i);
    182             try {
    183                 final XmlPullParser pp = (XmlPullParser) ppClass.newInstance();
    184 
    185                 for (Iterator iter = features.keySet().iterator(); iter.hasNext(); ) {
    186                     final String key = (String) iter.next();
    187                     final Boolean value = (Boolean) features.get(key);
    188                     if(value != null && value.booleanValue()) {
    189                         pp.setFeature(key, true);
    190                     }
    191                 }
    192                 return pp;
    193 
    194             } catch(Exception ex) {
    195                 issues.append (ppClass.getName () + ": "+ ex.toString ()+"; ");
    196             }
    197         }
    198 
    199         throw new XmlPullParserException ("could not create parser: "+issues);
    200     }
    201 
    202 
    203     /**
    204      * Creates a new instance of a XML Serializer.
    205      *
    206      * <p><b>NOTE:</b> factory features are not used for XML Serializer.
    207      *
    208      * @return A new instance of a XML Serializer.
    209      * @throws XmlPullParserException if a parser cannot be created which satisfies the
    210      * requested configuration.
    211      */
    212 
    213     public XmlSerializer newSerializer() throws XmlPullParserException {
    214 
    215         if (serializerClasses == null) {
    216             throw new XmlPullParserException
    217                 ("Factory initialization incomplete - has not tried "+classNamesLocation);
    218         }
    219         if(serializerClasses.size() == 0) {
    220             throw new XmlPullParserException
    221                 ("No valid serializer classes found in "+classNamesLocation);
    222         }
    223 
    224         final StringBuffer issues = new StringBuffer ();
    225 
    226         for (int i = 0; i < serializerClasses.size (); i++) {
    227             final Class ppClass = (Class) serializerClasses.get(i);
    228             try {
    229                 final XmlSerializer ser = (XmlSerializer) ppClass.newInstance();
    230 
    231                 return ser;
    232 
    233             } catch(Exception ex) {
    234                 issues.append (ppClass.getName () + ": "+ ex.toString ()+"; ");
    235             }
    236         }
    237 
    238         throw new XmlPullParserException ("could not create serializer: "+issues);
    239     }
    240 
    241     /**
    242      * Create a new instance of a PullParserFactory that can be used
    243      * to create XML pull parsers (see class description for more
    244      * details).
    245      *
    246      * @return a new instance of a PullParserFactory, as returned by newInstance (null, null);
    247      */
    248     public static XmlPullParserFactory newInstance () throws XmlPullParserException {
    249         return newInstance(null, null);
    250     }
    251 
    252     public static XmlPullParserFactory newInstance (String classNames, Class context)
    253         throws XmlPullParserException {
    254 
    255         /*
    256         if (context == null) {
    257             //NOTE: make sure context uses the same class loader as API classes
    258             //      this is the best we can do without having access to context classloader in J2ME
    259             //      if API is in the same classloader as implementation then this will work
    260             context = referenceContextClass;
    261         }
    262 
    263         String  classNamesLocation = null;
    264 
    265         if (classNames == null || classNames.length() == 0 || "DEFAULT".equals(classNames)) {
    266             try {
    267                 InputStream is = context.getResourceAsStream (RESOURCE_NAME);
    268 
    269                 if (is == null) throw new XmlPullParserException
    270                         ("resource not found: "+RESOURCE_NAME
    271                              +" make sure that parser implementing XmlPull API is available");
    272                 final StringBuffer sb = new StringBuffer();
    273 
    274                 while (true) {
    275                     final int ch = is.read();
    276                     if (ch < 0) break;
    277                     else if (ch > ' ')
    278                         sb.append((char) ch);
    279                 }
    280                 is.close ();
    281 
    282                 classNames = sb.toString ();
    283             }
    284             catch (Exception e) {
    285                 throw new XmlPullParserException (null, null, e);
    286             }
    287             classNamesLocation = "resource "+RESOURCE_NAME+" that contained '"+classNames+"'";
    288         } else {
    289             classNamesLocation =
    290                 "parameter classNames to newInstance() that contained '"+classNames+"'";
    291         }
    292         */
    293         classNames = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
    294 
    295         XmlPullParserFactory factory = null;
    296         final ArrayList parserClasses = new ArrayList();
    297         final ArrayList serializerClasses = new ArrayList();
    298         int pos = 0;
    299 
    300         while (pos < classNames.length ()) {
    301             int cut = classNames.indexOf (',', pos);
    302 
    303             if (cut == -1) cut = classNames.length ();
    304             final String name = classNames.substring (pos, cut);
    305 
    306             Class candidate = null;
    307             Object instance = null;
    308 
    309             try {
    310                 candidate = Class.forName (name);
    311                 // necessary because of J2ME .class issue
    312                 instance = candidate.newInstance ();
    313             }
    314             catch (Exception e) {}
    315 
    316             if (candidate != null) {
    317                 boolean recognized = false;
    318                 if (instance instanceof XmlPullParser) {
    319                     parserClasses.add(candidate);
    320                     recognized = true;
    321                 }
    322                 if (instance instanceof XmlSerializer) {
    323                     serializerClasses.add(candidate);
    324                     recognized = true;
    325                 }
    326                 if (instance instanceof XmlPullParserFactory) {
    327                     if (factory == null) {
    328                         factory = (XmlPullParserFactory) instance;
    329                     }
    330                     recognized = true;
    331                 }
    332                 if (!recognized) {
    333                     throw new XmlPullParserException ("incompatible class: "+name);
    334                 }
    335             }
    336             pos = cut + 1;
    337         }
    338 
    339         if (factory == null) {
    340             factory = new XmlPullParserFactory ();
    341         }
    342         factory.parserClasses = parserClasses;
    343         factory.serializerClasses = serializerClasses;
    344         factory.classNamesLocation = "org.kxml2.io.kXmlParser,org.kxml2.io.KXmlSerializer";
    345         return factory;
    346     }
    347 }
    348