| /******************************************************************************* |
| * Copyright (c) 2008, 2009 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.pde.api.tools.internal.tasks; |
| |
| import java.io.BufferedInputStream; |
| import java.io.File; |
| import java.io.FileInputStream; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.io.PrintWriter; |
| import java.io.StringWriter; |
| import java.util.ArrayList; |
| import java.util.Collections; |
| import java.util.Comparator; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Properties; |
| import java.util.Set; |
| |
| import org.apache.tools.ant.BuildException; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.NullProgressMonitor; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.pde.api.tools.internal.IApiCoreConstants; |
| import org.eclipse.pde.api.tools.internal.IApiXmlConstants; |
| import org.eclipse.pde.api.tools.internal.builder.BaseApiAnalyzer; |
| import org.eclipse.pde.api.tools.internal.builder.BuildContext; |
| import org.eclipse.pde.api.tools.internal.model.StubApiComponent; |
| import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; |
| import org.eclipse.pde.api.tools.internal.problems.ApiProblemFilter; |
| import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
| import org.eclipse.pde.api.tools.internal.provisional.IApiFilterStore; |
| import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants; |
| import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
| import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
| import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; |
| import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemFilter; |
| import org.eclipse.pde.api.tools.internal.util.Util; |
| import org.w3c.dom.DOMException; |
| import org.w3c.dom.Document; |
| import org.w3c.dom.Element; |
| import org.w3c.dom.NodeList; |
| |
| /** |
| * Ant task to run the API tool verification during Eclipse build. |
| */ |
| public class APIToolsAnalysisTask extends CommonUtilsTask { |
| /** |
| * This filter store is only used to filter problem using existing filters. |
| * It doesn't add or remove any filters. |
| */ |
| private static class AntFilterStore implements IApiFilterStore { |
| private static final String GLOBAL = "!global!"; //$NON-NLS-1$ |
| private static int loadIntegerAttribute(Element element, String name) { |
| String value = element.getAttribute(name); |
| if(value.length() == 0) { |
| return -1; |
| } |
| try { |
| int number = Integer.parseInt(value); |
| return number; |
| } |
| catch(NumberFormatException nfe) {} |
| return -1; |
| } |
| private boolean debug; |
| |
| private Map fFilterMap; |
| |
| public AntFilterStore(boolean debug, String filtersRoot, String componentID) { |
| this.initialize(filtersRoot, componentID); |
| } |
| |
| public void addFiltersFor(IApiProblem[] problems) { |
| // do nothing |
| } |
| |
| public void addFilters(IApiProblemFilter[] filters) { |
| // do nothing |
| } |
| |
| private boolean argumentsEquals(String[] problemMessageArguments, |
| String[] filterProblemMessageArguments) { |
| // filter problems message arguments are always simple name |
| // problem message arguments are fully qualified name outside the IDE |
| int length = problemMessageArguments.length; |
| if (length == filterProblemMessageArguments.length) { |
| for (int i = 0; i < length; i++) { |
| String problemMessageArgument = problemMessageArguments[i]; |
| String filterProblemMessageArgument = filterProblemMessageArguments[i]; |
| if (problemMessageArgument.equals(filterProblemMessageArgument)) { |
| continue; |
| } |
| int index = problemMessageArgument.lastIndexOf('.'); |
| int filterProblemIndex = filterProblemMessageArgument.lastIndexOf('.'); |
| if (index == -1) { |
| if (filterProblemIndex == -1) { |
| return false; // simple names should match |
| } |
| if (filterProblemMessageArgument.substring(filterProblemIndex + 1).equals(problemMessageArgument)) { |
| continue; |
| } else { |
| return false; |
| } |
| } else if (filterProblemIndex != -1) { |
| return false; // fully qualified name should match |
| } else { |
| if (problemMessageArgument.substring(index + 1).equals(filterProblemMessageArgument)) { |
| continue; |
| } else { |
| return false; |
| } |
| } |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| public void dispose() { |
| // do nothing |
| } |
| |
| public IApiProblemFilter[] getFilters(IResource resource) { |
| return null; |
| } |
| |
| public IResource[] getResources() { |
| return null; |
| } |
| |
| /** |
| * Initialize the filter store using the given component id |
| */ |
| private void initialize(String filtersRoot, String componentID) { |
| if(fFilterMap != null) { |
| return; |
| } |
| if(this.debug) { |
| System.out.println("null filter map, creating a new one"); //$NON-NLS-1$ |
| } |
| fFilterMap = new HashMap(5); |
| String xml = null; |
| InputStream contents = null; |
| try { |
| File filterFileParent = new File(filtersRoot, componentID); |
| if (!filterFileParent.exists()) { |
| return; |
| } |
| contents = new BufferedInputStream(new FileInputStream(new File(filterFileParent, IApiCoreConstants.API_FILTERS_XML_NAME))); |
| xml = new String(Util.getInputStreamAsCharArray(contents, -1, IApiCoreConstants.UTF_8)); |
| } |
| catch(IOException ioe) {} |
| finally { |
| if (contents != null) { |
| try { |
| contents.close(); |
| } catch(IOException e) { |
| // ignore |
| } |
| } |
| } |
| if(xml == null) { |
| return; |
| } |
| Element root = null; |
| try { |
| root = Util.parseDocument(xml); |
| } |
| catch(CoreException ce) { |
| ApiPlugin.log(ce); |
| } |
| if (root == null) { |
| return; |
| } |
| if (!root.getNodeName().equals(IApiXmlConstants.ELEMENT_COMPONENT)) { |
| return; |
| } |
| String component = root.getAttribute(IApiXmlConstants.ATTR_ID); |
| if(component.length() == 0) { |
| return; |
| } |
| String versionValue = root.getAttribute(IApiXmlConstants.ATTR_VERSION); |
| int version = 0; |
| if(versionValue.length() != 0) { |
| try { |
| version = Integer.parseInt(versionValue); |
| } catch (NumberFormatException e) { |
| // ignore |
| } |
| } |
| if (version < 2) { |
| // we discard all filters since there is no way to retrieve the type name |
| return; |
| } |
| NodeList resources = root.getElementsByTagName(IApiXmlConstants.ELEMENT_RESOURCE); |
| ArrayList newfilters = new ArrayList(); |
| for(int i = 0; i < resources.getLength(); i++) { |
| Element element = (Element) resources.item(i); |
| String typeName = element.getAttribute(IApiXmlConstants.ATTR_TYPE); |
| if(typeName == null || typeName.length() == 0) { |
| continue; |
| } |
| NodeList filters = element.getElementsByTagName(IApiXmlConstants.ELEMENT_FILTER); |
| for(int j = 0; j < filters.getLength(); j++) { |
| element = (Element) filters.item(j); |
| int id = loadIntegerAttribute(element, IApiXmlConstants.ATTR_ID); |
| if(id <= 0) { |
| continue; |
| } |
| String[] messageargs = null; |
| NodeList elements = element.getElementsByTagName(IApiXmlConstants.ELEMENT_PROBLEM_MESSAGE_ARGUMENTS); |
| if (elements.getLength() != 1) continue; |
| Element messageArguments = (Element) elements.item(0); |
| NodeList arguments = messageArguments.getElementsByTagName(IApiXmlConstants.ELEMENT_PROBLEM_MESSAGE_ARGUMENT); |
| int length = arguments.getLength(); |
| messageargs = new String[length]; |
| for (int k = 0; k < length; k++) { |
| Element messageArgument = (Element) arguments.item(k); |
| messageargs[k] = messageArgument.getAttribute(IApiXmlConstants.ATTR_VALUE); |
| } |
| newfilters.add(ApiProblemFactory.newApiProblem(null, typeName, messageargs, null, null, -1, -1, -1, id)); |
| } |
| } |
| internalAddFilters(componentID, (IApiProblem[]) newfilters.toArray(new IApiProblem[newfilters.size()])); |
| newfilters.clear(); |
| } |
| |
| /** |
| * Internal use method that allows auto-persisting of the filter file to be turned on or off |
| * @param problems the problems to add the the store |
| * @param persist if the filters should be auto-persisted after they are added |
| */ |
| private void internalAddFilters(String componentID, IApiProblem[] problems) { |
| if(problems == null) { |
| if(this.debug) { |
| System.out.println("null problems array not addding filters"); //$NON-NLS-1$ |
| } |
| return; |
| } |
| for(int i = 0; i < problems.length; i++) { |
| IApiProblem problem = problems[i]; |
| IApiProblemFilter filter = new ApiProblemFilter(componentID, problem); |
| String typeName = problem.getTypeName(); |
| if (typeName == null) { |
| typeName = GLOBAL; |
| } |
| Set filters = (Set) fFilterMap.get(typeName); |
| if(filters == null) { |
| filters = new HashSet(); |
| fFilterMap.put(typeName, filters); |
| } |
| filters.add(filter); |
| } |
| } |
| |
| public boolean isFiltered(IApiProblem problem) { |
| if (this.fFilterMap == null || this.fFilterMap.isEmpty()) return false; |
| String typeName = problem.getTypeName(); |
| Set filters = (Set) this.fFilterMap.get(typeName); |
| if (filters == null) return false; |
| for (Iterator iterator = filters.iterator(); iterator.hasNext();) { |
| IApiProblemFilter filter = (IApiProblemFilter) iterator.next(); |
| if (problem.getCategory() == IApiProblem.CATEGORY_USAGE) { |
| // write our own matching implementation |
| return matchUsageProblem(filter.getUnderlyingProblem(), problem); |
| } else if (filter.getUnderlyingProblem().equals(problem)) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| private boolean matchUsageProblem(IApiProblem filterProblem, IApiProblem problem) { |
| if (problem.getId() == filterProblem.getId()) { |
| // check arguments |
| String problemPath = problem.getResourcePath(); |
| String filterProblemPath = filterProblem.getResourcePath(); |
| if (problemPath == null) { |
| if (filterProblemPath != null) { |
| return false; |
| } |
| } else if (filterProblemPath == null) { |
| return false; |
| } else if (!new Path(problemPath).equals(new Path(filterProblemPath))) { |
| return false; |
| } |
| String problemTypeName = problem.getTypeName(); |
| String filterProblemTypeName = filterProblem.getTypeName(); |
| if (problemTypeName == null) { |
| if (filterProblemTypeName != null) { |
| return false; |
| } |
| } else if (filterProblemTypeName == null) { |
| return false; |
| } else if (!problemTypeName.equals(filterProblemTypeName)) { |
| return false; |
| } |
| return argumentsEquals(problem.getMessageArguments(), filterProblem.getMessageArguments()); |
| } |
| return false; |
| } |
| public boolean removeFilters(IApiProblemFilter[] filters) { |
| return false; |
| } |
| } |
| private static class Summary { |
| List apiBundleVersionProblems = new ArrayList(); |
| List apiCompatibilityProblems = new ArrayList(); |
| List apiUsageProblems = new ArrayList(); |
| String componentID; |
| |
| public Summary(String componentID, IApiProblem[] apiProblems) { |
| this.componentID = componentID; |
| for (int i = 0, max = apiProblems.length; i < max; i++) { |
| IApiProblem problem = apiProblems[i]; |
| switch(problem.getCategory()) { |
| case IApiProblem.CATEGORY_COMPATIBILITY : |
| apiCompatibilityProblems.add(problem); |
| break; |
| case IApiProblem.CATEGORY_USAGE : |
| apiUsageProblems.add(problem); |
| break; |
| case IApiProblem.CATEGORY_VERSION : |
| apiBundleVersionProblems.add(problem); |
| } |
| } |
| } |
| private void dumpProblems(String title, List problemsList, |
| PrintWriter printWriter) { |
| printWriter.println(title); |
| if (problemsList.size() != 0) { |
| for (Iterator iterator = problemsList.iterator(); iterator.hasNext(); ) { |
| IApiProblem problem = (IApiProblem) iterator.next(); |
| printWriter.println(problem.getMessage()); |
| } |
| } else { |
| printWriter.println("None"); //$NON-NLS-1$ |
| } |
| } |
| public String getDetails() { |
| StringWriter writer = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(writer); |
| |
| printWriter.println("=================================================================================="); //$NON-NLS-1$ |
| printWriter.println("Details for " + this.componentID + ":"); //$NON-NLS-1$//$NON-NLS-2$ |
| printWriter.println("=================================================================================="); //$NON-NLS-1$ |
| dumpProblems("Usage", apiUsageProblems, printWriter); //$NON-NLS-1$ |
| dumpProblems("Compatibility", apiCompatibilityProblems, printWriter); //$NON-NLS-1$ |
| dumpProblems("Bundle versions", apiBundleVersionProblems, printWriter); //$NON-NLS-1$ |
| printWriter.println("=================================================================================="); //$NON-NLS-1$ |
| printWriter.flush(); |
| printWriter.close(); |
| return String.valueOf(writer.getBuffer()); |
| } |
| public String getTitle() { |
| StringWriter writer = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(writer); |
| printTitle(printWriter); |
| |
| printWriter.flush(); |
| printWriter.close(); |
| return String.valueOf(writer.getBuffer()); |
| } |
| private void printTitle(PrintWriter printWriter) { |
| printWriter.print("Results for " + this.componentID + " : "); //$NON-NLS-1$ //$NON-NLS-2$ |
| printWriter.print('('); |
| printWriter.print("total: "); //$NON-NLS-1$ |
| printWriter.print( |
| apiUsageProblems.size() |
| + apiBundleVersionProblems.size() |
| + apiCompatibilityProblems.size()); |
| printWriter.print(','); |
| printWriter.print("usage: "); //$NON-NLS-1$ |
| printWriter.print(apiUsageProblems.size()); |
| printWriter.print(','); |
| printWriter.print("compatibility: "); //$NON-NLS-1$ |
| printWriter.print(apiCompatibilityProblems.size()); |
| printWriter.print(','); |
| printWriter.print("bundle version: "); //$NON-NLS-1$ |
| printWriter.print(apiBundleVersionProblems.size()); |
| printWriter.println(')'); |
| } |
| public String toString() { |
| StringWriter writer = new StringWriter(); |
| PrintWriter printWriter = new PrintWriter(writer); |
| printTitle(printWriter); |
| |
| dumpProblems("Usage", apiUsageProblems, printWriter); //$NON-NLS-1$ |
| dumpProblems("Compatibility", apiCompatibilityProblems, printWriter); //$NON-NLS-1$ |
| dumpProblems("Bundle versions", apiBundleVersionProblems, printWriter); //$NON-NLS-1$ |
| |
| printWriter.flush(); |
| printWriter.close(); |
| return String.valueOf(writer.getBuffer()); |
| } |
| } |
| public static final String BUNDLE_VERSION = "bundleVersion"; //$NON-NLS-1$ |
| public static final String COMPATIBILITY = "compatibility"; //$NON-NLS-1$ |
| |
| private static final Summary[] NO_SUMMARIES = new Summary[0]; |
| public static final String USAGE = "usage"; //$NON-NLS-1$ |
| |
| private Set excludedElement; |
| private String filters; |
| private Properties properties; |
| |
| private Summary[] createAllSummaries(Map allProblems) { |
| Set entrySet = allProblems.entrySet(); |
| int size = entrySet.size(); |
| if (size == 0) { |
| return NO_SUMMARIES; |
| } |
| List allEntries = new ArrayList(); |
| allEntries.addAll(entrySet); |
| Collections.sort(allEntries, new Comparator() { |
| public int compare(Object o1, Object o2) { |
| Map.Entry entry1 = (Map.Entry) o1; |
| Map.Entry entry2 = (Map.Entry) o2; |
| return ((String) entry1.getKey()).compareTo((String) entry2.getKey()); |
| } |
| }); |
| Summary[] summaries = new Summary[size]; |
| int i = 0; |
| for (Iterator iterator = allEntries.iterator(); iterator.hasNext();) { |
| Map.Entry entry = (Map.Entry) iterator.next(); |
| summaries[i++] = createProblemSummary((String) entry.getKey(), (IApiProblem[]) entry.getValue()); |
| } |
| if (this.debug) { |
| dumpSummaries(summaries); |
| } |
| return summaries; |
| } |
| private Summary createProblemSummary(String componentID, IApiProblem[] apiProblems) { |
| return new Summary(componentID, apiProblems); |
| } |
| private void dumpReport(Summary[] summaries, List bundlesNames) { |
| for (int i = 0, max = summaries.length; i < max; i++) { |
| Summary summary = summaries[i]; |
| String contents = null; |
| String componentID = summary.componentID; |
| if (this.excludedElement != null && this.excludedElement.contains(componentID)) { |
| continue; |
| } |
| try { |
| Document document = Util.newDocument(); |
| Element report = document.createElement(IApiXmlConstants.ELEMENT_API_TOOL_REPORT); |
| report.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_REPORT_CURRENT_VERSION); |
| report.setAttribute(IApiXmlConstants.ATTR_COMPONENT_ID, componentID); |
| document.appendChild(report); |
| |
| Element category = document.createElement(IApiXmlConstants.ATTR_CATEGORY); |
| category.setAttribute(IApiXmlConstants.ATTR_KEY, Integer.toString(IApiProblem.CATEGORY_COMPATIBILITY)); |
| category.setAttribute(IApiXmlConstants.ATTR_VALUE, COMPATIBILITY); |
| insertAPIProblems(category, document, summary.apiCompatibilityProblems); |
| report.appendChild(category); |
| |
| category = document.createElement(IApiXmlConstants.ATTR_CATEGORY); |
| category.setAttribute(IApiXmlConstants.ATTR_KEY, Integer.toString(IApiProblem.CATEGORY_USAGE)); |
| category.setAttribute(IApiXmlConstants.ATTR_VALUE, USAGE); |
| insertAPIProblems(category, document, summary.apiUsageProblems); |
| report.appendChild(category); |
| |
| category = document.createElement(IApiXmlConstants.ATTR_CATEGORY); |
| category.setAttribute(IApiXmlConstants.ATTR_KEY, Integer.toString(IApiProblem.CATEGORY_VERSION)); |
| category.setAttribute(IApiXmlConstants.ATTR_VALUE, BUNDLE_VERSION); |
| insertAPIProblems(category, document, summary.apiBundleVersionProblems); |
| report.appendChild(category); |
| |
| contents = Util.serializeDocument(document); |
| } catch (DOMException e) { |
| throw new BuildException(e); |
| } catch (CoreException e) { |
| throw new BuildException(e); |
| } |
| if (contents != null) { |
| saveReport(componentID, contents, "report.xml"); //$NON-NLS-1$ |
| } |
| } |
| if (bundlesNames != null && bundlesNames.size() != 0) { |
| String contents = null; |
| try { |
| Document document = Util.newDocument(); |
| Element report = document.createElement(IApiXmlConstants.ELEMENT_API_TOOL_REPORT); |
| report.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_REPORT_CURRENT_VERSION); |
| document.appendChild(report); |
| |
| for (Iterator iterator = bundlesNames.iterator(); iterator.hasNext();) { |
| String bundleName = (String) iterator.next(); |
| if (this.excludedElement == null || !this.excludedElement.contains(bundleName)) { |
| Element bundle = document.createElement(IApiXmlConstants.ELEMENT_BUNDLE); |
| bundle.setAttribute(IApiXmlConstants.ATTR_NAME, bundleName); |
| report.appendChild(bundle); |
| } |
| } |
| contents = Util.serializeDocument(document); |
| } catch (DOMException e) { |
| throw new BuildException(e); |
| } catch (CoreException e) { |
| throw new BuildException(e); |
| } |
| if (contents != null) { |
| saveReport("allNonApiBundles", contents, "report.xml"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| } |
| private void dumpSummaries(Summary[] summaries) { |
| for (int i = 0, max = summaries.length; i < max; i++) { |
| System.out.println(summaries[i].getTitle()); |
| } |
| for (int i = 0, max = summaries.length; i < max; i++) { |
| System.out.println(summaries[i].getDetails()); |
| } |
| } |
| /** |
| * Run the api tools verification task |
| * |
| * @throws BuildException exception is thrown if anything goes wrong during the verification |
| */ |
| public void execute() throws BuildException { |
| if (this.referenceBaselineLocation == null |
| || this.currentBaselineLocation == null |
| || this.reportLocation == null) { |
| StringWriter out = new StringWriter(); |
| PrintWriter writer = new PrintWriter(out); |
| writer.println( |
| NLS.bind(Messages.printArguments, |
| new String[] { |
| this.referenceBaselineLocation, |
| this.currentBaselineLocation, |
| this.reportLocation, |
| }) |
| ); |
| writer.flush(); |
| writer.close(); |
| throw new BuildException(String.valueOf(out.getBuffer())); |
| } |
| if (this.debug) { |
| System.out.println("reference : " + this.referenceBaselineLocation); //$NON-NLS-1$ |
| System.out.println("baseline to compare : " + this.currentBaselineLocation); //$NON-NLS-1$ |
| System.out.println("report location : " + this.reportLocation); //$NON-NLS-1$ |
| if (this.filters != null) { |
| System.out.println("filter store : " + this.filters); //$NON-NLS-1$ |
| } else { |
| System.out.println("No filter store"); //$NON-NLS-1$ |
| } |
| if (this.excludeListLocation != null) { |
| System.out.println("exclude list location : " + this.excludeListLocation); //$NON-NLS-1$ |
| } else { |
| System.out.println("No exclude list location"); //$NON-NLS-1$ |
| } |
| } |
| if (this.excludeListLocation != null) { |
| this.excludedElement = CommonUtilsTask.initializeExcludedElement(this.excludeListLocation); |
| } |
| // unzip reference |
| long time = 0; |
| if (this.debug) { |
| time = System.currentTimeMillis(); |
| } |
| File referenceInstallDir = extractSDK(REFERENCE, this.referenceBaselineLocation); |
| |
| File baselineInstallDir = extractSDK(CURRENT, this.currentBaselineLocation); |
| if (this.debug) { |
| System.out.println("Preparation of baseline installation : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| time = System.currentTimeMillis(); |
| } |
| // run the comparison |
| // create baseline for the reference |
| IApiBaseline referenceBaseline = createBaseline(REFERENCE_BASELINE_NAME, getInstallDir(referenceInstallDir), this.eeFileLocation); |
| IApiBaseline currentBaseline = createBaseline(CURRENT_BASELINE_NAME, getInstallDir(baselineInstallDir), this.eeFileLocation); |
| |
| if (this.debug) { |
| System.out.println("Creation of both baselines : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| time = System.currentTimeMillis(); |
| } |
| Map allProblems = new HashMap(); |
| List allNonApiBundles = new ArrayList(); |
| List allApiBundles = new ArrayList(); |
| try { |
| IApiComponent[] apiComponents = currentBaseline.getApiComponents(); |
| int length = apiComponents.length; |
| int apiToolsComponents = 0; |
| Set visitedApiComponentNames = new HashSet(); |
| for (int i = 0; i < length; i++) { |
| IApiComponent apiComponent = apiComponents[i]; |
| String name = apiComponent.getSymbolicName(); |
| visitedApiComponentNames.add(name); |
| if (apiComponent.isSystemComponent()) { |
| continue; |
| } |
| if (!Util.isApiToolsComponent(apiComponent)) { |
| allNonApiBundles.add(name); |
| continue; |
| } |
| apiToolsComponents++; |
| allApiBundles.add(name); |
| BaseApiAnalyzer analyzer = new BaseApiAnalyzer(); |
| try { |
| analyzer.analyzeComponent(null, getFilterStore(name), this.properties, referenceBaseline, apiComponent, new BuildContext(), new NullProgressMonitor()); |
| IApiProblem[] problems = analyzer.getProblems(); |
| // remove duplicates |
| problems = removeDuplicates(problems); |
| if (problems.length != 0) { |
| allProblems.put(name, problems); |
| } |
| } catch(RuntimeException e) { |
| ApiPlugin.log(e); |
| throw e; |
| } finally { |
| analyzer.dispose(); |
| } |
| } |
| if (debug) { |
| System.out.println("Total number of components in current baseline :" + length); //$NON-NLS-1$ |
| System.out.println("Total number of api tools components in current baseline :" + allApiBundles.size()); //$NON-NLS-1$ |
| System.out.println("Details:"); //$NON-NLS-1$ |
| Collections.sort(allApiBundles); |
| for (Iterator iterator = allApiBundles.iterator(); iterator.hasNext(); ) { |
| System.out.println(iterator.next()); |
| } |
| System.out.println("=============================================================================="); //$NON-NLS-1$ |
| System.out.println("Total number of non-api tools components in current baseline :" + allNonApiBundles.size()); //$NON-NLS-1$ |
| System.out.println("Details:"); //$NON-NLS-1$ |
| Collections.sort(allNonApiBundles); |
| for (Iterator iterator = allNonApiBundles.iterator(); iterator.hasNext(); ) { |
| System.out.println(iterator.next()); |
| } |
| } |
| IApiComponent[] baselineApiComponents = referenceBaseline.getApiComponents(); |
| for (int i = 0, max = baselineApiComponents.length; i < max; i++) { |
| IApiComponent apiComponent = baselineApiComponents[i]; |
| String id = apiComponent.getSymbolicName(); |
| if (!visitedApiComponentNames.remove(id)) { |
| //remove component in the current baseline |
| IApiProblem problem = ApiProblemFactory.newApiProblem(id, |
| null, |
| new String[] { id }, |
| new String[] { |
| IApiMarkerConstants.MARKER_ATTR_HANDLE_ID, |
| IApiMarkerConstants.API_MARKER_ATTR_ID |
| }, |
| new Object[] { |
| id, |
| new Integer(IApiMarkerConstants.COMPATIBILITY_MARKER_ID), |
| }, |
| 0, |
| -1, |
| -1, |
| IApiProblem.CATEGORY_COMPATIBILITY, |
| IDelta.API_BASELINE_ELEMENT_TYPE, |
| IDelta.REMOVED, |
| IDelta.API_COMPONENT); |
| allProblems.put(id, new IApiProblem[] { problem }); |
| } |
| } |
| } finally { |
| if (this.debug) { |
| System.out.println("API tools verification check : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| time = System.currentTimeMillis(); |
| } |
| referenceBaseline.dispose(); |
| currentBaseline.dispose(); |
| StubApiComponent.disposeAllCaches(); |
| deleteBaseline(this.referenceBaselineLocation, referenceInstallDir); |
| deleteBaseline(this.currentBaselineLocation, baselineInstallDir); |
| if (this.debug) { |
| System.out.println("Cleanup : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| } |
| Summary[] summaries = createAllSummaries(allProblems); |
| |
| try { |
| dumpReport(summaries, allNonApiBundles); |
| } catch(RuntimeException e) { |
| ApiPlugin.log(e); |
| throw e; |
| } |
| } |
| private IApiProblem[] removeDuplicates(IApiProblem[] problems) { |
| int length = problems.length; |
| if (length <= 1) return problems; |
| Set uniqueProblems = new HashSet(length); |
| List allProblems = null; |
| for (int i = 0; i < length; i++) { |
| IApiProblem apiProblem = problems[i]; |
| String message = apiProblem.getMessage(); |
| if (!uniqueProblems.contains(message)) { |
| if (allProblems == null) { |
| allProblems = new ArrayList(length); |
| } |
| uniqueProblems.add(message); |
| allProblems.add(apiProblem); |
| } |
| } |
| return (IApiProblem[]) allProblems.toArray(new IApiProblem[allProblems.size()]); |
| } |
| private IApiFilterStore getFilterStore(String name) { |
| if (this.filters == null) return null; |
| return new AntFilterStore(this.debug, this.filters, name); |
| } |
| /** |
| * Returns an element that contains all the api problem nodes. |
| * |
| * @param document the given xml document |
| * @param problems the given problem to dump into the document |
| * @return an element that contains all the api problem nodes or null if an error occured |
| */ |
| private void insertAPIProblems(Element root, Document document, List problems) throws CoreException { |
| Element apiProblems = document.createElement(IApiXmlConstants.ELEMENT_API_PROBLEMS); |
| root.appendChild(apiProblems); |
| Element element = null; |
| // sort the problem by type name |
| Collections.sort(problems, new Comparator() { |
| public int compare(Object o1, Object o2) { |
| IApiProblem p1 = (IApiProblem) o1; |
| IApiProblem p2 = (IApiProblem) o2; |
| return p1.getTypeName().compareTo(p2.getTypeName()); |
| } |
| }); |
| for(Iterator iterator = problems.iterator(); iterator.hasNext(); ) { |
| IApiProblem problem = (IApiProblem) iterator.next(); |
| element = document.createElement(IApiXmlConstants.ELEMENT_API_PROBLEM); |
| element.setAttribute(IApiXmlConstants.ATTR_TYPE_NAME, String.valueOf(problem.getTypeName())); |
| element.setAttribute(IApiXmlConstants.ATTR_ID, Integer.toString(problem.getId())); |
| element.setAttribute(IApiXmlConstants.ATTR_LINE_NUMBER, Integer.toString(problem.getLineNumber())); |
| element.setAttribute(IApiXmlConstants.ATTR_CHAR_START, Integer.toString(problem.getCharStart())); |
| element.setAttribute(IApiXmlConstants.ATTR_CHAR_END, Integer.toString(problem.getCharEnd())); |
| element.setAttribute(IApiXmlConstants.ATTR_ELEMENT_KIND, Integer.toString(problem.getElementKind())); |
| element.setAttribute(IApiXmlConstants.ATTR_SEVERITY, Integer.toString(getSeverity(problem))); |
| element.setAttribute(IApiXmlConstants.ATTR_KIND, Integer.toString(problem.getKind())); |
| element.setAttribute(IApiXmlConstants.ATTR_FLAGS, Integer.toString(problem.getFlags())); |
| element.setAttribute(IApiXmlConstants.ATTR_MESSAGE, problem.getMessage()); |
| String[] extraMarkerAttributeIds = problem.getExtraMarkerAttributeIds(); |
| if (extraMarkerAttributeIds != null && extraMarkerAttributeIds.length != 0) { |
| int length = extraMarkerAttributeIds.length; |
| Object[] extraMarkerAttributeValues = problem.getExtraMarkerAttributeValues(); |
| Element extraArgumentsElement = document.createElement(IApiXmlConstants.ELEMENT_PROBLEM_EXTRA_ARGUMENTS); |
| for (int j = 0; j < length; j++) { |
| Element extraArgumentElement = document.createElement(IApiXmlConstants.ELEMENT_PROBLEM_EXTRA_ARGUMENT); |
| extraArgumentElement.setAttribute(IApiXmlConstants.ATTR_ID, extraMarkerAttributeIds[j]); |
| extraArgumentElement.setAttribute(IApiXmlConstants.ATTR_VALUE, String.valueOf(extraMarkerAttributeValues[j])); |
| extraArgumentsElement.appendChild(extraArgumentElement); |
| } |
| element.appendChild(extraArgumentsElement); |
| } |
| String[] messageArguments = problem.getMessageArguments(); |
| if (messageArguments != null && messageArguments.length != 0) { |
| int length = messageArguments.length; |
| Element messageArgumentsElement = document.createElement(IApiXmlConstants.ELEMENT_PROBLEM_MESSAGE_ARGUMENTS); |
| for (int j = 0; j < length; j++) { |
| Element messageArgumentElement = document.createElement(IApiXmlConstants.ELEMENT_PROBLEM_MESSAGE_ARGUMENT); |
| messageArgumentElement.setAttribute(IApiXmlConstants.ATTR_VALUE, String.valueOf(messageArguments[j])); |
| messageArgumentsElement.appendChild(messageArgumentElement); |
| } |
| element.appendChild(messageArgumentsElement); |
| } |
| apiProblems.appendChild(element); |
| } |
| } |
| /** |
| * By default, we return a warning severity. |
| * @param problem the given problem |
| * @return the problem's severity |
| */ |
| private int getSeverity(IApiProblem problem) { |
| if (this.properties != null) { |
| String key = ApiProblemFactory.getProblemSeverityId(problem); |
| if (key != null) { |
| String value = this.properties.getProperty(key, null); |
| if (value != null) { |
| if (ApiPlugin.VALUE_ERROR.equals(value)) { |
| return ApiPlugin.SEVERITY_ERROR; |
| } |
| } |
| } |
| } |
| return ApiPlugin.SEVERITY_WARNING; |
| } |
| |
| /** |
| * Set the debug value. |
| * <p>The possible values are: <code>true</code>, <code>false</code></p> |
| * <p>Default is <code>false</code>.</p> |
| * |
| * @param debugValue the given debug value |
| */ |
| public void setDebug(String debugValue) { |
| this.debug = Boolean.toString(true).equals(debugValue); |
| } |
| /** |
| * Set the execution environment file to use. |
| * <p>By default, an execution environment file corresponding to a JavaSE-1.6 execution environment |
| * is used.</p> |
| * <p>The file is specified using an absolute path. This is optional.</p> |
| * |
| * @param eeFileLocation the given execution environment file |
| */ |
| public void setEEFile(String eeFileLocation) { |
| this.eeFileLocation = eeFileLocation; |
| } |
| /** |
| * Set the exclude list location. |
| * |
| * <p>The exclude list is used to know what bundles should excluded from the xml report generated by the task |
| * execution. Lines starting with '#' are ignored from the excluded elements.</p> |
| * <p>The format of the exclude list file looks like this:</p> |
| * <pre> |
| * # DOC BUNDLES |
| * org.eclipse.jdt.doc.isv |
| * org.eclipse.jdt.doc.user |
| * org.eclipse.pde.doc.user |
| * org.eclipse.platform.doc.isv |
| * org.eclipse.platform.doc.user |
| * # NON-ECLIPSE BUNDLES |
| * com.ibm.icu |
| * com.jcraft.jsch |
| * javax.servlet |
| * javax.servlet.jsp |
| * ... |
| * </pre> |
| * <p>The location is set using an absolute path.</p> |
| * |
| * @param excludeListLocation the given location for the excluded list file |
| */ |
| public void setExcludeList(String excludeListLocation) { |
| this.excludeListLocation = excludeListLocation; |
| } |
| /** |
| * Set the root directory of API filters to use during the analysis. |
| * |
| * <p>The argument is the root directory of the .api_filters files that should be used to filter potential |
| * problems created by the api tooling analysis. The root needs to contain the following structure:</p> |
| * <pre> |
| * root |
| * | |
| * +-- component name (i.e. org.eclipse.jface) |
| * | |
| * +--- .api_filters |
| * </pre> |
| * |
| * @param filters the root of the .api_filters files |
| */ |
| public void setFilters(String filters) { |
| this.filters = filters; |
| } |
| /** |
| * Set the preferences for the task. |
| * |
| * <p>The preferences are used to configure problem severities. Problem severities have |
| * three possible values: Ignore, Warning, or Error. The set of problems detected is defined |
| * by corresponding problem preference keys in API tools.</p> |
| * <p>If the given location doesn't exist, the preferences won't be set.</p> |
| * <p>Lines starting with '#' are ignored. The format of the preferences file looks like this:</p> |
| * <pre> |
| * #Thu Nov 20 17:35:06 EST 2008 |
| * ANNOTATION_ELEMENT_TYPE_ADDED_METHOD_WITHOUT_DEFAULT_VALUE=Ignore |
| * ANNOTATION_ELEMENT_TYPE_CHANGED_TYPE_CONVERSION=Ignore |
| * ANNOTATION_ELEMENT_TYPE_REMOVED_FIELD=Ignore |
| * ANNOTATION_ELEMENT_TYPE_REMOVED_METHOD=Ignore |
| * ANNOTATION_ELEMENT_TYPE_REMOVED_TYPE_MEMBER=Warning |
| * API_COMPONENT_ELEMENT_TYPE_REMOVED_API_TYPE=Ignore |
| * API_COMPONENT_ELEMENT_TYPE_REMOVED_TYPE=Ignore |
| * CLASS_ELEMENT_TYPE_ADDED_METHOD=Error |
| * CLASS_ELEMENT_TYPE_ADDED_RESTRICTIONS=Ignore |
| * CLASS_ELEMENT_TYPE_ADDED_TYPE_PARAMETER=Ignore |
| * CLASS_ELEMENT_TYPE_CHANGED_CONTRACTED_SUPERINTERFACES_SET=Ignore |
| * ... |
| * </pre> |
| * <p>The keys can be found in {@link org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes}.</p> |
| * <p>The location is set using an absolute path.</p> |
| * |
| * @param preferencesLocation the location of the preference file |
| */ |
| public void setPreferences(String preferencesLocation) { |
| File preferencesFile = new File(preferencesLocation); |
| if (!preferencesFile.exists()) { |
| return; |
| } |
| BufferedInputStream inputStream = null; |
| try { |
| inputStream = new BufferedInputStream(new FileInputStream(preferencesFile)); |
| Properties temp = new Properties(); |
| temp.load(inputStream); |
| this.properties = temp; |
| } catch (IOException e) { |
| // ignore |
| } finally { |
| if (inputStream != null) { |
| try { |
| inputStream.close(); |
| } catch(IOException e) { |
| // ignore |
| } |
| } |
| } |
| } |
| /** |
| * Set the location of the current product or baseline that you want to compare against |
| * the reference baseline. |
| * |
| * <p>It can be a .zip, .jar, .tgz, .tar.gz file, or a directory that corresponds to |
| * the Eclipse installation folder. This is the directory is which you can find the |
| * Eclipse executable. |
| * </p> |
| * |
| * @param baselineLocation the given location for the baseline to analyze |
| */ |
| public void setProfile(String baselineLocation) { |
| this.currentBaselineLocation = baselineLocation; |
| } |
| /** |
| * Set the location of the reference baseline. |
| * |
| * <p>It can be a .zip, .jar, .tgz, .tar.gz file, or a directory that corresponds to |
| * the Eclipse installation folder. This is the directory is which you can find the |
| * Eclipse executable. |
| * </p> |
| * <p>The location is set using an absolute path.</p> |
| * |
| * @param baselineLocation the given location for the reference baseline to analyze |
| */ |
| public void setBaseline(String baselineLocation) { |
| this.referenceBaselineLocation = baselineLocation; |
| } |
| /** |
| * Set the output location where the reports will be generated. |
| * |
| * <p>Once the task is completed, reports are available in this directory using a structure |
| * similar to the filter root. A sub-folder is created for each component that has problems |
| * to be reported. Each sub-folder contains a file called "report.xml". </p> |
| * |
| * <p>A special folder called "allNonApiBundles" is also created in this folder that contains a xml file called |
| * "report.xml". This file lists all the bundles that are not using the api tooling nature.</p> |
| * |
| * @param baselineLocation the given location for the reference baseline to analyze |
| */ |
| public void setReport(String reportLocation) { |
| this.reportLocation = reportLocation; |
| } |
| } |