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