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, boolean state) throws XmlPullParserException {
     89         features.put(name, state);
     90     }
     91 
     92 
     93     /**
     94      * Return the current value of the feature with given name.
     95      * <p><b>NOTE:</b> factory features are not used for XML Serializer.
     96      *
     97      * @param name The name of feature to be retrieved.
     98      * @return The value of named feature.
     99      *     Unknown features are <string>always</strong> returned as false
    100      */
    101 
    102     public boolean getFeature (String name) {
    103         Boolean value = (Boolean) features.get(name);
    104         return value != null ? value.booleanValue() : false;
    105     }
    106 
    107     /**
    108      * Specifies that the parser produced by this factory will provide
    109      * support for XML namespaces.
    110      * By default the value of this is set to false.
    111      *
    112      * @param awareness true if the parser produced by this code
    113      *    will provide support for XML namespaces;  false otherwise.
    114      */
    115 
    116     public void setNamespaceAware(boolean awareness) {
    117         features.put (XmlPullParser.FEATURE_PROCESS_NAMESPACES, awareness);
    118     }
    119 
    120     /**
    121      * Indicates whether or not the factory is configured to produce
    122      * parsers which are namespace aware
    123      * (it simply set feature XmlPullParser.FEATURE_PROCESS_NAMESPACES to true or false).
    124      *
    125      * @return  true if the factory is configured to produce parsers
    126      *    which are namespace aware; false otherwise.
    127      */
    128 
    129     public boolean isNamespaceAware() {
    130         return getFeature (XmlPullParser.FEATURE_PROCESS_NAMESPACES);
    131     }
    132 
    133 
    134     /**
    135      * Specifies that the parser produced by this factory will be validating
    136      * (it simply set feature XmlPullParser.FEATURE_VALIDATION to true or false).
    137      *
    138      * By default the value of this is set to false.
    139      *
    140      * @param validating - if true the parsers created by this factory  must be validating.
    141      */
    142 
    143     public void setValidating(boolean validating) {
    144         features.put (XmlPullParser.FEATURE_VALIDATION, validating);
    145     }
    146 
    147     /**
    148      * Indicates whether or not the factory is configured to produce parsers
    149      * which validate the XML content during parse.
    150      *
    151      * @return   true if the factory is configured to produce parsers
    152      * which validate the XML content during parse; false otherwise.
    153      */
    154 
    155     public boolean isValidating() {
    156         return getFeature (XmlPullParser.FEATURE_VALIDATION);
    157     }
    158 
    159     /**
    160      * Creates a new instance of a XML Pull Parser
    161      * using the currently configured factory features.
    162      *
    163      * @return A new instance of a XML Pull Parser.
    164      * @throws XmlPullParserException if a parser cannot be created which satisfies the
    165      * requested configuration.
    166      */
    167 
    168     public XmlPullParser newPullParser() throws XmlPullParserException {
    169 
    170         if (parserClasses == null) throw new XmlPullParserException
    171                 ("Factory initialization was incomplete - has not tried "+classNamesLocation);
    172 
    173         if (parserClasses.size() == 0) throw new XmlPullParserException
    174                 ("No valid parser classes found in "+classNamesLocation);
    175 
    176         final StringBuilder issues = new StringBuilder();
    177 
    178         for (int i = 0; i < parserClasses.size(); i++) {
    179             final Class ppClass = (Class) parserClasses.get(i);
    180             try {
    181                 final XmlPullParser pp = (XmlPullParser) ppClass.newInstance();
    182 
    183                 for (Iterator iter = features.keySet().iterator(); iter.hasNext(); ) {
    184                     final String key = (String) iter.next();
    185                     final Boolean value = (Boolean) features.get(key);
    186                     if(value != null && value.booleanValue()) {
    187                         pp.setFeature(key, true);
    188                     }
    189                 }
    190                 return pp;
    191 
    192             } catch(Exception ex) {
    193                 issues.append (ppClass.getName () + ": "+ ex.toString ()+"; ");
    194             }
    195         }
    196 
    197         throw new XmlPullParserException ("could not create parser: "+issues);
    198     }
    199 
    200 
    201     /**
    202      * Creates a new instance of a XML Serializer.
    203      *
    204      * <p><b>NOTE:</b> factory features are not used for XML Serializer.
    205      *
    206      * @return A new instance of a XML Serializer.
    207      * @throws XmlPullParserException if a parser cannot be created which satisfies the
    208      * requested configuration.
    209      */
    210 
    211     public XmlSerializer newSerializer() throws XmlPullParserException {
    212 
    213         if (serializerClasses == null) {
    214             throw new XmlPullParserException
    215                 ("Factory initialization incomplete - has not tried "+classNamesLocation);
    216         }
    217         if(serializerClasses.size() == 0) {
    218             throw new XmlPullParserException
    219                 ("No valid serializer classes found in "+classNamesLocation);
    220         }
    221 
    222         final StringBuilder issues = new StringBuilder ();
    223 
    224         for (int i = 0; i < serializerClasses.size (); i++) {
    225             final Class ppClass = (Class) serializerClasses.get(i);
    226             try {
    227                 final XmlSerializer ser = (XmlSerializer) ppClass.newInstance();
    228 
    229                 return ser;
    230 
    231             } catch(Exception ex) {
    232                 issues.append (ppClass.getName () + ": "+ ex.toString ()+"; ");
    233             }
    234         }
    235 
    236         throw new XmlPullParserException ("could not create serializer: "+issues);
    237     }
    238 
    239     /**
    240      * Create a new instance of a PullParserFactory that can be used
    241      * to create XML pull parsers (see class description for more
    242      * details).
    243      *
    244      * @return a new instance of a PullParserFactory, as returned by newInstance (null, null);
    245      */
    246     public static XmlPullParserFactory newInstance () throws XmlPullParserException {
    247         return newInstance(null, null);
    248     }
    249 
    250     public static XmlPullParserFactory newInstance (String classNames, Class context)
    251         throws XmlPullParserException {
    252 
    253         /*
    254         if (context == null) {
    255             //NOTE: make sure context uses the same class loader as API classes
    256             //      this is the best we can do without having access to context classloader in J2ME
    257             //      if API is in the same classloader as implementation then this will work
    258             context = referenceContextClass;
    259         }
    260 
    261         String  classNamesLocation = null;
    262 
    263         if (classNames == null || classNames.length() == 0 || "DEFAULT".equals(classNames)) {
    264             try {
    265                 InputStream is = context.getResourceAsStream (RESOURCE_NAME);
    266 
    267                 if (is == null) throw new XmlPullParserException
    268                         ("resource not found: "+RESOURCE_NAME
    269                              +" make sure that parser implementing XmlPull API is available");
    270                 final StringBuilder sb = new StringBuilder();
    271 
    272                 while (true) {
    273                     final int ch = is.read();
    274                     if (ch < 0) break;
    275                     else if (ch > ' ')
    276                         sb.append((char) ch);
    277                 }
    278                 is.close ();
    279 
    280                 classNames = sb.toString ();
    281             }
    282             catch (Exception e) {
    283                 throw new XmlPullParserException (null, null, e);
    284             }
    285             classNamesLocation = "resource "+RESOURCE_NAME+" that contained '"+classNames+"'";
    286         } else {
    287             classNamesLocation =
    288                 "parameter classNames to newInstance() that contained '"+classNames+"'";
    289         }
    290         */
    291         classNames = "org.kxml2.io.KXmlParser,org.kxml2.io.KXmlSerializer";
    292 
    293         XmlPullParserFactory factory = null;
    294         final ArrayList parserClasses = new ArrayList();
    295         final ArrayList serializerClasses = new ArrayList();
    296         int pos = 0;
    297 
    298         while (pos < classNames.length ()) {
    299             int cut = classNames.indexOf (',', pos);
    300 
    301             if (cut == -1) cut = classNames.length ();
    302             final String name = classNames.substring (pos, cut);
    303 
    304             Class candidate = null;
    305             Object instance = null;
    306 
    307             try {
    308                 candidate = Class.forName (name);
    309                 // necessary because of J2ME .class issue
    310                 instance = candidate.newInstance ();
    311             }
    312             catch (Exception e) {}
    313 
    314             if (candidate != null) {
    315                 boolean recognized = false;
    316                 if (instance instanceof XmlPullParser) {
    317                     parserClasses.add(candidate);
    318                     recognized = true;
    319                 }
    320                 if (instance instanceof XmlSerializer) {
    321                     serializerClasses.add(candidate);
    322                     recognized = true;
    323                 }
    324                 if (instance instanceof XmlPullParserFactory) {
    325                     if (factory == null) {
    326                         factory = (XmlPullParserFactory) instance;
    327                     }
    328                     recognized = true;
    329                 }
    330                 if (!recognized) {
    331                     throw new XmlPullParserException ("incompatible class: "+name);
    332                 }
    333             }
    334             pos = cut + 1;
    335         }
    336 
    337         if (factory == null) {
    338             factory = new XmlPullParserFactory ();
    339         }
    340         factory.parserClasses = parserClasses;
    341         factory.serializerClasses = serializerClasses;
    342         factory.classNamesLocation = "org.kxml2.io.kXmlParser,org.kxml2.io.KXmlSerializer";
    343         return factory;
    344     }
    345 }
    346