blob: 2c18e7bb04853632182d3ed0bb89761291d830dc [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2004, 2007 IBM Corporation and others.
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License 2.0
* which accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
* Contributors:
* IBM Corporation - initial API and implementation
*******************************************************************************/
package org.eclipse.wtp.releng.tools.component.api.violation;
import java.io.File;
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 javax.xml.transform.TransformerConfigurationException;
import javax.xml.transform.TransformerException;
import org.eclipse.wtp.releng.tools.component.CommandOptionParser;
import org.eclipse.wtp.releng.tools.component.IClazz;
import org.eclipse.wtp.releng.tools.component.ILibrary;
import org.eclipse.wtp.releng.tools.component.ILocation;
import org.eclipse.wtp.releng.tools.component.IPluginXML;
import org.eclipse.wtp.releng.tools.component.api.ClassUse;
import org.eclipse.wtp.releng.tools.component.api.ComponentUse;
import org.eclipse.wtp.releng.tools.component.api.ComponentUseEmitter;
import org.eclipse.wtp.releng.tools.component.api.Source;
import org.eclipse.wtp.releng.tools.component.internal.AbstractEmitter;
import org.eclipse.wtp.releng.tools.component.internal.ComponentDepends;
import org.eclipse.wtp.releng.tools.component.internal.ComponentRef;
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;
public class ComponentViolationEmitter extends AbstractEmitter
{
public static final String CONST_COMPONENT_VIOLATION_XML = "component-violation.xml";
public static final String CONST_COMPONENT_VIOLATION_HTML = "component-violation.html";
public static final String OPTION_ECLIPSE_DIR = "eclipseDir";
public static final String OPTION_COMPONENT_XML_DIR = "compXMLDir";
public static final String OPTION_COMPONENT_REF_DIR = "compRefDir";
public static final String OPTION_COMPONENT_VIOLATION_DIR = "compVioDir";
public static final String OPTION_INCLUDE = "include";
public static final String OPTION_EXCLUDE = "exclude";
public static final String OPTION_GEN_HTML = "genHTML";
public static final String OPTION_GEN_USAGE = "genUsage";
public static final String OPTION_DEBUG = "debug";
private String compViolationDir;
private Map pluginId2Plugin;
private Map fragmentId2Fragment;
private Map compLoc2CompXML;
private Map compLoc2CompRef;
private List classUseIncludes;
private List classUseIncludesMatch;
private List classUseExcludes;
private List classUseExcludesMatch;
private boolean genHTML;
private boolean genUsage;
private boolean classRefOnly;
private boolean debug;
private ComponentUseEmitter compUseEmitter;
public ComponentViolationEmitter(String compViolationDir)
{
this.compViolationDir = addTrailingSeperator(compViolationDir);
genUsage = false;
classRefOnly = false;
debug = false;
}
public void init(List eclipseDirs, List compXMLDirs, List compRefDirs)
{
compLoc2CompXML = new HashMap();
compLoc2CompRef = 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);
}
if (compRefDirs != null)
{
for (Iterator it = compRefDirs.iterator(); it.hasNext();)
{
File compRefFile = new File(addTrailingSeperator((String)it.next()));
if (compRefFile.exists())
harvestComponents(compRefFile, compLoc2CompRef);
}
}
init();
}
public void init(Map compLoc2CompXML, Map compLoc2CompRef, Map pluginId2Plugin, Map fragmentId2Fragment)
{
this.compLoc2CompXML = compLoc2CompXML;
this.compLoc2CompRef = compLoc2CompRef;
this.pluginId2Plugin = pluginId2Plugin;
this.fragmentId2Fragment = fragmentId2Fragment;
init();
}
private void init()
{
compUseEmitter = new ComponentUseEmitter(genUsage ? compViolationDir : null);
compUseEmitter.setClassUseIncludes(classUseIncludes);
compUseEmitter.setClassUseIncludesMatch(classUseIncludesMatch);
compUseEmitter.setClassUseExcludes(classUseExcludes);
compUseEmitter.setClassUseExcludesMatch(classUseExcludesMatch);
compUseEmitter.setClassRefOnly(classRefOnly);
compUseEmitter.setDebug(debug);
compUseEmitter.init(compLoc2CompXML, pluginId2Plugin, fragmentId2Fragment);
}
public Map getCompRefs()
{
return new HashMap(compLoc2CompRef);
}
public void setCompRefs(Map compLoc2CompRef)
{
this.compLoc2CompRef = compLoc2CompRef;
}
public List getClassUseIncludes()
{
return classUseIncludes;
}
public void setClassUseIncludes(List includes)
{
this.classUseIncludes = includes;
}
public List getClassUseIncludesMatch()
{
return classUseIncludesMatch;
}
public void setClassUseIncludesMatch(List includesMatch)
{
this.classUseIncludesMatch = includesMatch;
}
public List getClassUseExcludes()
{
return classUseExcludes;
}
public void setClassUseExcludes(List excludes)
{
this.classUseExcludes = excludes;
}
public List getClassUseExcludesMatch()
{
return classUseExcludesMatch;
}
public void setClassUseExcludesMatch(List excludesMatch)
{
this.classUseExcludesMatch = excludesMatch;
}
/**
* @return Returns the genHTML.
*/
public boolean isGenHTML()
{
return genHTML;
}
/**
* @param genHTML The genHTML to set.
*/
public void setGenHTML(boolean genHTML)
{
this.genHTML = genHTML;
}
public boolean getGenUsage()
{
return genUsage;
}
public void setGenUsage(boolean genUsage)
{
this.genUsage = genUsage;
}
/**
* @return Returns the classRefOnly.
*/
public boolean isClassRefOnly()
{
return classRefOnly;
}
/**
* @param classRefOnly
* The classRefOnly to set.
*/
public void setClassRefOnly(boolean classRefOnly)
{
this.classRefOnly = classRefOnly;
}
/**
* @return Returns the debug.
*/
public boolean isDebug()
{
return debug;
}
/**
* @param debug The debug to set.
*/
public void setDebug(boolean debug)
{
this.debug = debug;
}
public void genComponentViolationXML() throws IOException
{
ComponentViolationSummary summary = new ComponentViolationSummary();
for (Iterator it = compLoc2CompXML.keySet().iterator(); it.hasNext();)
summary.add(genComponentViolationXML((String)it.next()));
if (compViolationDir != null)
{
summary.save(new FileLocation(new File(compViolationDir + "index.xml")));
if (genHTML)
{
try
{
summary.saveAsHTML(new FileLocation(new File(compViolationDir + "index.html")));
}
catch (TransformerConfigurationException e)
{
e.printStackTrace();
}
catch (TransformerException e)
{
e.printStackTrace();
}
}
}
}
public ComponentUse genComponentViolationXML(String compLoc) throws IOException
{
ComponentUse compUse = null;
ComponentXML componentXML = (ComponentXML)compLoc2CompXML.get(compLoc);
if (componentXML != null)
{
componentXML.load();
compUse = compUseEmitter.genComponentUseXML(compLoc);
compUse.save();
for (Iterator pluginsIt = componentXML.getPlugins().iterator(); pluginsIt.hasNext();)
{
IPluginXML pluginXML = (IPluginXML)pluginId2Plugin.get(((Plugin)pluginsIt.next()).getId());
if (pluginXML != null)
validateComponentUse(pluginXML, compUse);
}
ComponentDepends depends = componentXML.getComponentDepends();
boolean unrestricted = depends.isUnrestricted();
List dependNames = null;
if (!unrestricted)
{
dependNames = new ArrayList();
Collection compRefs = depends.getComponentRefs();
for (Iterator it = compRefs.iterator(); it.hasNext();)
dependNames.add(((ComponentRef)it.next()).getName());
}
for (Iterator it = compLoc2CompXML.values().iterator(); it.hasNext() && compUse.getSources().size() > 0;)
{
ComponentXML compXML = (ComponentXML)it.next();
if (unrestricted || dependNames.contains(compXML.getName()))
{
compXML.load();
validateComponentUse(compXML, compUse);
}
}
for (Iterator it = compLoc2CompRef.values().iterator(); it.hasNext() && compUse.getSources().size() > 0;)
{
ComponentXML compXML = (ComponentXML)it.next();
if (unrestricted || dependNames.contains(compXML.getName()))
{
compXML.load();
validateComponentUse(compXML, compUse);
}
}
if (compViolationDir != null)
{
String compName = compUse.getName();
System.out.println("Writing component-violation.xml for " + compName);
StringBuffer sb = new StringBuffer(compViolationDir);
sb.append(compName);
sb.append('/');
compUse.setLocation(new FileLocation(new File(sb.toString() + CONST_COMPONENT_VIOLATION_XML)));
compUse.save();
if (genHTML)
{
try
{
ILocation html = new FileLocation(new File(sb.toString() + CONST_COMPONENT_VIOLATION_HTML));
compUse.setLocation(html);
compUse.saveAsHTML(html);
}
catch (TransformerConfigurationException e)
{
e.printStackTrace();
}
catch (TransformerException e)
{
e.printStackTrace();
}
}
}
}
return compUse;
}
public Source genViolation(String compLoc, IClazz clazz)
{
Source source = null;
ComponentXML componentXML = (ComponentXML)compLoc2CompXML.get(compLoc);
if (componentXML != null)
{
boolean valid = false;
source = compUseEmitter.genUse(clazz);
for (Iterator pluginsIt = componentXML.getPlugins().iterator(); pluginsIt.hasNext();)
{
IPluginXML pluginXML = (IPluginXML)pluginId2Plugin.get(((Plugin)pluginsIt.next()).getId());
if (pluginXML != null)
{
if (validateComponentUse(pluginXML, source))
{
valid = true;
break;
}
}
}
ComponentDepends depends = componentXML.getComponentDepends();
boolean unrestricted = depends.isUnrestricted();
List dependNames = null;
if (!unrestricted)
{
dependNames = new ArrayList();
Collection compRefs = depends.getComponentRefs();
for (Iterator it = compRefs.iterator(); it.hasNext();)
dependNames.add(((ComponentRef)it.next()).getName());
}
if (!valid)
{
for (Iterator it = compLoc2CompXML.values().iterator(); it.hasNext();)
{
ComponentXML comp = (ComponentXML)it.next();
if (unrestricted || dependNames.contains(comp.getName()))
{
valid = validateComponentUse(comp, source);
if (valid)
break;
}
}
}
if (!valid)
{
for (Iterator it = compLoc2CompRef.values().iterator(); it.hasNext();)
{
ComponentXML comp = (ComponentXML)it.next();
if (unrestricted || dependNames.contains(comp.getName()))
{
valid = validateComponentUse(comp, source);
if (valid)
break;
}
}
}
}
return source;
}
private void validateComponentUse(IPluginXML pluginXML, ComponentUse compUse)
{
List sources = compUse.getSources();
for (int i = 0; i < sources.size(); i++)
{
if (validateComponentUse(pluginXML, (Source)sources.get(i)))
{
sources.remove(i);
i--;
}
}
}
private boolean validateComponentUse(IPluginXML pluginXML, Source source)
{
List classUses = new ArrayList(source.getClassUses());
for (int j = 0; j < classUses.size(); j++)
{
ClassUse classUse = (ClassUse)classUses.get(j);
if (validateComponentUse(pluginXML, classUse))
{
classUses.remove(j);
j--;
}
}
return classUses.size() == 0;
}
private boolean validateComponentUse(IPluginXML pluginXML, ClassUse classUse)
{
String classUseName = classUse.getName();
int dollarSign = classUseName.indexOf('$');
if (dollarSign != -1)
classUseName = classUseName.substring(0, dollarSign);
List libs = pluginXML.getLibraries();
for (Iterator libsIt = libs.iterator(); libsIt.hasNext();)
{
ILibrary lib = (ILibrary)libsIt.next();
Map types = lib.getTypes();
if (types.containsKey(classUseName))
return true;
}
return false;
}
private void validateComponentUse(ComponentXML compXML, ComponentUse compUse)
{
List sources = compUse.getSources();
for (int i = 0; i < sources.size(); i++)
{
if (validateComponentUse(compXML, (Source)sources.get(i)))
{
sources.remove(i);
i--;
}
}
}
private boolean validateComponentUse(ComponentXML compXML, Source source)
{
List classUses = new ArrayList(source.getClassUses());
for (int j = 0; j < classUses.size(); j++)
{
ClassUse classUse = (ClassUse)classUses.get(j);
if (validateComponentUse(compXML, classUse))
{
classUses.remove(j);
j--;
}
}
return classUses.size() == 0;
}
private boolean validateComponentUse(ComponentXML compXML, ClassUse classUse)
{
String classUseName = classUse.getName();
Collection pkgs = compXML.getPackages();
for (Iterator pkgsIt = pkgs.iterator(); pkgsIt.hasNext();)
{
Package pkg = (Package)pkgsIt.next();
String pkgName = pkg.getName();
if (classUseName.startsWith(pkgName) && classUseName.substring(pkgName.length() + 1).indexOf('.') == -1)
{
Collection types = pkg.getTypes();
for (Iterator typesIt = types.iterator(); typesIt.hasNext();)
{
Type type = (Type)typesIt.next();
if (classUseName.equals(type.getName()))
{
if (!classUse.isReference() || type.isReference())
if (!classUse.isSubclass() || type.isSubclass())
if (!classUse.isImplement() || type.isImplement())
if (!classUse.isInstantiate() || type.isInstantiate())
return true;
return false;
}
}
return pkg.isApi();
}
}
return false;
}
public static void main(String[] args)
{
CommandOptionParser optionParser = new CommandOptionParser(args);
Map options = optionParser.getOptions();
List eclipseDir = (List)options.get(ComponentViolationEmitter.OPTION_ECLIPSE_DIR);
List compXMLDir = (List)options.get(ComponentViolationEmitter.OPTION_COMPONENT_XML_DIR);
List compRefDir = (List)options.get(ComponentViolationEmitter.OPTION_COMPONENT_REF_DIR);
List compViolationDir = (List)options.get(ComponentViolationEmitter.OPTION_COMPONENT_VIOLATION_DIR);
List includes = (List)options.get(ComponentViolationEmitter.OPTION_INCLUDE);
List excludes = (List)options.get(ComponentViolationEmitter.OPTION_EXCLUDE);
List genHTML = (List)options.get(ComponentViolationEmitter.OPTION_GEN_HTML);
List genUsage = (List)options.get(ComponentViolationEmitter.OPTION_GEN_USAGE);
List classRefOnly = (List)options.get(ComponentUseEmitter.OPTION_CLASS_REF_ONLY);
List debug = (List)options.get(ComponentUseEmitter.OPTION_DEBUG);
if (eclipseDir == null || compXMLDir == null || compViolationDir == null || eclipseDir.size() < 1 || compXMLDir.size() < 1 || compViolationDir.size() < 1)
{
printUsage();
System.exit(-1);
}
List includesStart = null;
List includesMatch = null;
if (includes != null)
{
for (Iterator it = includes.iterator(); it.hasNext();)
{
String s = (String)it.next();
if (s.charAt(0) == '*' && s.charAt(s.length() - 1) == '*')
{
if (includesMatch == null)
includesMatch = new ArrayList(1);
includesMatch.add(s.substring(1, s.length() - 1));
}
else
{
if (includesStart == null)
includesStart = new ArrayList(1);
includesStart.add(s);
}
}
}
List excludesStart = null;
List excludesMatch = null;
if (excludes != null)
{
for (Iterator it = excludes.iterator(); it.hasNext();)
{
String s = (String)it.next();
if (s.charAt(0) == '*' && s.charAt(s.length() - 1) == '*')
{
if (excludesMatch == null)
excludesMatch = new ArrayList(1);
excludesMatch.add(s.substring(1, s.length() - 1));
}
else
{
if (excludesStart == null)
excludesStart = new ArrayList(1);
excludesStart.add(s);
}
}
}
ComponentViolationEmitter compViolationEmitter = new ComponentViolationEmitter((String)compViolationDir.get(0));
compViolationEmitter.setClassUseIncludes(includesStart);
compViolationEmitter.setClassUseExcludesMatch(includesMatch);
compViolationEmitter.setClassUseExcludes(excludesStart);
compViolationEmitter.setClassUseExcludesMatch(excludesMatch);
compViolationEmitter.setGenHTML(genHTML != null);
compViolationEmitter.setGenUsage(genUsage != null);
compViolationEmitter.setClassRefOnly(classRefOnly != null);
compViolationEmitter.setDebug(debug != null);
compViolationEmitter.init(eclipseDir, compXMLDir, compRefDir);
try
{
compViolationEmitter.genComponentViolationXML();
}
catch (IOException ioe)
{
ioe.printStackTrace();
}
}
private static void printUsage()
{
System.out.println("Usage: java org.eclipse.wtp.releng.tools.component.violaion.ComponentViolationEmitter -eclipseDir <eclipseDir> -compXMLDir <compXMLDir> -compVioDir <compVioDir> [-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>\tdirectories containing component.xml that will be checked for API violations");
System.out.println("\t-compVioDir\t<compVioDir>\toutput directory of component-violation.xml");
System.out.println("");
System.out.println("where options include:");
System.out.println("");
System.out.println("\t-compRefDir\t<compRefDir>\tdirectories containing component.xml being referenced");
System.out.println("\t-include\t<include>\tspace seperated packages to include");
System.out.println("\t-exclude\t<exclude>\tspace seperated packages to exclude");
System.out.println("\t-genHTML\t\t\tgenerate violation report in HTML");
System.out.println("\t-genUsage\t\t\tgenerate component-use.xml");
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)");
}
}