### Eclipse Workspace Patch 1.0 #P org.eclipse.pde.api.tools Index: src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java,v retrieving revision 1.87 diff -u -r1.87 BaseApiAnalyzer.java --- src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java 4 Mar 2009 01:44:08 -0000 1.87 +++ src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java 6 Mar 2009 19:52:48 -0000 @@ -301,7 +301,7 @@ } } if (changedTypes != null) { - // check the ones not already checked as part of typenames check + // check the ones not already checked as part of type names check for (int i = 0; i < changedTypes.length; i++) { String typeName = changedTypes[i]; if (typeNamesSet == null || !typeNamesSet.remove(typeName)) { @@ -1550,7 +1550,7 @@ Util.EMPTY_STRING); } } - // analyse version of required components + // analyze version of required components ReexportedBundleVersionInfo info = null; if (problem != null) { switch (problem.getKind()) { @@ -1580,7 +1580,7 @@ } break; case IApiProblem.MINOR_VERSION_CHANGE : - // check if there is a version change required due to reexported bundles + // check if there is a version change required due to re-exported bundles info = checkBundleVersionsOfReexportedBundles(reference, component); if (info != null) { switch(info.kind) { @@ -1602,7 +1602,7 @@ } break; case IApiProblem.MINOR_VERSION_CHANGE_NO_NEW_API : - // check if there is a version change required due to reexported bundles + // check if there is a version change required due to re-exported bundles info = checkBundleVersionsOfReexportedBundles(reference, component); if (info != null) { switch(info.kind) { Index: src/org/eclipse/pde/api/tools/internal/builder/BuilderMessages.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/BuilderMessages.java,v retrieving revision 1.32 diff -u -r1.32 BuilderMessages.java --- src/org/eclipse/pde/api/tools/internal/builder/BuilderMessages.java 7 Jan 2009 19:29:01 -0000 1.32 +++ src/org/eclipse/pde/api/tools/internal/builder/BuilderMessages.java 6 Mar 2009 19:52:48 -0000 @@ -20,6 +20,7 @@ public static String checking_api_usage; public static String AbstractTypeLeakDetector_vis_type_has_no_api_description; + public static String ApiAnalysisBuilder_builder_for_project; public static String ApiAnalysisBuilder_finding_affected_source_files; public static String ApiAnalysisBuilder_initializing_analyzer; public static String ApiProblemFactory_problem_message_not_found; Index: src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java,v retrieving revision 1.93 diff -u -r1.93 ApiAnalysisBuilder.java --- src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java 5 Mar 2009 20:57:12 -0000 1.93 +++ src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java 6 Mar 2009 19:52:47 -0000 @@ -22,18 +22,14 @@ import java.util.Date; import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; import java.util.Map; -import java.util.Set; import java.util.jar.JarFile; -import org.eclipse.core.resources.IFile; 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.IResourceDeltaVisitor; import org.eclipse.core.resources.IWorkspaceRoot; import org.eclipse.core.resources.IncrementalProjectBuilder; import org.eclipse.core.resources.ResourcesPlugin; @@ -41,7 +37,6 @@ import org.eclipse.core.runtime.IPath; import org.eclipse.core.runtime.IProgressMonitor; import org.eclipse.core.runtime.IStatus; -import org.eclipse.core.runtime.NullProgressMonitor; import org.eclipse.core.runtime.OperationCanceledException; import org.eclipse.core.runtime.Path; import org.eclipse.core.runtime.Platform; @@ -49,16 +44,9 @@ import org.eclipse.core.runtime.SubMonitor; import org.eclipse.jdt.core.IClasspathAttribute; import org.eclipse.jdt.core.IClasspathEntry; -import org.eclipse.jdt.core.ICompilationUnit; -import org.eclipse.jdt.core.IJavaElement; import org.eclipse.jdt.core.IJavaProject; -import org.eclipse.jdt.core.IType; 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.ReferenceCollection; -import org.eclipse.jdt.internal.core.builder.State; -import org.eclipse.jdt.internal.core.builder.StringSet; import org.eclipse.osgi.service.resolver.BundleDescription; import org.eclipse.osgi.util.NLS; import org.eclipse.pde.api.tools.internal.ApiDescriptionManager; @@ -82,93 +70,30 @@ */ public class ApiAnalysisBuilder extends IncrementalProjectBuilder { /** - * Visits a resource delta to determine if the changes have been made that might required a rebuild: - * - modification to the manifest file - * - removal of the .api_filter file - */ - class ResourceDeltaVisitor implements IResourceDeltaVisitor { - IProject[] projects; - public ResourceDeltaVisitor(IProject[] projects) { - this.projects = projects; - } - private boolean fRequireFullBuild = false; - - /** - * Returns whether a full build should be run. - * - * @return whether a full build should be run - */ - boolean shouldRunFullBuild() { - return fRequireFullBuild; - } - - /* (non-Javadoc) - * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) - */ - public boolean visit(IResourceDelta delta) throws CoreException { - switch (delta.getResource().getType()) { - case IResource.ROOT: - case IResource.PROJECT: - return !fRequireFullBuild; - case IResource.FOLDER: - return !fRequireFullBuild; - case IResource.FILE: - if (delta.getResource().getProjectRelativePath().equals(MANIFEST_PATH)) { - fRequireFullBuild = true; - break; - } - IResource resource = delta.getResource(); - String fileName = resource.getName(); - if (Util.isClassFile(fileName)) { - findAffectedSourceFiles(delta); - } else if (Util.isJavaFileName(fileName)) { - IProject project = resource.getProject(); - if (fCurrentProject.equals(project)) { - if (delta.getKind() == IResourceDelta.ADDED) { - fAddedRemovedDeltas.add(delta); - } - fTypesToCheck.add(resource); - } else if (this.projects != null) { - loop: for (int i = 0, max = this.projects.length; i < max; i++) { - if (this.projects[i].equals(project)) { - fTypesToCheck.add(resource); - break loop; - } - } - } - } else if (!fRequireFullBuild && IApiCoreConstants.API_FILTERS_XML_NAME.equals(fileName)) { - switch(delta.getKind()) { - case IResourceDelta.REMOVED : - case IResourceDelta.REPLACED : - case IResourceDelta.CHANGED : - case IResourceDelta.ADDED : - fRequireFullBuild = true; - } - } - } - return false; - } - } - /** * Constant used for controlling tracing in the API tool builder */ - private static boolean DEBUG = Util.DEBUG; + static boolean DEBUG = Util.DEBUG; /** * Project relative path to the manifest file. */ - private static final IPath MANIFEST_PATH = new Path(JarFile.MANIFEST_NAME); + static final IPath MANIFEST_PATH = new Path(JarFile.MANIFEST_NAME); /** - * Internal flag used to determine what created the marker, as there is overlap for reference kinds and deltas + * Project relative path to the .api_filters file */ - public static final int REF_TYPE_FLAG = 0; + static final IPath FILTER_PATH = new Path(".settings").append(IApiCoreConstants.API_FILTERS_XML_NAME); //$NON-NLS-1$ + + /** + * 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 Api Tooling */ - public static final String SOURCE = "Api Tooling"; //$NON-NLS-1$ + static final String SOURCE = "Api Tooling"; //$NON-NLS-1$ /** * Method used for initializing tracing in the API tool builder @@ -176,51 +101,33 @@ public static void setDebug(boolean debugValue) { DEBUG = debugValue || Util.DEBUG; } + /** * The current project for which this builder was defined */ - private IProject fCurrentProject = null; + private IProject currentproject = null; /** * The API analyzer for this builder */ - private IApiAnalyzer fAnalyzer = null; + private IApiAnalyzer analyzer = null; /** * Maps prerequisite projects to their output location(s) */ - private HashMap fProjectToOutputLocations = new HashMap(); - - /** - * List of type names to lookup for each project context to find dependents of - */ - private StringSet fTypes = new StringSet(3); - - /** - * List of package names to qualify type names - */ - private StringSet fPackages = new StringSet(3); - - /** - * The type that we want to check for API problems - */ - private HashSet fTypesToCheck = new HashSet(); - /** - * The set of added/removed deltas that come directly from the builder resource delta - */ - private HashSet fAddedRemovedDeltas = new HashSet(5); + private HashMap projecttooutputlocations = new HashMap(); /** * Current build state */ - private BuildState fBuildState; + private BuildState buildstate = null; /** * Cleans up markers associated with API tooling on the given resource. * * @param resource */ - public static void cleanupMarkers(IResource resource) { + void cleanupMarkers(IResource resource) { cleanupUsageMarkers(resource); cleanupCompatibilityMarkers(resource); cleanupUnsupportedTagMarkers(resource); @@ -230,7 +137,7 @@ * Cleans up unsupported Javadoc tag markers on the specified resource * @param resource */ - private static void cleanupUnsupportedTagMarkers(IResource resource) { + void cleanupUnsupportedTagMarkers(IResource resource) { try { if(DEBUG) { System.out.println("cleaning unsupported tag problems"); //$NON-NLS-1$ @@ -245,7 +152,7 @@ * Cleans up only API compatibility markers on the given {@link IResource} * @param resource the given resource */ - private static void cleanupCompatibilityMarkers(IResource resource) { + void cleanupCompatibilityMarkers(IResource resource) { try { if (resource != null && resource.isAccessible()) { resource.deleteMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); @@ -266,7 +173,7 @@ * cleans up only API usage markers from the given {@link IResource} * @param resource */ - private static void cleanupUsageMarkers(IResource resource) { + void cleanupUsageMarkers(IResource resource) { try { if (resource != null && resource.isAccessible()) { resource.deleteMarkers(IApiMarkerConstants.API_USAGE_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); @@ -276,39 +183,17 @@ } } - /** - * Adds a type to search for dependents of in considered projects for an incremental build - * - * @param path - */ - private void addDependentsOf(IPath path) { - // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X' - path = path.setDevice(null); - String packageName = path.removeLastSegments(1).toString(); - String typeName = path.lastSegment(); - int memberIndex = typeName.indexOf('$'); - if (memberIndex > 0) { - typeName = typeName.substring(0, memberIndex); - } - if (fTypes.add(typeName) && fPackages.add(packageName) && DEBUG) { - System.out.println(" will look for dependents of " + typeName + " in " + packageName); //$NON-NLS-1$ //$NON-NLS-2$ - } - } - /* (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 { - fCurrentProject = getProject(); - fAnalyzer = getAnalyzer(); - if (fCurrentProject == null || - !fCurrentProject.isAccessible() || - !fCurrentProject.hasNature(ApiPlugin.NATURE_ID) || - hasBeenBuilt(fCurrentProject)) { - return new IProject[0]; + this.currentproject = getProject(); + this.analyzer = getAnalyzer(); + if (!this.currentproject.isAccessible() || !this.currentproject.hasNature(ApiPlugin.NATURE_ID) || hasBeenBuilt(this.currentproject)) { + return NO_PROJECTS; } if (DEBUG) { - System.out.println("\nStarting build of " + fCurrentProject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println("\nStarting build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ } updateMonitor(monitor, 0); SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 2); @@ -326,63 +211,60 @@ case AUTO_BUILD : case INCREMENTAL_BUILD : { IResourceDelta[] deltas = getDeltas(projects); - boolean shouldRunFullBuild = false; - fBuildState = getLastBuiltState(fCurrentProject); - if (fBuildState == null) { + if(deltas.length == 0) { buildAll(baseline, localMonitor.newChild(1)); - } else { - IProject[] reexportedProjects = null; - String[] projectNames = this.fBuildState.getReexportedComponents(); - int length = projectNames.length; - if (length != 0) { - List allProjects = new ArrayList(); - IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); - for (int i = 0, max = projectNames.length; i < max; i++) { - String projectName = projectNames[i]; - IProject project = root.getProject(projectName); - if (project.isAccessible()) { - // select only projects that don't exist in the reference baseline - if (baseline != null && baseline.getApiComponent(projectName) == null) { - allProjects.add(project); - } - } - } - if (allProjects.size() != 0) { - reexportedProjects = new IProject[allProjects.size()]; - allProjects.toArray(reexportedProjects); - } + break; + } + this.buildstate = getLastBuiltState(currentproject); + if (this.buildstate == null) { + buildAll(baseline, localMonitor.newChild(1)); + break; + } + IResourceDelta manifest = null; + IResourceDelta filters = null; + 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){ + break; } - ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(reexportedProjects); - for (int i = 0; i < deltas.length; i++) { - deltas[i].accept(visitor); - if (visitor.shouldRunFullBuild()) { - shouldRunFullBuild = true; - break; - } + } + if (manifest != null || filters != null) { + if (DEBUG) { + System.out.println("Performing full build since MANIFEST.MF or .api_filters was modified"); //$NON-NLS-1$ } - if (shouldRunFullBuild) { - if (DEBUG) { - System.out.println("Performing full build since MANIFEST.MF was modified"); //$NON-NLS-1$ - } - buildAll(baseline, localMonitor.newChild(1)); - } else if (deltas.length == 0) { - if (DEBUG) { - System.out.println("Performing full build since deltas are missing after incremental request"); //$NON-NLS-1$ - } - buildAll(baseline, localMonitor.newChild(1)); - } else { - State state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(fCurrentProject, new NullProgressMonitor()); - if (state == null) { - buildAll(baseline, localMonitor.newChild(1)); - } else { - build(state, baseline, localMonitor.newChild(1)); + buildAll(baseline, localMonitor.newChild(1)); + } + IProject[] reexportedProjects = null; + String[] projectNames = this.buildstate.getReexportedComponents(); + int length = projectNames.length; + if (length != 0) { + List allProjects = new ArrayList(); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + for (int i = 0, max = projectNames.length; i < max; i++) { + String projectName = projectNames[i]; + IProject project = root.getProject(projectName); + if (project.isAccessible()) { + // select only projects that don't exist in the reference baseline + if (baseline != null && baseline.getApiComponent(projectName) == null) { + allProjects.add(project); + } } } + if (allProjects.size() != 0) { + reexportedProjects = new IProject[allProjects.size()]; + allProjects.toArray(reexportedProjects); + } + } + IncrementalApiBuilder incbuilder = new IncrementalApiBuilder(this); + incbuilder.build(baseline, deltas, localMonitor.newChild(1)); } break; } - } - updateMonitor(monitor, 0); + updateMonitor(monitor, 0); } catch(CoreException e) { IStatus status = e.getStatus(); if (status == null || status.getCode() != ApiPlugin.REPORT_BASELINE_IS_DISPOSED) { @@ -390,26 +272,22 @@ } ApiPlugin.log(e); } finally { - fTypes.clear(); - fPackages.clear(); - fTypesToCheck.clear(); - fAddedRemovedDeltas.clear(); - fProjectToOutputLocations.clear(); + this.projecttooutputlocations.clear(); updateMonitor(monitor, 0); - fAnalyzer.dispose(); + this.analyzer.dispose(); if(baseline != null) { baseline.close(); } if(monitor != null) { monitor.done(); } - if (fBuildState != null) { - saveBuiltState(fCurrentProject, fBuildState); - fBuildState = null; + if (this.buildstate != null) { + saveBuiltState(this.currentproject, this.buildstate); + this.buildstate = null; } } if (DEBUG) { - System.out.println("Finished build of " + fCurrentProject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ + System.out.println("Finished build of " + currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ } return projects; } @@ -418,14 +296,14 @@ * Performs a full build for the project * @param monitor */ - private void buildAll(IApiBaseline baseline, IProgressMonitor monitor) throws CoreException { + void buildAll(IApiBaseline baseline, IProgressMonitor monitor) throws CoreException { IApiBaseline wsprofile = null; try { clearLastState(); - fBuildState = new BuildState(); + this.buildstate = new BuildState(); SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 4); - localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_initializing_analyzer, fCurrentProject.getName())); - cleanupMarkers(fCurrentProject); + 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); @@ -441,7 +319,7 @@ // Compatibility checks IApiComponent apiComponent = wsprofile.getApiComponent(id); if(apiComponent != null) { - fAnalyzer.analyzeComponent(fBuildState, null, null, baseline, apiComponent, null, null, localMonitor.newChild(1)); + this.analyzer.analyzeComponent(this.buildstate, null, null, baseline, apiComponent, null, null, localMonitor.newChild(1)); updateMonitor(localMonitor, 1); createMarkers(); updateMonitor(localMonitor, 1); @@ -465,13 +343,13 @@ */ protected void createMarkers() { try { - fCurrentProject.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); - fCurrentProject.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); - fCurrentProject.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); + this.currentproject.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE); + this.currentproject.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); + this.currentproject.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); } catch (CoreException e) { ApiPlugin.log(e); } - IApiProblem[] problems = fAnalyzer.getProblems(); + IApiProblem[] problems = this.analyzer.getProblems(); String type = null; for(int i = 0; i < problems.length; i++) { int category = problems[i].getCategory(); @@ -555,7 +433,7 @@ IApiMarkerConstants.MARKER_ATTR_PROBLEM_ID}, new Object[] { problem.getMessage(), - new Integer(ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), this.fCurrentProject)), + new Integer(ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), this.currentproject)), new Integer(line), new Integer(problem.getCharStart()), new Integer(problem.getCharEnd()), @@ -597,7 +475,7 @@ if (resourcePath == null) { return null; } - IResource resource = fCurrentProject.findMember(new Path(resourcePath)); + IResource resource = currentproject.findMember(new Path(resourcePath)); if(resource == null) { return null; } @@ -631,7 +509,7 @@ * @param ticks * @throws OperationCanceledException */ - private void updateMonitor(IProgressMonitor monitor, int ticks) throws OperationCanceledException { + void updateMonitor(IProgressMonitor monitor, int ticks) throws OperationCanceledException { if(monitor != null) { monitor.worked(ticks); if (monitor.isCanceled()) { @@ -640,232 +518,21 @@ } } - /** - * Builds an API delta using the default profile (from the workspace settings and the current - * workspace profile - * @param state - * @param monitor - */ - private void build(final State state, IApiBaseline baseline, IProgressMonitor monitor) throws CoreException { - IApiBaseline wsprofile = null; - try { - clearLastState(); // so if the build fails, a full build will be triggered - int typesToCheckSize = fTypesToCheck.size(); - SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 2 + (typesToCheckSize != 0 ? 3 : 0)); - localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, fCurrentProject.getName())); - updateMonitor(localMonitor, 0); - collectAffectedSourceFiles(state, fTypesToCheck); - updateMonitor(localMonitor, 1); - if (typesToCheckSize != 0) { - IPluginModelBase currentModel = getCurrentModel(); - if (currentModel != null) { - wsprofile = getWorkspaceProfile(); - if (wsprofile == null) { - if (DEBUG) { - System.err.println("Could not retrieve a workspace profile"); //$NON-NLS-1$ - } - return; - } - String id = currentModel.getBundleDescription().getSymbolicName(); - IApiComponent apiComponent = wsprofile.getApiComponent(id); - if(apiComponent == null) { - return; - } - List tnames = new ArrayList(typesToCheckSize), - cnames = new ArrayList(typesToCheckSize); - collectAllQualifiedNames(fTypesToCheck, tnames, cnames, localMonitor.newChild(1)); - updateMonitor(localMonitor, 1); - fAnalyzer.analyzeComponent(fBuildState, - null, - null, - baseline, - apiComponent, - (String[])tnames.toArray(new String[tnames.size()]), - (String[])cnames.toArray(new String[cnames.size()]), - localMonitor.newChild(1)); - updateMonitor(localMonitor, 1); - createMarkers(); - updateMonitor(localMonitor, 1); - } - } - } - finally { - if(wsprofile != null) { - wsprofile.close(); - } - if(monitor != null) { - monitor.done(); - } - } - } - - /** - * Returns an array of type names, and cleans up markers for the specified resource - * @param alltypes the listing of {@link IFile}s to get qualified names from - * @param changedtypes the listing of {@link IFile}s that have actually changed (from the {@link IResourceDelta} - * @param tnames the list to collect all type names into (including inner member names) - * @param cnames the list to collect the changed type names into - * @param monitor - */ - private void collectAllQualifiedNames(final HashSet alltypes, List tnames, List cnames, final IProgressMonitor monitor) { - IType[] types = null; - IFile file = null; - for (Iterator iterator = alltypes.iterator(); iterator.hasNext(); ) { - file = (IFile) iterator.next(); - ICompilationUnit unit = (ICompilationUnit) JavaCore.create(file); - if(!unit.exists()) { - continue; - } - IType type = unit.findPrimaryType(); - if(type == null) { - continue; - } - updateMonitor(monitor, 0); - cleanupUnsupportedTagMarkers(file); - updateMonitor(monitor, 0); - cleanupCompatibilityMarkers(file); - updateMonitor(monitor, 0); - cnames.add(type.getFullyQualifiedName()); - try { - cleanupUsageMarkers(file); - updateMonitor(monitor, 0); - types = unit.getAllTypes(); - String tname = null; - for (int i = 0; i < types.length; i++) { - IType type2 = types[i]; - if (type2.isMember()) { - tname = type2.getFullyQualifiedName('$'); - } else { - tname = type2.getFullyQualifiedName(); - } - tnames.add(tname); - } - } catch (JavaModelException e) { - ApiPlugin.log(e.getStatus()); - } - updateMonitor(monitor, 0); - } - // inject removed types inside changed type names so that we can properly detect type removal - for (Iterator iterator = this.fAddedRemovedDeltas.iterator(); iterator.hasNext(); ) { - IResourceDelta delta = (IResourceDelta) iterator.next(); - if (delta.getKind() != IResourceDelta.REMOVED) continue; - IResource resource = delta.getResource(); - IPath typePath = resolveJavaPathFromResource(resource); - if(typePath == null) { - continue; - } - // record removed type names (package + type) - StringBuffer buffer = new StringBuffer(); - String[] segments = typePath.segments(); - for (int i = 0, max = segments.length; i < max; i++) { - if (i > 0) { - buffer.append('.'); - } - buffer.append(segments[i]); - } - cnames.add(String.valueOf(buffer)); - } - // clean up markers on added deltas - if (!this.fAddedRemovedDeltas.isEmpty()) { - IResource manifestFile = Util.getManifestFile(this.fCurrentProject); - if (manifestFile != null) { - try { - IMarker[] markers = manifestFile.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); - for (int i = 0, max = markers.length; i < max; i++) { - IMarker marker = markers[i]; - String typeName = marker.getAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, null); - if (typeName != null) { - for (Iterator iterator = this.fAddedRemovedDeltas.iterator(); iterator.hasNext(); ) { - IResourceDelta delta = (IResourceDelta) iterator.next(); - if (delta.getKind() != IResourceDelta.ADDED) continue; - ICompilationUnit unit = (ICompilationUnit) JavaCore.create(delta.getResource()); - if(!unit.exists()) { - continue; - } - IType type = unit.findPrimaryType(); - if(type == null) { - continue; - } - if (typeName.equals(type.getFullyQualifiedName())) { - marker.delete(); - return; - } else { - // check secondary types - try { - types = unit.getAllTypes(); - for (int j = 0; j < types.length; j++) { - IType type2 = types[i]; - String fullyQualifiedName = null; - if (type2.isMember()) { - fullyQualifiedName = type2.getFullyQualifiedName('$'); - } else { - fullyQualifiedName = type2.getFullyQualifiedName(); - } - if (typeName.equals(fullyQualifiedName)) { - marker.delete(); - return; - } - } - } catch (JavaModelException e) { - ApiPlugin.log(e.getStatus()); - } - } - } - } - } - } catch (CoreException e) { - ApiPlugin.log(e.getStatus()); - } - } - } - IResource resource = fCurrentProject.findMember(MANIFEST_PATH); - if (resource != null) { - try { - IMarker[] markers = resource.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); - loop: for (int i = 0, max = markers.length; i < max; i++) { - IMarker marker = markers[i]; - String typeNameFromMarker = Util.getTypeNameFromMarker(marker); - for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) { - String typeName = (String) iterator.next(); - if (typeName.equals(typeNameFromMarker)) { - marker.delete(); - continue loop; - } - } - } - markers = resource.findMarkers(IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); - loop: for (int i = 0, max = markers.length; i < max; i++) { - IMarker marker = markers[i]; - String typeNameFromMarker = Util.getTypeNameFromMarker(marker); - for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) { - String typeName = (String) iterator.next(); - if (typeName.equals(typeNameFromMarker)) { - marker.delete(); - continue loop; - } - } - } - } catch (CoreException e) { - ApiPlugin.log(e); - } - } - } - /* (non-Javadoc) * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor) */ protected void clean(IProgressMonitor monitor) throws CoreException { - fCurrentProject = getProject(); - SubMonitor localmonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.CleaningAPIDescription, new String[] {fCurrentProject.getName()}), 2); + this.currentproject = getProject(); + SubMonitor localmonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.CleaningAPIDescription, new String[] {currentproject.getName()}), 2); try { // clean up all existing markers - cleanupUsageMarkers(fCurrentProject); - cleanupCompatibilityMarkers(fCurrentProject); - cleanupUnsupportedTagMarkers(fCurrentProject); - fCurrentProject.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); + cleanupUsageMarkers(this.currentproject); + cleanupCompatibilityMarkers(this.currentproject); + cleanupUnsupportedTagMarkers(this.currentproject); + this.currentproject.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); updateMonitor(localmonitor, 1); //clean up the .api_settings - cleanupApiDescription(fCurrentProject); + cleanupApiDescription(this.currentproject); updateMonitor(localmonitor, 1); } finally { @@ -882,93 +549,13 @@ ApiDescriptionManager.getDefault().clean(JavaCore.create(project), true, false); } } - /** - * Collects the complete set of affected source files from the current project context based on the current JDT build state. - * - * @param state - */ - private void collectAffectedSourceFiles(State state, Set typesToCheck) { - // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X' - char[][][] internedQualifiedNames = ReferenceCollection.internQualifiedNames(fPackages); - // if a well known qualified name was found then we can skip over these - if (internedQualifiedNames.length < fPackages.elementSize) { - internedQualifiedNames = null; - } - char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(fTypes, true); - // if a well known name was found then we can skip over these - if (internedSimpleNames.length < fTypes.elementSize) { - internedSimpleNames = null; - } - Object[] keyTable = state.getReferences().keyTable; - Object[] valueTable = state.getReferences().valueTable; - next : for (int i = 0, l = valueTable.length; i < l; i++) { - String typeLocator = (String) keyTable[i]; - if (typeLocator != null) { - ReferenceCollection refs = (ReferenceCollection) valueTable[i]; - if (refs.includes(internedQualifiedNames, internedSimpleNames, null)) { - IFile file = fCurrentProject.getFile(typeLocator); - if (file == null) { - continue next; - } - if (DEBUG) { - System.out.println(" adding affected source file " + typeLocator); //$NON-NLS-1$ - } - typesToCheck.add(file); - } - } - } - } - - - /** - * Finds affected source files for a resource that has changed that either contains class files or is itself a class file - * @param binaryDelta - */ - private void findAffectedSourceFiles(IResourceDelta binaryDelta) { - IResource resource = binaryDelta.getResource(); - if(resource.getType() == IResource.FILE) { - if (Util.isClassFile(resource.getName())) { - switch (binaryDelta.getKind()) { - case IResourceDelta.REMOVED : - fAddedRemovedDeltas.add(binaryDelta); - //$FALL-THROUGH$ - case IResourceDelta.ADDED : { - IPath typePath = resolveJavaPathFromResource(resource); - if(typePath == null) { - return; - } - if (DEBUG) { - System.out.println("Found added/removed class file " + typePath); //$NON-NLS-1$ - } - addDependentsOf(typePath); - return; - } - case IResourceDelta.CHANGED : { - if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0) { - return; // skip it since it really isn't changed - } - IPath typePath = resolveJavaPathFromResource(resource); - if(typePath == null) { - return; - } - if (DEBUG) { - System.out.println("Found changed class file " + typePath); //$NON-NLS-1$ - } - addDependentsOf(typePath); - } - } - return; - } - } - } - /** * @return the current {@link IPluginModelBase} based on the current project for this builder */ - private IPluginModelBase getCurrentModel() { + IPluginModelBase getCurrentModel() { IPluginModelBase[] workspaceModels = PluginRegistry.getWorkspaceModels(); - IPath location = fCurrentProject.getLocation(); + IPath location = this.currentproject.getLocation(); IPluginModelBase currentModel = null; BundleDescription desc = null; loop: for (int i = 0, max = workspaceModels.length; i < max; i++) { @@ -994,10 +581,10 @@ */ private IResourceDelta[] getDeltas(IProject[] projects) { if(DEBUG) { - System.out.println("Searching for deltas for build of project: "+fCurrentProject.getName()); //$NON-NLS-1$ + System.out.println("Searching for deltas for build of project: "+this.currentproject.getName()); //$NON-NLS-1$ } ArrayList deltas = new ArrayList(); - IResourceDelta delta = getDelta(fCurrentProject); + IResourceDelta delta = getDelta(this.currentproject); if(delta != null) { if (DEBUG) { System.out.println("Found a delta: " + delta); //$NON-NLS-1$ @@ -1032,15 +619,15 @@ */ private IProject[] getRequiredProjects(boolean includebinaries) throws CoreException { IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); - if (fCurrentProject == null || workspaceRoot == null) { + if (this.currentproject == null || workspaceRoot == null) { return new IProject[0]; } ArrayList projects = new ArrayList(); try { - IJavaProject javaProject = JavaCore.create(fCurrentProject); + IJavaProject javaProject = JavaCore.create(this.currentproject); HashSet blocations = new HashSet(); blocations.add(javaProject.getOutputLocation()); - fProjectToOutputLocations.put(fCurrentProject, blocations); + projecttooutputlocations.put(this.currentproject, blocations); IClasspathEntry[] entries = javaProject.getResolvedClasspath(true); for (int i = 0, l = entries.length; i < l; i++) { IClasspathEntry entry = entries[i]; @@ -1088,7 +675,7 @@ } } } - fProjectToOutputLocations.put(p, bins); + this.projecttooutputlocations.put(p, bins); } } } @@ -1104,9 +691,19 @@ /** * @return the workspace {@link IApiProfile} */ - private IApiBaseline getWorkspaceProfile() throws CoreException { + IApiBaseline getWorkspaceProfile() throws CoreException { return ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline(); } + + /** + * Returns the output paths of the given project or null if none have been computed + * @param project + * @return the output paths for the given project or null + */ + HashSet getProjectOutputPaths(IProject project) { + return (HashSet) this.projecttooutputlocations.get(project); + } + /** * Returns is the given classpath entry is optional or not * @param entry @@ -1121,39 +718,12 @@ } return false; } - - /** - * Resolves the java path from the given resource - * @param resource - * @return the resolved path or null if the resource is not part of the java model - */ - private IPath resolveJavaPathFromResource(IResource resource) { - IJavaElement element = JavaCore.create(resource); - if(element != null) { - switch(element.getElementType()) { - case IJavaElement.CLASS_FILE: { - org.eclipse.jdt.core.IClassFile classfile = (org.eclipse.jdt.core.IClassFile) element; - IType type = classfile.getType(); - HashSet paths = (HashSet) fProjectToOutputLocations.get(resource.getProject()); - IPath prefix = null; - for(Iterator iter = paths.iterator(); iter.hasNext();) { - prefix = (IPath) iter.next(); - if(prefix.isPrefixOf(type.getPath())) { - return type.getPath().removeFirstSegments(prefix.segmentCount()).removeFileExtension(); - } - } - break; - } - } - } - return null; - } /* (non-Javadoc) * @see java.lang.Object#toString() */ public String toString() { - return "Builder for project: ["+fCurrentProject.getName()+"]"; //$NON-NLS-1$ //$NON-NLS-2$ + return NLS.bind(BuilderMessages.ApiAnalysisBuilder_builder_for_project, this.currentproject.getName()); } /** @@ -1170,7 +740,7 @@ /** * Reads the build state for the relevant project. */ - protected static BuildState readState(IProject project) throws CoreException { + static BuildState readState(IProject project) throws CoreException { File file = getSerializationFile(project); if (file != null && file.exists()) { try { @@ -1221,7 +791,7 @@ /** * Returns the File to use for saving and restoring the last built state for the given project. */ - private static File getSerializationFile(IProject project) { + static File getSerializationFile(IProject project) { if (!project.exists()) { return null; } @@ -1235,7 +805,7 @@ * @param state * @throws CoreException */ - private static void saveBuiltState(IProject project, BuildState state) throws CoreException { + static void saveBuiltState(IProject project, BuildState state) throws CoreException { if (DEBUG) { System.out.println("Saving build state for project: "+project.getName()); //$NON-NLS-1$ } @@ -1281,7 +851,7 @@ * Clears the last build state by setting it to null * @throws CoreException */ - private void clearLastState() throws CoreException { - setLastBuiltState(fCurrentProject, null); + void clearLastState() throws CoreException { + setLastBuiltState(this.currentproject, null); } } Index: src/org/eclipse/pde/api/tools/internal/builder/buildermessages.properties =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/builder/buildermessages.properties,v retrieving revision 1.44 diff -u -r1.44 buildermessages.properties --- src/org/eclipse/pde/api/tools/internal/builder/buildermessages.properties 7 Jan 2009 19:29:01 -0000 1.44 +++ src/org/eclipse/pde/api/tools/internal/builder/buildermessages.properties 6 Mar 2009 19:52:48 -0000 @@ -13,6 +13,7 @@ building_workspace_profile=Building workspace API profile checking_api_usage=Checking API use of ''{0}'' AbstractTypeLeakDetector_vis_type_has_no_api_description=Visible type {0} has no API description +ApiAnalysisBuilder_builder_for_project=Builder for project: [{0}] ApiAnalysisBuilder_finding_affected_source_files=Finding affected source in ''{0}'' ApiAnalysisBuilder_initializing_analyzer=Initializing analyzer for ''{0}'' ApiProblemFactory_problem_message_not_found=Message not found for id: {0} Index: src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java,v retrieving revision 1.6 diff -u -r1.6 ApiBaselineManager.java --- src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java 26 Feb 2009 16:23:30 -0000 1.6 +++ src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java 6 Mar 2009 19:52:46 -0000 @@ -56,7 +56,6 @@ import org.eclipse.jdt.core.IPackageFragment; import org.eclipse.jdt.core.IPackageFragmentRoot; import org.eclipse.jdt.core.JavaCore; -import org.eclipse.pde.api.tools.internal.builder.ApiAnalysisBuilder; import org.eclipse.pde.api.tools.internal.model.ApiBaseline; import org.eclipse.pde.api.tools.internal.model.ApiModelFactory; import org.eclipse.pde.api.tools.internal.model.StubApiComponent; @@ -880,7 +879,6 @@ IJavaProject jp = JavaCore.create(project); if (jp.exists()) { ApiDescriptionManager.getDefault().clean(jp, true, true); - ApiAnalysisBuilder.cleanupMarkers(resource); } } } catch (CoreException e) { Index: src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java,v retrieving revision 1.19 diff -u -r1.19 ApiDescriptionManager.java --- src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java 4 Mar 2009 00:24:40 -0000 1.19 +++ src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java 6 Mar 2009 19:52:46 -0000 @@ -139,8 +139,8 @@ * Cleans the API description for the given project. * * @param project - * @param whether to delete the file on disk - * @param whether to remove the cached API description + * @param delete whether to delete the file on disk + * @param remove whether to remove the cached API description */ public synchronized void clean(IJavaProject project, boolean delete, boolean remove) { ProjectApiDescription desc = null; Index: src/org/eclipse/pde/api/tools/internal/builder/IncrementalApiBuilder.java =================================================================== RCS file: src/org/eclipse/pde/api/tools/internal/builder/IncrementalApiBuilder.java diff -N src/org/eclipse/pde/api/tools/internal/builder/IncrementalApiBuilder.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ src/org/eclipse/pde/api/tools/internal/builder/IncrementalApiBuilder.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,507 @@ +/******************************************************************************* + * Copyright (c) 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.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Set; + +import org.eclipse.core.resources.IFile; +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.IResourceDeltaVisitor; +import org.eclipse.core.runtime.CoreException; +import org.eclipse.core.runtime.IPath; +import org.eclipse.core.runtime.IProgressMonitor; +import org.eclipse.core.runtime.SubMonitor; +import org.eclipse.jdt.core.ICompilationUnit; +import org.eclipse.jdt.core.IJavaElement; +import org.eclipse.jdt.core.IType; +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.ReferenceCollection; +import org.eclipse.jdt.internal.core.builder.State; +import org.eclipse.jdt.internal.core.builder.StringSet; +import org.eclipse.osgi.util.NLS; +import org.eclipse.pde.api.tools.internal.IApiCoreConstants; +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.model.IApiBaseline; +import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; +import org.eclipse.pde.api.tools.internal.util.Util; +import org.eclipse.pde.core.plugin.IPluginModelBase; + +/** + * Used to incrementally build changed Java types + * + * @since 3.5 + */ +public class IncrementalApiBuilder { + + /** + * Visits a resource delta to determine if the changes have been made that might required a rebuild: + * - modification to the manifest file + * - removal of the .api_filter file + */ + class ResourceDeltaVisitor implements IResourceDeltaVisitor { + IProject[] projects; + public ResourceDeltaVisitor(IProject[] projects) { + this.projects = projects; + } + private boolean fRequireFullBuild = false; + + /** + * Returns whether a full build should be run. + * + * @return whether a full build should be run + */ + boolean shouldRunFullBuild() { + return fRequireFullBuild; + } + + /* (non-Javadoc) + * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta) + */ + public boolean visit(IResourceDelta delta) throws CoreException { + switch (delta.getResource().getType()) { + case IResource.ROOT: + case IResource.PROJECT: + return !fRequireFullBuild; + case IResource.FOLDER: + return !fRequireFullBuild; + case IResource.FILE: + IResource resource = delta.getResource(); + String fileName = resource.getName(); + if (Util.isClassFile(fileName)) { + findAffectedSourceFiles(delta); + } else if (Util.isJavaFileName(fileName)) { + IProject project = resource.getProject(); + if (IncrementalApiBuilder.this.project.equals(project)) { + if (delta.getKind() == IResourceDelta.ADDED) { + IncrementalApiBuilder.this.addremovedeltas.add(delta); + } + IncrementalApiBuilder.this.changedtypes.add(resource); + } else if (this.projects != null) { + loop: for (int i = 0, max = this.projects.length; i < max; i++) { + if (this.projects[i].equals(project)) { + IncrementalApiBuilder.this.changedtypes.add(resource); + break loop; + } + } + } + } else if (!fRequireFullBuild && IApiCoreConstants.API_FILTERS_XML_NAME.equals(fileName)) { + switch(delta.getKind()) { + case IResourceDelta.REMOVED : + case IResourceDelta.REPLACED : + case IResourceDelta.CHANGED : + case IResourceDelta.ADDED : + fRequireFullBuild = true; + } + } + } + return false; + } + } + + State state = null; + ApiAnalysisBuilder builder = null; + IProject project = null; + HashSet changedtypes = new HashSet(16); + HashSet addremovedeltas = new HashSet(8); + StringSet typenames = new StringSet(16); + StringSet packages = new StringSet(16); + + + /** + * Constructor + * @param project the current project context being built + * @param delta the {@link IResourceDelta} from the build framework + * @param buildstate the current build state from the {@link org.eclipse.jdt.internal.core.builder.JavaBuilder} + */ + public IncrementalApiBuilder(ApiAnalysisBuilder builder) { + this.builder = builder; + this.state = null; + } + + /** + * Incrementally builds using the {@link org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer} + * from the given {@link ApiAnalysisBuilder} + * + * @param baseline + * @param deltas + * @param monitor + * @throws CoreException + */ + public void build(IApiBaseline baseline, IResourceDelta[] deltas, IProgressMonitor monitor) throws CoreException { + this.project = this.builder.getProject(); + SubMonitor localmonitor = SubMonitor.convert(monitor, NLS.bind("API analysis: Incrementally building... {0}", this.project), 10); + try { + this.state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(this.project, localmonitor.newChild(1)); + if(this.state == null) { + this.builder.buildAll(baseline, localmonitor); + } + this.builder.clearLastState(); + //start incremental preparing + build(this.state, baseline, localmonitor.newChild(1)); + } + finally { + if(!localmonitor.isCanceled()) { + localmonitor.done(); + } + this.changedtypes.clear(); + } + } + + /** + * Collects the complete set of affected source files from the current project context based on the current JDT build state. + * + * @param state + */ + private void collectAffectedSourceFiles(State state, Set typesToCheck) { + // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X' + char[][][] internedQualifiedNames = ReferenceCollection.internQualifiedNames(this.packages); + // if a well known qualified name was found then we can skip over these + if (internedQualifiedNames.length < this.packages.elementSize) { + internedQualifiedNames = null; + } + char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(this.typenames, true); + // if a well known name was found then we can skip over these + if (internedSimpleNames.length < this.typenames.elementSize) { + internedSimpleNames = null; + } + Object[] keyTable = state.getReferences().keyTable; + Object[] valueTable = state.getReferences().valueTable; + next : for (int i = 0, l = valueTable.length; i < l; i++) { + String typeLocator = (String) keyTable[i]; + if (typeLocator != null) { + ReferenceCollection refs = (ReferenceCollection) valueTable[i]; + if (refs.includes(internedQualifiedNames, internedSimpleNames, null)) { + IFile file = this.project.getFile(typeLocator); + if (file == null) { + continue next; + } + if (ApiAnalysisBuilder.DEBUG) { + System.out.println(" adding affected source file " + typeLocator); //$NON-NLS-1$ + } + typesToCheck.add(file); + } + } + } + } + + /** + * Finds affected source files for a resource that has changed that either contains class files or is itself a class file + * @param binaryDelta + */ + private void findAffectedSourceFiles(IResourceDelta binaryDelta) { + IResource resource = binaryDelta.getResource(); + if(resource.getType() == IResource.FILE) { + if (Util.isClassFile(resource.getName())) { + switch (binaryDelta.getKind()) { + case IResourceDelta.REMOVED : + //fAddedRemovedDeltas.add(binaryDelta); + //$FALL-THROUGH$ + case IResourceDelta.ADDED : { + IPath typePath = resolveJavaPathFromResource(resource); + if(typePath == null) { + return; + } + if (ApiAnalysisBuilder.DEBUG) { + System.out.println("Found added/removed class file " + typePath); //$NON-NLS-1$ + } + addDependentsOf(typePath); + return; + } + case IResourceDelta.CHANGED : { + if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0) { + return; // skip it since it really isn't changed + } + IPath typePath = resolveJavaPathFromResource(resource); + if(typePath == null) { + return; + } + if (ApiAnalysisBuilder.DEBUG) { + System.out.println("Found changed class file " + typePath); //$NON-NLS-1$ + } + addDependentsOf(typePath); + } + } + return; + } + } + } + + /** + * Adds a type to search for dependents of in considered projects for an incremental build + * + * @param path + */ + void addDependentsOf(IPath path) { + // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X' + path = path.setDevice(null); + String packageName = path.removeLastSegments(1).toString(); + String typeName = path.lastSegment(); + int memberIndex = typeName.indexOf('$'); + if (memberIndex > 0) { + typeName = typeName.substring(0, memberIndex); + } + if (this.typenames.add(typeName) && this.packages.add(packageName) && ApiAnalysisBuilder.DEBUG) { + System.out.println(" will look for dependents of " + typeName + " in " + packageName); //$NON-NLS-1$ //$NON-NLS-2$ + } + } + + /** + * Builds an API delta using the default profile (from the workspace settings and the current + * workspace profile + * @param state + * @param monitor + */ + private void build(final State state, IApiBaseline baseline, IProgressMonitor monitor) throws CoreException { + IApiBaseline wsprofile = null; + try { + int typesToCheckSize = this.changedtypes.size(); + SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 2 + (typesToCheckSize != 0 ? 3 : 0)); + localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, this.project.getName())); + this.builder.updateMonitor(localMonitor, 0); + collectAffectedSourceFiles(state, this.changedtypes); + this.builder.updateMonitor(localMonitor, 1); + if (typesToCheckSize != 0) { + IPluginModelBase currentModel = this.builder.getCurrentModel(); + if (currentModel != null) { + wsprofile = this.builder.getWorkspaceProfile(); + if (wsprofile == null) { + if (ApiAnalysisBuilder.DEBUG) { + System.err.println("Could not retrieve a workspace profile"); //$NON-NLS-1$ + } + return; + } + String id = currentModel.getBundleDescription().getSymbolicName(); + IApiComponent apiComponent = wsprofile.getApiComponent(id); + if(apiComponent == null) { + return; + } + List tnames = new ArrayList(typesToCheckSize), + cnames = new ArrayList(typesToCheckSize); + collectAllQualifiedNames(this.changedtypes, tnames, cnames, localMonitor.newChild(1)); + this.builder.updateMonitor(localMonitor, 1); + this.builder.getAnalyzer().analyzeComponent(null, + null, + null, + baseline, + apiComponent, + (String[])tnames.toArray(new String[tnames.size()]), + (String[])cnames.toArray(new String[cnames.size()]), + localMonitor.newChild(1)); + this.builder.updateMonitor(localMonitor, 1); + this.builder.createMarkers(); + this.builder.updateMonitor(localMonitor, 1); + } + } + } + finally { + if(wsprofile != null) { + wsprofile.close(); + } + if(monitor != null) { + monitor.done(); + } + } + } + + /** + * Returns an array of type names, and cleans up markers for the specified resource + * @param alltypes the listing of {@link IFile}s to get qualified names from + * @param changedtypes the listing of {@link IFile}s that have actually changed (from the {@link IResourceDelta} + * @param tnames the list to collect all type names into (including inner member names) + * @param cnames the list to collect the changed type names into + * @param monitor + */ + private void collectAllQualifiedNames(final HashSet alltypes, List tnames, List cnames, final IProgressMonitor monitor) { + IType[] types = null; + IFile file = null; + for (Iterator iterator = alltypes.iterator(); iterator.hasNext(); ) { + file = (IFile) iterator.next(); + ICompilationUnit unit = (ICompilationUnit) JavaCore.create(file); + if(!unit.exists()) { + continue; + } + IType type = unit.findPrimaryType(); + if(type == null) { + continue; + } + this.builder.updateMonitor(monitor, 0); + this.builder.cleanupUnsupportedTagMarkers(file); + this.builder.updateMonitor(monitor, 0); + this.builder.cleanupCompatibilityMarkers(file); + this.builder.updateMonitor(monitor, 0); + cnames.add(type.getFullyQualifiedName()); + try { + this.builder.cleanupUsageMarkers(file); + this.builder.updateMonitor(monitor, 0); + types = unit.getAllTypes(); + String tname = null; + for (int i = 0; i < types.length; i++) { + IType type2 = types[i]; + if (type2.isMember()) { + tname = type2.getFullyQualifiedName('$'); + } else { + tname = type2.getFullyQualifiedName(); + } + tnames.add(tname); + } + } catch (JavaModelException e) { + ApiPlugin.log(e.getStatus()); + } + this.builder.updateMonitor(monitor, 0); + } + // inject removed types inside changed type names so that we can properly detect type removal + for (Iterator iterator = this.addremovedeltas.iterator(); iterator.hasNext(); ) { + IResourceDelta delta = (IResourceDelta) iterator.next(); + if (delta.getKind() != IResourceDelta.REMOVED) continue; + IResource resource = delta.getResource(); + IPath typePath = resolveJavaPathFromResource(resource); + if(typePath == null) { + continue; + } + // record removed type names (package + type) + StringBuffer buffer = new StringBuffer(); + String[] segments = typePath.segments(); + for (int i = 0, max = segments.length; i < max; i++) { + if (i > 0) { + buffer.append('.'); + } + buffer.append(segments[i]); + } + cnames.add(String.valueOf(buffer)); + } + // clean up markers on added deltas + if (!this.addremovedeltas.isEmpty()) { + IResource manifestFile = Util.getManifestFile(this.project); + if (manifestFile != null) { + try { + IMarker[] markers = manifestFile.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); + for (int i = 0, max = markers.length; i < max; i++) { + IMarker marker = markers[i]; + String typeName = marker.getAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, null); + if (typeName != null) { + for (Iterator iterator = this.addremovedeltas.iterator(); iterator.hasNext(); ) { + IResourceDelta delta = (IResourceDelta) iterator.next(); + if (delta.getKind() != IResourceDelta.ADDED) { + continue; + } + ICompilationUnit unit = (ICompilationUnit) JavaCore.create(delta.getResource()); + if(!unit.exists()) { + continue; + } + IType type = unit.findPrimaryType(); + if(type == null) { + continue; + } + if (typeName.equals(type.getFullyQualifiedName())) { + marker.delete(); + return; + } else { + // check secondary types + try { + types = unit.getAllTypes(); + for (int j = 0; j < types.length; j++) { + IType type2 = types[i]; + String fullyQualifiedName = null; + if (type2.isMember()) { + fullyQualifiedName = type2.getFullyQualifiedName('$'); + } else { + fullyQualifiedName = type2.getFullyQualifiedName(); + } + if (typeName.equals(fullyQualifiedName)) { + marker.delete(); + return; + } + } + } catch (JavaModelException e) { + ApiPlugin.log(e.getStatus()); + } + } + } + } + } + } catch (CoreException e) { + ApiPlugin.log(e.getStatus()); + } + } + } + IResource resource = this.project.findMember(ApiAnalysisBuilder.MANIFEST_PATH); + if (resource != null) { + try { + IMarker[] markers = resource.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); + loop: for (int i = 0, max = markers.length; i < max; i++) { + IMarker marker = markers[i]; + String typeNameFromMarker = Util.getTypeNameFromMarker(marker); + for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) { + String typeName = (String) iterator.next(); + if (typeName.equals(typeNameFromMarker)) { + marker.delete(); + continue loop; + } + } + } + markers = resource.findMarkers(IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); + loop: for (int i = 0, max = markers.length; i < max; i++) { + IMarker marker = markers[i]; + String typeNameFromMarker = Util.getTypeNameFromMarker(marker); + for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) { + String typeName = (String) iterator.next(); + if (typeName.equals(typeNameFromMarker)) { + marker.delete(); + continue loop; + } + } + } + } catch (CoreException e) { + ApiPlugin.log(e); + } + } + } + + /** + * Resolves the java path from the given resource + * @param resource + * @return the resolved path or null if the resource is not part of the java model + */ + IPath resolveJavaPathFromResource(IResource resource) { + IJavaElement element = JavaCore.create(resource); + if(element != null) { + switch(element.getElementType()) { + case IJavaElement.CLASS_FILE: { + org.eclipse.jdt.core.IClassFile classfile = (org.eclipse.jdt.core.IClassFile) element; + IType type = classfile.getType(); + HashSet paths = this.builder.getProjectOutputPaths(resource.getProject()); + if(paths == null) { + return null; + } + IPath prefix = null; + for(Iterator iter = paths.iterator(); iter.hasNext();) { + prefix = (IPath) iter.next(); + if(prefix.isPrefixOf(type.getPath())) { + return type.getPath().removeFirstSegments(prefix.segmentCount()).removeFileExtension(); + } + } + break; + } + } + } + return null; + } +}