Home | History | Annotate | Download | only in jdiff
      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