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