blob: 3a934b6c4ba81d877f7b6b8b806057ac447ab8c6 [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.adopters;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.FileWriter;
import java.io.IOException;
import java.io.Writer;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.TreeMap;
import org.eclipse.wtp.releng.tools.component.CommandOptionParser;
import org.eclipse.wtp.releng.tools.component.ILocation;
import org.eclipse.wtp.releng.tools.component.api.ComponentXMLVisitor;
import org.eclipse.wtp.releng.tools.component.internal.ComponentXML;
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;
/**
* This class is used to take the output usage.xml files generated from SimpleClass2Reference
* and combine them into a helpful CSV or HTML file format displaying API and non-API internal
* usage for each logically grouped component team in WTP. The main method can be invoked with
* the following three command line arguments:
*
* "src" = the zip or folder location of the usage.xml files
* "api" = the zip or folder location of the component.xml files defining the API base
* "output" = the location of the output csv and html combined files
*/
public class CombineClass2Reference extends ComponentTeamScanner implements IOutputConstants {
// Command Line arguments
public static final String ARG_SOURCE = "src"; //$NON-NLS-1$
public static final String ARG_OUTPUT = "output"; //$NON-NLS-1$
public static final String ARG_API = "api"; //$NON-NLS-1$
public static final String ARG_SHOW_ADOPTER_NUMBERS = "showAdopterNumbers"; //$NON-NLS-1$
public static final String ARG_SHOW_DETAILED_NUMBERS = "showDetailedNumbers"; //$NON-NLS-1$
// Instance variables for command line arguments
private Collection src;
private String output;
private Collection api;
private boolean shouldShowIndividualAdopters = false;
private boolean shouldShowDetailedNumbers = false;
private List contactNames;
// Class variables for String values
private static final String CLASS_CVS_FILE_EXTENSION = ".class.csv"; //$NON-NLS-1$
private static final String PACKAGE_CVS_FILE_EXTENSION = ".pkg.csv"; //$NON-NLS-1$
private static final String PLUGIN_CVS_FILE_EXTENSION = ".plugin.csv"; //$NON-NLS-1$
private static final String CLASS_HTML_FILE_EXTENSION = ".class.html"; //$NON-NLS-1$
private static final String PACKAGE_HTML_FILE_EXTENSION = ".pkg.html"; //$NON-NLS-1$
private static final String PLUGIN_HTML_FILE_EXTENSION = ".plugin.html"; //$NON-NLS-1$
private static final String COMBINE_PLUGINS_FILE = "org/eclipse/wtp/releng/tools/component/xsl/combine-plugin2ref.xsl"; //$NON-NLS-1$
private static final String COMBINE_PACKAGES_FILE = "org/eclipse/wtp/releng/tools/component/xsl/combine-pkg2ref.xsl"; //$NON-NLS-1$
private static final String COMBINE_CLASSES_FILE = "org/eclipse/wtp/releng/tools/component/xsl/combine-class2ref.xsl"; //$NON-NLS-1$
// Class variables for reference usage constants
private static final int CLASS_USAGE = 0;
private static final int PACKAGE_USAGE = 1;
private static final int PLUGIN_USAGE = 2;
/**
* The UsageCount class is a simple caching mechanism to track API and non API usages.
*/
private class UsageCount {
/**
* Uses in an accordance with API contract
*/
public int apiUse = 0;
/**
* Internal or other non API usages outside API contract
*/
public int nonAPIUse = 0;
private HashMap methodRefs = null;
private HashMap fieldRefs = null;
private HashMap nestedUsageCounts;
/**
* @return HashMap of the nested MethodRefs for this class usage
*/
public HashMap getMethodRefs() {
if (methodRefs == null)
methodRefs = new HashMap();
return methodRefs;
}
/**
* @return HashMap of nested FieldRefs for this class usage
*/
public HashMap getFieldRefs() {
if (fieldRefs == null)
fieldRefs = new HashMap();
return fieldRefs;
}
/**
* @return HashMap of nested Usage Counts for specific product details
*/
public HashMap getNestedUsageCounts() {
if (nestedUsageCounts==null)
nestedUsageCounts = new HashMap();
return nestedUsageCounts;
}
/**
* Get the nested usage count according to the string contact passed or create a new one.
*
* @param contactInfo
* @return UsageCount
*/
public UsageCount getNestedUsageCount(String contactInfo) {
UsageCount usageCount = (UsageCount) getNestedUsageCounts().get(contactInfo);
if (usageCount == null) {
usageCount = new UsageCount();
getNestedUsageCounts().put(contactInfo, usageCount);
}
return usageCount;
}
/**
* Add the given method refs to the existing method refs
* @param someMethodRefs Collection
*/
public void addMethodRefs(Collection someMethodRefs) {
for (Iterator i = someMethodRefs.iterator(); i.hasNext();) {
MethodRef aMethodRef = (MethodRef) i.next();
if (!getMethodRefs().keySet().contains(aMethodRef.getName())) {
getMethodRefs().put(aMethodRef.getName(), new Integer(aMethodRef.getRefCount()));
} else {
int updatedCount = aMethodRef.getRefCount()+ ((Integer)getMethodRefs().get(aMethodRef.getName())).intValue();
getMethodRefs().put(aMethodRef.getName(), new Integer(updatedCount));
}
}
}
/**
* Add the given field refs to the existing field refs
* @param someFieldRefs Collection
*/
public void addFieldRefs(Collection someFieldRefs) {
for (Iterator i = someFieldRefs.iterator(); i.hasNext();) {
FieldRef aFieldRef = (FieldRef) i.next();
if (!getFieldRefs().keySet().contains(aFieldRef.getName())) {
getFieldRefs().put(aFieldRef.getName(), new Integer(aFieldRef.getRefCount()));
} else {
int updatedCount = aFieldRef.getRefCount()+ ((Integer)getFieldRefs().get(aFieldRef.getName())).intValue();
getFieldRefs().put(aFieldRef.getName(), new Integer(updatedCount));
}
}
}
/**
* Add the given methodrefs data into the existing method refs data
* @param someMethodRefs HashMap
*/
public void addMethodRefs(HashMap someMethodRefs) {
if (someMethodRefs == null)
return;
for (Iterator i = someMethodRefs.keySet().iterator(); i.hasNext();) {
String aMethodRefName = (String) i.next();
if (!getMethodRefs().keySet().contains(aMethodRefName)) {
getMethodRefs().put(aMethodRefName, someMethodRefs.get(aMethodRefName));
} else {
int updatedCount = ((Integer)someMethodRefs.get(aMethodRefName)).intValue()+ ((Integer)getMethodRefs().get(aMethodRefName)).intValue();
getMethodRefs().put(aMethodRefName, new Integer(updatedCount));
}
}
}
/**
* Add the given field ref data into the existing field ref data
* @param someFieldRefs
*/
public void addFieldRefs(HashMap someFieldRefs) {
if (someFieldRefs == null)
return;
for (Iterator i = someFieldRefs.keySet().iterator(); i.hasNext();) {
String aFieldRefName = (String) i.next();
if (!getFieldRefs().keySet().contains(aFieldRefName)) {
getFieldRefs().put(aFieldRefName, someFieldRefs.get(aFieldRefName));
} else {
int updatedCount = ((Integer)someFieldRefs.get(aFieldRefName)).intValue()+ ((Integer)getFieldRefs().get(aFieldRefName)).intValue();
getFieldRefs().put(aFieldRefName, new Integer(updatedCount));
}
}
}
}
/**
* Default Constructor
*/
public CombineClass2Reference() {
super();
}
/**
* @return Collection of source usage.xml files
*/
public Collection getSrc() {
return src;
}
/**
* Cache the command line argument for where the source usage.xml files are
* @param src
*/
public void setSrc(Collection src) {
this.src = src;
}
/**
* @return String location of the output for the generated files
*/
public String getOutput() {
return output;
}
/**
* Cache the command line argument output location for the generated files
* @param output
*/
public void setOutput(String output) {
this.output = output;
}
/**
* @return Collection of API locations for component.xml files
*/
public Collection getApi() {
return api;
}
/**
* Cache the command line argument for where the API component.xml file locations are
* @param api
*/
public void setApi(Collection api) {
this.api = api;
}
/**
* This is a helper method to create a map of plugin ids and associated component.xml files.
* These component.xml files are the one specified by the collection in the "api" command
* line argument.
*
* @return Map of plugin ids to component.xml files
*/
private Map collectComponentXMLFiles() {
Map pluginId2CompXML = new HashMap();
if (getApi() != null) {
for (Iterator i = getApi().iterator(); i.hasNext();) {
// For each API file or location, create a Location object
ILocation apiLocation = Location.createLocation(new File((String)i.next()));
// Create a visitor to traverse the location and collect all contained component.xml files
ComponentXMLVisitor compXMLVisitor = new ComponentXMLVisitor();
apiLocation.accept(compXMLVisitor);
// For each component.xml found, find the plugins it corresponds to
for (Iterator it = compXMLVisitor.getCompXMLs().iterator(); it.hasNext();) {
ComponentXML compXML = (ComponentXML)it.next();
// For each plugin, add a mapping for the plugin to the component.xml file
for (Iterator it2 = compXML.getPlugins().iterator(); it2.hasNext();) {
pluginId2CompXML.put(((Plugin)it2.next()).getId(), compXML);
}
}
}
}
return pluginId2CompXML;
}
/**
* This method drives the combination of the usages in the usage.xml files and based on the
* API information in the component.xml files in the given map, it will appropriately
* tabulate non-API and API usage information for class references, package references, and
* plugin references.
*
* @param pluginId2CompXML
*/
private void processUsages(Map pluginId2CompXML) {
// Iterate over all of the source usage.xml files provided by the "src" collection argument
for (Iterator it = getSrc().iterator(); it.hasNext();) {
FileInputStream fis = null;
try {
// Open a file input stream on the current source usage.xml file
String file = (String)it.next();
fis = new FileInputStream(file);
// Create a references object to parse the usage.xml file and cache the references
References refs = new References();
refs.load(fis);
String currentContactName = refs.getContactInfo();
// Only cache the adopter contact info if we are showing individual adopter usage info
if (isShouldShowIndividualAdopters())
cacheContactName(currentContactName);
// Iterate through the list of plugins referenced in usage.xml file
for (Iterator it2 = refs.getPluginRefs().iterator(); it2.hasNext();) {
PluginRef pluginRef = (PluginRef)it2.next();
String pluginId = pluginRef.getId();
// Retrieve the corresponding component.xml file for the current plugin referenced
ComponentXML compXML = (ComponentXML)pluginId2CompXML.get(pluginId);
// Get the corresponding component team from that plugin id
ComponentTeam compTeam = getComponentTeam(pluginId);
// Iterate through the class references in that plugin reference
for (Iterator it3 = pluginRef.getClassRefs().iterator(); it3.hasNext();) {
ClassRef classRef = (ClassRef)it3.next();
// Update the component team's cached reference counts with the current class reference
updateComponentTeamUsageCounts(compTeam,compXML,classRef,pluginId,currentContactName);
}
}
} catch (Throwable t) {
throw new RuntimeException(t);
} finally {
// Close the current file input stream
if (fis != null) {
try {
fis.close();
} catch (IOException ioe) {}
}
}
}
}
/**
* Cache a given contact for an adopter usage to show individual adopter usage
* @param aContactName String
*/
private void cacheContactName(String aContactName) {
if (!getContactNames().contains(aContactName))
getContactNames().add(aContactName);
}
/**
* @return List of all the known contact names. This is empty if not showing adopter numbers.
*/
private List getContactNames() {
if (contactNames == null)
contactNames = new ArrayList();
return contactNames;
}
/**
* The execute method drives the combination operation by collecting component.xml files
* from the "api" command line arugment, processing references in usage.xml files from the
* "src" command line argument, and then writing them out in CSV and HTML file format to
* a location specified by the "output" command line argument.
*/
private void execute() {
// Collect the plugin to component.xml file map from the specified collection of
// component.xml files in the "api" command line argument.
Map pluginId2CompXML = collectComponentXMLFiles();
// Process the usages in the usage.xml files provided by the "src" collection command arg
processUsages(pluginId2CompXML);
// Generate the output files for combined usage in CSV format
generateCSVFiles();
// Generate the output files for combined usage in HTML format
generateHTMLFiles();
}
private List getOrderedReferencedCountKeys(final TreeMap referenceCounts) {
String[] keys = (String[]) referenceCounts.keySet().toArray(new String[referenceCounts.keySet().size()]);
Comparator nonAPIComparator = new Comparator() {
public int compare(Object o1, Object o2) {
UsageCount usageCount1 = (UsageCount) referenceCounts.get(o1);
UsageCount usageCount2 = (UsageCount) referenceCounts.get(o2);
return usageCount2.nonAPIUse-usageCount1.nonAPIUse;
}
};
Arrays.sort(keys,nonAPIComparator);
return Arrays.asList(keys);
}
/**
* Helper method to update the passed in component team's cached usage counts based on the
* given class reference and known API's in the provided component.xml file.
*
* @param compTeam
* @param compXML
* @param classRef
* @param pluginId
*/
private void updateComponentTeamUsageCounts(ComponentTeam compTeam, ComponentXML compXML, ClassRef classRef, String pluginId, String contactInfo) {
String name = classRef.getName();
String pkgName = getPackageName(name);
// Get the usage count object for the current referenced class
UsageCount usageCount = getUsageCount(compXML, classRef, pkgName, name.substring(pkgName.length() + 1));
// Get the component team's cached class reference usage count
UsageCount classUsageCount = (UsageCount)compTeam.getClassReferenceCounts().get(name);
if (classUsageCount == null)
classUsageCount = new UsageCount();
classUsageCount.addMethodRefs(usageCount.getMethodRefs());
classUsageCount.addFieldRefs(usageCount.getFieldRefs());
UsageCount nestedClassUsageCount = classUsageCount.getNestedUsageCount(contactInfo);
// Update the component team's cached class usage count with the current usage count
classUsageCount.apiUse += usageCount.apiUse;
classUsageCount.nonAPIUse += usageCount.nonAPIUse;
nestedClassUsageCount.apiUse += usageCount.apiUse;
nestedClassUsageCount.nonAPIUse += usageCount.nonAPIUse;
// Put the updated nested class Usage count back into the cache
classUsageCount.getNestedUsageCounts().put(contactInfo, nestedClassUsageCount);
// Put the updated class Usage count back into the cache on the component team
compTeam.getClassReferenceCounts().put(name, classUsageCount);
// Get the component team's cached package reference usage count
UsageCount pkgUsageCount = (UsageCount)compTeam.getPackageReferenceCounts().get(pkgName);
if (pkgUsageCount == null)
pkgUsageCount = new UsageCount();
UsageCount nestedPkgUsageCount = pkgUsageCount.getNestedUsageCount(contactInfo);
// Update the component team's cached package reference count with current usage count
pkgUsageCount.apiUse += usageCount.apiUse;
pkgUsageCount.nonAPIUse += usageCount.nonAPIUse;
nestedPkgUsageCount.apiUse += usageCount.apiUse;
nestedPkgUsageCount.nonAPIUse += usageCount.nonAPIUse;
// Put the updated nested package usage count back into the cache
pkgUsageCount.getNestedUsageCounts().put(contactInfo, nestedPkgUsageCount);
// Put the updated package usage count back into the cache on the component team
compTeam.getPackageReferenceCounts().put(pkgName, pkgUsageCount);
// Get the component team's cached plugin reference usage count
UsageCount pluginUsageCount = (UsageCount)compTeam.getPluginReferenceCounts().get(pluginId);
if (pluginUsageCount == null)
pluginUsageCount = new UsageCount();
UsageCount nestedPluginUsageCount = pluginUsageCount.getNestedUsageCount(contactInfo);
// Update the component team's cached plugin reference count with current usage count
pluginUsageCount.apiUse += usageCount.apiUse;
pluginUsageCount.nonAPIUse += usageCount.nonAPIUse;
nestedPluginUsageCount.apiUse += usageCount.apiUse;
nestedPluginUsageCount.nonAPIUse += usageCount.nonAPIUse;
// Put the update nested plugin usage count back into the cache
pluginUsageCount.getNestedUsageCounts().put(contactInfo, nestedPluginUsageCount);
// Put the update plugin usage count back into the cache on the component team
compTeam.getPluginReferenceCounts().put(pluginId, pluginUsageCount);
}
/**
* This method will cache and return the usages of the class reference into a UsageCount object.
* It will check the passed in component.xml to see if the reference is using a qualified API
* or if it is an internal usage. The types of references which qualify are class references,
* subclasses, implementers, or instantiators.
*
* @param compXML
* @param classRef
* @param pkgName
* @param localName
* @return UsageCount
*/
private UsageCount getUsageCount(ComponentXML compXML, ClassRef classRef, String pkgName, String localName) {
// Create UsageCount object
UsageCount usageCount = new UsageCount();
int refCount = classRef.getRefCount();
int subclassCount = classRef.getSubclassCount();
int implCount = classRef.getImplementCount();
int instantiateCount = classRef.getInstantiateCount();
// If the component.xml is null, or the package referenced does not exist in the component.xml
// we know the reference cannot be an API use, so just add all the references to the non-API
// count and return.
if (compXML==null || compXML.getPackage(pkgName)==null) {
usageCount.nonAPIUse = refCount + subclassCount + implCount + instantiateCount;
usageCount.addMethodRefs(classRef.getMethodRefs());
usageCount.addFieldRefs(classRef.getFieldRefs());
return usageCount;
}
// Get the referenced package from the component.xml file
Package pkg = compXML.getPackage(pkgName);
// Get the references type from the references pckage in the component.xml file
Type type = pkg.getType(localName);
if (type == null) {
// If the type is null, but the package is an API package, update the API count
if (pkg.isApi()) {
usageCount.apiUse = refCount + subclassCount + implCount + instantiateCount;
return usageCount;
}
// If the type is null, but the package is not API, update the non-API count
usageCount.nonAPIUse = refCount + subclassCount + implCount + instantiateCount;
return usageCount;
}
// Handle the cases where we have a valid component.xml package and type.
// If the type is not a valid API reference, increment the non-API ref count
if (!type.isReference()) {
usageCount.nonAPIUse += classRef.getRefCount();
usageCount.addMethodRefs(classRef.getMethodRefs());
usageCount.addFieldRefs(classRef.getFieldRefs());
} else {
// Otherwise, increment API ref count
usageCount.apiUse += classRef.getRefCount();
}
// If the type is not a valid API subclass, increment the non-API subclass count
if (!type.isSubclass()) {
usageCount.nonAPIUse += classRef.getSubclassCount();
} else {
// Otherwise, increment API subclass count
usageCount.apiUse += classRef.getSubclassCount();
}
// If the type is not a valid API implementor, increment the non-API implementor count
if (!type.isImplement()) {
usageCount.nonAPIUse += classRef.getImplementCount();
} else {
// Otherwise increment API implementor count
usageCount.apiUse += classRef.getImplementCount();
}
// If the type is not a valid API instantiation, increment the non-API instantiation count
if (!type.isInstantiate()) {
usageCount.nonAPIUse += classRef.getInstantiateCount();
} else {
// Otherwise increment API instantation count
usageCount.apiUse += classRef.getInstantiateCount();
}
// Return the usage count
return usageCount;
}
/**
* Given a fully qualified class name, return the package name.
*
* @param a fully qualified className
* @return the package name
*/
private String getPackageName(String className) {
int i = className.lastIndexOf('.');
if (i != -1)
return className.substring(0, i);
return ""; //$NON-NLS-1$
}
/**
* Generate CSV format files displaying the combined internal and API usage for classes,
* packages, and plugins.
*/
private void generateCSVFiles() {
FileWriter classWriter = null;
FileWriter pkgWriter = null;
FileWriter pluginWriter = null;
try {
// Create the file writers
classWriter = new FileWriter(getOutput() + CLASS_CVS_FILE_EXTENSION);
pkgWriter = new FileWriter(getOutput() + PACKAGE_CVS_FILE_EXTENSION);
pluginWriter = new FileWriter(getOutput() + PLUGIN_CVS_FILE_EXTENSION);
// For each component team, write the CVS file content for the component's usage
for (int i=0; i<getComponentTeams().size(); i++) {
ComponentTeam compTeam = (ComponentTeam) getComponentTeams().get(i);
writeCompTeamCSV(compTeam, classWriter, pkgWriter, pluginWriter);
}
} catch (IOException ioe) {
throw new RuntimeException(ioe);
} finally {
// Close the class usage file writer
if (classWriter != null) {
try {
classWriter.close();
} catch (IOException ioe) {}
}
// Close the package usage file writer
if (pkgWriter != null) {
try {
pkgWriter.close();
} catch (IOException ioe) {}
}
// Close the plugin usage file writer
if (pluginWriter != null) {
try {
pluginWriter.close();
} catch (IOException ioe) {}
}
}
}
/**
* This method drives the creation of the CSV file contents for the given component team
* and corresponding writers.
*
* @param compTeam
* @param classWriter
* @param pkgWriter
* @param pluginWriter
* @throws IOException
*/
private void writeCompTeamCSV(ComponentTeam compTeam, Writer classWriter, Writer pkgWriter, Writer pluginWriter) throws IOException {
writeCompTeamCSV(compTeam,classWriter,compTeam.getClassReferenceCounts());
writeCompTeamCSV(compTeam,pkgWriter,compTeam.getPackageReferenceCounts());
writeCompTeamCSV(compTeam,pluginWriter,compTeam.getPluginReferenceCounts());
}
/**
* This is a helper method to write out the given component team's reference counts in
* a CSV file format.
*
* @param compTeam
* @param writer
* @param referenceCounts
* @throws IOException
*/
private void writeCompTeamCSV(ComponentTeam compTeam, Writer writer, TreeMap referenceCounts) throws IOException {
// Write the team name
writer.write(compTeam.getTeamName());
writer.write(LINE_BREAK);
// Order reference counts by usage type
List orderedKeys = getOrderedReferencedCountKeys(referenceCounts);
// For each name key, retrieve the corresponding usage count values
for (Iterator it = orderedKeys.iterator(); it.hasNext();) {
String name = (String)it.next();
// Get the corresponding usage count for the name key
UsageCount usageCount = (UsageCount)referenceCounts.get(name);
// Write out the internal and api usages
writer.write(name);
writer.write(COMMA);
writer.write(String.valueOf(usageCount.nonAPIUse));
writer.write(COMMA);
writer.write(String.valueOf(usageCount.apiUse));
writer.write(LINE_BREAK);
}
writer.write(LINE_BREAK);
}
/**
* Generate the HTML files for comibined usage ref counts of classes, packages, and plugins.
*/
private void generateHTMLFiles() {
try {
// Create a new output stream
ByteArrayOutputStream baos = new ByteArrayOutputStream();
baos.write(XML_ROOT_BEGIN.getBytes());
// For each component team, write out the combined component team specific usage data
for (int i=0; i<getComponentTeams().size(); i++) {
ComponentTeam compTeam = (ComponentTeam) getComponentTeams().get(i);
writeCompTeamXML(compTeam, baos);
}
// Close the output stream
baos.write(XML_ROOT_END.getBytes());
baos.close();
// Create a byte array from the output stream contents
byte[] content = baos.toByteArray();
// Write out the byte array xml to a html file output stream for the plugin references
// This transform will do a XSLT operation using the file combine-plugin2ref.xsl
XSLUtil.transform (
ClassLoader.getSystemResourceAsStream(COMBINE_PLUGINS_FILE),
new ByteArrayInputStream(content),
new FileOutputStream(getOutput() + PLUGIN_HTML_FILE_EXTENSION)
);
// Write out the byte array xml to a html file output stream for the package references
// This transform will do a XSLT operation using the file combine-pkg2ref.xsl
XSLUtil.transform (
ClassLoader.getSystemResourceAsStream(COMBINE_PACKAGES_FILE),
new ByteArrayInputStream(content),
new FileOutputStream(getOutput() + PACKAGE_HTML_FILE_EXTENSION)
);
// Write out the byte array xml to a html file output stream for the class references
// This transform will do a XSLT operation using the file combine-class2ref.xsl
XSLUtil.transform (
ClassLoader.getSystemResourceAsStream(COMBINE_CLASSES_FILE),
new ByteArrayInputStream(content),
new FileOutputStream(getOutput() + CLASS_HTML_FILE_EXTENSION)
);
} catch (Throwable t) {
t.printStackTrace();
throw new RuntimeException(t);
}
}
/**
* This method will drive the combined output file html contents for the given component
* team in xml format.
*
* @param compTeam
* @param baos
* @throws IOException
*/
private void writeCompTeamXML(ComponentTeam compTeam, ByteArrayOutputStream baos) throws IOException {
baos.write("<team lead=\"".getBytes()); //$NON-NLS-1$
baos.write(compTeam.getTeamName().getBytes());
baos.write("\">".getBytes()); //$NON-NLS-1$
for (Iterator nestedKeys = getContactNames().iterator(); nestedKeys.hasNext();) {
String currentKey = (String) nestedKeys.next();
baos.write("<source name=\"".getBytes()); //$NON-NLS-1$
baos.write(currentKey.getBytes());
baos.write("\"/>".getBytes()); //$NON-NLS-1$
}
writeCompTeamXML(baos,CLASS_USAGE,compTeam.getClassReferenceCounts());
writeCompTeamXML(baos,PACKAGE_USAGE,compTeam.getPackageReferenceCounts());
writeCompTeamXML(baos,PLUGIN_USAGE,compTeam.getPluginReferenceCounts());
baos.write("</team>".getBytes()); //$NON-NLS-1$
}
/**
* This method will write in xml format the combined output for the given component team for
* the non API and API usage for the class usage file, the package usage file, and the plugin
* usage file.
*
* @param baos ByteArrayOutputStream
* @param usage class, package, or plugin?
* @param referenceCounts TreeMap
* @throws IOException
*/
private void writeCompTeamXML(ByteArrayOutputStream baos, int usage, TreeMap referenceCounts) throws IOException {
// Order reference counts by usage type
List orderedKeys = getOrderedReferencedCountKeys(referenceCounts);
// Iterate over the name keys of the references tree map
for (Iterator it = orderedKeys.iterator(); it.hasNext();) {
String name = (String)it.next();
// Retrieve the appropriate UsageCount from the map given the current name key
UsageCount usageCount = (UsageCount) referenceCounts.get(name);
switch (usage) {
// Class reference
case 0:
baos.write("<class name=\"".getBytes()); //$NON-NLS-1$
break;
// Package reference
case 1:
baos.write("<package name=\"".getBytes()); //$NON-NLS-1$
break;
// Plugin reference
case 2:
baos.write("<plugin id=\"".getBytes()); //$NON-NLS-1$
break;
}
// Write the api and internal usage for the current reference
baos.write(name.getBytes());
baos.write("\" api=\"".getBytes()); //$NON-NLS-1$
baos.write(String.valueOf(usageCount.apiUse).getBytes());
baos.write("\" internal=\"".getBytes()); //$NON-NLS-1$
baos.write(String.valueOf(usageCount.nonAPIUse).getBytes());
baos.write("\">".getBytes()); //$NON-NLS-1$
for (Iterator nestedKeys = getContactNames().iterator(); nestedKeys.hasNext();) {
String currentKey = (String) nestedKeys.next();
UsageCount nestedCount = usageCount.getNestedUsageCount(currentKey);
// Add nested usages for each source
baos.write("<source name=\"".getBytes()); //$NON-NLS-1$
baos.write(currentKey.getBytes());
baos.write("\" api=\"".getBytes()); //$NON-NLS-1$
baos.write(String.valueOf(nestedCount.apiUse).getBytes());
baos.write("\" internal=\"".getBytes()); //$NON-NLS-1$
baos.write(String.valueOf(nestedCount.nonAPIUse).getBytes());
baos.write("\"/>".getBytes()); //$NON-NLS-1$
}
// Add detailed sections for method and field information if necessary
if (isShouldShowDetailedNumbers())
showDetailedMethodAndFieldSections(baos, usageCount);
// Close out current entry type
switch (usage) {
// Class reference
case 0:
baos.write("</class>".getBytes()); //$NON-NLS-1$
break;
// Package reference
case 1:
baos.write("</package>".getBytes()); //$NON-NLS-1$
break;
// Plugin reference
case 2:
baos.write("</plugin>".getBytes()); //$NON-NLS-1$
break;
}
}
}
/**
* This helper method will add the detailed numbers for the method and field usage for a particular
* class UsageCount.
*
* @param baos
* @param usageCount
* @throws IOException
*/
private void showDetailedMethodAndFieldSections(ByteArrayOutputStream baos, UsageCount usageCount) throws IOException {
// Serialize out the method refs for the class usage report
for (Iterator nestedMethodRefs = usageCount.getMethodRefs().keySet().iterator(); nestedMethodRefs.hasNext();) {
String currentKey = (String) nestedMethodRefs.next();
int count = ((Integer)usageCount.getMethodRefs().get(currentKey)).intValue();
// Add nested usages for each source
baos.write("<method name=\"".getBytes()); //$NON-NLS-1$
baos.write(encode(currentKey).getBytes());
baos.write("\" internal=\"".getBytes()); //$NON-NLS-1$
baos.write(String.valueOf(count).getBytes());
baos.write("\"/>".getBytes()); //$NON-NLS-1$
}
// Serialize out the field refs for the class usage report
for (Iterator nestedFieldRefs = usageCount.getFieldRefs().keySet().iterator(); nestedFieldRefs.hasNext();) {
String currentKey = (String) nestedFieldRefs.next();
int count = ((Integer)usageCount.getFieldRefs().get(currentKey)).intValue();
// Add nested usages for each source
baos.write("<field name=\"".getBytes()); //$NON-NLS-1$
baos.write(encode(currentKey).getBytes());
baos.write("\" internal=\"".getBytes()); //$NON-NLS-1$
baos.write(String.valueOf(count).getBytes());
baos.write("\"/>".getBytes()); //$NON-NLS-1$
}
}
/**
* Encode all occurences of "<" with "&lt;" in the given string
*
* @param s String
* @return encoded String
*/
protected String encode(String s) {
int index = s.indexOf('<');
if (index != -1) {
StringBuffer sb = new StringBuffer(s);
while (index != -1) {
sb.deleteCharAt(index);
sb.insert(index, new char[] {'&', 'l', 't', ';'}, 0, 4);
index = sb.toString().indexOf('<');
}
return sb.toString();
}
return s;
}
/**
* This is the static main method used for launching this reference usage combination
* application. It will create and set up the class instance and then invoke the execute
* method to do the combination.
*
* @param args
*/
public static void main(String[] args) {
// Use the command option parser to parse the command line arguments
CommandOptionParser optionParser = new CommandOptionParser(args);
Map options = optionParser.getOptions();
Collection src = (Collection)options.get(ARG_SOURCE);
Collection output = (Collection)options.get(ARG_OUTPUT);
Collection api = (Collection)options.get(ARG_API);
Collection showAdopterNumbers = (Collection)options.get(ARG_SHOW_ADOPTER_NUMBERS);
Collection showDetailedNumbers = (Collection)options.get(ARG_SHOW_DETAILED_NUMBERS);
// If the usage is improper or arguments are not valid, prompt for proper usage
if (src == null || output == null || src.isEmpty() || output.isEmpty()) {
printUsage();
System.exit(-1);
}
// Create a new instance of the class and set the command line argument values
CombineClass2Reference class2Ref = new CombineClass2Reference();
class2Ref.setSrc(src);
class2Ref.setOutput((String)output.iterator().next());
class2Ref.setApi(api);
if (showAdopterNumbers!=null)
class2Ref.setShouldShowIndividualAdopters(new Boolean((String)showAdopterNumbers.iterator().next()).booleanValue());
if (showDetailedNumbers!=null)
class2Ref.setShouldShowDetailedNumbers(new Boolean((String)showDetailedNumbers.iterator().next()).booleanValue());
// Execute the combination method
class2Ref.execute();
}
/**
* This is a helper method to the user to print out an error message of the proper usage of
* the arguments to be passed and the location of the output files.
* See IOutputConstants for messages.
*/
private static void printUsage() {
System.out.println(PRINT_USAGE_COMBINED);
System.out.println(""); //$NON-NLS-1$
System.out.println(PRINT_SOURCE_LOCATION);
System.out.println(PRINT_OUTPUT_LOCATION);
System.out.println(PRINT_COMPONENT_XML_API_LOCATION);
}
/**
* @return boolean should show individual adopter numbers
*/
public boolean isShouldShowIndividualAdopters() {
return shouldShowIndividualAdopters;
}
/**
* Set whether or not to show individual adopter numbers in combined report
* @param shouldShowIndividualAdopters
*/
public void setShouldShowIndividualAdopters(boolean shouldShowIndividualAdopters) {
this.shouldShowIndividualAdopters = shouldShowIndividualAdopters;
}
/**
* @return boolean should show method and field detailed numbers
*/
public boolean isShouldShowDetailedNumbers() {
return shouldShowDetailedNumbers;
}
/**
* Set whether or not to show method and field detailed numbers
* @param shouldShowDetailedNumbers
*/
public void setShouldShowDetailedNumbers(boolean shouldShowDetailedNumbers) {
this.shouldShowDetailedNumbers = shouldShowDetailedNumbers;
}
}