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