blob: 973332c8058a948e93e544724a9669577970049e [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;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
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.jdt.core.dom.Modifier;
import org.eclipse.jdt.core.util.ClassFormatException;
import org.eclipse.jdt.core.util.IClassFileReader;
import org.eclipse.jdt.core.util.IExceptionAttribute;
import org.eclipse.jdt.core.util.IFieldInfo;
import org.eclipse.jdt.core.util.IMethodInfo;
import org.eclipse.jdt.internal.core.util.ClassFileReader;
import org.eclipse.wtp.releng.tools.component.CommandOptionParser;
import org.eclipse.wtp.releng.tools.component.IClassVisitor;
import org.eclipse.wtp.releng.tools.component.ILocation;
import org.eclipse.wtp.releng.tools.component.ILocationVisitor;
import org.eclipse.wtp.releng.tools.component.api.violation.LibVisitor;
import org.eclipse.wtp.releng.tools.component.images.ImagesUtil;
import org.eclipse.wtp.releng.tools.component.internal.ComponentXML;
import org.eclipse.wtp.releng.tools.component.internal.FileLocation;
import org.eclipse.wtp.releng.tools.component.internal.Location;
import org.eclipse.wtp.releng.tools.component.internal.Package;
import org.eclipse.wtp.releng.tools.component.internal.Plugin;
import org.eclipse.wtp.releng.tools.component.internal.Type;
import org.eclipse.wtp.releng.tools.component.xsl.XSLUtil;
public class API2ComponentAPI implements IClassVisitor
{
private Collection api;
private Collection src;
private String outputDir;
private Collection includes;
private Collection excludes;
private boolean html;
private boolean readInterface = true;
private boolean skipAPIGen = false;
private Map interface2ImplClasses = new HashMap(0);
private Map super2SubClasses = new HashMap(0);
private boolean includeInnerClass = false;
private boolean includeInterfaces = false;
public Collection getApi()
{
return api;
}
public void setApi(String api)
{
this.api = new ArrayList(1);
this.api.add(api);
}
public void setApi(Collection api)
{
this.api = api;
}
public Collection getSrc()
{
return src;
}
public void setSrc(String src)
{
this.src = new ArrayList(1);
this.src.add(src);
}
public void setSrc(Collection src)
{
this.src = src;
}
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 boolean isHtml()
{
return html;
}
public void setHtml(boolean html)
{
this.html = html;
}
public boolean isReadInterface()
{
return readInterface;
}
public void setReadInterface(boolean readInterface)
{
this.readInterface = readInterface;
}
public boolean isSkipAPIGen()
{
return skipAPIGen;
}
public void setSkipAPIGen(boolean skipAPIGen)
{
this.skipAPIGen = skipAPIGen;
}
public boolean isIncludeInnerClass()
{
return includeInnerClass;
}
public void setIncludeInnerClass(boolean includeInnerClass)
{
this.includeInnerClass = includeInnerClass;
}
public boolean isIncludeInterfaces()
{
return includeInterfaces;
}
public void setIncludeInterfaces(boolean includeInterfaces)
{
this.includeInterfaces = includeInterfaces;
}
public List getImplClasses(String interfaceName)
{
List list = new ArrayList();
getImplClasses(interfaceName, list);
return list;
}
private void getImplClasses(String interfaceName, List list)
{
List implClasses = (List)interface2ImplClasses.get(interfaceName);
if (implClasses != null)
{
list.addAll(implClasses);
for (Iterator it = implClasses.iterator(); it.hasNext();)
{
String className = (String)it.next();
if (interface2ImplClasses.get(className) != null)
getImplClasses(className, list);
else
{
getSubClasses(className, list);
getSuperClasses(className, list);
}
}
}
}
private void getSuperClasses(String className, List list)
{
ClassHierarchyInfo info = (ClassHierarchyInfo)super2SubClasses.get(className);
if (info != null)
{
String superClassName = info.getSuperClass();
while (superClassName != null)
{
list.add(superClassName);
superClassName = null;
info = (ClassHierarchyInfo)super2SubClasses.get(superClassName);
if (info != null)
superClassName = info.getSuperClass();
}
}
}
private void getSubClasses(String className, List list)
{
ClassHierarchyInfo info = (ClassHierarchyInfo)super2SubClasses.get(className);
if (info != null)
{
List subClasses = info.getSubClass();
if (subClasses != null)
{
list.addAll(subClasses);
for (Iterator it = subClasses.iterator(); it.hasNext();)
getSubClasses((String)it.next(), list);
}
}
}
private Map pluginId2CompXML = new HashMap();
public void execute()
{
// Collection component.xml files
for (Iterator i = api.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);
}
}
}
// Generate api-info.xml files
for (Iterator it = src.iterator(); it.hasNext();)
{
ILocation srcLocation = Location.createLocation(new File((String)it.next()));
LibVisitor libVisitor = new LibVisitor();
if (srcLocation != null) {
srcLocation.accept(libVisitor);
libVisitor.setClassVisitor(this);
srcLocation.accept(libVisitor);
} else {
System.out.println("Null source location found, continuing.");
}
}
try
{
if (cachedCompAPI != null)
cachedCompAPI.save();
}
catch (IOException e)
{
throw new RuntimeException(e);
}
// Generate HTML
if (isHtml())
genHTML();
}
private void genHTML()
{
ImagesUtil.copyAll(outputDir);
final StringBuffer summary = new StringBuffer();
summary.append("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
summary.append("<root>");
ILocation outputLoc = Location.createLocation(new File(outputDir));
outputLoc.accept(new ILocationVisitor()
{
public boolean accept(ILocation location)
{
if (location.getName().endsWith("api-info.xml"))
{
summary.append("<api-info file=\"");
summary.append(location.getAbsolutePath().substring(outputDir.length()));
summary.append("\"/>");
}
return true;
}
});
summary.append("</root>");
try
{
XSLUtil.transform(ClassLoader.getSystemResourceAsStream("org/eclipse/wtp/releng/tools/component/xsl/api-info-summary.xsl"), new ByteArrayInputStream(summary.toString().getBytes()), new FileOutputStream(new File(outputDir + "/api-info-summary.html")), outputDir);
}
catch (Throwable e)
{
e.printStackTrace();
}
}
public boolean visit(String pluginId, ILocation classLoc)
{
try
{
IClassFileReader reader = read(classLoc);
String className = new String(reader.getClassName()).replace('/', '.');
if (include(className))
{
int i = className.lastIndexOf('.');
String packageName = (i != -1) ? className.substring(0, i) : "";
String localName = (i != -1) ? className.substring(i + 1) : className;
ClassAPIInfo classAPIInfo = isAPI(reader, pluginId, packageName, localName);
boolean isAPI = classAPIInfo.api;
if (readInterface)
{
String superClassName = new String(reader.getSuperclassName()).replace('/', '.');
ClassHierarchyInfo info = new ClassHierarchyInfo();
info.setSuperClass(superClassName);
super2SubClasses.put(className, info);
info = (ClassHierarchyInfo)super2SubClasses.get(superClassName);
if (info == null)
{
info = new ClassHierarchyInfo();
super2SubClasses.put(superClassName, info);
}
info.addSubClass(className);
char[][] names = reader.getInterfaceNames();
for (int j = 0; j < names.length; j++)
{
String interfaceName = new String(names[j]).replace('/', '.');
List implClasses = (List)interface2ImplClasses.get(interfaceName);
if (implClasses == null)
{
implClasses = new ArrayList(1);
interface2ImplClasses.put(interfaceName, implClasses);
}
implClasses.add(className);
}
}
if (!isSkipAPIGen() && isAPI)
{
try
{
ComponentAPI compAPI = getComponentAPI(pluginId);
PackageAPI pkgAPI = (PackageAPI)compAPI.getPackageAPI(packageName);
if (pkgAPI == null)
{
pkgAPI = new PackageAPI();
pkgAPI.setName(packageName);
compAPI.addPackageAPI(pkgAPI);
}
ClassAPI classAPI = pkgAPI.getClassAPI(localName);
if (classAPI == null)
{
boolean isExclusive = isExclusive(pluginId, packageName);
if (reader == null)
reader = read(classLoc);
classAPI = new ClassAPI();
classAPI.setName(localName);
classAPI.setAccess(reader.getAccessFlags());
classAPI.setSuperClass(new String(reader.getSuperclassName()).replace('/', '.'));
if (includeInterfaces)
{
char[][] names = reader.getInterfaceNames();
for (int j = 0; j < names.length; j++)
{
classAPI.addInterface(new String(names[j]).replace('/', '.'));
}
}
pkgAPI.addClassAPI(classAPI);
IMethodInfo[] methods = reader.getMethodInfos();
for (int j = 0; j < methods.length; j++)
{
int methodAccessFlag = methods[j].getAccessFlags();
if (!Modifier.isPrivate(methodAccessFlag))
{
if (!isExclusive || Modifier.isPublic(methodAccessFlag) || (classAPIInfo.subclass && Modifier.isProtected(methodAccessFlag)))
{
MethodAPI methodAPI = new MethodAPI();
methodAPI.setName(new String(methods[j].getName()));
methodAPI.setAccess(methodAccessFlag);
methodAPI.setDescriptor(new String(methods[j].getDescriptor()));
IExceptionAttribute exceptionAttr = methods[j].getExceptionAttribute();
if (exceptionAttr != null)
{
char[][] exceptionNames = exceptionAttr.getExceptionNames();
List exceptions = new ArrayList(exceptionNames.length);
for (int k = 0; k < exceptionNames.length; k++)
{
exceptions.add(new String(exceptionNames[k]).replace('/', '.'));
}
methodAPI.addThrows(exceptions);
}
classAPI.addMethodAPI(methodAPI);
}
}
}
IFieldInfo[] fields = reader.getFieldInfos();
for (int j = 0; j < fields.length; j++)
{
int fieldAccessFlag = fields[j].getAccessFlags();
if (!Modifier.isPrivate(fieldAccessFlag))
{
if (!isExclusive || Modifier.isPublic(fieldAccessFlag) || Modifier.isProtected(fieldAccessFlag))
{
FieldAPI fieldAPI = new FieldAPI();
fieldAPI.setName(new String(fields[j].getName()));
fieldAPI.setAccess(fieldAccessFlag);
fieldAPI.setDescriptor(new String(fields[j].getDescriptor()));
classAPI.addFieldAPI(fieldAPI);
}
}
}
}
}
catch (IOException e)
{
throw new RuntimeException(e);
}
catch (ClassFormatException e)
{
throw new RuntimeException(e);
}
}
}
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
catch (ClassFormatException cfe)
{
cfe.printStackTrace();
}
return true;
}
private ClassAPIInfo isAPI(IClassFileReader reader, String pluginId, String packageName, String localName)
{
boolean innerClass = localName.indexOf('$') != -1;
int classAccessFlag = reader.getAccessFlags();
if (Modifier.isPrivate(classAccessFlag))
{
return new ClassAPIInfo(false, false);
}
else if (!includeInnerClass && innerClass && !Modifier.isPublic(classAccessFlag) && !Modifier.isProtected(classAccessFlag))
{
return new ClassAPIInfo(false, false);
}
else
{
ComponentXML compXML = (ComponentXML)pluginId2CompXML.get(pluginId);
if (compXML != null)
{
Package pkg = compXML.getPackage(packageName);
if (pkg != null)
{
// package protected, check exclusive
if (pkg.isExclusive() && !Modifier.isPublic(classAccessFlag) && !Modifier.isProtected(classAccessFlag))
{
return new ClassAPIInfo(false, false);
}
Type type;
int innerClassSep = localName.indexOf('$');
if (innerClassSep == -1)
type = pkg.getType(localName);
else
type = pkg.getType(localName.substring(0, innerClassSep));
if (type != null)
{
if (!type.isReference() && !type.isSubclass() && !type.isImplement() && !type.isInstantiate())
{
return new ClassAPIInfo(false, false);
}
else
{
return new ClassAPIInfo(true, type.isSubclass());
}
}
else
{
return new ClassAPIInfo(pkg.isApi(), true);
}
}
else
{
return new ClassAPIInfo(false, false);
}
}
else
{
return new ClassAPIInfo(false, false);
}
}
}
private boolean isExclusive(String pluginId, String packageName)
{
ComponentXML compXML = (ComponentXML)pluginId2CompXML.get(pluginId);
if (compXML != null)
{
Package pkg = compXML.getPackage(packageName);
if (pkg != null)
{
return pkg.isExclusive();
}
}
return true;
}
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.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 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 ComponentAPI cachedCompAPI;
private ComponentAPI getComponentAPI(String id) throws IOException
{
if (cachedCompAPI != null)
{
if (cachedCompAPI.getName().equals(id))
{
return cachedCompAPI;
}
else
{
cachedCompAPI.save();
}
}
StringBuffer sb = new StringBuffer(outputDir);
sb.append(id);
sb.append("/api-info.xml");
File file = new File(sb.toString());
cachedCompAPI = new ComponentAPI();
cachedCompAPI.setName(id);
cachedCompAPI.setLocation(new FileLocation(file));
if (file.exists())
cachedCompAPI.load();
return cachedCompAPI;
}
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;
}
}
private class ClassAPIInfo
{
public boolean api;
public boolean subclass;
public ClassAPIInfo(boolean api, boolean subclass)
{
this.api = api;
this.subclass = subclass;
}
}
private class ClassHierarchyInfo
{
private String superClass;
private List subClass;
public List getSubClass()
{
return subClass;
}
public void addSubClass(String subClass)
{
if (this.subClass == null)
this.subClass = new ArrayList(1);
this.subClass.add(subClass);
}
public String getSuperClass()
{
return superClass;
}
public void setSuperClass(String superClass)
{
this.superClass = superClass;
}
}
public static void main(String[] args)
{
CommandOptionParser optionParser = new CommandOptionParser(args);
Map options = optionParser.getOptions();
Collection api = (Collection)options.get("api");
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 html = (Collection)options.get("html");
if (api == null || src == null || outputDir == null || api.isEmpty() || src.isEmpty() || outputDir.isEmpty())
{
printUsage();
System.exit(-1);
}
API2ComponentAPI api2CompAPI = new API2ComponentAPI();
api2CompAPI.setApi(api);
api2CompAPI.setSrc(src);
api2CompAPI.setOutputDir((String)outputDir.iterator().next());
api2CompAPI.setIncludes(includes);
api2CompAPI.setExcludes(excludes);
api2CompAPI.setHtml(html != null);
api2CompAPI.execute();
}
private static void printUsage()
{
System.out.println("Usage: java org.eclipse.wtp.releng.tools.component.api.API2ComponentAPI -api <api> -src <src> -outputDir <outputDir> [-options]");
System.out.println("");
System.out.println("\t-api\t\t<api>\t\tlocation of your component.xml");
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-html\t\t\t\t\tgenerate HTML output");
}
}