package jdiff;
|
|
import java.io.*;
|
import java.util.*;
|
import javax.xml.parsers.ParserConfigurationException;
|
|
/* For SAX parsing in APIHandler */
|
import org.xml.sax.Attributes;
|
import org.xml.sax.SAXException;
|
import org.xml.sax.SAXParseException;
|
import org.xml.sax.XMLReader;
|
import org.xml.sax.InputSource;
|
import org.xml.sax.helpers.*;
|
|
/**
|
* Creates an API object from an XML file. The API object is the internal
|
* representation of an API.
|
* All methods in this class for populating an API object are static.
|
*
|
* See the file LICENSE.txt for copyright details.
|
* @author Matthew Doar, mdoar@pobox.com
|
*/
|
public class XMLToAPI {
|
|
/** The instance of the API object which is populated from the file. */
|
private static API api_ = null;
|
|
/** Default constructor. */
|
private XMLToAPI() {
|
}
|
|
/**
|
* Read the file where the XML representing the API is stored.
|
*
|
* @param filename The full name of the file containing the XML
|
* representing the API
|
* @param createGlobalComments If set, then store possible comments
|
* @param apiName The simple name of the API file. If -oldapidir and
|
* -newapidir are not used, then this is the same as
|
* the filename parameter
|
*/
|
public static API readFile(String filename, boolean createGlobalComments,
|
String apiName) {
|
// The instance of the API object which is populated from the file.
|
api_ = new API();
|
api_.name_ = apiName; // Checked later
|
try {
|
XMLReader parser = null;
|
DefaultHandler handler = new APIHandler(api_, createGlobalComments);
|
try {
|
parser = javax.xml.parsers.SAXParserFactory.newInstance().newSAXParser().getXMLReader();
|
} catch (SAXException saxe) {
|
System.out.println("SAXException: " + saxe);
|
saxe.printStackTrace();
|
System.exit(1);
|
} catch (ParserConfigurationException pce) {
|
System.out.println("ParserConfigurationException: " + pce);
|
pce.printStackTrace();
|
System.exit(1);
|
}
|
|
if (validateXML) {
|
parser.setFeature("https://xml.org/sax/features/namespaces", true);
|
parser.setFeature("https://xml.org/sax/features/validation", true);
|
parser.setFeature("https://apache.org/xml/features/validation/schema", true);
|
}
|
|
parser.setContentHandler(handler);
|
parser.setErrorHandler(handler);
|
parser.parse(new InputSource(new FileInputStream(new File(filename))));
|
} catch(org.xml.sax.SAXNotRecognizedException snre) {
|
System.out.println("SAX Parser does not recognize feature: " + snre);
|
snre.printStackTrace();
|
System.exit(1);
|
} catch(org.xml.sax.SAXNotSupportedException snse) {
|
System.out.println("SAX Parser feature is not supported: " + snse);
|
snse.printStackTrace();
|
System.exit(1);
|
} catch(org.xml.sax.SAXException saxe) {
|
System.out.println("SAX Exception parsing file '" + filename + "' : " + saxe);
|
saxe.printStackTrace();
|
System.exit(1);
|
} catch(java.io.IOException ioe) {
|
System.out.println("IOException parsing file '" + filename + "' : " + ioe);
|
ioe.printStackTrace();
|
System.exit(1);
|
}
|
|
// Add the inherited methods and fields to each class
|
addInheritedElements();
|
return api_;
|
} //readFile()
|
|
/**
|
* Add the inherited methods and fields to each class in turn.
|
*/
|
public static void addInheritedElements() {
|
Iterator iter = api_.packages_.iterator();
|
while (iter.hasNext()) {
|
PackageAPI pkg = (PackageAPI)(iter.next());
|
Iterator iter2 = pkg.classes_.iterator();
|
while (iter2.hasNext()) {
|
ClassAPI cls = (ClassAPI)(iter2.next());
|
// Look up any inherited classes or interfaces
|
if (cls.extends_ != null) {
|
ClassAPI parent = (ClassAPI)api_.classes_.get(cls.extends_);
|
if (parent != null)
|
addInheritedElements(cls, parent, cls.extends_);
|
}
|
if (cls.implements_.size() != 0) {
|
Iterator iter3 = cls.implements_.iterator();
|
while (iter3.hasNext()) {
|
String implName = (String)(iter3.next());
|
ClassAPI parent = (ClassAPI)api_.classes_.get(implName);
|
if (parent != null)
|
addInheritedElements(cls, parent, implName);
|
}
|
}
|
} //while (iter2.hasNext())
|
} //while (iter.hasNext())
|
}
|
|
/**
|
* Add all the inherited methods and fields in the second class to
|
* the first class, marking them as inherited from the second class.
|
* Do not add a method or a field if it is already defined locally.
|
*
|
* Only elements at the specified visibility level or
|
* higher appear in the XML file. All that remains to be tested for
|
* a private element, which is never inherited.
|
*
|
* If the parent class inherits any classes or interfaces, call this
|
* method recursively with those parents.
|
*/
|
public static void addInheritedElements(ClassAPI child, ClassAPI parent,
|
String fqParentName) {
|
if (parent.methods_.size() != 0) {
|
Iterator iter = parent.methods_.iterator();
|
while (iter.hasNext()) {
|
MethodAPI m = (MethodAPI)(iter.next());
|
// See if it the method is overridden locally
|
boolean overridden = false;
|
Iterator iter2 = child.methods_.iterator();
|
while (iter2.hasNext()) {
|
MethodAPI localM = (MethodAPI)(iter2.next());
|
if (localM.name_.compareTo(m.name_) == 0 &&
|
localM.getSignature().compareTo(m.getSignature()) == 0)
|
overridden = true;
|
}
|
if (!overridden && m.inheritedFrom_ == null &&
|
m.modifiers_.visibility != null &&
|
m.modifiers_.visibility.compareTo("private") != 0) {
|
MethodAPI m2 = new MethodAPI(m);
|
m2.inheritedFrom_ = fqParentName;
|
child.methods_.add(m2);
|
}
|
}
|
}
|
if (parent.fields_.size() != 0) {
|
Iterator iter = parent.fields_.iterator();
|
while (iter.hasNext()) {
|
FieldAPI f = (FieldAPI)(iter.next());
|
if (child.fields_.indexOf(f) == -1 &&
|
f.inheritedFrom_ == null &&
|
f.modifiers_.visibility != null &&
|
f.modifiers_.visibility.compareTo("private") != 0) {
|
FieldAPI f2 = new FieldAPI(f);
|
f2.inheritedFrom_ = fqParentName;
|
child.fields_.add(f2);
|
}
|
}
|
}
|
|
// Look up any inherited classes or interfaces
|
if (parent.extends_ != null) {
|
ClassAPI parent2 = (ClassAPI)api_.classes_.get(parent.extends_);
|
if (parent2 != null)
|
addInheritedElements(child, parent2, parent.extends_);
|
}
|
if (parent.implements_.size() != 0) {
|
Iterator iter3 = parent.implements_.iterator();
|
while (iter3.hasNext()) {
|
String implName = (String)(iter3.next());
|
ClassAPI parent2 = (ClassAPI)api_.classes_.get(implName);
|
if (parent2 != null)
|
addInheritedElements(child, parent2, implName);
|
}
|
}
|
}
|
|
//
|
// Methods to add data to an API object. Called by the XML parser.
|
//
|
|
/**
|
* Set the name of the API object.
|
*
|
* @param name The name of the package.
|
*/
|
public static void nameAPI(String name) {
|
if (name == null) {
|
System.out.println("Warning: no API identifier found in the XML file '" + api_.name_ + "'");
|
String filename = api_.name_.substring(0, api_.name_.lastIndexOf(".xml"));
|
// System.out.println(" api level:" + filename);
|
System.out.println("Using '" + filename + "' as the API identifier.");
|
api_.name_ = filename;
|
// System.exit(3);
|
return;
|
}
|
// Check the given name against the filename currently stored in
|
// the name_ field
|
String filename2 = name.replace(' ','_');
|
filename2 += ".xml";
|
if (filename2.compareTo(api_.name_) != 0) {
|
System.out.println("Warning: API identifier in the XML file (" +
|
name + ") differs from the name of the file '" +
|
api_.name_ + "'");
|
}
|
api_.name_ = name;
|
}
|
|
/**
|
* Create a new package and add it to the API. Called by the XML parser.
|
*
|
* @param name The name of the package.
|
*/
|
public static void addPackage(String name) {
|
api_.currPkg_ = new PackageAPI(name);
|
api_.packages_.add(api_.currPkg_);
|
}
|
|
/**
|
* Create a new class and add it to the current package. Called by the XML parser.
|
*
|
* @param name The name of the class.
|
* @param parent The name of the parent class, null if no class is extended.
|
* @param modifiers Modifiers for this class.
|
*/
|
public static void addClass(String name, String parent,
|
boolean isAbstract,
|
Modifiers modifiers) {
|
api_.currClass_ = new ClassAPI(name, parent, false, isAbstract, modifiers);
|
api_.currPkg_.classes_.add(api_.currClass_);
|
String fqName = api_.currPkg_.name_ + "." + name;
|
ClassAPI caOld = (ClassAPI)api_.classes_.put(fqName, api_.currClass_);
|
if (caOld != null) {
|
System.out.println("Warning: duplicate class : " + fqName + " found. Using the first instance only.");
|
}
|
}
|
|
/**
|
* Add an new interface and add it to the current package. Called by the
|
* XML parser.
|
*
|
* @param name The name of the interface.
|
* @param parent The name of the parent interface, null if no
|
* interface is extended.
|
*/
|
public static void addInterface(String name, String parent,
|
boolean isAbstract,
|
Modifiers modifiers) {
|
api_.currClass_ = new ClassAPI(name, parent, true, isAbstract, modifiers);
|
api_.currPkg_.classes_.add(api_.currClass_);
|
}
|
|
/**
|
* Add an inherited interface to the current class. Called by the XML
|
* parser.
|
*
|
* @param name The name of the inherited interface.
|
*/
|
public static void addImplements(String name) {
|
api_.currClass_.implements_.add(name);
|
}
|
|
/**
|
* Add a constructor to the current class. Called by the XML parser.
|
*
|
* @param name The name of the constructor (optional).
|
* @param type The type of the constructor.
|
* @param modifiers Modifiers for this constructor.
|
*/
|
public static void addCtor(String name, String type, Modifiers modifiers) {
|
String t = type;
|
if (t == null)
|
t = "void";
|
api_.currCtor_ = new ConstructorAPI(name, t, modifiers);
|
api_.currClass_.ctors_.add(api_.currCtor_);
|
}
|
|
/**
|
* Add a method to the current class. Called by the XML parser.
|
*
|
* @param name The name of the method.
|
* @param returnType The return type of the method, null if it is void.
|
* @param modifiers Modifiers for this method.
|
*/
|
public static void addMethod(String name, String returnType,
|
boolean isAbstract, boolean isNative,
|
boolean isSynchronized, Modifiers modifiers) {
|
String rt = returnType;
|
if (rt == null)
|
rt = "void";
|
api_.currMethod_ = new MethodAPI(name, rt, isAbstract, isNative,
|
isSynchronized, modifiers);
|
api_.currClass_.methods_.add(api_.currMethod_);
|
}
|
|
/**
|
* Add a field to the current class. Called by the XML parser.
|
*
|
* @param name The name of the field.
|
* @param type The type of the field, null if it is void.
|
* @param modifiers Modifiers for this field.
|
*/
|
public static void addField(String name, String type, boolean isTransient,
|
boolean isVolatile, String value, Modifiers modifiers) {
|
String t = type;
|
if (t == null)
|
t = "void";
|
api_.currField_ = new FieldAPI(name, t, isTransient, isVolatile, value, modifiers);
|
api_.currClass_.fields_.add(api_.currField_);
|
}
|
|
/**
|
* Add a parameter to the current method or constructor. Called by the XML parser.
|
* Constuctors have their type (signature) in an attribute, since it
|
* is often shorter and makes parsing a little easier.
|
*
|
* @param name The name of the parameter.
|
* @param type The type of the parameter, null if it is void.
|
* @param isConstructor Whether the given parameter is for a constructor
|
*/
|
public static void addParam(String name, String type, boolean isConstructor) {
|
String t = type;
|
if (t == null)
|
t = "void";
|
ParamAPI paramAPI = new ParamAPI(name, t);
|
if (isConstructor) {
|
api_.currCtor_.params_.add(paramAPI);
|
} else {
|
api_.currMethod_.params_.add(paramAPI);
|
}
|
}
|
|
/**
|
* Add an exception to the current method or constructor.
|
* Called by the XML parser.
|
*
|
* @param name The name of the parameter.
|
* @param type The type of the parameter.
|
* May be null in JDiff1.0.8 and earlier versions.
|
* @param currElement Name of the current element.
|
*/
|
public static void addException(String name, String type, String currElement) {
|
String exceptionId = type;
|
if (type == null || !showExceptionTypes)
|
exceptionId = name;
|
if (currElement.compareTo("method") == 0) {
|
if (api_.currMethod_.exceptions_.compareTo("no exceptions") == 0)
|
api_.currMethod_.exceptions_ = exceptionId;
|
else
|
api_.currMethod_.exceptions_ += ", " + exceptionId;
|
} else {
|
if (api_.currCtor_.exceptions_.compareTo("no exceptions") == 0)
|
api_.currCtor_.exceptions_ = exceptionId;
|
else
|
api_.currCtor_.exceptions_ += ", " + exceptionId;
|
}
|
}
|
|
/**
|
* If set, validate the XML which represents an API. By default, this is
|
* not set for reasons of efficiency, and also because if JDiff generated
|
* the XML, it should not need validating.
|
*/
|
public static boolean validateXML = false;
|
|
/**
|
* If set, then store and display the whole qualified name of exceptions.
|
* If not set, then store and display just the name of the exception,
|
* which is shorter, but may not detect when an exception changes class,
|
* but retains the same name.
|
*/
|
private static boolean showExceptionTypes = true;
|
}
|