blob: 3976908bdee1370cb82885bca7d2266881194246 [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.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import org.eclipse.wtp.releng.tools.component.CommandOptionParser;
import org.eclipse.wtp.releng.tools.component.IClazz;
import org.eclipse.wtp.releng.tools.component.IClazzVisitor;
import org.eclipse.wtp.releng.tools.component.ILocation;
import org.eclipse.wtp.releng.tools.component.IPluginXML;
import org.eclipse.wtp.releng.tools.component.internal.AbstractEmitter;
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.Package;
import org.eclipse.wtp.releng.tools.component.internal.Plugin;
import org.eclipse.wtp.releng.tools.component.internal.Type;
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.core.util.IModifierConstants;
public class ComponentAPIEmitter extends AbstractEmitter implements IClazzVisitor
{
public static final String OPTION_ECLIPSE_DIR = "eclipseDir";
public static final String OPTION_COMPONENT_XML_DIR = "compXMLDir";
public static final String OPTION_COMPONENT_API_DIR = "compAPIDir";
private static final String NAME_CONSTRUCTOR = "<init>";
private static final String NAME_STATIC_BLOCK = "<clinit>";
private String compAPIDir;
private boolean genInternalAPIs;
private boolean noFieldAPIs;
private Map compLoc2CompXML;
private Map pluginId2Plugin;
private Map fragmentId2Fragment;
private ComponentXML compXML;
private ComponentAPI compAPI;
private String timestamp;
public ComponentAPIEmitter(String compAPIDir)
{
this.compAPIDir = addTrailingSeperator(compAPIDir);
genInternalAPIs = false;
noFieldAPIs = false;
Calendar c = Calendar.getInstance();
StringBuffer sb = new StringBuffer(String.valueOf(c.get(Calendar.YEAR)));
int month = c.get(Calendar.MONTH);
if (month < 10)
sb.append("0");
sb.append(String.valueOf(month));
int date = c.get(Calendar.DATE);
if (date < 10)
sb.append("0");
sb.append(String.valueOf(date));
timestamp = sb.toString();
}
public void init(List eclipseDirs, List compXMLDirs)
{
compLoc2CompXML = new HashMap();
pluginId2Plugin = new HashMap();
fragmentId2Fragment = new HashMap();
for (Iterator it = eclipseDirs.iterator(); it.hasNext();)
{
File eclipseFile = new File(addTrailingSeperator((String)it.next()));
if (eclipseFile.exists())
harvestPlugins(eclipseFile, pluginId2Plugin, fragmentId2Fragment);
}
linkPluginsAndFragments(pluginId2Plugin, fragmentId2Fragment);
for (Iterator it = compXMLDirs.iterator(); it.hasNext();)
{
File compXMLFile = new File(addTrailingSeperator((String)it.next()));
if (compXMLFile.exists())
harvestComponents(compXMLFile, compLoc2CompXML);
}
}
public void init(Map compLoc2CompXML, Map pluginId2Plugin, Map fragmentId2Fragment)
{
this.compLoc2CompXML = compLoc2CompXML;
this.pluginId2Plugin = pluginId2Plugin;
this.fragmentId2Fragment = fragmentId2Fragment;
}
public String getTimestamp()
{
return timestamp;
}
public void setTimestamp(String timestamp)
{
this.timestamp = timestamp;
}
public boolean getNoFieldAPIs()
{
return noFieldAPIs;
}
public void setNoFieldAPIs(boolean noFieldAPIs)
{
this.noFieldAPIs = noFieldAPIs;
}
public void genComponentApiXml() throws IOException
{
for (Iterator it = compLoc2CompXML.keySet().iterator(); it.hasNext();)
genComponentApiXml((String)it.next());
}
public ComponentAPI genComponentApiXml(String compLoc) throws IOException
{
compXML = (ComponentXML)compLoc2CompXML.get(compLoc);
if (compXML != null)
{
compXML.load();
String compName = compXML.getName();
compAPI = newComponentAPI(compXML);
for (Iterator pluginsIt = compXML.getPlugins().iterator(); pluginsIt.hasNext();)
{
IPluginXML plugin = (IPluginXML)pluginId2Plugin.get(((Plugin)pluginsIt.next()).getId());
if (plugin != null)
plugin.accept(this);
}
compXML = null;
if (compAPIDir != null)
System.out.println("Writing component-api.xml for " + compName);
compAPI.save();
return compAPI;
}
return null;
}
public boolean visit(IClazz clazz)
{
if (compXML == null || compAPI == null)
return false;
ClassAPI classAPI = addClassAPI(compXML, clazz, compAPI);
if (classAPI != null)
{
boolean isPkgProtected = true;
List publicMethods = new ArrayList();
List protectedMethods = new ArrayList();
IMethodInfo[] methodInfos = clazz.getMethodInfo();
for (int i = 0; i < methodInfos.length; i++)
{
int access = methodInfos[i].getAccessFlags();
String methodName = new String(methodInfos[i].getName());
if (isPkgProtected && methodName.equals(NAME_CONSTRUCTOR))
isPkgProtected = !(isBit(access, IModifierConstants.ACC_PUBLIC) || isBit(access, IModifierConstants.ACC_PROTECTED));
if (clazz.isInterface() || ((isBit(access, IModifierConstants.ACC_PUBLIC) || isBit(access, IModifierConstants.ACC_PROTECTED)) && !methodName.equals(NAME_STATIC_BLOCK)))
{
String[] exs = new String[0];
IExceptionAttribute exAttr = methodInfos[i].getExceptionAttribute();
if (exAttr != null)
{
char[][] exChars = exAttr.getExceptionNames();
exs = new String[exChars.length];
for (int j = 0; j < exs.length; j++)
exs[j] = decodeClassName(new String(exChars[j]));
}
if (isBit(access, IModifierConstants.ACC_PUBLIC))
publicMethods.add(newMethodAPI(methodName, access, new String(methodInfos[i].getDescriptor()), exs));
else
protectedMethods.add(newMethodAPI(methodName, access, new String(methodInfos[i].getDescriptor()), exs));
}
}
classAPI.getMethodAPIs().addAll(publicMethods);
if (!isPkgProtected)
classAPI.getMethodAPIs().addAll(protectedMethods);
if (!noFieldAPIs)
{
IFieldInfo[] fieldInfos = clazz.getFieldInfo();
for (int i = 0; i < fieldInfos.length; i++)
{
int access = fieldInfos[i].getAccessFlags();
if (!isBit(access, IModifierConstants.ACC_PRIVATE))
{
if (!isPkgProtected || isBit(access, IModifierConstants.ACC_PUBLIC))
{
addFieldAPI(classAPI, newFieldAPI(new String(fieldInfos[i].getName()), access, new String(fieldInfos[i].getDescriptor())));
}
}
}
}
}
return true;
}
private ClassAPI addClassAPI(ComponentXML compXML, IClazz clazz, ComponentAPI compAPI)
{
String typeName = clazz.getName();
String superClass = clazz.getSuperClass();
int index = typeName.lastIndexOf('.');
String pkgName;
String localName;
if (index != -1)
{
pkgName = typeName.substring(0, index);
localName = typeName.substring(index + 1);
}
else
{
pkgName = "";
localName = typeName;
}
if (genInternalAPIs)
{
ClassAPI internalClassAPI = new ClassAPI();
internalClassAPI.setName(typeName);
internalClassAPI.setSuperClass(superClass);
addClassAPI(compAPI, true, pkgName, internalClassAPI);
}
int access = clazz.getAccessFlags();
if (isBit(access, IModifierConstants.ACC_PUBLIC) || isBit(access, IModifierConstants.ACC_PROTECTED))
{
boolean isInterface = clazz.isInterface();
Package pkg = findPackage(compXML, pkgName);
if (pkg != null)
{
Type type = findType(pkg, localName);
if (type != null)
{
boolean ref = type.isReference();
boolean subclass = type.isSubclass() && !isInterface;
boolean implement = type.isImplement() && isInterface;
boolean instantiate = type.isInstantiate() && !isInterface;
if (ref || subclass || implement || instantiate)
{
// at least one public usage
ClassAPI externalClassAPI = newClassAPI(typeName, superClass, access, new Boolean(ref), new Boolean(subclass), new Boolean(implement), new Boolean(instantiate));
addClassAPI(compAPI, false, pkgName, externalClassAPI);
return externalClassAPI;
}
else
{
// all usage are set to false, in another word, it is an internal API
return null;
}
}
else if (pkg.isApi())
{
// api == true means, by default, a non-listed type is an external API
ClassAPI externalClassAPI = newClassAPI(typeName, superClass, access, Boolean.TRUE, new Boolean(!isInterface), new Boolean(isInterface), new Boolean(!isInterface));
addClassAPI(compAPI, false, pkgName, externalClassAPI);
return externalClassAPI;
}
else
{
// api == false means, by default, a non-listed type is an internal API
return null;
}
}
else
{
// package not defined in component.xml means it is an internal API
return null;
}
}
return null;
}
private void addClassAPI(ComponentAPI compAPI, boolean isInternal, String pkgName, ClassAPI classAPI)
{
Collection pkgAPIs = compAPI.getPackageAPIs();
PackageAPI pkgAPI = null;
for (Iterator it = pkgAPIs.iterator(); it.hasNext();)
{
PackageAPI nextPkg = (PackageAPI)it.next();
if (nextPkg.getName().equals(pkgName))
{
pkgAPI = nextPkg;
break;
}
}
if (pkgAPI == null)
{
pkgAPI = newPackageAPI(pkgName);
pkgAPIs.add(pkgAPI);
}
pkgAPI.addClassAPI(classAPI);
}
private void addFieldAPI(ClassAPI classAPI, FieldAPI fieldAPI)
{
classAPI.getFieldAPIs().add(fieldAPI);
}
private PackageAPI newPackageAPI(String pkgName)
{
PackageAPI pkg = new PackageAPI();
pkg.setName(pkgName);
return pkg;
}
private ComponentAPI newComponentAPI(ComponentXML compXML)
{
String compName = compXML.getName();
ILocation location = null;
if (compAPIDir != null)
{
StringBuffer sb = new StringBuffer(compAPIDir);
sb.append(compName);
sb.append('/');
sb.append("component-api.xml");
location = new FileLocation(new File(sb.toString()));
}
return newComponentAPI(compName, location);
}
private ComponentAPI newComponentAPI(String compName, ILocation location)
{
ComponentAPI compAPI = new ComponentAPI();
compAPI.setName(compName);
compAPI.setTimestamp(timestamp);
compAPI.setLocation(location);
return compAPI;
}
private ClassAPI newClassAPI(String className, String superClass, int access, Boolean ref, Boolean subclass, Boolean implement, Boolean instantiate)
{
ClassAPI classAPI = new ClassAPI();
classAPI.setName(className);
classAPI.setSuperClass(superClass);
classAPI.setAccess(access);
classAPI.setReference(ref);
classAPI.setSubclass(subclass);
classAPI.setImplement(implement);
classAPI.setInstantiate(instantiate);
return classAPI;
}
private MethodAPI newMethodAPI(String methodName, int access, String descriptor, String[] throwTypes)
{
MethodAPI methodAPI = new MethodAPI();
methodAPI.setName(methodName);
methodAPI.setAccess(access);
methodAPI.setDescriptor(descriptor);
List throwList = methodAPI.getThrows();
for (int i = 0; i < throwTypes.length; i++)
throwList.add(throwTypes[i]);
return methodAPI;
}
private FieldAPI newFieldAPI(String fieldName, int access, String descriptor)
{
FieldAPI fieldAPI = new FieldAPI();
fieldAPI.setName(fieldName);
fieldAPI.setAccess(access);
fieldAPI.setDescriptor(descriptor);
return fieldAPI;
}
private Package findPackage(ComponentXML compXML, String pkgName)
{
if (pkgName != null && compXML != null)
{
Collection pkgs = compXML.getPackages();
for (Iterator it = pkgs.iterator(); it.hasNext();)
{
Package pkg = (Package)it.next();
if (pkgName.equals(pkg.getName()))
return pkg;
}
}
return null;
}
private Type findType(Package pkg, String typeName)
{
if (typeName != null)
{
// If not overwritten, inner class inherts usages from base class
int i = typeName.indexOf('$');
String baseTypeName = (i != -1) ? typeName.substring(0, i) : null;
Type baseType = null;
Collection types = pkg.getTypes();
for (Iterator it = types.iterator(); it.hasNext();)
{
Type type = (Type)it.next();
String name = type.getName();
if (typeName.equals(name))
return type;
if (baseTypeName != null && baseType == null && baseTypeName.equals(name))
baseType = type;
}
if (baseType != null)
return baseType;
}
return null;
}
private boolean isBit(int flag, int bit)
{
return ((flag & bit) == bit);
}
public static void main(String[] args)
{
CommandOptionParser optionParser = new CommandOptionParser(args);
Map options = optionParser.getOptions();
List eclipseDir = (List)options.get(ComponentAPIEmitter.OPTION_ECLIPSE_DIR);
List compXMLDir = (List)options.get(ComponentAPIEmitter.OPTION_COMPONENT_XML_DIR);
List compAPIDir = (List)options.get(ComponentAPIEmitter.OPTION_COMPONENT_API_DIR);
if (eclipseDir == null || compXMLDir == null || compAPIDir == null || eclipseDir.size() < 1 || compXMLDir.size() < 1 || compAPIDir.size() < 1)
{
printUsage();
System.exit(-1);
}
ComponentAPIEmitter compAPIEmitter = new ComponentAPIEmitter((String)compAPIDir.get(0));
compAPIEmitter.init(eclipseDir, compXMLDir);
try
{
compAPIEmitter.genComponentApiXml();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
private static void printUsage()
{
System.out.println("Usage: java org.eclipse.wtp.releng.tools.component.api.ComponentAPIEmitter -eclipseDir <eclipseDir> -compXMLDir <compDir> -compAPIDir <compAPIDir> [-options]");
System.out.println("");
System.out.println("\t-eclipseDir\t<eclipseDir>\tspace seperated list of directories containing Eclipse plugins");
System.out.println("\t-compXMLDir\t<compXMLDir>\tdirectory containing component.xml");
System.out.println("\t-compAPIDir\t<compAPIDir>\toutput directory of component-api.xml");
}
}