• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 package jdiff;
2 
3 import java.io.*;
4 import java.util.*;
5 import javax.xml.parsers.ParserConfigurationException;
6 
7 /* For SAX parsing in APIHandler */
8 import org.xml.sax.Attributes;
9 import org.xml.sax.SAXException;
10 import org.xml.sax.SAXParseException;
11 import org.xml.sax.XMLReader;
12 import org.xml.sax.InputSource;
13 import 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  */
23 public 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. */
XMLToAPI()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      */
readFile(String filename, boolean createGlobalComments, String apiName)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("https://xml.org/sax/features/namespaces", true);
64                 parser.setFeature("https://xml.org/sax/features/validation", true);
65                 parser.setFeature("https://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      */
addInheritedElements()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      */
addInheritedElements(ClassAPI child, ClassAPI parent, String fqParentName)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      */
nameAPI(String name)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      */
addPackage(String name)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      */
addClass(String name, String parent, boolean isAbstract, Modifiers modifiers)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      */
addInterface(String name, String parent, boolean isAbstract, Modifiers modifiers)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      */
addImplements(String name)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      */
addCtor(String name, String type, Modifiers modifiers)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      */
addMethod(String name, String returnType, boolean isAbstract, boolean isNative, boolean isSynchronized, Modifiers modifiers)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      */
addField(String name, String type, boolean isTransient, boolean isVolatile, String value, Modifiers modifiers)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      */
addParam(String name, String type, boolean isConstructor)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      */
addException(String name, String type, String currElement)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