• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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@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. */
XMLToAPI()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      */
readFile(String filename, boolean createGlobalComments, String apiName)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      */
addInheritedElements()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      */
addInheritedElements(ClassAPI child, ClassAPI parent, String fqParentName)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      */
nameAPI(String name)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      */
addPackage(String name)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      */
addClass(String name, String parent, boolean isAbstract, Modifiers modifiers)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      */
addInterface(String name, String parent, boolean isAbstract, Modifiers modifiers)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      */
addImplements(String name)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      */
addCtor(String type, Modifiers modifiers)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      */
addMethod(String name, String returnType, boolean isAbstract, boolean isNative, boolean isSynchronized, Modifiers modifiers)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      */
addField(String name, String type, boolean isTransient, boolean isVolatile, String value, Modifiers modifiers)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      */
addParam(String name, String type)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      */
addException(String name, String type, String currElement)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