### Eclipse Workspace Patch 1.0 #P org.eclipse.pde.api.tools.tests Index: src/org/eclipse/pde/api/tools/builder/tests/usage/DependentUsageTests.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools.tests/src/org/eclipse/pde/api/tools/builder/tests/usage/DependentUsageTests.java,v retrieving revision 1.1 diff -u -r1.1 DependentUsageTests.java --- src/org/eclipse/pde/api/tools/builder/tests/usage/DependentUsageTests.java 24 Mar 2009 21:14:12 -0000 1.1 +++ src/org/eclipse/pde/api/tools/builder/tests/usage/DependentUsageTests.java 31 Mar 2009 03:38:31 -0000 @@ -363,4 +363,68 @@ setExpectedMessageArgs(new String[][] {{"interref", "test8"}}); deployTest("test8", XYZ_PATH, I_PATH, "interref.java", addtag); } + + /** + * Tests adding @noextend AND @noinstantiate tags to a class known to be used + * by another bundle + * + * Uses test9.java and classref.java + */ + public void testAddExtendInstantiateRestriction() throws Exception { + test9(true); + } + + /** + * Tests removing @noextend AND @noinstantiate tags to a class known to be used + * by another bundle + * + * Uses test9.java and classref.java + */ + public void testRemoveExtendInstantiateRestriction() throws Exception { + test9(false); + } + + private void test9(boolean addtag) throws Exception { + setExpectedProblemIds(new int[] { + ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_EXTEND, IApiProblem.NO_FLAGS), + ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_INSTANTIATE, IApiProblem.NO_FLAGS) + }); + setExpectedMessageArgs(new String[][] { + {"classref", "test9"}, + {"classref", "test9"} + }); + deployTest("test9", XYZ_PATH, C_PATH, "classref.java", addtag); + } + + /** + * Tests adding @noextend AND @noimplement tags to an interface known to be used + * by another bundle + * + * Uses test10.java and interref.java + */ + public void testAddExtendImplementRestriction() throws Exception { + test10(true); + } + + /** + * Tests removing @noextend AND @noimplement tags to an interface known to be used + * by another bundle + * + * Uses test10.java and interref.java + */ + public void testRemoveExtendImplementRestriction() throws Exception { + test10(false); + } + + private void test10(boolean addtag) throws Exception { + setExpectedProblemIds(new int[] { + ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_EXTEND, IApiProblem.NO_FLAGS), + ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_IMPLEMENT, IApiProblem.NO_FLAGS) + }); + setExpectedMessageArgs(new String[][] { + {"interref", "test10"}, + {"interref", "clazz"} + }); + deployTest("test10", XYZ_PATH, I_PATH, "interref.java", addtag); + } } Index: test-builder/usage/dependent/test10/test10.java =================================================================== RCS file: test-builder/usage/dependent/test10/test10.java diff -N test-builder/usage/dependent/test10/test10.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test-builder/usage/dependent/test10/test10.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,23 @@ +/******************************************************************************* + * 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 x.y.z; + +import i.interref; + +/** + * + */ +public interface test10 extends interref { + +} +class clazz implements interref { + +} Index: test-builder/usage/dependent/test10/withouttag/interref.java =================================================================== RCS file: test-builder/usage/dependent/test10/withouttag/interref.java diff -N test-builder/usage/dependent/test10/withouttag/interref.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test-builder/usage/dependent/test10/withouttag/interref.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,18 @@ +/******************************************************************************* + * 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 i; + +/** + * + */ +public interface interref { + +} Index: test-builder/usage/dependent/test9/withtag/classref.java =================================================================== RCS file: test-builder/usage/dependent/test9/withtag/classref.java diff -N test-builder/usage/dependent/test9/withtag/classref.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test-builder/usage/dependent/test9/withtag/classref.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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 c; + +/** + * @noextend + * @noinstantiate + */ +public class classref { + +} Index: test-builder/usage/dependent/test9/test9.java =================================================================== RCS file: test-builder/usage/dependent/test9/test9.java diff -N test-builder/usage/dependent/test9/test9.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test-builder/usage/dependent/test9/test9.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,21 @@ +/******************************************************************************* + * 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 x.y.z; + +import c.classref; + +/** + * + */ +public class test9 extends classref { + + classref ref = new classref(); +} Index: test-builder/usage/dependent/test10/withtag/interref.java =================================================================== RCS file: test-builder/usage/dependent/test10/withtag/interref.java diff -N test-builder/usage/dependent/test10/withtag/interref.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test-builder/usage/dependent/test10/withtag/interref.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,19 @@ +/******************************************************************************* + * 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 i; + +/** + * @noextend + * @noimplement + */ +public interface interref { + +} Index: test-builder/usage/dependent/test9/withouttag/classref.java =================================================================== RCS file: test-builder/usage/dependent/test9/withouttag/classref.java diff -N test-builder/usage/dependent/test9/withouttag/classref.java --- /dev/null 1 Jan 1970 00:00:00 -0000 +++ test-builder/usage/dependent/test9/withouttag/classref.java 1 Jan 1970 00:00:00 -0000 @@ -0,0 +1,18 @@ +/******************************************************************************* + * 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 c; + +/** + * + */ +public class classref { + +} #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.91 diff -u -r1.91 BaseApiAnalyzer.java --- src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java 16 Mar 2009 14:19:51 -0000 1.91 +++ src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java 31 Mar 2009 03:38:36 -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 31 Mar 2009 03:38:36 -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; @@ -32,7 +33,6 @@ public static String build_wrongFileFormat; public static String build_saveStateComplete; public static String build_cannotSaveState; - public static String IllegalExtendsProblemDetector_an_anonymous_declaration; public static String undefinedRange; public static String reportUnsatisfiedConstraint; 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.97 diff -u -r1.97 ApiAnalysisBuilder.java --- src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java 24 Mar 2009 18:50:44 -0000 1.97 +++ src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java 31 Mar 2009 03:38:35 -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,16 @@ } } - /** - * 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(); + 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); @@ -325,64 +209,43 @@ } case AUTO_BUILD : case INCREMENTAL_BUILD : { - IResourceDelta[] deltas = getDeltas(projects); - boolean shouldRunFullBuild = false; - fBuildState = getLastBuiltState(fCurrentProject); - if (fBuildState == null) { + this.buildstate = getLastBuiltState(currentproject); + if (this.buildstate == null) { buildAll(baseline, localMonitor.newChild(1)); - } else if (worthDoingFullBuild(projects)) { + break; + } + else if(worthDoingFullBuild(projects)) { 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); - } - } - ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(reexportedProjects); + } + IResourceDelta[] deltas = getDeltas(projects); + if(deltas.length < 1) { + buildAll(baseline, localMonitor.newChild(1)); + } + else { + IResourceDelta manifest = null; + IResourceDelta filters = null; for (int i = 0; i < deltas.length; i++) { - deltas[i].accept(visitor); - if (visitor.shouldRunFullBuild()) { - shouldRunFullBuild = true; + manifest = deltas[i].findMember(MANIFEST_PATH); + if(manifest != null) { break; } - } - if (shouldRunFullBuild) { - if (DEBUG) { - System.out.println("Performing full build since MANIFEST.MF was modified"); //$NON-NLS-1$ + filters = deltas[i].findMember(FILTER_PATH); + if(filters != null){ + break; } - buildAll(baseline, localMonitor.newChild(1)); - } else if (deltas.length == 0) { + } + if (manifest != null || filters != null) { if (DEBUG) { - System.out.println("Performing full build since deltas are missing after incremental request"); //$NON-NLS-1$ - } + System.out.println("Performing full build since MANIFEST.MF or .api_filters was modified"); //$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)); - } + } + else { + IncrementalApiBuilder builder = new IncrementalApiBuilder(this); + builder.build(baseline, deltas, this.buildstate, localMonitor.newChild(1)); } } - break; - } + } } updateMonitor(monitor, 0); } catch(CoreException e) { @@ -392,38 +255,36 @@ } ApiPlugin.log(e); } finally { - fTypes.clear(); - fPackages.clear(); - fTypesToCheck.clear(); - fAddedRemovedDeltas.clear(); - fProjectToOutputLocations.clear(); updateMonitor(monitor, 0); - fAnalyzer.dispose(); + if(this.analyzer != null) { + this.analyzer.dispose(); + this.analyzer = null; + } if(baseline != null) { baseline.close(); } if(monitor != null) { monitor.done(); } - if (fBuildState != null) { + if (this.buildstate != null) { for(int i = 0, max = projects.length; i < max; i++) { IProject project = projects[i]; if (Util.isApiProject(project)) { - fBuildState.addApiToolingDependentProject(project.getName()); + this.buildstate.addApiToolingDependentProject(project.getName()); } } - saveBuiltState(fCurrentProject, fBuildState); - fBuildState = 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 " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ } return projects; } private boolean worthDoingFullBuild(IProject[] projects) { - Set apiToolingDependentProjects = fBuildState.getApiToolingDependentProjects(); + Set apiToolingDependentProjects = this.buildstate.getApiToolingDependentProjects(); for (int i = 0, max = projects.length; i < max; i++) { IProject currentProject = projects[i]; if (Util.isApiProject(currentProject)) { @@ -437,18 +298,19 @@ } return false; } + /** * 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); @@ -464,7 +326,7 @@ // Compatibility checks IApiComponent apiComponent = wsprofile.getApiComponent(id); if(apiComponent != null) { - fAnalyzer.analyzeComponent(fBuildState, null, null, baseline, apiComponent, null, null, localMonitor.newChild(1)); + getAnalyzer().analyzeComponent(this.buildstate, null, null, baseline, apiComponent, null, null, localMonitor.newChild(1)); updateMonitor(localMonitor, 1); createMarkers(); updateMonitor(localMonitor, 1); @@ -488,13 +350,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 = getAnalyzer().getProblems(); String type = null; for(int i = 0; i < problems.length; i++) { int category = problems[i].getCategory(); @@ -578,7 +440,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()), @@ -620,7 +482,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; } @@ -654,7 +516,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()) { @@ -663,232 +525,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; - SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 6); - try { - clearLastState(); // so if the build fails, a full build will be triggered - localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, fCurrentProject.getName())); - updateMonitor(localMonitor, 0); - collectAffectedSourceFiles(state, fTypesToCheck); - updateMonitor(localMonitor, 1); - final int typesToCheckSize = fTypesToCheck.size(); - 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(!localMonitor.isCanceled()) { - localMonitor.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[] {this.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 { @@ -906,93 +557,13 @@ ApiDescriptionManager.getDefault().clean(JavaCore.create(project), true, true); } } - /** - * 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++) { @@ -1018,10 +589,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$ @@ -1041,11 +612,14 @@ } /** - * Returns the API analyzer to use with this instance of the builder + * Returns the API analyzer to use with this instance of the builder * @return the API analyzer to use */ - protected IApiAnalyzer getAnalyzer() { - return new BaseApiAnalyzer(); + protected synchronized IApiAnalyzer getAnalyzer() { + if(this.analyzer == null) { + this.analyzer = new BaseApiAnalyzer(); + } + return this.analyzer; } /** @@ -1056,15 +630,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]; @@ -1112,7 +686,7 @@ } } } - fProjectToOutputLocations.put(p, bins); + this.projecttooutputlocations.put(p, bins); } } } @@ -1128,9 +702,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 @@ -1145,39 +729,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()); } /** @@ -1194,7 +751,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 { @@ -1245,7 +802,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; } @@ -1259,7 +816,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$ } @@ -1305,7 +862,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 31 Mar 2009 03:38:36 -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} @@ -25,7 +26,6 @@ build_saveStateComplete = Saved in {0} ms build_wrongFileFormat = Wrong file format build_cannotSaveState = Error saving last build state for project {0} -IllegalExtendsProblemDetector_an_anonymous_declaration=An anonymous declaration ReferenceAnalyzer_analyzing_api_checking_use=Analyzing API: Checking API use ReferenceAnalyzer_analyzing_api=Analyzing API ReferenceAnalyzer_api_analysis_error=API analysis error 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.7 diff -u -r1.7 ApiBaselineManager.java --- src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java 26 Mar 2009 18:43:54 -0000 1.7 +++ src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java 31 Mar 2009 03:38:35 -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; @@ -888,7 +887,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.22 diff -u -r1.22 ApiDescriptionManager.java --- src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java 24 Mar 2009 18:50:44 -0000 1.22 +++ src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java 31 Mar 2009 03:38:35 -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; @@ -261,7 +261,8 @@ switch (delta.getKind()) { case IJavaElementDelta.CHANGED: { if ((flags & IJavaElementDelta.F_CONTENT) != 0 - || (flags & IJavaElementDelta.F_FINE_GRAINED) != 0) { + || (flags & IJavaElementDelta.F_FINE_GRAINED) != 0 + || (flags & IJavaElementDelta.F_PRIMARY_RESOURCE) != 0){ if (proj != null) { projectChanged(proj); return true; @@ -502,6 +503,5 @@ private static void abort(String message, Throwable exception) throws CoreException { IStatus status = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, exception); throw new CoreException(status); - } - + } } Index: src/org/eclipse/pde/api/tools/internal/CompilationUnit.java =================================================================== RCS file: /cvsroot/eclipse/pde/apitools/org.eclipse.pde.api.tools/src/org/eclipse/pde/api/tools/internal/CompilationUnit.java,v retrieving revision 1.3 diff -u -r1.3 CompilationUnit.java --- src/org/eclipse/pde/api/tools/internal/CompilationUnit.java 21 Feb 2008 20:32:15 -0000 1.3 +++ src/org/eclipse/pde/api/tools/internal/CompilationUnit.java 31 Mar 2009 03:38:35 -0000 @@ -75,4 +75,11 @@ } return new FileInputStream(new File(filepath)); } + + /* (non-Javadoc) + * @see java.lang.Object#toString() + */ + public String toString() { + return getName(); + } } 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,523 @@ +/******************************************************************************* + * 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.resources.IWorkspaceRoot; +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.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.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 collect changes that need to be built + */ + class ResourceDeltaVisitor implements IResourceDeltaVisitor { + HashSet projects = null; + IProject project = null; + + /** + * Constructor + * @param project + * @param projects + */ + public ResourceDeltaVisitor(IProject project, HashSet projects) { + this.project = project; + this.projects = projects; + } + /* (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: + case IResource.FOLDER: { + return true; + } + 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 (this.project.equals(project)) { + if (delta.getKind() == IResourceDelta.ADDED) { + IncrementalApiBuilder.this.addremovedeltas.add(delta); + } + IncrementalApiBuilder.this.changedtypes.add(resource); + } + else if (this.projects != null && this.projects.contains(project)) { + IncrementalApiBuilder.this.changedtypes.add(resource); + } + } + } + } + return false; + } + } + + 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; + } + + /** + * 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, BuildState buildstate, IProgressMonitor monitor) throws CoreException { + IProject project = this.builder.getProject(); + SubMonitor localmonitor = SubMonitor.convert(monitor, NLS.bind("API analysis: Incrementally building... {0}", project), 10); + try { + State state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(project, localmonitor.newChild(1)); + if(state == null) { + this.builder.buildAll(baseline, localmonitor); + return; + } + String[] projectNames = buildstate.getReexportedComponents(); + HashSet depprojects = null; + if (projectNames.length != 0) { + depprojects = new HashSet(); + IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot(); + IProject pj = null; + for (int i = 0, max = projectNames.length; i < max; i++) { + pj = root.getProject(projectNames[i]); + if (pj.isAccessible()) { + // select only projects that don't exist in the reference baseline + if (baseline != null && baseline.getApiComponent(projectNames[i]) == null) { + depprojects.add(pj); + } + } + } + } + ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(project, depprojects); + for (int i = 0; i < deltas.length; i++) { + deltas[i].accept(visitor); + } + build(project, state, baseline, buildstate, localmonitor.newChild(1)); + } + finally { + if(!localmonitor.isCanceled()) { + localmonitor.done(); + } + this.changedtypes.clear(); + this.addremovedeltas.clear(); + this.typenames.clear(); + this.packages.clear(); + } + } + + /** + * Builds an API delta using the default profile (from the workspace settings and the current + * @param project + * workspace profile + * @param state + * @param monitor + */ + private void build(final IProject project, final State state, IApiBaseline baseline, BuildState buildstate, IProgressMonitor monitor) throws CoreException { + IApiBaseline wsprofile = null; + try { + // clear the old state so a full build will occur if this one is cancelled or terminates prematurely + this.builder.clearLastState(); + SubMonitor localmonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 1); + collectAffectedSourceFiles(project, state, this.changedtypes); + int typesize = this.changedtypes.size(); + this.builder.updateMonitor(localmonitor, 1); + localmonitor.setWorkRemaining(1+(typesize != 0 ? 5 : 0)); + localmonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, project.getName())); + this.builder.updateMonitor(localmonitor, 0); + if (typesize != 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 comp = wsprofile.getApiComponent(id); + if(comp == null) { + return; + } + List tnames = new ArrayList(typesize), + cnames = new ArrayList(typesize); + collectAllQualifiedNames(project, this.changedtypes, tnames, cnames, localmonitor.newChild(1)); + this.builder.updateMonitor(localmonitor, 1); + this.builder.getAnalyzer().analyzeComponent(buildstate, + null, + null, + baseline, + comp, + (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(); + } + } + } + + /** + * Collects the complete set of affected source files from the current project context based on the current JDT build state. + * + * @param project + * @param state + * @param typesToCheck + */ + private void collectAffectedSourceFiles(final IProject project, 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; + IFile file = null; + String typeLocator = null; + next : for (int i = 0, l = valueTable.length; i < l; i++) { + typeLocator = (String) keyTable[i]; + if (typeLocator != null) { + ReferenceCollection refs = (ReferenceCollection) valueTable[i]; + if (refs.includes(internedQualifiedNames, internedSimpleNames, null)) { + file = 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 : + this.addremovedeltas.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$ + } + } + + /** + * 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 IProject project, 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 + IResourceDelta delta = null; + for (Iterator iterator = this.addremovedeltas.iterator(); iterator.hasNext(); ) { + 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(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(); ) { + 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 = 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; + } +}