blob: e58fdae9f8e2a7a1a4242e6d38e1a56007b06828 [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.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)");
}
}