| /******************************************************************************* |
| * Copyright (c) 2007, 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.builder; |
| |
| import java.util.ArrayList; |
| import java.util.Date; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.Map; |
| import java.util.Set; |
| import java.util.jar.JarFile; |
| |
| import org.eclipse.core.resources.IMarker; |
| import org.eclipse.core.resources.IProject; |
| import org.eclipse.core.resources.IResource; |
| import org.eclipse.core.resources.IResourceDelta; |
| import org.eclipse.core.resources.IWorkspaceRoot; |
| import org.eclipse.core.resources.IncrementalProjectBuilder; |
| import org.eclipse.core.resources.ResourcesPlugin; |
| import org.eclipse.core.runtime.CoreException; |
| import org.eclipse.core.runtime.IPath; |
| import org.eclipse.core.runtime.IProgressMonitor; |
| import org.eclipse.core.runtime.IStatus; |
| import org.eclipse.core.runtime.OperationCanceledException; |
| import org.eclipse.core.runtime.Path; |
| import org.eclipse.core.runtime.SubMonitor; |
| import org.eclipse.jdt.core.IClasspathAttribute; |
| import org.eclipse.jdt.core.IClasspathEntry; |
| import org.eclipse.jdt.core.IJavaModelMarker; |
| import org.eclipse.jdt.core.IJavaProject; |
| import org.eclipse.jdt.core.IPackageFragmentRoot; |
| import org.eclipse.jdt.core.JavaCore; |
| import org.eclipse.jdt.core.JavaModelException; |
| import org.eclipse.jdt.internal.core.JavaModelManager; |
| import org.eclipse.jdt.internal.core.builder.State; |
| import org.eclipse.osgi.service.resolver.BundleDescription; |
| import org.eclipse.osgi.util.NLS; |
| import org.eclipse.pde.api.tools.internal.ApiDescriptionManager; |
| import org.eclipse.pde.api.tools.internal.IApiCoreConstants; |
| import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; |
| import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
| import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants; |
| import org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer; |
| import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor; |
| 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.util.Util; |
| import org.eclipse.pde.core.plugin.IPluginModelBase; |
| import org.eclipse.pde.core.plugin.PluginRegistry; |
| |
| import com.ibm.icu.text.MessageFormat; |
| |
| /** |
| * Builder for creating API tooling resource markers |
| * @since 1.0.0 |
| */ |
| public class ApiAnalysisBuilder extends IncrementalProjectBuilder { |
| /** |
| * Constant used for controlling tracing in the API tool builder |
| */ |
| static boolean DEBUG = Util.DEBUG; |
| |
| /** |
| * Project relative path to the .settings folder |
| * @since 1.0.1 |
| */ |
| static final IPath SETTINGS_PATH = new Path(".settings"); //$NON-NLS-1$ |
| |
| /** |
| * Project relative path to the manifest file. |
| */ |
| static final IPath MANIFEST_PATH = new Path(JarFile.MANIFEST_NAME); |
| |
| /** |
| * Project relative path to the .api_filters file |
| */ |
| static final IPath FILTER_PATH = SETTINGS_PATH.append(IApiCoreConstants.API_FILTERS_XML_NAME); |
| |
| /** |
| * Empty listing of projects to be returned by the builder if there is nothing to do |
| */ |
| static final IProject[] NO_PROJECTS = new IProject[0]; |
| |
| /** |
| * Constant representing the name of the 'source' attribute on API tooling markers. |
| * Value is <code>Api Tooling</code> |
| */ |
| static final String SOURCE = "Api Tooling"; //$NON-NLS-1$ |
| |
| /** |
| * Method used for initializing tracing in the API tool builder |
| */ |
| public static void setDebug(boolean debugValue) { |
| DEBUG = debugValue || Util.DEBUG; |
| } |
| |
| /** |
| * The current project for which this builder was defined |
| */ |
| private IProject currentproject = null; |
| |
| /** |
| * The API analyzer for this builder |
| */ |
| private IApiAnalyzer analyzer = null; |
| |
| /** |
| * Maps prerequisite projects to their output location(s) |
| */ |
| HashMap output_locs = new HashMap(); |
| |
| /** |
| * Maps pre-requisite projects to their source locations |
| */ |
| HashMap src_locs = new HashMap(); |
| |
| /** |
| * Current build state |
| */ |
| private BuildState buildstate = null; |
| |
| /** |
| * Cleans up markers associated with API tooling on the given resource. |
| * |
| * @param resource |
| */ |
| void cleanupMarkers(IResource resource) { |
| cleanUnusedFilterMarkers(resource); |
| cleanupUsageMarkers(resource); |
| cleanupCompatibilityMarkers(resource); |
| cleanupUnsupportedTagMarkers(resource); |
| } |
| |
| /** |
| * Cleans up unsupported Javadoc tag markers on the specified resource |
| * @param resource |
| */ |
| void cleanupUnsupportedTagMarkers(IResource resource) { |
| try { |
| if(DEBUG) { |
| System.out.println("cleaning unsupported tag problems"); //$NON-NLS-1$ |
| } |
| resource.deleteMarkers(IApiMarkerConstants.UNSUPPORTED_TAG_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| } catch (CoreException e) { |
| ApiPlugin.log(e.getStatus()); |
| } |
| } |
| |
| /** |
| * Cleans up only API compatibility markers on the given {@link IResource} |
| * @param resource the given resource |
| */ |
| void cleanupCompatibilityMarkers(IResource resource) { |
| try { |
| if (resource != null && resource.isAccessible()) { |
| resource.deleteMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| resource.deleteMarkers(IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| if (resource.getType() == IResource.PROJECT) { |
| // on full builds |
| resource.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| resource.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); |
| resource.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); |
| } |
| } |
| } catch(CoreException e) { |
| ApiPlugin.log(e.getStatus()); |
| } |
| } |
| |
| /** |
| * cleans up only API usage markers from the given {@link IResource} |
| * @param resource |
| */ |
| void cleanupUsageMarkers(IResource resource) { |
| try { |
| if (resource != null && resource.isAccessible()) { |
| resource.deleteMarkers(IApiMarkerConstants.API_USAGE_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| } |
| } catch(CoreException e) { |
| ApiPlugin.log(e.getStatus()); |
| } |
| } |
| |
| /** |
| * Cleans up the unused API filter problems from the given resource |
| * @param resource |
| */ |
| void cleanUnusedFilterMarkers(IResource resource) { |
| try { |
| if(resource != null && resource.isAccessible()) { |
| resource.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| } |
| } |
| catch(CoreException ce) { |
| ApiPlugin.log(ce.getStatus()); |
| } |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { |
| this.currentproject = getProject(); |
| if (shouldAbort(this.currentproject)) { |
| return NO_PROJECTS; |
| } |
| if (DEBUG) { |
| System.out.println("\nApiAnalysis builder - Starting build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 8); |
| IApiBaseline wbaseline = ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline(); |
| if (wbaseline == null) { |
| if (DEBUG) { |
| System.err.println("Could not retrieve a workspace baseline"); //$NON-NLS-1$ |
| } |
| return NO_PROJECTS; |
| } |
| final IProject[] projects = getRequiredProjects(true); |
| IApiBaseline baseline = ApiPlugin.getDefault().getApiBaselineManager().getDefaultApiBaseline(); |
| try { |
| switch(kind) { |
| case FULL_BUILD : { |
| if (DEBUG) { |
| System.out.println("Performing full build as requested by user"); //$NON-NLS-1$ |
| } |
| buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
| break; |
| } |
| case AUTO_BUILD : |
| case INCREMENTAL_BUILD : { |
| this.buildstate = BuildState.getLastBuiltState(currentproject); |
| if (this.buildstate == null) { |
| buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
| break; |
| } |
| else if(worthDoingFullBuild(projects)) { |
| buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
| break; |
| } |
| else { |
| IResourceDelta[] deltas = getDeltas(projects); |
| if(deltas.length < 1) { |
| buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
| } |
| else { |
| IResourceDelta manifest = null; |
| IResourceDelta filters = null; |
| boolean filterbuild = false; |
| for (int i = 0; i < deltas.length; i++) { |
| manifest = deltas[i].findMember(MANIFEST_PATH); |
| if(manifest != null) { |
| break; |
| } |
| filters = deltas[i].findMember(FILTER_PATH); |
| if(filters != null){ |
| switch(filters.getKind()) { |
| case IResourceDelta.ADDED: |
| case IResourceDelta.REMOVED: { |
| filterbuild = true; |
| break; |
| } |
| case IResourceDelta.CHANGED: { |
| filterbuild = (filters.getFlags() & IResourceDelta.REPLACED) > 0; |
| break; |
| } |
| } |
| if(filterbuild) { |
| break; |
| } |
| } |
| } |
| if (manifest != null || filterbuild) { |
| if (DEBUG) { |
| System.out.println("Performing full build since MANIFEST.MF or .api_filters was modified"); //$NON-NLS-1$ |
| } |
| buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
| } |
| else { |
| State state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(this.currentproject, localMonitor.newChild(1)); |
| if(state == null) { |
| buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
| break; |
| } |
| BuildState.setLastBuiltState(this.currentproject, null); |
| IncrementalApiBuilder builder = new IncrementalApiBuilder(this); |
| builder.build(baseline, wbaseline, deltas, state, this.buildstate, localMonitor.newChild(1)); |
| } |
| } |
| } |
| } |
| } |
| Util.updateMonitor(localMonitor, 0); |
| |
| } |
| catch (OperationCanceledException oce) { |
| //do nothing, just end and clean up |
| } |
| catch(CoreException e) { |
| IStatus status = e.getStatus(); |
| if (status == null || status.getCode() != ApiPlugin.REPORT_BASELINE_IS_DISPOSED) { |
| throw e; |
| } |
| ApiPlugin.log(e); |
| } finally { |
| Util.updateMonitor(localMonitor, 0); |
| if(this.analyzer != null) { |
| this.analyzer.dispose(); |
| this.analyzer = null; |
| } |
| if(projects.length < 1) { |
| //if this build cycle indicates that more projects need to be built do not close |
| //the baselines yet, they might be re-read by another build cycle |
| if(baseline != null) { |
| baseline.close(); |
| } |
| } |
| Util.updateMonitor(localMonitor, 0); |
| if (this.buildstate != null) { |
| for(int i = 0, max = projects.length; i < max; i++) { |
| IProject project = projects[i]; |
| if (Util.isApiProject(project)) { |
| this.buildstate.addApiToolingDependentProject(project.getName()); |
| } |
| } |
| BuildState.saveBuiltState(this.currentproject, this.buildstate); |
| this.buildstate = null; |
| Util.updateMonitor(monitor, 0); |
| } |
| if(localMonitor != null) { |
| localMonitor.done(); |
| } |
| } |
| if (DEBUG) { |
| System.out.println("Finished build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ |
| } |
| return projects; |
| } |
| |
| /** |
| * Returns if the builder should abort the build of the given project. |
| * The build decides to abort if one of the following are true: |
| * <ul> |
| * <li>The project is not accessible</li> |
| * <li>The project does not have the API tools nature</li> |
| * <li>The project has already been built - as decided by the build framework</li> |
| * <li>The project has fatal JDT errors that prevent the creation of class files</li> |
| * </ul> |
| * @param project |
| * @return true if the builder should abort building the given project, false otherwise |
| * @throws CoreException |
| * @see {@link #hasBeenBuilt(IProject)} |
| * @see {@link #hasFatalProblems(IProject)} |
| * @since 1.1 |
| */ |
| boolean shouldAbort(IProject project) throws CoreException { |
| return !project.isAccessible() || |
| !project.hasNature(ApiPlugin.NATURE_ID) || |
| hasBeenBuilt(project) || |
| hasFatalProblems(project); |
| } |
| |
| /** |
| * Returns if the project we are about to build has fatal JDT problems that prevent |
| * class files from being built |
| * @param project |
| * @return true if the given project has fatal JDT problems |
| * @see |
| * @throws CoreException |
| * @see {@link org.eclipse.jdt.core.IJavaModelMarker#BUILDPATH_PROBLEM_MARKER} |
| * @since 1.1 |
| */ |
| boolean hasFatalProblems(IProject project) throws CoreException { |
| IMarker[] problems = project.findMarkers(IJavaModelMarker.JAVA_MODEL_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); |
| if(problems.length > 0) { |
| IApiProblem problem = ApiProblemFactory.newApiComponentResolutionProblem( |
| Path.EMPTY.toString(), |
| new String[] {project.getName()}, |
| null, |
| null, |
| IElementDescriptor.RESOURCE, |
| IApiProblem.FATAL_JDT_BUILDPATH_PROBLEM); |
| createMarkerForProblem( |
| IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION, |
| IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, |
| problem); |
| return true; |
| } |
| return false; |
| } |
| |
| /** |
| * if its worth doing a full build considering the given set if projects |
| * @param projects projects to check the build state for |
| * @return true if a full build should take place, false otherwise |
| */ |
| boolean worthDoingFullBuild(IProject[] projects) { |
| Set apiToolingDependentProjects = this.buildstate.getApiToolingDependentProjects(); |
| for (int i = 0, max = projects.length; i < max; i++) { |
| IProject currentProject = projects[i]; |
| if (Util.isApiProject(currentProject)) { |
| if (apiToolingDependentProjects.contains(currentProject.getName())) { |
| continue; |
| } |
| return true; |
| } else if (apiToolingDependentProjects.contains(currentProject.getName())) { |
| return true; |
| } |
| } |
| return false; |
| } |
| |
| /** |
| * Performs a full build for the project |
| * @param baseline the default baseline |
| * @param wbaseline the workspace baseline |
| * @param monitor |
| */ |
| void buildAll(IApiBaseline baseline, IApiBaseline wbaseline, IProgressMonitor monitor) throws CoreException { |
| SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 4); |
| try { |
| BuildState.setLastBuiltState(this.currentproject, null); |
| this.buildstate = new BuildState(); |
| localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_initializing_analyzer, currentproject.getName())); |
| cleanupMarkers(this.currentproject); |
| IPluginModelBase currentModel = getCurrentModel(); |
| if (currentModel != null) { |
| localMonitor.subTask(BuilderMessages.building_workspace_profile); |
| Util.updateMonitor(localMonitor, 1); |
| String id = currentModel.getBundleDescription().getSymbolicName(); |
| // Compatibility checks |
| IApiComponent apiComponent = wbaseline.getApiComponent(id); |
| if(apiComponent != null) { |
| getAnalyzer().analyzeComponent(this.buildstate, null, null, baseline, apiComponent, new BuildContext(), localMonitor.newChild(1)); |
| Util.updateMonitor(localMonitor, 1); |
| createMarkers(); |
| Util.updateMonitor(localMonitor, 1); |
| } |
| } |
| } |
| finally { |
| if(localMonitor != null) { |
| localMonitor.done(); |
| } |
| } |
| } |
| |
| /** |
| * Creates new markers are for the listing of problems added to this reporter. |
| * If no problems have been added to this reporter, or we are not running in the framework, |
| * no work is done. |
| */ |
| protected void createMarkers() { |
| try { |
| IResource manifest = Util.getManifestFile(this.currentproject); |
| if(manifest != null) { |
| manifest.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); |
| } |
| this.currentproject.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); |
| this.currentproject.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); |
| } catch (CoreException e) { |
| ApiPlugin.log(e); |
| } |
| IApiProblem[] problems = getAnalyzer().getProblems(); |
| String type = null; |
| for(int i = 0; i < problems.length; i++) { |
| int category = problems[i].getCategory(); |
| type = getProblemTypeFromCategory(category, problems[i].getKind()); |
| if(type == null) { |
| continue; |
| } |
| if(DEBUG) { |
| System.out.println("creating marker for: "+problems[i].toString()); //$NON-NLS-1$ |
| } |
| createMarkerForProblem(category, type, problems[i]); |
| } |
| } |
| |
| /** |
| * Returns the {@link IApiMarkerConstants} problem type given the |
| * problem category |
| * @param category the problem category - see {@link IApiProblem} for problem categories |
| * @param kind the kind of the problem - see {@link IApiProblem} for problem kinds |
| * @return the problem type or <code>null</code> |
| */ |
| String getProblemTypeFromCategory(int category, int kind) { |
| switch(category) { |
| case IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION : { |
| return IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER; |
| } |
| case IApiProblem.CATEGORY_API_BASELINE: { |
| return IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER; |
| } |
| case IApiProblem.CATEGORY_COMPATIBILITY: { |
| return IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER; |
| } |
| case IApiProblem.CATEGORY_SINCETAGS: { |
| return IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER; |
| } |
| case IApiProblem.CATEGORY_USAGE: { |
| if(kind == IApiProblem.UNSUPPORTED_TAG_USE) { |
| return IApiMarkerConstants.UNSUPPORTED_TAG_PROBLEM_MARKER; |
| } |
| if(kind == IApiProblem.UNUSED_PROBLEM_FILTERS) { |
| return IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER; |
| } |
| return IApiMarkerConstants.API_USAGE_PROBLEM_MARKER; |
| } |
| case IApiProblem.CATEGORY_VERSION: { |
| return IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER; |
| } |
| } |
| return null; |
| } |
| |
| /** |
| * Creates an {@link IMarker} on the resource specified |
| * in the problem (via its path) with the given problem |
| * attributes |
| * @param category the category of the problem - see {@link IApiProblem} for categories |
| * @param type the marker type to create - see {@link IApiMarkerConstants} for types |
| * @param problem the problem to create a marker from |
| */ |
| void createMarkerForProblem(int category, String type, IApiProblem problem) { |
| IResource resource = resolveResource(problem); |
| if(resource == null) { |
| return; |
| } |
| try { |
| IMarker marker = resource.createMarker(type); |
| int line = problem.getLineNumber(); |
| switch(category) { |
| case IApiProblem.CATEGORY_VERSION : |
| case IApiProblem.CATEGORY_API_BASELINE : |
| case IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION : { |
| break; |
| } |
| default : { |
| line++; |
| } |
| } |
| marker.setAttributes( |
| new String[] { |
| IMarker.MESSAGE, |
| IMarker.SEVERITY, |
| IMarker.LINE_NUMBER, |
| IMarker.CHAR_START, |
| IMarker.CHAR_END, |
| IMarker.SOURCE_ID, |
| IApiMarkerConstants.MARKER_ATTR_PROBLEM_ID}, |
| new Object[] { |
| problem.getMessage(), |
| new Integer(ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), this.currentproject)), |
| new Integer(line), |
| new Integer(problem.getCharStart()), |
| new Integer(problem.getCharEnd()), |
| ApiAnalysisBuilder.SOURCE, |
| new Integer(problem.getId()) |
| } |
| ); |
| //add message arguments, if any |
| String[] args = problem.getMessageArguments(); |
| if(args.length > 0) { |
| marker.setAttribute(IApiMarkerConstants.MARKER_ATTR_MESSAGE_ARGUMENTS, createArgAttribute(args)); |
| } |
| String typeName = problem.getTypeName(); |
| if (typeName != null) { |
| marker.setAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, typeName); |
| } |
| //add all other extra arguments, if any |
| if(problem.getExtraMarkerAttributeIds().length > 0) { |
| marker.setAttributes(problem.getExtraMarkerAttributeIds(), problem.getExtraMarkerAttributeValues()); |
| } |
| } catch (CoreException e) { |
| //ignore and continue |
| return; |
| } |
| } |
| |
| /** |
| * Resolves the resource from the path in the problem, returns <code>null</code> in |
| * the following cases: |
| * <ul> |
| * <li>The resource is not found in the parent project (findMember() returns null)</li> |
| * <li>The resource is not accessible (isAccessible() returns false</li> |
| * </ul> |
| * @param problem the problem to get the resource for |
| * @return the resource or <code>null</code> |
| */ |
| IResource resolveResource(IApiProblem problem) { |
| String resourcePath = problem.getResourcePath(); |
| if (resourcePath == null) { |
| return null; |
| } |
| IResource resource = currentproject.findMember(new Path(resourcePath)); |
| if(resource == null) { |
| return null; |
| } |
| if(!resource.isAccessible()) { |
| return null; |
| } |
| return resource; |
| } |
| |
| /** |
| * Creates a single string attribute from an array of strings. Uses the '#' char as |
| * a delimiter |
| * @param args |
| * @return a single string attribute from an array or arguments |
| */ |
| String createArgAttribute(String[] args) { |
| StringBuffer buff = new StringBuffer(); |
| for(int i = 0; i < args.length; i++) { |
| buff.append(args[i]); |
| if(i < args.length-1) { |
| buff.append("#"); //$NON-NLS-1$ |
| } |
| } |
| return buff.toString(); |
| } |
| |
| /* (non-Javadoc) |
| * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor) |
| */ |
| protected void clean(IProgressMonitor monitor) throws CoreException { |
| this.currentproject = getProject(); |
| SubMonitor localmonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.CleaningAPIDescription, new String[] {this.currentproject.getName()}), 2); |
| try { |
| // clean up all existing markers |
| cleanupUsageMarkers(this.currentproject); |
| cleanupCompatibilityMarkers(this.currentproject); |
| cleanupUnsupportedTagMarkers(this.currentproject); |
| this.currentproject.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
| Util.updateMonitor(localmonitor, 1); |
| //clean up the .api_settings |
| cleanupApiDescription(this.currentproject); |
| Util.updateMonitor(localmonitor, 1); |
| } |
| finally { |
| BuildState.setLastBuiltState(this.currentproject, null); |
| localmonitor.done(); |
| } |
| } |
| |
| /** |
| * Cleans the .api_settings file for the given project |
| * @param project |
| */ |
| void cleanupApiDescription(IProject project) { |
| if(project != null && project.exists()) { |
| ApiDescriptionManager.getDefault().clean(JavaCore.create(project), true, false); |
| } |
| } |
| |
| /** |
| * @return the current {@link IPluginModelBase} based on the current project for this builder |
| */ |
| IPluginModelBase getCurrentModel() { |
| IPluginModelBase[] workspaceModels = PluginRegistry.getWorkspaceModels(); |
| IPath location = this.currentproject.getLocation(); |
| IPluginModelBase currentModel = null; |
| BundleDescription desc = null; |
| loop: for (int i = 0, max = workspaceModels.length; i < max; i++) { |
| desc = workspaceModels[i].getBundleDescription(); |
| if(desc != null) { |
| Path path = new Path(desc.getLocation()); |
| if (path.equals(location)) { |
| currentModel = workspaceModels[i]; |
| break loop; |
| } |
| } |
| else if(DEBUG) { |
| System.out.println("Tried to look up bundle description for: " + workspaceModels[i].toString()); //$NON-NLS-1$ |
| } |
| } |
| return currentModel; |
| } |
| |
| /** |
| * Returns a listing of deltas for this project and for dependent projects |
| * @param projects |
| * @return |
| */ |
| IResourceDelta[] getDeltas(IProject[] projects) { |
| if(DEBUG) { |
| System.out.println("Searching for deltas for build of project: "+this.currentproject.getName()); //$NON-NLS-1$ |
| } |
| ArrayList deltas = new ArrayList(); |
| IResourceDelta delta = getDelta(this.currentproject); |
| if(delta != null) { |
| if (DEBUG) { |
| System.out.println("Found a delta: " + delta); //$NON-NLS-1$ |
| } |
| deltas.add(delta); |
| } |
| for(int i = 0; i < projects.length; i++) { |
| delta = getDelta(projects[i]); |
| if(delta != null) { |
| if (DEBUG) { |
| System.out.println("Found a delta: " + delta); //$NON-NLS-1$ |
| } |
| deltas.add(delta); |
| } |
| } |
| return (IResourceDelta[]) deltas.toArray(new IResourceDelta[deltas.size()]); |
| } |
| |
| /** |
| * Returns the API analyzer to use with this instance of the builder |
| * @return the API analyzer to use |
| */ |
| protected synchronized IApiAnalyzer getAnalyzer() { |
| if(this.analyzer == null) { |
| this.analyzer = new BaseApiAnalyzer(); |
| } |
| return this.analyzer; |
| } |
| |
| /** |
| * Returns the complete listing of required projects from the classpath of the backing project |
| * @param includeBinaryPrerequisites |
| * @return the list of projects required |
| * @throws CoreException |
| */ |
| IProject[] getRequiredProjects(boolean includebinaries) throws CoreException { |
| IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
| if (this.currentproject == null || workspaceRoot == null) { |
| return new IProject[0]; |
| } |
| ArrayList projects = new ArrayList(); |
| try { |
| IJavaProject javaProject = JavaCore.create(this.currentproject); |
| HashSet blocations = new HashSet(); |
| blocations.add(javaProject.getOutputLocation()); |
| this.output_locs.put(this.currentproject, blocations); |
| HashSet slocations = new HashSet(); |
| IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); |
| for (int i = 0; i < roots.length; i++) { |
| if(roots[i].isArchive()) { |
| continue; |
| } |
| slocations.add(roots[i].getPath()); |
| } |
| this.src_locs.put(this.currentproject, slocations); |
| IClasspathEntry[] entries = javaProject.getResolvedClasspath(true); |
| for (int i = 0, l = entries.length; i < l; i++) { |
| IClasspathEntry entry = entries[i]; |
| IPath path = entry.getPath(); |
| IProject p = null; |
| switch (entry.getEntryKind()) { |
| case IClasspathEntry.CPE_PROJECT : { |
| p = workspaceRoot.getProject(path.lastSegment()); // missing projects are considered too |
| if (isOptional(entry) && !p.hasNature(ApiPlugin.NATURE_ID)) {// except if entry is optional |
| p = null; |
| } |
| break; |
| } |
| case IClasspathEntry.CPE_LIBRARY : { |
| if (includebinaries && path.segmentCount() > 1) { |
| // some binary resources on the class path can come from projects that are not included in the project references |
| IResource resource = workspaceRoot.findMember(path.segment(0)); |
| if (resource instanceof IProject) { |
| p = (IProject) resource; |
| } |
| } |
| break; |
| } |
| case IClasspathEntry.CPE_SOURCE: { |
| IPath entrypath = entry.getOutputLocation(); |
| if(entrypath != null) { |
| blocations.add(entrypath); |
| } |
| } |
| } |
| if (p != null && !projects.contains(p)) { |
| projects.add(p); |
| //try to derive all of the output locations for each of the projects |
| javaProject = JavaCore.create(p); |
| HashSet bins = new HashSet(); |
| HashSet srcs = new HashSet(); |
| if(javaProject.exists()) { |
| bins.add(javaProject.getOutputLocation()); |
| IClasspathEntry[] source = javaProject.getRawClasspath(); |
| IPath entrypath = null; |
| for(int j = 0; j < source.length; j++) { |
| if(source[j].getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
| srcs.add(source[j].getPath()); |
| entrypath = source[j].getOutputLocation(); |
| if(entrypath != null) { |
| bins.add(entrypath); |
| } |
| } |
| } |
| this.output_locs.put(p, bins); |
| this.src_locs.put(p, srcs); |
| } |
| } |
| } |
| } |
| catch(JavaModelException e) { |
| return new IProject[0]; |
| } |
| IProject[] result = new IProject[projects.size()]; |
| projects.toArray(result); |
| return result; |
| } |
| |
| /** |
| * Returns the output paths of the given project or <code>null</code> if none have been computed |
| * @param project |
| * @return the output paths for the given project or <code>null</code> |
| */ |
| HashSet getProjectOutputPaths(IProject project) { |
| return (HashSet) this.output_locs.get(project); |
| } |
| |
| /** |
| * Returns is the given classpath entry is optional or not |
| * @param entry |
| * @return true if the specified {@link IClasspathEntry} is optional, false otherwise |
| */ |
| boolean isOptional(IClasspathEntry entry) { |
| IClasspathAttribute[] attribs = entry.getExtraAttributes(); |
| for (int i = 0, length = attribs.length; i < length; i++) { |
| IClasspathAttribute attribute = attribs[i]; |
| if (IClasspathAttribute.OPTIONAL.equals(attribute.getName()) && "true".equals(attribute.getValue())) //$NON-NLS-1$ |
| return true; |
| } |
| return false; |
| } |
| |
| /* (non-Javadoc) |
| * @see java.lang.Object#toString() |
| */ |
| public String toString() { |
| return NLS.bind(BuilderMessages.ApiAnalysisBuilder_builder_for_project, this.currentproject.getName()); |
| } |
| } |