blob: d15833f6ac96f23f3195b5750b48d019cf34333b [file] [log] [blame]
/*
* Copyright (c) 2012, 2013, 2015 Eike Stepper (Berlin, Germany) 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:
* Eike Stepper - initial API and implementation
*/
package org.eclipse.emf.cdo.releng.apireports;
import org.eclipse.buckminster.core.actor.AbstractActor;
import org.eclipse.buckminster.core.actor.IActionContext;
import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.ResourcesPlugin;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.OperationCanceledException;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jdt.core.IClassFile;
import org.eclipse.jdt.core.ICompilationUnit;
import org.eclipse.jdt.core.IJavaElement;
import org.eclipse.jdt.core.IJavaProject;
import org.eclipse.jdt.core.IPackageFragment;
import org.eclipse.jdt.core.IPackageFragmentRoot;
import org.eclipse.jdt.core.IType;
import org.eclipse.jdt.core.JavaCore;
import org.eclipse.jdt.core.JavaModelException;
import org.eclipse.pde.api.tools.internal.ApiBaselineManager;
import org.eclipse.pde.api.tools.internal.comparator.DeltaXmlVisitor;
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
import org.eclipse.pde.api.tools.internal.provisional.Factory;
import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiComparator;
import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiScope;
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.model.IApiScope;
import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
import java.util.Map;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* @author Eike Stepper
*/
public class ApiReportsActor extends AbstractActor
{
public ApiReportsActor()
{
}
@Override
protected IStatus internalPerform(IActionContext context, IProgressMonitor monitor) throws CoreException
{
Map<String, ? extends Object> properties = context.getAction().getActorProperties();
String baselineName = (String)properties.get("baseline");
if (baselineName == null || baselineName.length() == 0)
{
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Baseline name not specified");
}
String exclusionPatterns = (String)properties.get("exclude");
String reportFileName = System.getProperty("api.report", new File("api.xml").getAbsolutePath());
SubMonitor progress = SubMonitor.convert(monitor, 100);
progress.subTask("Collecting elements to compare");
try
{
progress.subTask("Computing deltas...");
File reportFile = new File(reportFileName);
try
{
progress.worked(25);
updateMonitor(progress);
BufferedWriter writer = null;
try
{
if (reportFile.exists())
{
reportFile.delete();
}
else
{
File parent = reportFile.getParentFile();
if (!parent.exists() && !parent.mkdirs())
{
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Failed to create report directory structure");
}
}
writer = new BufferedWriter(new FileWriter(reportFile));
List<Object> projects = collectProjects(exclusionPatterns);
IApiScope scope = walkStructureSelection(projects, monitor);
IApiBaseline baseline = ApiBaselineManager.getManager().getApiBaseline(baselineName);
if (baseline == null)
{
return new Status(IStatus.ERROR, Activator.PLUGIN_ID, "Baseline not found: " + baselineName);
}
IDelta delta = ApiComparator.compare(scope, baseline, VisibilityModifiers.API, false, true, monitor);
if (delta != null)
{
progress.worked(25);
updateMonitor(progress);
DeltaXmlVisitor visitor = new DeltaXmlVisitor();
delta.accept(visitor);
writer.write(visitor.getXML());
writer.flush();
progress.worked(25);
}
}
catch (IOException e)
{
ApiPlugin.log(e);
}
catch (CoreException e)
{
ApiPlugin.log(e);
}
finally
{
if (writer != null)
{
try
{
writer.close();
}
catch (IOException e)
{
// ignore
}
}
}
progress.worked(25);
return Status.OK_STATUS;
}
catch (OperationCanceledException e)
{
// ignore
}
}
finally
{
monitor.done();
}
return Status.CANCEL_STATUS;
}
private List<Object> collectProjects(String exclusionPatterns)
{
Pattern[] patterns = new Pattern[0];
if (exclusionPatterns != null)
{
String[] split = exclusionPatterns.split(",");
patterns = new Pattern[split.length];
for (int i = 0; i < split.length; i++)
{
Pattern pattern = Pattern.compile(split[i]);
patterns[i] = pattern;
}
}
List<Object> result = new ArrayList<Object>();
for (IProject project : ResourcesPlugin.getWorkspace().getRoot().getProjects())
{
if (project.isAccessible())
{
String name = project.getName();
if (!isExcluded(patterns, name))
{
IJavaProject javaProject = JavaCore.create(project);
if (javaProject != null)
{
result.add(javaProject);
}
}
}
}
return result;
}
private boolean isExcluded(Pattern[] patterns, String name)
{
for (Pattern pattern : patterns)
{
Matcher matcher = pattern.matcher(name);
if (matcher.matches())
{
return true;
}
}
return false;
}
public static ApiScope walkStructureSelection(List<Object> projects, IProgressMonitor monitor)
{
ApiScope scope = new ApiScope();
IApiBaseline workspaceBaseline = ApiBaselineManager.getManager().getWorkspaceBaseline();
if (workspaceBaseline == null)
{
return scope;
}
Collections.sort(projects, new Comparator()
{
public int compare(Object o1, Object o2)
{
if (o1 instanceof IJavaElement && o2 instanceof IJavaElement)
{
IJavaElement element = (IJavaElement)o1;
IJavaElement element2 = (IJavaElement)o2;
return element.getElementType() - element2.getElementType();
}
return 0;
}
});
for (Object project : projects)
{
if (project instanceof IJavaElement)
{
IJavaElement element = (IJavaElement)project;
IJavaProject javaProject = element.getJavaProject();
try
{
switch (element.getElementType())
{
case IJavaElement.COMPILATION_UNIT:
{
ICompilationUnit compilationUnit = (ICompilationUnit)element;
IApiComponent apiComponent = workspaceBaseline.getApiComponent(javaProject.getElementName());
if (apiComponent != null)
{
addElementFor(compilationUnit, apiComponent, scope);
}
break;
}
case IJavaElement.PACKAGE_FRAGMENT:
{
IPackageFragment fragment = (IPackageFragment)element;
IApiComponent apiComponent = workspaceBaseline.getApiComponent(javaProject.getElementName());
IPackageFragmentRoot packageFragmentRoot = (IPackageFragmentRoot)fragment
.getAncestor(IJavaElement.PACKAGE_FRAGMENT_ROOT);
boolean isArchive = false;
if (packageFragmentRoot != null)
{
isArchive = packageFragmentRoot.isArchive();
}
if (apiComponent != null)
{
addElementFor(fragment, isArchive, apiComponent, scope);
}
break;
}
case IJavaElement.PACKAGE_FRAGMENT_ROOT:
{
IPackageFragmentRoot fragmentRoot = (IPackageFragmentRoot)element;
IApiComponent apiComponent = workspaceBaseline.getApiComponent(javaProject.getElementName());
if (apiComponent != null)
{
addElementFor(fragmentRoot, apiComponent, scope);
}
break;
}
case IJavaElement.JAVA_PROJECT:
IApiComponent apiComponent = workspaceBaseline.getApiComponent(javaProject.getElementName());
if (apiComponent != null)
{
scope.addElement(apiComponent);
}
break;
}
}
catch (JavaModelException e)
{
ApiPlugin.log(e);
}
catch (CoreException e)
{
ApiPlugin.log(e);
}
}
}
return scope;
}
private static void addElementFor(IPackageFragmentRoot fragmentRoot, IApiComponent apiComponent, ApiScope scope)
throws JavaModelException, CoreException
{
boolean isArchive = fragmentRoot.isArchive();
IJavaElement[] packageFragments = fragmentRoot.getChildren();
for (int j = 0, max2 = packageFragments.length; j < max2; j++)
{
IPackageFragment packageFragment = (IPackageFragment)packageFragments[j];
addElementFor(packageFragment, isArchive, apiComponent, scope);
}
}
private static void addElementFor(IPackageFragment packageFragment, boolean isArchive, IApiComponent apiComponent,
ApiScope scope) throws JavaModelException, CoreException
{
// add package fragment elements only if this is an API package
IApiDescription apiDescription = apiComponent.getApiDescription();
IApiAnnotations annotations = apiDescription
.resolveAnnotations(Factory.packageDescriptor(packageFragment.getElementName()));
if (annotations == null || !VisibilityModifiers.isAPI(annotations.getVisibility()))
{
return;
}
if (isArchive)
{
IClassFile[] classFiles = packageFragment.getClassFiles();
for (int i = 0, max = classFiles.length; i < max; i++)
{
addElementFor(classFiles[i], apiComponent, scope);
}
}
else
{
ICompilationUnit[] units = packageFragment.getCompilationUnits();
for (int i = 0, max = units.length; i < max; i++)
{
addElementFor(units[i], apiComponent, scope);
}
}
}
private static void addElementFor(IClassFile classFile, IApiComponent apiComponent, ApiScope scope)
{
try
{
IApiTypeRoot typeRoot = apiComponent.findTypeRoot(classFile.getType().getFullyQualifiedName());
if (typeRoot != null)
{
scope.addElement(typeRoot);
}
}
catch (CoreException e)
{
ApiPlugin.log(e);
}
}
private static void addElementFor(ICompilationUnit compilationUnit, IApiComponent component, ApiScope scope)
throws JavaModelException
{
IType[] types = compilationUnit.getTypes();
for (int i = 0, max = types.length; i < max; i++)
{
try
{
IApiTypeRoot typeRoot = component.findTypeRoot(types[i].getFullyQualifiedName());
if (typeRoot != null)
{
scope.addElement(typeRoot);
}
}
catch (CoreException e)
{
ApiPlugin.log(e);
}
}
}
private static void updateMonitor(IProgressMonitor monitor, int work) throws OperationCanceledException
{
if (monitor == null)
{
return;
}
if (monitor.isCanceled())
{
throw new OperationCanceledException();
}
monitor.worked(work);
}
private static void updateMonitor(IProgressMonitor monitor) throws OperationCanceledException
{
updateMonitor(monitor, 0);
}
}