| /******************************************************************************* |
| * 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.classes; |
| |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.ArrayList; |
| import java.util.Collection; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.Vector; |
| import org.eclipse.jdt.core.Signature; |
| import org.eclipse.jdt.core.util.ClassFormatException; |
| import org.eclipse.jdt.core.util.IClassFileReader; |
| import org.eclipse.jdt.core.util.ICodeAttribute; |
| import org.eclipse.jdt.core.util.IConstantPool; |
| import org.eclipse.jdt.core.util.IConstantPoolConstant; |
| import org.eclipse.jdt.core.util.IConstantPoolEntry; |
| import org.eclipse.jdt.core.util.IFieldInfo; |
| import org.eclipse.jdt.core.util.ILineNumberAttribute; |
| import org.eclipse.jdt.core.util.IMethodInfo; |
| import org.eclipse.jdt.internal.core.util.ClassFileReader; |
| import org.eclipse.wtp.releng.tools.component.ILocation; |
| import org.eclipse.wtp.releng.tools.component.internal.FieldRef; |
| import org.eclipse.wtp.releng.tools.component.internal.FileLocation; |
| import org.eclipse.wtp.releng.tools.component.internal.InternalByteCodeVisitor; |
| import org.eclipse.wtp.releng.tools.component.internal.Location; |
| import org.eclipse.wtp.releng.tools.component.internal.MethodRef; |
| import org.eclipse.wtp.releng.tools.component.use.ClassUse; |
| import org.eclipse.wtp.releng.tools.component.use.ComponentUse; |
| import org.eclipse.wtp.releng.tools.component.use.FieldUse; |
| import org.eclipse.wtp.releng.tools.component.use.MethodUse; |
| import org.eclipse.wtp.releng.tools.component.use.Source; |
| import org.eclipse.wtp.releng.tools.component.util.CommandOptionParser; |
| |
| public class Class2Reference implements IClassVisitor |
| { |
| private String src; |
| private String outputDir; |
| private Collection includes; |
| private Collection excludes; |
| private Collection includePlugins; |
| private Collection excludePlugins; |
| private boolean classRefOnly; |
| private boolean debug; |
| |
| public Class2Reference() |
| { |
| this.classRefOnly = false; |
| this.debug = false; |
| } |
| |
| public String getOutputDir() |
| { |
| return outputDir; |
| } |
| |
| public void setOutputDir(String outputDir) |
| { |
| this.outputDir = addTrailingSeperator(outputDir); |
| } |
| |
| public String getSrc() |
| { |
| return src; |
| } |
| |
| public void setSrc(String src) |
| { |
| this.src = src; |
| } |
| |
| 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 getExcludePlugins() |
| { |
| return excludePlugins; |
| } |
| |
| public void setExcludePlugins(Collection excludePlugins) |
| { |
| this.excludePlugins = excludePlugins; |
| } |
| |
| public Collection getIncludePlugins() |
| { |
| return includePlugins; |
| } |
| |
| public void setIncludePlugins(Collection includePlugins) |
| { |
| this.includePlugins = includePlugins; |
| } |
| |
| public boolean isClassRefOnly() |
| { |
| return classRefOnly; |
| } |
| |
| public void setClassRefOnly(boolean classRefOnly) |
| { |
| this.classRefOnly = classRefOnly; |
| } |
| |
| public boolean isDebug() |
| { |
| return debug; |
| } |
| |
| public void setDebug(boolean debug) |
| { |
| this.debug = debug; |
| } |
| |
| public void execute() |
| { |
| ILocation srcLocation = Location.createLocation(new File(src)); |
| LibVisitor libVisitor = new LibVisitor(); |
| srcLocation.accept(libVisitor); |
| libVisitor.setClassVisitor(this); |
| srcLocation.accept(libVisitor); |
| try |
| { |
| if (cachedCompUse != null) |
| cachedCompUse.save(); |
| } |
| catch (IOException e) |
| { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| public boolean visit(String pluginId, ILocation classLoc) |
| { |
| if (includePlugins(pluginId)) |
| { |
| try |
| { |
| ComponentUse compUse = getComponentUse(pluginId); |
| compUse.addSource(visit(classLoc)); |
| } |
| catch (IOException e) |
| { |
| throw new RuntimeException(e); |
| } |
| catch (ClassFormatException e) |
| { |
| System.err.println("Plugin: " + pluginId + " Class: " + classLoc.getAbsolutePath()); |
| //throw new RuntimeException(e); |
| } |
| } |
| return true; |
| } |
| |
| public Source visit(ILocation classLoc) throws IOException, ClassFormatException |
| { |
| IClassFileReader reader = read(classLoc); |
| String className = new String(reader.getClassName()).replace('/', '.'); |
| Source source = new Source(); |
| source.setName(className); |
| // use: reference |
| for (Iterator it = getReferencedTypes(reader).iterator(); it.hasNext();) |
| { |
| String refClassName = (String)it.next(); |
| if (!className.equals(refClassName) && include(refClassName)) |
| { |
| setClassUse(source, refClassName, Boolean.TRUE, null, null, null, null); |
| } |
| } |
| // method info & field info |
| setMethodAndFieldInfoUses(reader, source); |
| // method ref & field ref |
| if (!isClassRefOnly()) |
| { |
| // method ref |
| List methodRefs = new ArrayList(); |
| List fieldRefs = new ArrayList(); |
| getRefs(reader, isDebug(), methodRefs, fieldRefs); |
| for (Iterator it = methodRefs.iterator(); it.hasNext();) |
| { |
| MethodRef methodRef = (MethodRef)it.next(); |
| String refClassName = methodRef.getClassName(); |
| if (!className.equals(refClassName) && include(refClassName)) |
| { |
| String methodName = methodRef.getMethodName(); |
| ClassUse classUse; |
| if (methodRef.isConstructor()) |
| { |
| // use: instantiate |
| classUse = setClassUse(source, refClassName, null, null, null, Boolean.TRUE, methodRef.getLines()); |
| } |
| else |
| { |
| // use: reference |
| classUse = setClassUse(source, refClassName, Boolean.TRUE, null, null, null, null); |
| } |
| MethodUse methodUse = new MethodUse(); |
| methodUse.setName(methodName); |
| methodUse.setDescriptor(methodRef.getMethodDescriptor()); |
| List lines = methodRef.getLines(); |
| if (lines != null) |
| methodUse.addLines(lines); |
| classUse.addMethodAPI(methodUse); |
| } |
| } |
| methodRefs = null; |
| // field ref |
| for (Iterator it = fieldRefs.iterator(); it.hasNext();) |
| { |
| FieldRef fieldRef = (FieldRef)it.next(); |
| String refClassName = fieldRef.getClassName(); |
| if (!className.equals(refClassName) && include(refClassName)) |
| { |
| ClassUse classUse = setClassUse(source, refClassName, Boolean.TRUE, null, null, null, null); |
| FieldUse fieldUse = new FieldUse(); |
| fieldUse.setName(fieldRef.getFieldName()); |
| fieldUse.setDescriptor(fieldRef.getFieldDescriptor()); |
| List lines = fieldRef.getLines(); |
| if (lines != null) |
| fieldUse.addLines(lines); |
| classUse.addFieldAPI(fieldUse); |
| } |
| } |
| fieldRefs = null; |
| // use: subclass |
| String superClass = new String(reader.getSuperclassName()).replace('/', '.'); |
| if (superClass != null && include(superClass)) |
| { |
| setClassUse(source, superClass, null, Boolean.TRUE, null, null, null); |
| } |
| // use: implement |
| char[][] interfaceNames = reader.getInterfaceNames(); |
| String[] interfaces = new String[interfaceNames.length]; |
| for (int i = 0; i < interfaces.length; i++) |
| interfaces[i] = new String(interfaceNames[i]).replace('/', '.'); |
| for (int i = 0; i < interfaces.length; i++) |
| if (include(interfaces[i])) |
| setClassUse(source, interfaces[i], null, null, Boolean.TRUE, null, null); |
| } |
| return source; |
| } |
| |
| private IClassFileReader read(ILocation classLoc) throws IOException, ClassFormatException |
| { |
| InputStream is = null; |
| ByteArrayOutputStream baos = null; |
| try |
| { |
| byte[] b = new byte[8192]; |
| baos = new ByteArrayOutputStream(8192); |
| is = classLoc.getInputStream(); |
| for (int read = is.read(b); read != -1; read = is.read(b)) |
| { |
| baos.write(b, 0, read); |
| } |
| is.close(); |
| baos.close(); |
| return new ClassFileReader(baos.toByteArray(), IClassFileReader.CONSTANT_POOL | IClassFileReader.METHOD_INFOS | IClassFileReader.METHOD_BODIES | IClassFileReader.FIELD_INFOS | IClassFileReader.SUPER_INTERFACES); |
| } |
| finally |
| { |
| if (is != null) |
| { |
| try |
| { |
| is.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| if (baos != null) |
| { |
| try |
| { |
| baos.close(); |
| } |
| catch (IOException e) |
| { |
| } |
| } |
| } |
| } |
| |
| private Set getReferencedTypes(IClassFileReader reader) |
| { |
| Set types = new HashSet(); |
| IConstantPool constantPool = reader.getConstantPool(); |
| int poolSize = constantPool.getConstantPoolCount(); |
| for (int i = 0; i < poolSize; i++) |
| { |
| // Extract the constant's referenced class (if that is even relevant) |
| if (constantPool.getEntryKind(i) == IConstantPoolConstant.CONSTANT_Class) |
| { |
| IConstantPoolEntry classEntry = constantPool.decodeEntry(i); |
| String signature = new String(classEntry.getClassInfoName()); |
| int index = signature.lastIndexOf('['); |
| if (index > -1) |
| { |
| // could be an array of a primitive type |
| if (signature.length() - (index + 1) == 1) |
| continue; |
| signature = Signature.toString(signature); |
| signature = signature.substring(0, signature.length() - 2 * (index + 1)); |
| signature = signature.replace('.', '$'); |
| } |
| String typeName = signature.replace('/', '.'); |
| types.add(typeName); |
| } |
| } |
| return types; |
| } |
| |
| private void getRefs(IClassFileReader reader, boolean debug, List methodRefs, List fieldRefs) |
| { |
| String className = new String(reader.getClassName()).replace('/', '.'); |
| IConstantPoolEntry[] refs = getConstantPoolEntries(reader, IConstantPoolConstant.CONSTANT_Methodref); |
| for (int i = 0; i < refs.length; i++) |
| { |
| String refClassName = new String(refs[i].getClassName()).replace('/', '.'); |
| if (!className.equals(refClassName) && include(refClassName)) |
| { |
| MethodRef methodRef = new MethodRef(); |
| methodRef.setPoolEntry(refs[i]); |
| methodRefs.add(methodRef); |
| } |
| } |
| refs = getConstantPoolEntries(reader, IConstantPoolConstant.CONSTANT_InterfaceMethodref); |
| for (int i = 0; i < refs.length; i++) |
| { |
| String refClassName = new String(refs[i].getClassName()).replace('/', '.'); |
| if (!className.equals(refClassName) && include(refClassName)) |
| { |
| MethodRef methodRef = new MethodRef(); |
| methodRef.setPoolEntry(refs[i]); |
| methodRefs.add(methodRef); |
| } |
| } |
| refs = getConstantPoolEntries(reader, IConstantPoolConstant.CONSTANT_Fieldref); |
| for (int i = 0; i < refs.length; i++) |
| { |
| String refClassName = new String(refs[i].getClassName()).replace('/', '.'); |
| if (!className.equals(refClassName) && include(refClassName)) |
| { |
| FieldRef fieldRef = new FieldRef(); |
| fieldRef.setPoolEntry(refs[i]); |
| fieldRefs.add(fieldRef); |
| } |
| } |
| if (debug) |
| { |
| IMethodInfo[] methodInfos = reader.getMethodInfos(); |
| for (int i = 0; i < methodInfos.length; i++) |
| { |
| ICodeAttribute codeAttr = methodInfos[i].getCodeAttribute(); |
| if (codeAttr != null) |
| { |
| ILineNumberAttribute lineNumAttr = codeAttr.getLineNumberAttribute(); |
| if (lineNumAttr != null) |
| { |
| InternalByteCodeVisitor byteCodeVisitor = new InternalByteCodeVisitor(methodRefs, fieldRefs, lineNumAttr); |
| try |
| { |
| codeAttr.traverse(byteCodeVisitor); |
| } |
| catch (ClassFormatException e) |
| { |
| e.printStackTrace(); |
| } |
| } |
| } |
| } |
| } |
| } |
| |
| private void setMethodAndFieldInfoUses(IClassFileReader reader, Source source) |
| { |
| String className = new String(reader.getClassName()).replace('/', '.'); |
| IMethodInfo[] methodInfos = reader.getMethodInfos(); |
| for (int i = 0; i < methodInfos.length; i++) |
| { |
| String desc = new String(methodInfos[i].getDescriptor()); |
| String returnTypeDesc = Signature.getReturnType(desc); |
| String returnType = toFullyQualifiedName(returnTypeDesc); |
| if (Signature.getTypeSignatureKind(returnTypeDesc) != Signature.BASE_TYPE_SIGNATURE && !className.equals(returnType) && include(returnType)) |
| { |
| setClassUse(source, returnType, Boolean.TRUE, null, null, null, null); |
| } |
| 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 && !className.equals(param) && include(param)) |
| { |
| setClassUse(source, param, Boolean.TRUE, null, null, null, null); |
| } |
| } |
| 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 && !className.equals(throwType) && include(throwType)) |
| { |
| setClassUse(source, throwType, Boolean.TRUE, null, null, null, null); |
| } |
| } |
| } |
| IFieldInfo[] fieldInfos = reader.getFieldInfos(); |
| for (int i = 0; i < fieldInfos.length; i++) |
| { |
| String desc = new String(fieldInfos[i].getDescriptor()); |
| String field = toFullyQualifiedName(desc); |
| if (Signature.getTypeSignatureKind(desc) != Signature.BASE_TYPE_SIGNATURE && !className.equals(field) && include(field)) |
| { |
| setClassUse(source, field, Boolean.TRUE, null, null, null, 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 IConstantPoolEntry[] getConstantPoolEntries(IClassFileReader reader, int kind) |
| { |
| List entries = new Vector(); |
| IConstantPool pool = reader.getConstantPool(); |
| int poolSize = pool.getConstantPoolCount(); |
| for (int i = 0; i < poolSize; i++) |
| if (pool.getEntryKind(i) == kind) |
| entries.add(pool.decodeEntry(i)); |
| return (IConstantPoolEntry[])entries.toArray(new IConstantPoolEntry[0]); |
| } |
| |
| private ClassUse setClassUse(Source source, String className, Boolean ref, Boolean subclass, Boolean implement, Boolean instantiate, List lines) |
| { |
| ClassUse classUse = source.getClassUse(className); |
| if (classUse == null) |
| { |
| classUse = new ClassUse(); |
| classUse.setName(className); |
| source.addClassUse(classUse); |
| } |
| if (ref != null && ref.booleanValue()) |
| classUse.setReference(Boolean.TRUE); |
| if (subclass != null && subclass.booleanValue()) |
| classUse.setSubclass(Boolean.TRUE); |
| if (implement != null && implement.booleanValue()) |
| classUse.setImplement(Boolean.TRUE); |
| if (instantiate != null && instantiate.booleanValue()) |
| classUse.setInstantiate(Boolean.TRUE); |
| if (lines != null) |
| classUse.addLines(lines); |
| return classUse; |
| } |
| |
| private 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 includePlugins(String id) |
| { |
| id = id.replace('/', '.'); |
| id = id.replace('\\', '.'); |
| if (excludePlugins != null && !excludePlugins.isEmpty()) |
| for (Iterator it = excludePlugins.iterator(); it.hasNext();) |
| if (id.matches((String)it.next())) |
| return false; |
| if (includePlugins != null && !includePlugins.isEmpty()) |
| { |
| for (Iterator it = includePlugins.iterator(); it.hasNext();) |
| if (id.matches((String)it.next())) |
| return true; |
| return false; |
| } |
| return true; |
| } |
| |
| private ComponentUse cachedCompUse; |
| |
| private ComponentUse getComponentUse(String id) throws IOException |
| { |
| if (cachedCompUse != null) |
| { |
| if (cachedCompUse.getName().equals(id)) |
| { |
| return cachedCompUse; |
| } |
| else |
| { |
| cachedCompUse.save(); |
| } |
| } |
| StringBuffer sb = new StringBuffer(outputDir); |
| sb.append(id); |
| sb.append("/component-use.xml"); |
| File file = new File(sb.toString()); |
| cachedCompUse = new ComponentUse(); |
| cachedCompUse.setName(id); |
| cachedCompUse.setLocation(new FileLocation(file)); |
| if (file.exists()) |
| cachedCompUse.load(); |
| return cachedCompUse; |
| } |
| |
| 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 outputDir = (Collection)options.get("outputDir"); |
| Collection includes = (Collection)options.get("includes"); |
| Collection excludes = (Collection)options.get("excludes"); |
| Collection includePlugins = (Collection)options.get("includePlugins"); |
| Collection excludePlugins = (Collection)options.get("excludePlugins"); |
| Collection classRefOnly = (Collection)options.get("classRefOnly"); |
| Collection debug = (Collection)options.get("debug"); |
| if (src == null || outputDir == null || src.isEmpty() || outputDir.isEmpty()) |
| { |
| printUsage(); |
| System.exit(-1); |
| } |
| Class2Reference class2Ref = new Class2Reference(); |
| class2Ref.setSrc((String)src.iterator().next()); |
| class2Ref.setOutputDir((String)outputDir.iterator().next()); |
| class2Ref.setIncludes(includes); |
| class2Ref.setExcludes(excludes); |
| class2Ref.setIncludePlugins(includePlugins); |
| class2Ref.setExcludePlugins(excludePlugins); |
| class2Ref.setClassRefOnly(classRefOnly != null); |
| class2Ref.setDebug(debug != null); |
| class2Ref.execute(); |
| } |
| |
| private static void printUsage() |
| { |
| System.out.println("Usage: java org.eclipse.wtp.releng.tools.component.classes.class2Reference -src <src> -outputDir <outputDir> [-options]"); |
| System.out.println(""); |
| System.out.println("\t-src\t\t<src>\t\tlocation of a Eclipse-based product"); |
| 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-includePlugins\t<includePlugins>\tspace seperated plugins to include"); |
| System.out.println("\t-excludePlugins\t<excludePlugins>\tspace seperated plugins to exclude"); |
| System.out.println("\t-classRefOnly\t\t\ttreat all violations as class reference"); |
| System.out.println("\t-debug\t\t\t\tgenerate debug information (ex. line numbers)"); |
| } |
| } |