blob: f48617c1ab9a612d1b2e2b07c2b2d8174a3a9629 [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.api.violation;
import java.io.ByteArrayInputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
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 org.eclipse.core.runtime.Platform;
import org.eclipse.jdt.core.Signature;
import org.eclipse.jdt.core.util.IModifierConstants;
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.ComponentXMLVisitor;
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.images.ImagesUtil;
import org.eclipse.wtp.releng.tools.component.internal.ComponentEntry;
import org.eclipse.wtp.releng.tools.component.internal.ComponentSummary;
import org.eclipse.wtp.releng.tools.component.internal.Location;
import org.eclipse.wtp.releng.tools.component.model.ComponentXML;
import org.eclipse.wtp.releng.tools.component.model.Package;
import org.eclipse.wtp.releng.tools.component.model.Plugin;
import org.eclipse.wtp.releng.tools.component.model.Type;
import org.eclipse.wtp.releng.tools.component.util.CommandOptionParser;
import org.eclipse.wtp.releng.tools.component.xsl.XSLUtil;
public class NonAPIDependencyScanner implements ILocationVisitor
{
private Collection src;
private Collection api;
private String outputDir;
private Collection includes;
private Collection excludes;
private Collection refapi;
private boolean skipAPIGen;
public Collection getApi()
{
return api;
}
public void setApi(Collection api)
{
this.api = api;
}
public String getOutputDir()
{
return outputDir;
}
public void setOutputDir(String outputDir)
{
this.outputDir = addTrailingSeperator(outputDir);
}
public Collection getIncludes()
{
return includes;
}
public void setIncludes(Collection includes)
{
this.includes = includes;
}
public Collection getExcludes()
{
return excludes;
}
public void setExcludes(Collection excludes)
{
this.excludes = excludes;
}
public Collection getRefapi()
{
return refapi;
}
public void setRefapi(Collection refapi)
{
this.refapi = refapi;
}
public Collection getSrc()
{
return src;
}
public void setSrc(Collection src)
{
this.src = src;
}
public boolean isSkipAPIGen()
{
return skipAPIGen;
}
public void setSkipAPIGen(boolean skipAPIGen)
{
this.skipAPIGen = skipAPIGen;
}
private API2ComponentAPI api2CompXML;
private ComponentSummary summary;
public void execute()
{
// Collect component.xml files
cacheCompXML(api);
cacheCompXML(refapi);
// Generate api-info.xml
api2CompXML = new API2ComponentAPI();
api2CompXML.setApi(api);
api2CompXML.setSrc(src);
api2CompXML.setOutputDir(outputDir);
api2CompXML.setReadInterface(true);
api2CompXML.setSkipAPIGen(skipAPIGen);
api2CompXML.execute();
// Generate non-API dependency report
summary = new ComponentSummary();
ILocation out = Location.createLocation(new File(outputDir));
out.accept(this);
// Generate HTML report
try
{
ImagesUtil.copyAll(outputDir);
XSLUtil.transform
(
ClassLoader.getSystemResourceAsStream("org/eclipse/wtp/releng/tools/component/xsl/component-api-violation.xsl"),
new ByteArrayInputStream(summary.toString().getBytes()),
new FileOutputStream(new File(outputDir + "component-api-violation-all.html")),
outputDir
);
}
catch (Throwable e)
{
try
{
XSLUtil.transform
(
Platform.getBundle("org.eclipse.wtp.releng.tools.component.core").getResource("org/eclipse/wtp/releng/tools/component/xsl/component-api-violation.xsl").openStream(),
new ByteArrayInputStream(summary.toString().getBytes()),
new FileOutputStream(new File(outputDir + "component-api-violation-all.html")),
outputDir
);
}
catch (Throwable e2)
{
e2.printStackTrace();
}
}
}
private Map pluginId2CompXML = new HashMap();
private void cacheCompXML(Collection locations)
{
for (Iterator i = locations.iterator(); i.hasNext();)
{
ILocation apiLocation = Location.createLocation(new File((String)i.next()));
ComponentXMLVisitor compXMLVisitor = new ComponentXMLVisitor();
apiLocation.accept(compXMLVisitor);
for (Iterator it = compXMLVisitor.getCompXMLs().iterator(); it.hasNext();)
{
ComponentXML compXML = (ComponentXML)it.next();
for (Iterator it2 = compXML.getPlugins().iterator(); it2.hasNext();)
{
pluginId2CompXML.put(((Plugin)it2.next()).getId(), compXML);
}
}
}
}
public boolean accept(ILocation location)
{
if (location.getName().equals("api-info.xml"))
{
try
{
ComponentAPI compAPI = new ComponentAPI();
compAPI.setLocation(location);
compAPI.load();
ComponentAPIViolation violation = getViolations(compAPI);
String compName = compAPI.getName();
StringBuffer sb = new StringBuffer(outputDir);
sb.append(compName);
sb.append("/component-api-violation.xml");
File file = new File(sb.toString());
file.getParentFile().mkdirs();
FileOutputStream fos = new FileOutputStream(file);
String content = violation.toString();
fos.write(content.getBytes());
fos.close();
ComponentEntry entry = new ComponentEntry();
entry.setCompName(compName);
entry.setRef(sb.toString());
summary.add(entry);
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
return true;
}
private ComponentAPIViolation getViolations(ComponentAPI compAPI)
{
String compName = compAPI.getName();
ComponentAPIViolation v = new ComponentAPIViolation();
v.setName(compName);
for (Iterator it = compAPI.getPackageAPIs().iterator(); it.hasNext();)
v.addAllViolations(genAPIViolation((PackageAPI)it.next(), compName));
return v;
}
private List genAPIViolation(PackageAPI pkgAPI, String compName)
{
List classViolations = new ArrayList();
for (Iterator it = pkgAPI.getClassAPIs().iterator(); it.hasNext();)
{
ClassViolation classViolation = getViolations((ClassAPI)it.next(), compName);
if (classViolation != null)
classViolations.add(classViolation);
}
return classViolations;
}
private ClassViolation getViolations(ClassAPI classAPI, String compName)
{
ClassViolation classViolation = new ClassViolation();
classViolation.setName(classAPI.getName());
boolean isSuperClassAPI;
String superClassName = classAPI.getSuperClass();
if (superClassName != null)
{
if (isInterface(classAPI.getAccess()))
isSuperClassAPI = isAPI(compName, superClassName, false, false, true, false);
else
isSuperClassAPI = isAPI(compName, superClassName, false, true, false, false);
if (!isSuperClassAPI)
{
SuperViolation superViolation = new SuperViolation();
superViolation.setName(superClassName);
classViolation.addViolation(superViolation);
}
}
for (Iterator it = classAPI.getMethodAPIs().iterator(); it.hasNext();)
{
MethodAPI methodAPI = (MethodAPI)it.next();
MethodViolation methodViolation = new MethodViolation();
methodViolation.setName(methodAPI.getName());
String desc = methodAPI.getDescriptor();
String returnTypeDesc = Signature.getReturnType(desc);
String returnType = toFullyQualifiedName(returnTypeDesc);
if (Signature.getTypeSignatureKind(returnTypeDesc) != Signature.BASE_TYPE_SIGNATURE && !isAPI(compName, returnType, true, false, false, false))
{
ReturnViolation returnViolation = new ReturnViolation();
returnViolation.setName(returnType);
methodViolation.addViolation(returnViolation);
}
String[] params = Signature.getParameterTypes(desc);
for (int j = 0; j < params.length; j++)
{
String param = toFullyQualifiedName(params[j]);
if (Signature.getTypeSignatureKind(params[j]) != Signature.BASE_TYPE_SIGNATURE && !isAPI(compName, param, true, false, false, false))
{
ParamViolation paramViolation = new ParamViolation();
paramViolation.setName(param);
methodViolation.addViolation(paramViolation);
}
}
String[] throwTypes = Signature.getThrownExceptionTypes(desc);
for (int j = 0; j < throwTypes.length; j++)
{
String throwType = toFullyQualifiedName(throwTypes[j]);
if (Signature.getTypeSignatureKind(throwTypes[j]) != Signature.BASE_TYPE_SIGNATURE && !isAPI(compName, throwType, true, false, false, false))
{
ThrowViolation throwViolation = new ThrowViolation();
throwViolation.setName(throwType);
methodViolation.addViolation(throwViolation);
}
}
if (methodViolation.countViolations() > 0)
classViolation.addViolation(methodViolation);
}
for (Iterator it = classAPI.getFieldAPIs().iterator(); it.hasNext();)
{
FieldAPI fieldAPI = (FieldAPI)it.next();
String desc = new String(fieldAPI.getDescriptor());
String field = toFullyQualifiedName(desc);
if (Signature.getTypeSignatureKind(desc) != Signature.BASE_TYPE_SIGNATURE && !isAPI(compName, field, true, false, false, false))
{
FieldViolation fieldViolation = new FieldViolation();
fieldViolation.setName(fieldAPI.getName());
fieldViolation.setType(field);
classViolation.addViolation(fieldViolation);
}
}
if (classViolation.countViolations() > 0)
return classViolation;
else
return null;
}
private String toFullyQualifiedName(String descriptor)
{
StringBuffer sb = new StringBuffer();
descriptor = descriptor.replace('/', '.');
sb.append(Signature.getSignatureQualifier(descriptor));
sb.append('.');
sb.append(Signature.getSignatureSimpleName(descriptor).replace('.', '$'));
return sb.toString();
}
private boolean isInterface(int access)
{
return ((access & IModifierConstants.ACC_INTERFACE) == IModifierConstants.ACC_INTERFACE);
}
protected boolean include(String name)
{
name = name.replace('/', '.');
name = name.replace('\\', '.');
if (excludes != null && !excludes.isEmpty())
for (Iterator it = excludes.iterator(); it.hasNext();)
if (name.matches((String)it.next()))
return false;
if (includes != null && !includes.isEmpty())
{
for (Iterator it = includes.iterator(); it.hasNext();)
if (name.matches((String)it.next()))
return true;
return false;
}
return true;
}
private boolean isAPI(String compName, String className, boolean ref, boolean subclass, boolean implement, boolean instantiate)
{
if (include(className))
{
String pkgName = null;
String typeName = null;
int dot = className.lastIndexOf('.');
if (dot != -1)
{
pkgName = className.substring(0, dot);
typeName = className.substring(dot + 1);
int brackets = typeName.indexOf("[]");
if (brackets != -1)
typeName = typeName.substring(0, brackets);
}
if (pkgName != null && typeName != null)
{
for (Iterator it = pluginId2CompXML.values().iterator(); it.hasNext();)
{
ComponentXML compXML = (ComponentXML)it.next();
for (Iterator pkgIt = compXML.getPackages().iterator(); pkgIt.hasNext();)
{
Package pkg = (Package)pkgIt.next();
if (pkgName.equals(pkg.getName()))
{
// if not overwritten, inner class inherits usages from base class
int index = typeName.indexOf('$');
String baseTypeName = (index != -1) ? typeName.substring(0, index) : null;
Type baseType = null;
for (Iterator typeIt = pkg.getTypes().iterator(); typeIt.hasNext();)
{
Type type = (Type)typeIt.next();
String name = type.getName();
if (typeName.equals(name))
{
if (ref && !type.isReference())
return false;
if (subclass && !type.isSubclass() && !((compXML.getName().equals(compName) || compXML.getPlugin(compName) != null) && type.isReference()))
return false;
if (implement && !type.isImplement() && !((compXML.getName().equals(compName) || compXML.getPlugin(compName) != null) && type.isReference()))
return false;
if (instantiate && !type.isInstantiate())
return false;
return true;
}
if (baseTypeName != null && baseType == null && baseTypeName.equals(name))
{
baseType = type;
}
}
if (baseType != null)
{
if (ref && !baseType.isReference())
return false;
if (subclass && !baseType.isSubclass())
return false;
if (implement && !baseType.isImplement())
return false;
if (instantiate && !baseType.isInstantiate())
return false;
return true;
}
return pkg.isApi();
}
}
}
}
return false;
}
else
{
return true;
}
}
protected 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 static void main(String[] args)
{
CommandOptionParser optionParser = new CommandOptionParser(args);
Map options = optionParser.getOptions();
Collection src = (Collection)options.get("src");
Collection api = (Collection)options.get("api");
Collection outputDir = (Collection)options.get("outputDir");
Collection includes = (Collection)options.get("includes");
Collection excludes = (Collection)options.get("excludes");
Collection refapi = (Collection)options.get("refapi");
Collection skipAPIGen = (Collection)options.get("skipAPIGen");
if (src == null || api == null || outputDir == null || src.size() < 1 || api.size() < 1 || outputDir.size() < 1)
{
printUsage();
System.exit(-1);
}
NonAPIDependencyScanner scanner = new NonAPIDependencyScanner();
scanner.setSrc(src);
scanner.setApi(api);
scanner.setOutputDir((String)outputDir.iterator().next());
scanner.setIncludes(includes);
scanner.setExcludes(excludes);
scanner.setRefapi(refapi);
scanner.setSkipAPIGen(skipAPIGen != null);
scanner.execute();
}
private static void printUsage()
{
System.out.println("Usage: java org.eclipse.wtp.releng.tools.component.api.violation.NonAPIDependencyScanner -src <src> -api <api> -outputDir <outputDir> [-options]");
System.out.println("");
System.out.println("\t-src\t\t<src>\t\tlocation of a Eclipse-based product (requires SDK build)");
System.out.println("\t-api\t\t<api>\t\tlocation of your component.xml");
System.out.println("\t-outputDir\t<outputDir>\toutput directory of component.xml files");
System.out.println("");
System.out.println("where options include:");
System.out.println("");
System.out.println("\t-includes\t<includes>\tspace seperated packages to include");
System.out.println("\t-excludes\t<excludes>\tspace seperated packages to exclude");
System.out.println("\t-refapi\t<refapi>\tlocation of component.xml being referenced");
System.out.println("\t-skipAPIGen\t\t\tskip api-info.xml generation and use existing ones");
}
}