package net.wuffies.japi; import java.lang.reflect.*; import java.util.zip.*; import java.io.IOException; import java.util.Enumeration; /** * Process a Java API and emit a machine-readable description of the API, * suitable for comparison to other APIs. Specifically, the perl script * japicompat.pl can test APIs for source/binary compatibility with each other. * * @author Stuart Ballard <sballard@wuffies.net> */ public class Japize { /** * Parse the program's arguments and perform the main processing. */ public static void main(String[] args) throws IllegalAccessException, IOException, ClassNotFoundException { if (args.length == 0) { System.err.println("Usage: Japize [-c classname ... | -f zipfile package ...] >output.japi"); } else if ("-c".equals(args[0])) { for (int i = 1; i < args.length; i++) { japizeClass(args[i]); } } else if ("-f".equals(args[0]) && args.length > 2) { for (int i = 2; i < args.length; i++) { processZip(args[1], args[i]); System.exit(0); } } else { System.err.println("Usage: Japize [-c classname ... | -f zipfile package ...]"); } } public static boolean japizeClass(String cname) throws NoSuchMethodException, IllegalAccessException, ClassNotFoundException { Class c = Class.forName(cname); int mods = c.getModifiers(); if (!Modifier.isPublic(mods) && !Modifier.isProtected(mods)) return false; String entry = c.getName() + "#"; String type = c.isInterface() ? "interface" : "class"; Class sup = c; int smods = mods; while (sup.getSuperclass() != null) { sup = sup.getSuperclass(); smods = sup.getModifiers(); if (!Modifier.isPublic(smods) && !Modifier.isProtected(smods)) { System.err.print("^"); } else { type += ":" + sup.getName(); } } Class[] ifaces = c.getInterfaces(); for (int i = 0; i < ifaces.length; i++) { int mds = ifaces[i].getModifiers(); if (Modifier.isPublic(mds) || Modifier.isProtected(mds)) { type += "*" + ifaces[i].getName(); } } printEntry(entry, type, c.getModifiers()); Field[] fields = c.getFields(); Method[] methods = c.getMethods(); Constructor[] constrs = c.getConstructors(); for (int i = 0; i < fields.length; i++) { int dmods = fields[i].getDeclaringClass().getModifiers(); if (!Modifier.isPublic(dmods) && !Modifier.isProtected(dmods)) { System.err.print(">"); continue; } mods = fields[i].getModifiers(); type = fields[i].getType().getName(); if (Modifier.isFinal(mods) && Modifier.isStatic(mods) && Modifier.isPublic(mods) && (fields[i].getType().isPrimitive() || type.equals("java.lang.String"))) { type += ":" + fields[i].get(null); } printEntry(c.getName() + "#" + fields[i].getName(), type, mods); } for (int i = 0; i < constrs.length; i++) { entry = c.getName() + "#("; Class[] params = constrs[i].getParameterTypes(); String comma = ""; for (int j = 0; j < params.length; j++) { entry += comma + params[j].getName(); comma = ","; } entry += ")"; type = "constructor"; Class[] excps = constrs[i].getExceptionTypes(); for (int j = 0; j < excps.length; j++) { type += "*" + excps[j].getName(); } printEntry(entry, type, constrs[i].getModifiers()); } for (int i = 0; i < methods.length; i++) { int dmods = methods[i].getDeclaringClass().getModifiers(); if (!Modifier.isPublic(dmods) && !Modifier.isProtected(dmods)) { System.err.print("}"); continue; } try { if (!methods[i].equals(c.getMethod(methods[i].getName(), methods[i].getParameterTypes()))) { System.err.print("!"); continue; } } catch (NoSuchMethodException e) { System.err.print("M"); } entry = c.getName() + "#" + methods[i].getName() + "("; Class[] params = methods[i].getParameterTypes(); String comma = ""; for (int j = 0; j < params.length; j++) { entry += comma + params[j].getName(); comma = ","; } entry += ")"; type = methods[i].getReturnType().getName(); Class[] excps = methods[i].getExceptionTypes(); for (int j = 0; j < excps.length; j++) { type += "*" + excps[j].getName(); } printEntry(entry, type, methods[i].getModifiers()); } return true; } public static void printEntry(String thing, String type, int mods) { if (!Modifier.isPublic(mods) && !Modifier.isProtected(mods)) return; System.out.print(thing + " "); System.out.print(Modifier.isPublic(mods) ? "public " : "protected "); System.out.print(Modifier.isAbstract(mods) ? "abstract " : "concrete "); System.out.print(Modifier.isStatic(mods) ? "static " : "instance "); System.out.print(Modifier.isFinal(mods) ? "final " : "nonfinal "); System.out.println(type); } public static void processZip(String fname, String pkg) throws NoSuchMethodException, IllegalAccessException, IOException, ClassNotFoundException { ZipFile z = new ZipFile(fname); Enumeration ents = z.entries(); String pkgDir = pkg.replace('.', '/') + "/"; while (ents.hasMoreElements()) { String ze = ((ZipEntry)ents.nextElement()).getName(); if (ze.startsWith(pkgDir) && ze.endsWith(".class")) { if (japizeClass(ze.substring(0, ze.length() - 6).replace('/', '.'))) { System.err.print("*"); } else { System.err.print("-"); } } else { System.err.print("_"); } System.err.flush(); } System.err.println("\nDone scanning for " + pkg); } }