blob: 64e2fa1f9237031c67c413cca6f7420564180872 [file] [log] [blame]
/*******************************************************************************
* 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");
}
}