| /******************************************************************************* |
| * 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"); |
| } |
| } |