| /******************************************************************************* |
| * Copyright (c) 2005 IBM Corporation and others. |
| * All rights reserved. This program and the accompanying materials |
| * are made available under the terms of the Eclipse Public License v1.0 |
| * which accompanies this distribution, and is available at |
| * http://www.eclipse.org/legal/epl-v10.html |
| * |
| * Contributors: |
| * IBM Corporation - initial API and implementation |
| *******************************************************************************/ |
| package org.eclipse.wtp.releng.tools.component.adopters; |
| |
| import java.io.ByteArrayInputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.IOException; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashMap; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.StringTokenizer; |
| import javax.xml.parsers.ParserConfigurationException; |
| import org.eclipse.core.runtime.IPlatformRunnable; |
| import org.eclipse.core.runtime.Platform; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.wtp.releng.tools.component.ILocation; |
| import org.eclipse.wtp.releng.tools.component.ILocationVisitor; |
| import org.eclipse.wtp.releng.tools.component.api.API2ComponentAPI; |
| import org.eclipse.wtp.releng.tools.component.api.ClassAPI; |
| import org.eclipse.wtp.releng.tools.component.api.ComponentAPI; |
| import org.eclipse.wtp.releng.tools.component.api.FieldAPI; |
| import org.eclipse.wtp.releng.tools.component.api.MethodAPI; |
| import org.eclipse.wtp.releng.tools.component.api.PackageAPI; |
| import org.eclipse.wtp.releng.tools.component.internal.Location; |
| import org.eclipse.wtp.releng.tools.component.util.CommandOptionParser; |
| import org.eclipse.wtp.releng.tools.component.xsl.XSLUtil; |
| import org.xml.sax.SAXException; |
| |
| public class APIRefCompatibilityScanner implements IPlatformRunnable |
| { |
| private String use; |
| private String outputDir; |
| |
| public String getOutputDir() |
| { |
| return outputDir; |
| } |
| |
| public void setOutputDir(String outputDir) |
| { |
| this.outputDir = addTrailingSeperator(outputDir); |
| } |
| |
| public String getUse() |
| { |
| return use; |
| } |
| |
| public void setUse(String use) |
| { |
| this.use = use; |
| } |
| |
| private Map pluginId2APIInfo = new HashMap(); |
| private Map pkg2APIInfo = new HashMap(); |
| private BreakageReport breakageReport = new BreakageReport(); |
| |
| public void execute() |
| { |
| ILocation apiInfoLoc = Location.createLocation(new File(outputDir + "_tmp_componentapiref")); |
| apiInfoLoc.accept(new ILocationVisitor() |
| { |
| public boolean accept(ILocation location) |
| { |
| if (location.getName().endsWith("api-info.xml")) |
| { |
| ComponentAPI compAPI = new ComponentAPI(); |
| compAPI.setLocation(location); |
| try |
| { |
| compAPI.load(); |
| pluginId2APIInfo.put(compAPI.getName(), location); |
| for (Iterator it = compAPI.getPackageAPIs().iterator(); it.hasNext();) |
| { |
| String pkgName = ((PackageAPI)it.next()).getName(); |
| List locations = (List)pkg2APIInfo.get(pkgName); |
| if (locations == null) |
| { |
| locations = new ArrayList(); |
| pkg2APIInfo.put(pkgName, locations); |
| } |
| locations.add(location); |
| } |
| } |
| catch (IOException ioe) |
| { |
| throw new RuntimeException(ioe); |
| } |
| } |
| return true; |
| } |
| }); |
| ILocation useLoc = Location.createLocation(new File(use)); |
| useLoc.accept(new ILocationVisitor() |
| { |
| public boolean accept(ILocation location) |
| { |
| if (location.getName().endsWith(".xml")) |
| { |
| try |
| { |
| References refs = new References(); |
| refs.load(location.getInputStream()); |
| for (Iterator it = refs.getPluginRefs().iterator(); it.hasNext();) |
| { |
| PluginRef pluginRef = (PluginRef)it.next(); |
| String pluginId = pluginRef.getId(); |
| ILocation loc = (ILocation)pluginId2APIInfo.get(pluginId); |
| if (loc != null) |
| { |
| ComponentAPI compAPI = new ComponentAPI(); |
| compAPI.setLocation(loc); |
| compAPI.load(); |
| for (Iterator it2 = pluginRef.getClassRefs().iterator(); it2.hasNext();) |
| { |
| ClassRef classRef = (ClassRef)it2.next(); |
| String className = classRef.getName(); |
| if (className.endsWith("[]")) |
| className = className.substring(0, className.length() - 2); |
| int i = className.lastIndexOf('.'); |
| String pkgName = (i != -1) ? className.substring(0, i) : ""; |
| String localName = (i != -1) ? className.substring(i + 1) : className; |
| PackageAPI pkgAPI = compAPI.getPackageAPI(pkgName); |
| if (pkgAPI != null) |
| { |
| ClassAPI classAPI = pkgAPI.getClassAPI(localName); |
| if (classAPI != null) |
| { |
| for (Iterator it3 = classRef.getMethodRefs().iterator(); it3.hasNext();) |
| { |
| MethodRef methodRef = (MethodRef)it3.next(); |
| String name = methodRef.getName(); |
| String desc = methodRef.getDescriptor(); |
| if (hasMethod(classAPI, name, desc)) |
| { |
| classRef.removeMethodRef(name, desc); |
| } |
| } |
| for (Iterator it3 = classRef.getFieldRefs().iterator(); it3.hasNext();) |
| { |
| FieldRef fieldRef = (FieldRef)it3.next(); |
| String name = fieldRef.getName(); |
| String desc = fieldRef.getDescriptor(); |
| if (hasField(classAPI, name)) |
| { |
| classRef.removeFieldRef(name, desc); |
| } |
| } |
| if (classRef.getMethodRefs().isEmpty() && classRef.getFieldRefs().isEmpty()) |
| { |
| pluginRef.removeClassRef(classRef.getName()); |
| } |
| } |
| } |
| } |
| if (pluginRef.getClassRefs().isEmpty()) |
| { |
| refs.removePluginRef(pluginId); |
| } |
| } |
| } |
| if (!refs.getPluginRefs().isEmpty()) |
| { |
| breakageReport.addRefs(refs); |
| } |
| } |
| catch (ParserConfigurationException pce) |
| { |
| throw new RuntimeException(pce); |
| } |
| catch (SAXException saxe) |
| { |
| throw new RuntimeException(saxe); |
| } |
| catch (IOException ioe) |
| { |
| throw new RuntimeException(ioe); |
| } |
| } |
| return true; |
| } |
| }); |
| try |
| { |
| save(); |
| genHTML(); |
| } |
| catch (IOException ioe) |
| { |
| throw new RuntimeException(ioe); |
| } |
| } |
| |
| private boolean hasMethod(ClassAPI classAPI, String name, String desc) |
| { |
| if (classAPI != null && name != null && desc != null) |
| { |
| if (classAPI.getMethodAPI(name, desc) != null) |
| { |
| return true; |
| } |
| else |
| { |
| String superClassName = classAPI.getSuperClass(); |
| if (hasMethod(superClassName, name, desc)) |
| { |
| return true; |
| } |
| else |
| { |
| for (Iterator it = classAPI.getInterfaces().iterator(); it.hasNext();) |
| { |
| if (hasMethod(it.next().toString(), name, desc)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private boolean hasMethod(String className, String name, String desc) |
| { |
| int i = className.lastIndexOf('.'); |
| String pkgName = (i != -1) ? className.substring(0, i) : ""; |
| String localName = (i != -1) ? className.substring(i + 1) : className; |
| List locations = (List)pkg2APIInfo.get(pkgName); |
| if (locations != null) |
| { |
| for (Iterator it = locations.iterator(); it.hasNext();) |
| { |
| ILocation location = (ILocation)it.next(); |
| // ILocation location = (ILocation)pkg2APIInfo.get(pkgName); |
| // if (location != null) |
| // { |
| try |
| { |
| ComponentAPI compAPI = getComponentAPI(location); |
| if (compAPI != null) |
| { |
| PackageAPI pkgAPI = compAPI.getPackageAPI(pkgName); |
| if (pkgAPI != null) |
| { |
| ClassAPI superClassAPI = pkgAPI.getClassAPI(localName); |
| if (superClassAPI != null) |
| { |
| return hasMethod(superClassAPI, name, desc); |
| } |
| } |
| } |
| } |
| catch (IOException ioe) |
| { |
| ioe.printStackTrace(); |
| } |
| } |
| } |
| else |
| { |
| // use reflection, needed for java.* packages |
| ClassAPI superClassAPI = loadClass(className); |
| if (superClassAPI != null) |
| { |
| return hasMethod(superClassAPI, name, desc); |
| } |
| } |
| return false; |
| } |
| |
| private boolean hasField(ClassAPI classAPI, String name) |
| { |
| if (classAPI != null && name != null) |
| { |
| if (classAPI.getFieldAPI(name) != null) |
| { |
| return true; |
| } |
| else |
| { |
| String superClassName = classAPI.getSuperClass(); |
| if (hasField(superClassName, name)) |
| { |
| return true; |
| } |
| else |
| { |
| for (Iterator it = classAPI.getInterfaces().iterator(); it.hasNext();) |
| { |
| if (hasField(it.next().toString(), name)) |
| { |
| return true; |
| } |
| } |
| return false; |
| } |
| } |
| } |
| return false; |
| } |
| |
| private boolean hasField(String className, String name) |
| { |
| int i = className.lastIndexOf('.'); |
| String pkgName = (i != -1) ? className.substring(0, i) : ""; |
| String localName = (i != -1) ? className.substring(i + 1) : className; |
| List locations = (List)pkg2APIInfo.get(pkgName); |
| if (locations != null) |
| { |
| for (Iterator it = locations.iterator(); it.hasNext();) |
| { |
| ILocation location = (ILocation)it.next(); |
| // ILocation location = (ILocation)pkg2APIInfo.get(pkgName); |
| // if (location != null) |
| // { |
| try |
| { |
| ComponentAPI compAPI = getComponentAPI(location); |
| if (compAPI != null) |
| { |
| PackageAPI pkgAPI = compAPI.getPackageAPI(pkgName); |
| if (pkgAPI != null) |
| { |
| ClassAPI superClassAPI = pkgAPI.getClassAPI(localName); |
| if (superClassAPI != null) |
| { |
| return hasField(superClassAPI, name); |
| } |
| } |
| } |
| } |
| catch (IOException ioe) |
| { |
| ioe.printStackTrace(); |
| } |
| } |
| } |
| else |
| { |
| // use reflection, needed for java.* packages |
| ClassAPI superClassAPI = loadClass(className); |
| if (superClassAPI != null) |
| { |
| return hasField(superClassAPI, name); |
| } |
| } |
| return false; |
| } |
| |
| private ClassAPI loadClass(String className) |
| { |
| // Use reflection to load other packages |
| try |
| { |
| Class clazz = ClassLoader.getSystemClassLoader().loadClass(className); |
| ClassAPI classAPI = new ClassAPI(); |
| classAPI.setName(className); |
| if (className.equals("java.lang.Object")) |
| classAPI.setSuperClass(""); |
| else |
| classAPI.setSuperClass("java.lang.Object"); |
| // TODO: We need to add protected methods as well |
| Method[] publicMethods = clazz.getMethods(); |
| Field[] publicFields = clazz.getFields(); |
| for (int j = 0; j < publicMethods.length; j++) |
| { |
| MethodAPI methodAPI = new MethodAPI(); |
| methodAPI.setName(publicMethods[j].getName()); |
| StringBuffer descriptor = new StringBuffer(); |
| descriptor.append('('); |
| Class[] params = publicMethods[j].getParameterTypes(); |
| for (int k = 0; k < params.length; k++) |
| { |
| String paramName = params[k].getName().replace('.', '/'); |
| if (paramName.charAt(0) == '[') |
| descriptor.append(paramName); |
| else |
| descriptor.append(Signature.createTypeSignature(paramName, true)); |
| //descriptor.append(params[k].toString()); |
| } |
| descriptor.append(')'); |
| Class returnType = publicMethods[j].getReturnType(); |
| if (returnType != null) |
| { |
| String returnTypeName = returnType.getName().replace('.', '/'); |
| if (returnTypeName.charAt(0) == '[') |
| descriptor.append(returnTypeName); |
| else |
| descriptor.append(Signature.createTypeSignature(returnTypeName, true)); |
| //descriptor.append(returnType.toString()); |
| } |
| else |
| { |
| descriptor.append('V'); |
| } |
| methodAPI.setDescriptor(descriptor.toString()); |
| classAPI.addMethodAPI(methodAPI); |
| } |
| for (int j = 0; j < publicFields.length; j++) |
| { |
| FieldAPI fieldAPI = new FieldAPI(); |
| fieldAPI.setName(publicFields[j].getName()); |
| classAPI.addFieldAPI(fieldAPI); |
| } |
| return classAPI; |
| } |
| catch (ClassNotFoundException cnfe) |
| { |
| return null; |
| // do nothing |
| } |
| } |
| |
| private int cacheSize = 20; |
| private List cachedIds = new ArrayList(cacheSize); |
| private List cachedCompAPIs = new ArrayList(cacheSize); |
| |
| private ComponentAPI getComponentAPI(ILocation location) throws IOException |
| { |
| int index = cachedIds.indexOf(location); |
| if (index != -1) |
| { |
| ComponentAPI compAPI = (ComponentAPI)cachedCompAPIs.get(index); |
| if (index != 0) |
| { |
| cachedIds.remove(index); |
| cachedCompAPIs.remove(index); |
| cachedIds.add(0, location); |
| cachedCompAPIs.add(0, compAPI); |
| } |
| return compAPI; |
| } |
| ComponentAPI compAPI = new ComponentAPI(); |
| compAPI.setLocation(location); |
| compAPI.load(); |
| if (cachedCompAPIs.size() == cacheSize) |
| { |
| cachedIds.remove(cacheSize - 1); |
| cachedCompAPIs.remove(cacheSize - 1); |
| } |
| cachedIds.add(0, location); |
| cachedCompAPIs.add(0, compAPI); |
| return compAPI; |
| } |
| |
| public void save() throws IOException |
| { |
| File file = new File(outputDir + "api-ref-compatibility.xml"); |
| file.getParentFile().mkdirs(); |
| breakageReport.save(new FileOutputStream(file)); |
| } |
| |
| private void genHTML() |
| { |
| try |
| { |
| XSLUtil.transform(Platform.getBundle("org.eclipse.wtp.releng.tools.component.core").getResource("org/eclipse/wtp/releng/tools/component/xsl/api-ref-compatibility.xsl").openStream(), new ByteArrayInputStream(breakageReport.toString().getBytes()), new FileOutputStream(new File(outputDir + "api-ref-compatibility.html")), outputDir); |
| } |
| catch (Throwable e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| protected static String addTrailingSeperator(String s) |
| { |
| if (s != null && !s.endsWith("/") && !s.endsWith("\\")) |
| { |
| StringBuffer sb = new StringBuffer(s); |
| sb.append('/'); |
| return sb.toString(); |
| } |
| else |
| { |
| return s; |
| } |
| } |
| |
| public Object run(Object arguments) |
| { |
| String src = System.getProperty("src"); |
| String use = System.getProperty("use"); |
| String outputDir = System.getProperty("outputDir"); |
| List args = new ArrayList(); |
| if (src != null) |
| { |
| args.add("-src"); |
| args.addAll(tokenize(src)); |
| } |
| if (use != null) |
| { |
| args.add("-use"); |
| args.addAll(tokenize(use)); |
| } |
| args.add("-outputDir"); |
| args.add(outputDir); |
| try |
| { |
| main((String[])args.toArray(new String[0])); |
| } |
| catch (Throwable t) |
| { |
| t.printStackTrace(); |
| } |
| return IPlatformRunnable.EXIT_OK; |
| } |
| |
| private List tokenize(String s) |
| { |
| StringTokenizer st = new StringTokenizer(s, ","); |
| List tokens = new ArrayList(st.countTokens()); |
| while(st.hasMoreTokens()) |
| tokens.add(st.nextToken()); |
| return tokens; |
| } |
| |
| public static void main(String[] args) |
| { |
| CommandOptionParser optionParser = new CommandOptionParser(args); |
| Map options = optionParser.getOptions(); |
| Collection src = (Collection)options.get("src"); |
| Collection use = (Collection)options.get("use"); |
| Collection outputDir = (Collection)options.get("outputDir"); |
| if (src == null || use == null || outputDir == null || src.isEmpty() || use.isEmpty() || outputDir.isEmpty()) |
| { |
| printUsage(); |
| System.exit(-1); |
| } |
| String outputDirString = addTrailingSeperator((String)outputDir.iterator().next()); |
| String componentxmlref = outputDirString + "_tmp_componentxmlref"; |
| String componentapiref = outputDirString + "_tmp_componentapiref"; |
| |
| Plugin2API plugin2API = new Plugin2API(); |
| plugin2API.setSrc(src); |
| plugin2API.setOutputDir(componentxmlref); |
| plugin2API.execute(); |
| |
| API2ComponentAPI api2CompAPI = new API2ComponentAPI(); |
| api2CompAPI.setApi(componentxmlref); |
| api2CompAPI.setOutputDir(componentapiref); |
| api2CompAPI.setIncludeInnerClass(true); |
| api2CompAPI.setIncludeInterfaces(true); |
| for (Iterator it = src.iterator(); it.hasNext();) |
| { |
| api2CompAPI.setSrc((String)it.next()); |
| api2CompAPI.execute(); |
| } |
| |
| APIRefCompatibilityScanner scanner = new APIRefCompatibilityScanner(); |
| scanner.setUse((String)use.iterator().next()); |
| scanner.setOutputDir(outputDirString); |
| scanner.execute(); |
| } |
| |
| private static void printUsage() |
| { |
| System.out.println("Usage: java org.eclipse.wtp.releng.tools.component.adopters.APIRefCompatibilityScanner -src <src> -use <use> -outputDir <outputDir> [-options]"); |
| System.out.println(""); |
| System.out.println("\t-src\t\t<src>\t\tlocation of your Eclipse-based product"); |
| System.out.println("\t-use\t\t<use>\t\tlocation of adopters' API usage data"); |
| System.out.println("\t-outputDir\t<outputDir>\toutput directory"); |
| } |
| } |