1package jdiff; 2 3import java.io.*; 4import java.util.*; 5import javax.xml.parsers.ParserConfigurationException; 6 7/* For SAX parsing in APIHandler */ 8import org.xml.sax.Attributes; 9import org.xml.sax.SAXException; 10import org.xml.sax.SAXParseException; 11import org.xml.sax.XMLReader; 12import org.xml.sax.InputSource; 13import org.xml.sax.helpers.*; 14 15/** 16 * Creates an API object from an XML file. The API object is the internal 17 * representation of an API. 18 * All methods in this class for populating an API object are static. 19 * 20 * See the file LICENSE.txt for copyright details. 21 * @author Matthew Doar, mdoar@pobox.com 22 */ 23public class XMLToAPI { 24 25 /** The instance of the API object which is populated from the file. */ 26 private static API api_ = null; 27 28 /** Default constructor. */ 29 private XMLToAPI() { 30 } 31 32 /** 33 * Read the file where the XML representing the API is stored. 34 * 35 * @param filename The full name of the file containing the XML 36 * representing the API 37 * @param createGlobalComments If set, then store possible comments 38 * @param apiName The simple name of the API file. If -oldapidir and 39 * -newapidir are not used, then this is the same as 40 * the filename parameter 41 */ 42 public static API readFile(String filename, boolean createGlobalComments, 43 String apiName) { 44 // The instance of the API object which is populated from the file. 45 api_ = new API(); 46 api_.name_ = apiName; // Checked later 47 try { 48 XMLReader parser = null; 49 DefaultHandler handler = new APIHandler(api_, createGlobalComments); 50 try { 51 parser = javax.xml.parsers.SAXParserFactory.newInstance().newSAXParser().getXMLReader(); 52 } catch (SAXException saxe) { 53 System.out.println("SAXException: " + saxe); 54 saxe.printStackTrace(); 55 System.exit(1); 56 } catch (ParserConfigurationException pce) { 57 System.out.println("ParserConfigurationException: " + pce); 58 pce.printStackTrace(); 59 System.exit(1); 60 } 61 62 if (validateXML) { 63 parser.setFeature("http://xml.org/sax/features/namespaces", true); 64 parser.setFeature("http://xml.org/sax/features/validation", true); 65 parser.setFeature("http://apache.org/xml/features/validation/schema", true); 66 } 67 68 parser.setContentHandler(handler); 69 parser.setErrorHandler(handler); 70 parser.parse(new InputSource(new FileInputStream(new File(filename)))); 71 } catch(org.xml.sax.SAXNotRecognizedException snre) { 72 System.out.println("SAX Parser does not recognize feature: " + snre); 73 snre.printStackTrace(); 74 System.exit(1); 75 } catch(org.xml.sax.SAXNotSupportedException snse) { 76 System.out.println("SAX Parser feature is not supported: " + snse); 77 snse.printStackTrace(); 78 System.exit(1); 79 } catch(org.xml.sax.SAXException saxe) { 80 System.out.println("SAX Exception parsing file '" + filename + "' : " + saxe); 81 saxe.printStackTrace(); 82 System.exit(1); 83 } catch(java.io.IOException ioe) { 84 System.out.println("IOException parsing file '" + filename + "' : " + ioe); 85 ioe.printStackTrace(); 86 System.exit(1); 87 } 88 89 // Add the inherited methods and fields to each class 90 addInheritedElements(); 91 return api_; 92 } //readFile() 93 94 /** 95 * Add the inherited methods and fields to each class in turn. 96 */ 97 public static void addInheritedElements() { 98 Iterator iter = api_.packages_.iterator(); 99 while (iter.hasNext()) { 100 PackageAPI pkg = (PackageAPI)(iter.next()); 101 Iterator iter2 = pkg.classes_.iterator(); 102 while (iter2.hasNext()) { 103 ClassAPI cls = (ClassAPI)(iter2.next()); 104 // Look up any inherited classes or interfaces 105 if (cls.extends_ != null) { 106 ClassAPI parent = (ClassAPI)api_.classes_.get(cls.extends_); 107 if (parent != null) 108 addInheritedElements(cls, parent, cls.extends_); 109 } 110 if (cls.implements_.size() != 0) { 111 Iterator iter3 = cls.implements_.iterator(); 112 while (iter3.hasNext()) { 113 String implName = (String)(iter3.next()); 114 ClassAPI parent = (ClassAPI)api_.classes_.get(implName); 115 if (parent != null) 116 addInheritedElements(cls, parent, implName); 117 } 118 } 119 } //while (iter2.hasNext()) 120 } //while (iter.hasNext()) 121 } 122 123 /** 124 * Add all the inherited methods and fields in the second class to 125 * the first class, marking them as inherited from the second class. 126 * Do not add a method or a field if it is already defined locally. 127 * 128 * Only elements at the specified visibility level or 129 * higher appear in the XML file. All that remains to be tested for 130 * a private element, which is never inherited. 131 * 132 * If the parent class inherits any classes or interfaces, call this 133 * method recursively with those parents. 134 */ 135 public static void addInheritedElements(ClassAPI child, ClassAPI parent, 136 String fqParentName) { 137 if (parent.methods_.size() != 0) { 138 Iterator iter = parent.methods_.iterator(); 139 while (iter.hasNext()) { 140 MethodAPI m = (MethodAPI)(iter.next()); 141 // See if it the method is overridden locally 142 boolean overridden = false; 143 Iterator iter2 = child.methods_.iterator(); 144 while (iter2.hasNext()) { 145 MethodAPI localM = (MethodAPI)(iter2.next()); 146 if (localM.name_.compareTo(m.name_) == 0 && 147 localM.getSignature().compareTo(m.getSignature()) == 0) 148 overridden = true; 149 } 150 if (!overridden && m.inheritedFrom_ == null && 151 m.modifiers_.visibility != null && 152 m.modifiers_.visibility.compareTo("private") != 0) { 153 MethodAPI m2 = new MethodAPI(m); 154 m2.inheritedFrom_ = fqParentName; 155 child.methods_.add(m2); 156 } 157 } 158 } 159 if (parent.fields_.size() != 0) { 160 Iterator iter = parent.fields_.iterator(); 161 while (iter.hasNext()) { 162 FieldAPI f = (FieldAPI)(iter.next()); 163 if (child.fields_.indexOf(f) == -1 && 164 f.inheritedFrom_ == null && 165 f.modifiers_.visibility != null && 166 f.modifiers_.visibility.compareTo("private") != 0) { 167 FieldAPI f2 = new FieldAPI(f); 168 f2.inheritedFrom_ = fqParentName; 169 child.fields_.add(f2); 170 } 171 } 172 } 173 174 // Look up any inherited classes or interfaces 175 if (parent.extends_ != null) { 176 ClassAPI parent2 = (ClassAPI)api_.classes_.get(parent.extends_); 177 if (parent2 != null) 178 addInheritedElements(child, parent2, parent.extends_); 179 } 180 if (parent.implements_.size() != 0) { 181 Iterator iter3 = parent.implements_.iterator(); 182 while (iter3.hasNext()) { 183 String implName = (String)(iter3.next()); 184 ClassAPI parent2 = (ClassAPI)api_.classes_.get(implName); 185 if (parent2 != null) 186 addInheritedElements(child, parent2, implName); 187 } 188 } 189 } 190 191// 192// Methods to add data to an API object. Called by the XML parser. 193// 194 195 /** 196 * Set the name of the API object. 197 * 198 * @param name The name of the package. 199 */ 200 public static void nameAPI(String name) { 201 if (name == null) { 202 System.out.println("Warning: no API identifier found in the XML file '" + api_.name_ + "'"); 203 String filename = api_.name_.substring(0, api_.name_.lastIndexOf(".xml")); 204 // System.out.println(" api level:" + filename); 205 System.out.println("Using '" + filename + "' as the API identifier."); 206 api_.name_ = filename; 207 // System.exit(3); 208 return; 209 } 210 // Check the given name against the filename currently stored in 211 // the name_ field 212 String filename2 = name.replace(' ','_'); 213 filename2 += ".xml"; 214 if (filename2.compareTo(api_.name_) != 0) { 215 System.out.println("Warning: API identifier in the XML file (" + 216 name + ") differs from the name of the file '" + 217 api_.name_ + "'"); 218 } 219 api_.name_ = name; 220 } 221 222 /** 223 * Create a new package and add it to the API. Called by the XML parser. 224 * 225 * @param name The name of the package. 226 */ 227 public static void addPackage(String name) { 228 api_.currPkg_ = new PackageAPI(name); 229 api_.packages_.add(api_.currPkg_); 230 } 231 232 /** 233 * Create a new class and add it to the current package. Called by the XML parser. 234 * 235 * @param name The name of the class. 236 * @param parent The name of the parent class, null if no class is extended. 237 * @param modifiers Modifiers for this class. 238 */ 239 public static void addClass(String name, String parent, 240 boolean isAbstract, 241 Modifiers modifiers) { 242 api_.currClass_ = new ClassAPI(name, parent, false, isAbstract, modifiers); 243 api_.currPkg_.classes_.add(api_.currClass_); 244 String fqName = api_.currPkg_.name_ + "." + name; 245 ClassAPI caOld = (ClassAPI)api_.classes_.put(fqName, api_.currClass_); 246 if (caOld != null) { 247 System.out.println("Warning: duplicate class : " + fqName + " found. Using the first instance only."); 248 } 249 } 250 251 /** 252 * Add an new interface and add it to the current package. Called by the 253 * XML parser. 254 * 255 * @param name The name of the interface. 256 * @param parent The name of the parent interface, null if no 257 * interface is extended. 258 */ 259 public static void addInterface(String name, String parent, 260 boolean isAbstract, 261 Modifiers modifiers) { 262 api_.currClass_ = new ClassAPI(name, parent, true, isAbstract, modifiers); 263 api_.currPkg_.classes_.add(api_.currClass_); 264 } 265 266 /** 267 * Add an inherited interface to the current class. Called by the XML 268 * parser. 269 * 270 * @param name The name of the inherited interface. 271 */ 272 public static void addImplements(String name) { 273 api_.currClass_.implements_.add(name); 274 } 275 276 /** 277 * Add a constructor to the current class. Called by the XML parser. 278 * 279 * @param name The name of the constructor (optional). 280 * @param type The type of the constructor. 281 * @param modifiers Modifiers for this constructor. 282 */ 283 public static void addCtor(String name, String type, Modifiers modifiers) { 284 String t = type; 285 if (t == null) 286 t = "void"; 287 api_.currCtor_ = new ConstructorAPI(name, t, modifiers); 288 api_.currClass_.ctors_.add(api_.currCtor_); 289 } 290 291 /** 292 * Add a method to the current class. Called by the XML parser. 293 * 294 * @param name The name of the method. 295 * @param returnType The return type of the method, null if it is void. 296 * @param modifiers Modifiers for this method. 297 */ 298 public static void addMethod(String name, String returnType, 299 boolean isAbstract, boolean isNative, 300 boolean isSynchronized, Modifiers modifiers) { 301 String rt = returnType; 302 if (rt == null) 303 rt = "void"; 304 api_.currMethod_ = new MethodAPI(name, rt, isAbstract, isNative, 305 isSynchronized, modifiers); 306 api_.currClass_.methods_.add(api_.currMethod_); 307 } 308 309 /** 310 * Add a field to the current class. Called by the XML parser. 311 * 312 * @param name The name of the field. 313 * @param type The type of the field, null if it is void. 314 * @param modifiers Modifiers for this field. 315 */ 316 public static void addField(String name, String type, boolean isTransient, 317 boolean isVolatile, String value, Modifiers modifiers) { 318 String t = type; 319 if (t == null) 320 t = "void"; 321 api_.currField_ = new FieldAPI(name, t, isTransient, isVolatile, value, modifiers); 322 api_.currClass_.fields_.add(api_.currField_); 323 } 324 325 /** 326 * Add a parameter to the current method or constructor. Called by the XML parser. 327 * Constuctors have their type (signature) in an attribute, since it 328 * is often shorter and makes parsing a little easier. 329 * 330 * @param name The name of the parameter. 331 * @param type The type of the parameter, null if it is void. 332 * @param isConstructor Whether the given parameter is for a constructor 333 */ 334 public static void addParam(String name, String type, boolean isConstructor) { 335 String t = type; 336 if (t == null) 337 t = "void"; 338 ParamAPI paramAPI = new ParamAPI(name, t); 339 if (isConstructor) { 340 api_.currCtor_.params_.add(paramAPI); 341 } else { 342 api_.currMethod_.params_.add(paramAPI); 343 } 344 } 345 346 /** 347 * Add an exception to the current method or constructor. 348 * Called by the XML parser. 349 * 350 * @param name The name of the parameter. 351 * @param type The type of the parameter. 352 * May be null in JDiff1.0.8 and earlier versions. 353 * @param currElement Name of the current element. 354 */ 355 public static void addException(String name, String type, String currElement) { 356 String exceptionId = type; 357 if (type == null || !showExceptionTypes) 358 exceptionId = name; 359 if (currElement.compareTo("method") == 0) { 360 if (api_.currMethod_.exceptions_.compareTo("no exceptions") == 0) 361 api_.currMethod_.exceptions_ = exceptionId; 362 else 363 api_.currMethod_.exceptions_ += ", " + exceptionId; 364 } else { 365 if (api_.currCtor_.exceptions_.compareTo("no exceptions") == 0) 366 api_.currCtor_.exceptions_ = exceptionId; 367 else 368 api_.currCtor_.exceptions_ += ", " + exceptionId; 369 } 370 } 371 372 /** 373 * If set, validate the XML which represents an API. By default, this is 374 * not set for reasons of efficiency, and also because if JDiff generated 375 * the XML, it should not need validating. 376 */ 377 public static boolean validateXML = false; 378 379 /** 380 * If set, then store and display the whole qualified name of exceptions. 381 * If not set, then store and display just the name of the exception, 382 * which is shorter, but may not detect when an exception changes class, 383 * but retains the same name. 384 */ 385 private static boolean showExceptionTypes = true; 386} 387