View | Details | Raw Unified | Return to bug 233643 | Differences between
and this patch

Collapse All | Expand All

(-)src/org/eclipse/pde/api/tools/builder/tests/usage/DependentUsageTests.java (+64 lines)
Lines 363-366 Link Here
363
		setExpectedMessageArgs(new String[][] {{"interref", "test8"}});
363
		setExpectedMessageArgs(new String[][] {{"interref", "test8"}});
364
		deployTest("test8", XYZ_PATH, I_PATH, "interref.java", addtag);
364
		deployTest("test8", XYZ_PATH, I_PATH, "interref.java", addtag);
365
	}
365
	}
366
	
367
	/**
368
	 * Tests adding @noextend AND @noinstantiate tags to a class known to be used 
369
	 * by another bundle
370
	 * 
371
	 *  Uses test9.java and classref.java
372
	 */
373
	public void testAddExtendInstantiateRestriction() throws Exception {
374
		test9(true);
375
	}
376
	
377
	/**
378
	 * Tests removing @noextend AND @noinstantiate tags to a class known to be used 
379
	 * by another bundle
380
	 * 
381
	 * Uses test9.java and classref.java
382
	 */
383
	public void testRemoveExtendInstantiateRestriction() throws Exception {
384
		test9(false);
385
	}
386
	
387
	private void test9(boolean addtag) throws Exception {
388
		setExpectedProblemIds(new int[] {
389
				ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_EXTEND, IApiProblem.NO_FLAGS),
390
				ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_INSTANTIATE, IApiProblem.NO_FLAGS)
391
		});
392
		setExpectedMessageArgs(new String[][] {
393
					{"classref", "test9"},
394
					{"classref", "test9"}
395
				});
396
		deployTest("test9", XYZ_PATH, C_PATH, "classref.java", addtag);
397
	}
398
	
399
	/**
400
	 * Tests adding @noextend AND @noimplement tags to an interface known to be used 
401
	 * by another bundle
402
	 * 
403
	 *  Uses test10.java and interref.java
404
	 */
405
	public void testAddExtendImplementRestriction() throws Exception {
406
		test10(true);
407
	}
408
	
409
	/**
410
	 * Tests removing @noextend AND @noimplement tags to an interface known to be used 
411
	 * by another bundle
412
	 * 
413
	 * Uses test10.java and interref.java
414
	 */
415
	public void testRemoveExtendImplementRestriction() throws Exception {
416
		test10(false);
417
	}
418
	
419
	private void test10(boolean addtag) throws Exception {
420
		setExpectedProblemIds(new int[] {
421
				ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_EXTEND, IApiProblem.NO_FLAGS),
422
				ApiProblemFactory.createProblemId(IApiProblem.CATEGORY_USAGE, IElementDescriptor.TYPE, IApiProblem.ILLEGAL_IMPLEMENT, IApiProblem.NO_FLAGS)
423
		});
424
		setExpectedMessageArgs(new String[][] {
425
					{"interref", "test10"},
426
					{"interref", "clazz"}
427
				});
428
		deployTest("test10", XYZ_PATH, I_PATH, "interref.java", addtag);
429
	}
366
}
430
}
(-)test-builder/usage/dependent/test10/test10.java (+23 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package x.y.z;
12
13
import i.interref;
14
15
/**
16
 * 
17
 */
18
public interface test10 extends interref {
19
20
}
21
class clazz implements interref {
22
	
23
}
(-)test-builder/usage/dependent/test10/withouttag/interref.java (+18 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package i;
12
13
/**
14
 * 
15
 */
16
public interface interref {
17
18
}
(-)test-builder/usage/dependent/test9/withtag/classref.java (+19 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package c;
12
13
/**
14
 * @noextend
15
 * @noinstantiate
16
 */
17
public class classref {
18
19
}
(-)test-builder/usage/dependent/test9/test9.java (+21 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package x.y.z;
12
13
import c.classref;
14
15
/**
16
 * 
17
 */
18
public class test9 extends classref {
19
20
	classref ref = new classref();
21
}
(-)test-builder/usage/dependent/test10/withtag/interref.java (+19 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package i;
12
13
/**
14
 * @noextend
15
 * @noimplement
16
 */
17
public interface interref {
18
19
}
(-)test-builder/usage/dependent/test9/withouttag/classref.java (+18 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package c;
12
13
/**
14
 * 
15
 */
16
public class classref {
17
18
}
(-)src/org/eclipse/pde/api/tools/internal/builder/BaseApiAnalyzer.java (-4 / +4 lines)
Lines 301-307 Link Here
301
					}
301
					}
302
				}
302
				}
303
				if (changedTypes != null) {
303
				if (changedTypes != null) {
304
					// check the ones not already checked as part of typenames check
304
					// check the ones not already checked as part of type names check
305
					for (int i = 0; i < changedTypes.length; i++) {
305
					for (int i = 0; i < changedTypes.length; i++) {
306
						String typeName = changedTypes[i];
306
						String typeName = changedTypes[i];
307
						if (typeNamesSet == null || !typeNamesSet.remove(typeName)) {
307
						if (typeNamesSet == null || !typeNamesSet.remove(typeName)) {
Lines 1550-1556 Link Here
1550
							Util.EMPTY_STRING);
1550
							Util.EMPTY_STRING);
1551
				}
1551
				}
1552
			}
1552
			}
1553
			// analyse version of required components
1553
			// analyze version of required components
1554
			ReexportedBundleVersionInfo info = null;
1554
			ReexportedBundleVersionInfo info = null;
1555
			if (problem != null) {
1555
			if (problem != null) {
1556
				switch (problem.getKind()) {
1556
				switch (problem.getKind()) {
Lines 1580-1586 Link Here
1580
						}
1580
						}
1581
						break;
1581
						break;
1582
					case IApiProblem.MINOR_VERSION_CHANGE :
1582
					case IApiProblem.MINOR_VERSION_CHANGE :
1583
						// check if there is a version change required due to reexported bundles
1583
						// check if there is a version change required due to re-exported bundles
1584
						info = checkBundleVersionsOfReexportedBundles(reference, component);
1584
						info = checkBundleVersionsOfReexportedBundles(reference, component);
1585
						if (info != null) {
1585
						if (info != null) {
1586
							switch(info.kind) {
1586
							switch(info.kind) {
Lines 1602-1608 Link Here
1602
						}
1602
						}
1603
						break;
1603
						break;
1604
					case IApiProblem.MINOR_VERSION_CHANGE_NO_NEW_API :
1604
					case IApiProblem.MINOR_VERSION_CHANGE_NO_NEW_API :
1605
						// check if there is a version change required due to reexported bundles
1605
						// check if there is a version change required due to re-exported bundles
1606
						info = checkBundleVersionsOfReexportedBundles(reference, component);
1606
						info = checkBundleVersionsOfReexportedBundles(reference, component);
1607
						if (info != null) {
1607
						if (info != null) {
1608
							switch(info.kind) {
1608
							switch(info.kind) {
(-)src/org/eclipse/pde/api/tools/internal/builder/BuilderMessages.java (-1 / +1 lines)
Lines 20-25 Link Here
20
	public static String checking_api_usage;
20
	public static String checking_api_usage;
21
21
22
	public static String AbstractTypeLeakDetector_vis_type_has_no_api_description;
22
	public static String AbstractTypeLeakDetector_vis_type_has_no_api_description;
23
	public static String ApiAnalysisBuilder_builder_for_project;
23
	public static String ApiAnalysisBuilder_finding_affected_source_files;
24
	public static String ApiAnalysisBuilder_finding_affected_source_files;
24
	public static String ApiAnalysisBuilder_initializing_analyzer;
25
	public static String ApiAnalysisBuilder_initializing_analyzer;
25
	public static String ApiProblemFactory_problem_message_not_found;
26
	public static String ApiProblemFactory_problem_message_not_found;
Lines 32-38 Link Here
32
	public static String build_wrongFileFormat;
33
	public static String build_wrongFileFormat;
33
	public static String build_saveStateComplete;
34
	public static String build_saveStateComplete;
34
	public static String build_cannotSaveState;
35
	public static String build_cannotSaveState;
35
	public static String IllegalExtendsProblemDetector_an_anonymous_declaration;
36
	public static String undefinedRange;
36
	public static String undefinedRange;
37
	public static String reportUnsatisfiedConstraint;
37
	public static String reportUnsatisfiedConstraint;
38
38
(-)src/org/eclipse/pde/api/tools/internal/builder/ApiAnalysisBuilder.java (-554 / +111 lines)
Lines 22-39 Link Here
22
import java.util.Date;
22
import java.util.Date;
23
import java.util.HashMap;
23
import java.util.HashMap;
24
import java.util.HashSet;
24
import java.util.HashSet;
25
import java.util.Iterator;
26
import java.util.List;
27
import java.util.Map;
25
import java.util.Map;
28
import java.util.Set;
26
import java.util.Set;
29
import java.util.jar.JarFile;
27
import java.util.jar.JarFile;
30
28
31
import org.eclipse.core.resources.IFile;
32
import org.eclipse.core.resources.IMarker;
29
import org.eclipse.core.resources.IMarker;
33
import org.eclipse.core.resources.IProject;
30
import org.eclipse.core.resources.IProject;
34
import org.eclipse.core.resources.IResource;
31
import org.eclipse.core.resources.IResource;
35
import org.eclipse.core.resources.IResourceDelta;
32
import org.eclipse.core.resources.IResourceDelta;
36
import org.eclipse.core.resources.IResourceDeltaVisitor;
37
import org.eclipse.core.resources.IWorkspaceRoot;
33
import org.eclipse.core.resources.IWorkspaceRoot;
38
import org.eclipse.core.resources.IncrementalProjectBuilder;
34
import org.eclipse.core.resources.IncrementalProjectBuilder;
39
import org.eclipse.core.resources.ResourcesPlugin;
35
import org.eclipse.core.resources.ResourcesPlugin;
Lines 41-47 Link Here
41
import org.eclipse.core.runtime.IPath;
37
import org.eclipse.core.runtime.IPath;
42
import org.eclipse.core.runtime.IProgressMonitor;
38
import org.eclipse.core.runtime.IProgressMonitor;
43
import org.eclipse.core.runtime.IStatus;
39
import org.eclipse.core.runtime.IStatus;
44
import org.eclipse.core.runtime.NullProgressMonitor;
45
import org.eclipse.core.runtime.OperationCanceledException;
40
import org.eclipse.core.runtime.OperationCanceledException;
46
import org.eclipse.core.runtime.Path;
41
import org.eclipse.core.runtime.Path;
47
import org.eclipse.core.runtime.Platform;
42
import org.eclipse.core.runtime.Platform;
Lines 49-64 Link Here
49
import org.eclipse.core.runtime.SubMonitor;
44
import org.eclipse.core.runtime.SubMonitor;
50
import org.eclipse.jdt.core.IClasspathAttribute;
45
import org.eclipse.jdt.core.IClasspathAttribute;
51
import org.eclipse.jdt.core.IClasspathEntry;
46
import org.eclipse.jdt.core.IClasspathEntry;
52
import org.eclipse.jdt.core.ICompilationUnit;
53
import org.eclipse.jdt.core.IJavaElement;
54
import org.eclipse.jdt.core.IJavaProject;
47
import org.eclipse.jdt.core.IJavaProject;
55
import org.eclipse.jdt.core.IType;
56
import org.eclipse.jdt.core.JavaCore;
48
import org.eclipse.jdt.core.JavaCore;
57
import org.eclipse.jdt.core.JavaModelException;
49
import org.eclipse.jdt.core.JavaModelException;
58
import org.eclipse.jdt.internal.core.JavaModelManager;
59
import org.eclipse.jdt.internal.core.builder.ReferenceCollection;
60
import org.eclipse.jdt.internal.core.builder.State;
61
import org.eclipse.jdt.internal.core.builder.StringSet;
62
import org.eclipse.osgi.service.resolver.BundleDescription;
50
import org.eclipse.osgi.service.resolver.BundleDescription;
63
import org.eclipse.osgi.util.NLS;
51
import org.eclipse.osgi.util.NLS;
64
import org.eclipse.pde.api.tools.internal.ApiDescriptionManager;
52
import org.eclipse.pde.api.tools.internal.ApiDescriptionManager;
Lines 82-174 Link Here
82
 */
70
 */
83
public class ApiAnalysisBuilder extends IncrementalProjectBuilder {
71
public class ApiAnalysisBuilder extends IncrementalProjectBuilder {
84
	/**
72
	/**
85
	 * Visits a resource delta to determine if the changes have been made that might required a rebuild:
86
	 * - modification to the manifest file
87
	 * - removal of the .api_filter file
88
	 */
89
	class ResourceDeltaVisitor implements IResourceDeltaVisitor {
90
		IProject[] projects;
91
		public ResourceDeltaVisitor(IProject[] projects) {
92
			this.projects = projects;
93
		}
94
		private boolean fRequireFullBuild = false;
95
96
		/**
97
		 * Returns whether a full build should be run.
98
		 * 
99
		 * @return whether a full build should be run
100
		 */
101
		boolean shouldRunFullBuild() {
102
			return fRequireFullBuild;
103
		}
104
105
		/* (non-Javadoc)
106
		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
107
		 */
108
		public boolean visit(IResourceDelta delta) throws CoreException {
109
			switch (delta.getResource().getType()) {
110
				case IResource.ROOT:
111
				case IResource.PROJECT:
112
					return !fRequireFullBuild;
113
				case IResource.FOLDER:
114
					return !fRequireFullBuild; 
115
				case IResource.FILE:
116
					if (delta.getResource().getProjectRelativePath().equals(MANIFEST_PATH)) {
117
						fRequireFullBuild = true;
118
						break;
119
					}
120
					IResource resource = delta.getResource();
121
					String fileName = resource.getName();
122
					if (Util.isClassFile(fileName)) {
123
						findAffectedSourceFiles(delta);
124
					} else if (Util.isJavaFileName(fileName)) {
125
						IProject project = resource.getProject();
126
						if (fCurrentProject.equals(project)) {
127
							if (delta.getKind() == IResourceDelta.ADDED) {
128
								fAddedRemovedDeltas.add(delta);
129
							}
130
							fTypesToCheck.add(resource);
131
						} else if (this.projects != null) {
132
							loop: for (int i = 0, max = this.projects.length; i < max; i++) {
133
								if (this.projects[i].equals(project)) {
134
									fTypesToCheck.add(resource);
135
									break loop;
136
								}
137
							}
138
						}
139
					} else if (!fRequireFullBuild && IApiCoreConstants.API_FILTERS_XML_NAME.equals(fileName)) {
140
						switch(delta.getKind()) {
141
							case IResourceDelta.REMOVED :
142
							case IResourceDelta.REPLACED :
143
							case IResourceDelta.CHANGED :
144
							case IResourceDelta.ADDED :
145
								fRequireFullBuild = true;
146
						}
147
					}
148
			}
149
			return false;
150
		}
151
	}
152
	/**
153
	 * Constant used for controlling tracing in the API tool builder
73
	 * Constant used for controlling tracing in the API tool builder
154
	 */
74
	 */
155
	private static boolean DEBUG = Util.DEBUG;
75
	static boolean DEBUG = Util.DEBUG;
156
	
76
	
157
	/**
77
	/**
158
	 * Project relative path to the manifest file.
78
	 * Project relative path to the manifest file.
159
	 */
79
	 */
160
	private static final IPath MANIFEST_PATH = new Path(JarFile.MANIFEST_NAME);
80
	static final IPath MANIFEST_PATH = new Path(JarFile.MANIFEST_NAME);
161
	
81
	
162
	/**
82
	/**
163
	 * Internal flag used to determine what created the marker, as there is overlap for reference kinds and deltas
83
	 * Project relative path to the .api_filters file
164
	 */
84
	 */
165
	public static final int REF_TYPE_FLAG = 0;
85
	static final IPath FILTER_PATH = new Path(".settings").append(IApiCoreConstants.API_FILTERS_XML_NAME); //$NON-NLS-1$
86
	
87
	/**
88
	 * Empty listing of projects to be returned by the builder if there is nothing to do
89
	 */
90
	static final IProject[] NO_PROJECTS = new IProject[0];
166
91
167
	/**
92
	/**
168
	 * Constant representing the name of the 'source' attribute on API tooling markers.
93
	 * Constant representing the name of the 'source' attribute on API tooling markers.
169
	 * Value is <code>Api Tooling</code>
94
	 * Value is <code>Api Tooling</code>
170
	 */
95
	 */
171
	public static final String SOURCE = "Api Tooling"; //$NON-NLS-1$
96
	static final String SOURCE = "Api Tooling"; //$NON-NLS-1$
172
	
97
	
173
	/**
98
	/**
174
	 * Method used for initializing tracing in the API tool builder
99
	 * Method used for initializing tracing in the API tool builder
Lines 176-226 Link Here
176
	public static void setDebug(boolean debugValue) {
101
	public static void setDebug(boolean debugValue) {
177
		DEBUG = debugValue || Util.DEBUG;
102
		DEBUG = debugValue || Util.DEBUG;
178
	}
103
	}
104
	
179
	/**
105
	/**
180
	 * The current project for which this builder was defined
106
	 * The current project for which this builder was defined
181
	 */
107
	 */
182
	private IProject fCurrentProject = null;
108
	private IProject currentproject = null;
183
	
109
	
184
	/**
110
	/**
185
	 * The API analyzer for this builder
111
	 * The API analyzer for this builder
186
	 */
112
	 */
187
	private IApiAnalyzer fAnalyzer = null;
113
	private IApiAnalyzer analyzer = null;
188
	
114
	
189
	/**
115
	/**
190
	 * Maps prerequisite projects to their output location(s)
116
	 * Maps prerequisite projects to their output location(s)
191
	 */
117
	 */
192
	private HashMap fProjectToOutputLocations = new HashMap();
118
	private HashMap projecttooutputlocations = new HashMap();
193
	
194
	/**
195
	 * List of type names to lookup for each project context to find dependents of
196
	 */
197
	private StringSet fTypes = new StringSet(3);
198
	
199
	/**
200
	 * List of package names to qualify type names
201
	 */
202
	private StringSet fPackages = new StringSet(3);
203
	
204
	/**
205
	 * The type that we want to check for API problems
206
	 */
207
	private HashSet fTypesToCheck = new HashSet();
208
	/**
209
	 * The set of added/removed deltas that come directly from the builder resource delta 
210
	 */
211
	private HashSet fAddedRemovedDeltas = new HashSet(5);
212
	
119
	
213
	/**
120
	/**
214
	 * Current build state
121
	 * Current build state
215
	 */
122
	 */
216
	private BuildState fBuildState;
123
	private BuildState buildstate = null;
217
	
124
	
218
	/**
125
	/**
219
	 * Cleans up markers associated with API tooling on the given resource.
126
	 * Cleans up markers associated with API tooling on the given resource.
220
	 * 
127
	 * 
221
	 * @param resource
128
	 * @param resource
222
	 */
129
	 */
223
	public static void cleanupMarkers(IResource resource) {
130
	void cleanupMarkers(IResource resource) {
224
		cleanupUsageMarkers(resource);
131
		cleanupUsageMarkers(resource);
225
		cleanupCompatibilityMarkers(resource);
132
		cleanupCompatibilityMarkers(resource);
226
		cleanupUnsupportedTagMarkers(resource);
133
		cleanupUnsupportedTagMarkers(resource);
Lines 230-236 Link Here
230
	 * Cleans up unsupported Javadoc tag markers on the specified resource
137
	 * Cleans up unsupported Javadoc tag markers on the specified resource
231
	 * @param resource
138
	 * @param resource
232
	 */
139
	 */
233
	private static void cleanupUnsupportedTagMarkers(IResource resource) {
140
	void cleanupUnsupportedTagMarkers(IResource resource) {
234
		try {
141
		try {
235
			if(DEBUG) {
142
			if(DEBUG) {
236
				System.out.println("cleaning unsupported tag problems"); //$NON-NLS-1$
143
				System.out.println("cleaning unsupported tag problems"); //$NON-NLS-1$
Lines 245-251 Link Here
245
	 * Cleans up only API compatibility markers on the given {@link IResource}
152
	 * Cleans up only API compatibility markers on the given {@link IResource}
246
	 * @param resource the given resource
153
	 * @param resource the given resource
247
	 */
154
	 */
248
	private static void cleanupCompatibilityMarkers(IResource resource) {
155
	void cleanupCompatibilityMarkers(IResource resource) {
249
		try {
156
		try {
250
			if (resource != null && resource.isAccessible()) {
157
			if (resource != null && resource.isAccessible()) {
251
				resource.deleteMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
158
				resource.deleteMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
Lines 266-272 Link Here
266
	 * cleans up only API usage markers from the given {@link IResource}
173
	 * cleans up only API usage markers from the given {@link IResource}
267
	 * @param resource
174
	 * @param resource
268
	 */
175
	 */
269
	private static void cleanupUsageMarkers(IResource resource) {
176
	void cleanupUsageMarkers(IResource resource) {
270
		try {
177
		try {
271
			if (resource != null && resource.isAccessible()) {
178
			if (resource != null && resource.isAccessible()) {
272
				resource.deleteMarkers(IApiMarkerConstants.API_USAGE_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
179
				resource.deleteMarkers(IApiMarkerConstants.API_USAGE_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
Lines 276-314 Link Here
276
		}
183
		}
277
	}
184
	}
278
	
185
	
279
	/**
280
	 * Adds a type to search for dependents of in considered projects for an incremental build
281
	 * 
282
	 * @param path
283
	 */
284
	private void addDependentsOf(IPath path) {
285
		// the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
286
		path = path.setDevice(null);
287
		String packageName = path.removeLastSegments(1).toString();
288
		String typeName = path.lastSegment();
289
		int memberIndex = typeName.indexOf('$');
290
		if (memberIndex > 0) {
291
			typeName = typeName.substring(0, memberIndex);
292
		}
293
		if (fTypes.add(typeName) && fPackages.add(packageName) && DEBUG) {
294
			System.out.println("  will look for dependents of " + typeName + " in " + packageName); //$NON-NLS-1$ //$NON-NLS-2$
295
		}
296
	}
297
	
298
	/* (non-Javadoc)
186
	/* (non-Javadoc)
299
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
187
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor)
300
	 */
188
	 */
301
	protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
189
	protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException {
302
		fCurrentProject = getProject();
190
		this.currentproject = getProject();
303
		fAnalyzer = getAnalyzer();
191
		if (!this.currentproject.isAccessible() || !this.currentproject.hasNature(ApiPlugin.NATURE_ID) || hasBeenBuilt(this.currentproject)) {
304
		if (fCurrentProject == null || 
192
			return NO_PROJECTS;
305
				!fCurrentProject.isAccessible() || 
306
				!fCurrentProject.hasNature(ApiPlugin.NATURE_ID) ||
307
				hasBeenBuilt(fCurrentProject)) {
308
			return new IProject[0];
309
		}
193
		}
310
		if (DEBUG) {
194
		if (DEBUG) {
311
			System.out.println("\nStarting build of " + fCurrentProject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$
195
			System.out.println("\nStarting build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$
312
		}
196
		}
313
		updateMonitor(monitor, 0);
197
		updateMonitor(monitor, 0);
314
		SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 2);
198
		SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 2);
Lines 325-388 Link Here
325
				}
209
				}
326
				case AUTO_BUILD :
210
				case AUTO_BUILD :
327
				case INCREMENTAL_BUILD : {
211
				case INCREMENTAL_BUILD : {
328
					IResourceDelta[] deltas = getDeltas(projects);
212
					this.buildstate = getLastBuiltState(currentproject);
329
					boolean shouldRunFullBuild = false;
213
					if (this.buildstate == null) {
330
					fBuildState = getLastBuiltState(fCurrentProject);
331
					if (fBuildState == null) {
332
						buildAll(baseline, localMonitor.newChild(1));
214
						buildAll(baseline, localMonitor.newChild(1));
333
					} else if (worthDoingFullBuild(projects)) {
215
						break;
216
					}
217
					else if(worthDoingFullBuild(projects)) {
334
						buildAll(baseline, localMonitor.newChild(1));
218
						buildAll(baseline, localMonitor.newChild(1));
335
					} else {
219
					}
336
						IProject[] reexportedProjects = null;
220
					IResourceDelta[] deltas = getDeltas(projects);
337
						String[] projectNames = this.fBuildState.getReexportedComponents();
221
					if(deltas.length < 1) {
338
						int length = projectNames.length;
222
						buildAll(baseline, localMonitor.newChild(1));
339
						if (length != 0) {
223
					}
340
							List allProjects = new ArrayList();
224
					else {	
341
							IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
225
						IResourceDelta manifest = null;
342
							for (int i = 0, max = projectNames.length; i < max; i++) {
226
						IResourceDelta filters = null;
343
								String projectName = projectNames[i];
344
								IProject project = root.getProject(projectName);
345
								if (project.isAccessible()) {
346
									// select only projects that don't exist in the reference baseline
347
									if (baseline != null && baseline.getApiComponent(projectName) == null) {
348
										allProjects.add(project);
349
									}
350
								}
351
							}
352
							if (allProjects.size() != 0) {
353
								reexportedProjects = new IProject[allProjects.size()];
354
								allProjects.toArray(reexportedProjects);
355
							}
356
						}
357
						ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(reexportedProjects);
358
						for (int i = 0; i < deltas.length; i++) {
227
						for (int i = 0; i < deltas.length; i++) {
359
							deltas[i].accept(visitor);
228
							manifest = deltas[i].findMember(MANIFEST_PATH);
360
							if (visitor.shouldRunFullBuild()) {
229
							if(manifest != null) {
361
								shouldRunFullBuild = true;
362
								break;
230
								break;
363
							}
231
							}
364
						}
232
							filters = deltas[i].findMember(FILTER_PATH);
365
						if (shouldRunFullBuild) {
233
							if(filters != null){
366
							if (DEBUG) {
234
								break;
367
								System.out.println("Performing full build since MANIFEST.MF was modified"); //$NON-NLS-1$
368
							}
235
							}
369
							buildAll(baseline, localMonitor.newChild(1));
236
						}
370
						} else if (deltas.length == 0) {
237
						if (manifest != null || filters != null) {
371
							if (DEBUG) {
238
							if (DEBUG) {
372
								System.out.println("Performing full build since deltas are missing after incremental request"); //$NON-NLS-1$
239
								System.out.println("Performing full build since MANIFEST.MF or .api_filters was modified"); //$NON-NLS-1$
373
							}
240
	 						}
374
							buildAll(baseline, localMonitor.newChild(1));
241
							buildAll(baseline, localMonitor.newChild(1));
375
						} else {
242
						}
376
							State state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(fCurrentProject, new NullProgressMonitor());
243
						else {
377
							if (state == null) {
244
							IncrementalApiBuilder builder = new IncrementalApiBuilder(this);
378
								buildAll(baseline, localMonitor.newChild(1));
245
							builder.build(baseline, deltas, this.buildstate, localMonitor.newChild(1));
379
							} else {
380
								build(state, baseline, localMonitor.newChild(1));
381
							}
382
						}
246
						}
383
					}
247
					}
384
					break;
248
				}	
385
				}
386
			}
249
			}
387
			updateMonitor(monitor, 0);
250
			updateMonitor(monitor, 0);
388
		} catch(CoreException e) {
251
		} catch(CoreException e) {
Lines 392-429 Link Here
392
			}
255
			}
393
			ApiPlugin.log(e);
256
			ApiPlugin.log(e);
394
		} finally {
257
		} finally {
395
			fTypes.clear();
396
			fPackages.clear();
397
			fTypesToCheck.clear();
398
			fAddedRemovedDeltas.clear();
399
			fProjectToOutputLocations.clear();
400
			updateMonitor(monitor, 0);
258
			updateMonitor(monitor, 0);
401
			fAnalyzer.dispose();
259
			if(this.analyzer != null) {
260
				this.analyzer.dispose();
261
				this.analyzer = null;
262
			}
402
			if(baseline != null) {
263
			if(baseline != null) {
403
				baseline.close();
264
				baseline.close();
404
			}
265
			}
405
			if(monitor != null) {
266
			if(monitor != null) {
406
				monitor.done();
267
				monitor.done();
407
			}
268
			}
408
			if (fBuildState != null) {
269
			if (this.buildstate != null) {
409
				for(int i = 0, max = projects.length; i < max; i++) {
270
				for(int i = 0, max = projects.length; i < max; i++) {
410
					IProject project = projects[i];
271
					IProject project = projects[i];
411
					if (Util.isApiProject(project)) {
272
					if (Util.isApiProject(project)) {
412
						fBuildState.addApiToolingDependentProject(project.getName());
273
						this.buildstate.addApiToolingDependentProject(project.getName());
413
					}
274
					}
414
				}
275
				}
415
				saveBuiltState(fCurrentProject, fBuildState);
276
				saveBuiltState(this.currentproject, this.buildstate);
416
				fBuildState = null;
277
				this.buildstate = null;
417
			}
278
			}
418
		}
279
		}
419
		if (DEBUG) {
280
		if (DEBUG) {
420
			System.out.println("Finished build of " + fCurrentProject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$
281
			System.out.println("Finished build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$
421
		}
282
		}
422
		return projects;
283
		return projects;
423
	}
284
	}
424
	
285
	
425
	private boolean worthDoingFullBuild(IProject[] projects) {
286
	private boolean worthDoingFullBuild(IProject[] projects) {
426
		Set apiToolingDependentProjects = fBuildState.getApiToolingDependentProjects();
287
		Set apiToolingDependentProjects = this.buildstate.getApiToolingDependentProjects();
427
		for (int i = 0, max = projects.length; i < max; i++) {
288
		for (int i = 0, max = projects.length; i < max; i++) {
428
			IProject currentProject = projects[i];
289
			IProject currentProject = projects[i];
429
			if (Util.isApiProject(currentProject)) {
290
			if (Util.isApiProject(currentProject)) {
Lines 437-454 Link Here
437
		}
298
		}
438
		return false;
299
		return false;
439
	}
300
	}
301
	
440
	/**
302
	/**
441
	 * Performs a full build for the project
303
	 * Performs a full build for the project
442
	 * @param monitor
304
	 * @param monitor
443
	 */
305
	 */
444
	private void buildAll(IApiBaseline baseline, IProgressMonitor monitor) throws CoreException {
306
	void buildAll(IApiBaseline baseline, IProgressMonitor monitor) throws CoreException {
445
		IApiBaseline wsprofile = null;
307
		IApiBaseline wsprofile = null;
446
		try {
308
		try {
447
			clearLastState();
309
			clearLastState();
448
			fBuildState = new BuildState();
310
			this.buildstate = new BuildState();
449
			SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 4);
311
			SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 4);
450
			localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_initializing_analyzer, fCurrentProject.getName()));
312
			localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_initializing_analyzer, currentproject.getName()));
451
			cleanupMarkers(fCurrentProject);
313
			cleanupMarkers(this.currentproject);
452
			IPluginModelBase currentModel = getCurrentModel();
314
			IPluginModelBase currentModel = getCurrentModel();
453
			if (currentModel != null) {
315
			if (currentModel != null) {
454
				localMonitor.subTask(BuilderMessages.building_workspace_profile);
316
				localMonitor.subTask(BuilderMessages.building_workspace_profile);
Lines 464-470 Link Here
464
				// Compatibility checks
326
				// Compatibility checks
465
				IApiComponent apiComponent = wsprofile.getApiComponent(id);
327
				IApiComponent apiComponent = wsprofile.getApiComponent(id);
466
				if(apiComponent != null) {
328
				if(apiComponent != null) {
467
					fAnalyzer.analyzeComponent(fBuildState, null, null, baseline, apiComponent, null, null, localMonitor.newChild(1));
329
					getAnalyzer().analyzeComponent(this.buildstate, null, null, baseline, apiComponent, null, null, localMonitor.newChild(1));
468
					updateMonitor(localMonitor, 1);
330
					updateMonitor(localMonitor, 1);
469
					createMarkers();
331
					createMarkers();
470
					updateMonitor(localMonitor, 1);
332
					updateMonitor(localMonitor, 1);
Lines 488-500 Link Here
488
	 */
350
	 */
489
	protected void createMarkers() {
351
	protected void createMarkers() {
490
		try {
352
		try {
491
			fCurrentProject.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
353
			this.currentproject.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, true, IResource.DEPTH_INFINITE);
492
			fCurrentProject.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, true, IResource.DEPTH_ZERO);
354
			this.currentproject.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, true, IResource.DEPTH_ZERO);
493
			fCurrentProject.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, true, IResource.DEPTH_ZERO);
355
			this.currentproject.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, true, IResource.DEPTH_ZERO);
494
		} catch (CoreException e) {
356
		} catch (CoreException e) {
495
			ApiPlugin.log(e);
357
			ApiPlugin.log(e);
496
		}
358
		}
497
		IApiProblem[] problems = fAnalyzer.getProblems();
359
		IApiProblem[] problems = getAnalyzer().getProblems();
498
		String type = null;
360
		String type = null;
499
		for(int i = 0; i < problems.length; i++) {
361
		for(int i = 0; i < problems.length; i++) {
500
			int category = problems[i].getCategory();
362
			int category = problems[i].getCategory();
Lines 578-584 Link Here
578
							IApiMarkerConstants.MARKER_ATTR_PROBLEM_ID},
440
							IApiMarkerConstants.MARKER_ATTR_PROBLEM_ID},
579
					new Object[] {
441
					new Object[] {
580
							problem.getMessage(),
442
							problem.getMessage(),
581
							new Integer(ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), this.fCurrentProject)),
443
							new Integer(ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), this.currentproject)),
582
							new Integer(line),
444
							new Integer(line),
583
							new Integer(problem.getCharStart()),
445
							new Integer(problem.getCharStart()),
584
							new Integer(problem.getCharEnd()),
446
							new Integer(problem.getCharEnd()),
Lines 620-626 Link Here
620
		if (resourcePath == null) {
482
		if (resourcePath == null) {
621
			return null;
483
			return null;
622
		}
484
		}
623
		IResource resource = fCurrentProject.findMember(new Path(resourcePath));
485
		IResource resource = currentproject.findMember(new Path(resourcePath));
624
		if(resource == null) {
486
		if(resource == null) {
625
			return null;
487
			return null;
626
		}
488
		}
Lines 654-660 Link Here
654
	 * @param ticks
516
	 * @param ticks
655
	 * @throws OperationCanceledException
517
	 * @throws OperationCanceledException
656
	 */
518
	 */
657
	private void updateMonitor(IProgressMonitor monitor, int ticks) throws OperationCanceledException {
519
	void updateMonitor(IProgressMonitor monitor, int ticks) throws OperationCanceledException {
658
		if(monitor != null) {
520
		if(monitor != null) {
659
			monitor.worked(ticks);
521
			monitor.worked(ticks);
660
			if (monitor.isCanceled()) {
522
			if (monitor.isCanceled()) {
Lines 663-894 Link Here
663
		}
525
		}
664
	}
526
	}
665
	
527
	
666
	/**
667
	 * Builds an API delta using the default profile (from the workspace settings and the current
668
	 * workspace profile
669
	 * @param state
670
	 * @param monitor
671
	 */
672
	private void build(final State state, IApiBaseline baseline, IProgressMonitor monitor) throws CoreException {
673
		IApiBaseline wsprofile = null;
674
		SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 6);
675
		try {
676
			clearLastState(); // so if the build fails, a full build will be triggered
677
			localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, fCurrentProject.getName()));
678
			updateMonitor(localMonitor, 0);
679
			collectAffectedSourceFiles(state, fTypesToCheck);
680
			updateMonitor(localMonitor, 1);
681
			final int typesToCheckSize = fTypesToCheck.size();
682
			if (typesToCheckSize != 0) {
683
				IPluginModelBase currentModel = getCurrentModel();
684
				if (currentModel != null) {
685
					wsprofile = getWorkspaceProfile();
686
					if (wsprofile == null) {
687
						if (DEBUG) {
688
							System.err.println("Could not retrieve a workspace profile"); //$NON-NLS-1$
689
						}
690
						return;
691
					}
692
					String id = currentModel.getBundleDescription().getSymbolicName();
693
					IApiComponent apiComponent = wsprofile.getApiComponent(id);
694
					if(apiComponent == null) {
695
						return;
696
					}
697
					List tnames = new ArrayList(typesToCheckSize),
698
						 cnames = new ArrayList(typesToCheckSize);
699
					collectAllQualifiedNames(fTypesToCheck, tnames, cnames, localMonitor.newChild(1));
700
					updateMonitor(localMonitor, 1);
701
					fAnalyzer.analyzeComponent(fBuildState, 
702
							null, 
703
							null, 
704
							baseline, 
705
							apiComponent, 
706
							(String[])tnames.toArray(new String[tnames.size()]), 
707
							(String[])cnames.toArray(new String[cnames.size()]), 
708
							localMonitor.newChild(1));
709
					updateMonitor(localMonitor, 1);
710
					createMarkers();
711
					updateMonitor(localMonitor, 1);
712
				}
713
			}
714
		}
715
		finally {
716
			if(wsprofile != null) {
717
				wsprofile.close();
718
			}
719
			if(!localMonitor.isCanceled()) {
720
				localMonitor.done();
721
			}
722
		}
723
	}
724
	
725
	/**
726
	 * Returns an array of type names, and cleans up markers for the specified resource
727
	 * @param alltypes the listing of {@link IFile}s to get qualified names from
728
	 * @param changedtypes the listing of {@link IFile}s that have actually changed (from the {@link IResourceDelta}
729
	 * @param tnames the list to collect all type names into (including inner member names)
730
	 * @param cnames the list to collect the changed type names into
731
	 * @param monitor
732
	 */
733
	private void collectAllQualifiedNames(final HashSet alltypes, List tnames, List cnames, final IProgressMonitor monitor) {
734
		IType[] types = null;
735
		IFile file = null;
736
		for (Iterator iterator = alltypes.iterator(); iterator.hasNext(); ) {
737
			file = (IFile) iterator.next();
738
			ICompilationUnit unit = (ICompilationUnit) JavaCore.create(file);
739
			if(!unit.exists()) {
740
				continue;
741
			}
742
			IType type = unit.findPrimaryType();
743
			if(type == null) {
744
				continue;
745
			}
746
			updateMonitor(monitor, 0);
747
			cleanupUnsupportedTagMarkers(file);
748
			updateMonitor(monitor, 0);
749
			cleanupCompatibilityMarkers(file);
750
			updateMonitor(monitor, 0);
751
			cnames.add(type.getFullyQualifiedName());
752
			try {
753
				cleanupUsageMarkers(file);
754
				updateMonitor(monitor, 0);
755
				types = unit.getAllTypes();
756
				String tname = null;
757
				for (int i = 0; i < types.length; i++) {
758
					IType type2 = types[i];
759
					if (type2.isMember()) {
760
						tname = type2.getFullyQualifiedName('$');
761
					} else {
762
						tname = type2.getFullyQualifiedName();
763
					}
764
					tnames.add(tname);
765
				}
766
			} catch (JavaModelException e) {
767
				ApiPlugin.log(e.getStatus());
768
			}
769
			updateMonitor(monitor, 0);
770
		}
771
		// inject removed types inside changed type names so that we can properly detect type removal
772
		for (Iterator iterator = this.fAddedRemovedDeltas.iterator(); iterator.hasNext(); ) {
773
			IResourceDelta delta = (IResourceDelta) iterator.next();
774
			if (delta.getKind() != IResourceDelta.REMOVED) continue;
775
			IResource resource = delta.getResource();
776
			IPath typePath = resolveJavaPathFromResource(resource);
777
			if(typePath == null) {
778
				continue;
779
			}
780
			// record removed type names (package + type)
781
			StringBuffer buffer = new StringBuffer();
782
			String[] segments = typePath.segments();
783
			for (int i = 0, max = segments.length; i < max; i++) {
784
				if (i > 0) {
785
					buffer.append('.');
786
				}
787
				buffer.append(segments[i]);
788
			}
789
			cnames.add(String.valueOf(buffer));
790
		}
791
		// clean up markers on added deltas
792
		if (!this.fAddedRemovedDeltas.isEmpty()) {
793
			IResource manifestFile = Util.getManifestFile(this.fCurrentProject);
794
			if (manifestFile != null) {
795
				try {
796
					IMarker[] markers = manifestFile.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
797
					for (int i = 0, max = markers.length; i < max; i++) {
798
						IMarker marker = markers[i];
799
						String typeName = marker.getAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, null);
800
						if (typeName != null) {
801
							for (Iterator iterator = this.fAddedRemovedDeltas.iterator(); iterator.hasNext(); ) {
802
								IResourceDelta delta = (IResourceDelta) iterator.next();
803
								if (delta.getKind() != IResourceDelta.ADDED) continue;
804
								ICompilationUnit unit = (ICompilationUnit) JavaCore.create(delta.getResource());
805
								if(!unit.exists()) {
806
									continue;
807
								}
808
								IType type = unit.findPrimaryType();
809
								if(type == null) {
810
									continue;
811
								}
812
								if (typeName.equals(type.getFullyQualifiedName())) {
813
									marker.delete();
814
									return;
815
								} else {
816
									// check secondary types
817
									try {
818
										types = unit.getAllTypes();
819
										for (int j = 0; j < types.length; j++) {
820
											IType type2 = types[i];
821
											String fullyQualifiedName = null;
822
											if (type2.isMember()) {
823
												fullyQualifiedName = type2.getFullyQualifiedName('$');
824
											} else {
825
												fullyQualifiedName = type2.getFullyQualifiedName();
826
											}
827
											if (typeName.equals(fullyQualifiedName)) {
828
												marker.delete();
829
												return;
830
											}
831
										}
832
									} catch (JavaModelException e) {
833
										ApiPlugin.log(e.getStatus());
834
									}
835
								}
836
							}
837
						}
838
					}
839
				} catch (CoreException e) {
840
					ApiPlugin.log(e.getStatus());
841
				}
842
			}
843
		}
844
		IResource resource = fCurrentProject.findMember(MANIFEST_PATH);
845
		if (resource != null) {
846
			try {
847
				IMarker[] markers = resource.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
848
				loop: for (int i = 0, max = markers.length; i < max; i++) {
849
					IMarker marker = markers[i];
850
					String typeNameFromMarker = Util.getTypeNameFromMarker(marker);
851
					for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) {
852
						String typeName = (String) iterator.next();
853
						if (typeName.equals(typeNameFromMarker)) {
854
							marker.delete();
855
							continue loop;
856
						}
857
					}
858
				}
859
				markers = resource.findMarkers(IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
860
				loop: for (int i = 0, max = markers.length; i < max; i++) {
861
					IMarker marker = markers[i];
862
					String typeNameFromMarker = Util.getTypeNameFromMarker(marker);
863
					for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) {
864
						String typeName = (String) iterator.next();
865
						if (typeName.equals(typeNameFromMarker)) {
866
							marker.delete();
867
							continue loop;
868
						}
869
					}
870
				}
871
			} catch (CoreException e) {
872
				ApiPlugin.log(e);
873
			}
874
		}
875
	}
876
	
877
	/* (non-Javadoc)
528
	/* (non-Javadoc)
878
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor)
529
	 * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor)
879
	 */
530
	 */
880
	protected void clean(IProgressMonitor monitor) throws CoreException {
531
	protected void clean(IProgressMonitor monitor) throws CoreException {
881
		fCurrentProject = getProject();
532
		this.currentproject = getProject();
882
		SubMonitor localmonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.CleaningAPIDescription, new String[] {fCurrentProject.getName()}), 2);
533
		SubMonitor localmonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.CleaningAPIDescription, new String[] {this.currentproject.getName()}), 2);
883
		try {
534
		try {
884
			// clean up all existing markers
535
			// clean up all existing markers
885
			cleanupUsageMarkers(fCurrentProject);
536
			cleanupUsageMarkers(this.currentproject);
886
			cleanupCompatibilityMarkers(fCurrentProject);
537
			cleanupCompatibilityMarkers(this.currentproject);
887
			cleanupUnsupportedTagMarkers(fCurrentProject);
538
			cleanupUnsupportedTagMarkers(this.currentproject);
888
			fCurrentProject.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
539
			this.currentproject.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
889
			updateMonitor(localmonitor, 1);
540
			updateMonitor(localmonitor, 1);
890
			//clean up the .api_settings
541
			//clean up the .api_settings
891
			cleanupApiDescription(fCurrentProject);
542
			cleanupApiDescription(this.currentproject);
892
			updateMonitor(localmonitor, 1);
543
			updateMonitor(localmonitor, 1);
893
		}
544
		}
894
		finally {
545
		finally {
Lines 906-998 Link Here
906
			ApiDescriptionManager.getDefault().clean(JavaCore.create(project), true, true);
557
			ApiDescriptionManager.getDefault().clean(JavaCore.create(project), true, true);
907
		}
558
		}
908
	}
559
	}
909
	/**
910
	 * Collects the complete set of affected source files from the current project context based on the current JDT build state.
911
	 * 
912
	 * @param state
913
	 */
914
	private void collectAffectedSourceFiles(State state, Set typesToCheck) {
915
		// the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
916
		char[][][] internedQualifiedNames = ReferenceCollection.internQualifiedNames(fPackages);
917
		// if a well known qualified name was found then we can skip over these
918
		if (internedQualifiedNames.length < fPackages.elementSize) {
919
			internedQualifiedNames = null;
920
		}
921
		char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(fTypes, true);
922
		// if a well known name was found then we can skip over these
923
		if (internedSimpleNames.length < fTypes.elementSize) {
924
			internedSimpleNames = null;
925
		}
926
		Object[] keyTable = state.getReferences().keyTable;
927
		Object[] valueTable = state.getReferences().valueTable;
928
		next : for (int i = 0, l = valueTable.length; i < l; i++) {
929
			String typeLocator = (String) keyTable[i];
930
			if (typeLocator != null) {
931
				ReferenceCollection refs = (ReferenceCollection) valueTable[i];
932
				if (refs.includes(internedQualifiedNames, internedSimpleNames, null)) {
933
					IFile file = fCurrentProject.getFile(typeLocator);
934
					if (file == null) {
935
						continue next;
936
					}
937
					if (DEBUG) {
938
						System.out.println("  adding affected source file " + typeLocator); //$NON-NLS-1$
939
					}
940
					typesToCheck.add(file);
941
				}
942
			}
943
		}
944
	}
945
	
946
	
560
	
947
948
	/**
949
	 * Finds affected source files for a resource that has changed that either contains class files or is itself a class file
950
	 * @param binaryDelta
951
	 */
952
	private void findAffectedSourceFiles(IResourceDelta binaryDelta) {
953
		IResource resource = binaryDelta.getResource();
954
		if(resource.getType() == IResource.FILE) {
955
			if (Util.isClassFile(resource.getName())) {
956
				switch (binaryDelta.getKind()) {
957
					case IResourceDelta.REMOVED :
958
						fAddedRemovedDeltas.add(binaryDelta);
959
						//$FALL-THROUGH$
960
					case IResourceDelta.ADDED : {
961
						IPath typePath = resolveJavaPathFromResource(resource);
962
						if(typePath == null) {
963
							return;
964
						}
965
						if (DEBUG) {
966
							System.out.println("Found added/removed class file " + typePath); //$NON-NLS-1$
967
						}
968
						addDependentsOf(typePath);
969
						return;
970
					}
971
					case IResourceDelta.CHANGED : {
972
						if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0) {
973
							return; // skip it since it really isn't changed
974
						}
975
						IPath typePath = resolveJavaPathFromResource(resource);
976
						if(typePath == null) {
977
							return;
978
						}
979
						if (DEBUG) {
980
							System.out.println("Found changed class file " + typePath); //$NON-NLS-1$
981
						}
982
						addDependentsOf(typePath);
983
					}
984
				}
985
				return;
986
			}
987
		}
988
	}
989
990
	/**
561
	/**
991
	 * @return the current {@link IPluginModelBase} based on the current project for this builder
562
	 * @return the current {@link IPluginModelBase} based on the current project for this builder
992
	 */
563
	 */
993
	private IPluginModelBase getCurrentModel() {
564
	IPluginModelBase getCurrentModel() {
994
		IPluginModelBase[] workspaceModels = PluginRegistry.getWorkspaceModels();
565
		IPluginModelBase[] workspaceModels = PluginRegistry.getWorkspaceModels();
995
		IPath location = fCurrentProject.getLocation();
566
		IPath location = this.currentproject.getLocation();
996
		IPluginModelBase currentModel = null;
567
		IPluginModelBase currentModel = null;
997
		BundleDescription desc = null;
568
		BundleDescription desc = null;
998
		loop: for (int i = 0, max = workspaceModels.length; i < max; i++) {
569
		loop: for (int i = 0, max = workspaceModels.length; i < max; i++) {
Lines 1018-1027 Link Here
1018
	 */
589
	 */
1019
	private IResourceDelta[] getDeltas(IProject[] projects) {
590
	private IResourceDelta[] getDeltas(IProject[] projects) {
1020
		if(DEBUG) {
591
		if(DEBUG) {
1021
			System.out.println("Searching for deltas for build of project: "+fCurrentProject.getName()); //$NON-NLS-1$
592
			System.out.println("Searching for deltas for build of project: "+this.currentproject.getName()); //$NON-NLS-1$
1022
		}
593
		}
1023
		ArrayList deltas = new ArrayList();
594
		ArrayList deltas = new ArrayList();
1024
		IResourceDelta delta = getDelta(fCurrentProject);
595
		IResourceDelta delta = getDelta(this.currentproject);
1025
		if(delta != null) {
596
		if(delta != null) {
1026
			if (DEBUG) {
597
			if (DEBUG) {
1027
				System.out.println("Found a delta: " + delta); //$NON-NLS-1$
598
				System.out.println("Found a delta: " + delta); //$NON-NLS-1$
Lines 1041-1051 Link Here
1041
	}
612
	}
1042
613
1043
	/**
614
	/**
1044
	 * Returns the API analyzer to use with this instance of the  builder
615
	 * Returns the API analyzer to use with this instance of the builder
1045
	 * @return the API analyzer to use
616
	 * @return the API analyzer to use
1046
	 */
617
	 */
1047
	protected IApiAnalyzer getAnalyzer() {
618
	protected synchronized IApiAnalyzer getAnalyzer() {
1048
		return new BaseApiAnalyzer();
619
		if(this.analyzer == null) {
620
			this.analyzer = new BaseApiAnalyzer();
621
		}
622
		return this.analyzer;
1049
	}
623
	}
1050
	
624
	
1051
	/**
625
	/**
Lines 1056-1070 Link Here
1056
	 */
630
	 */
1057
	private IProject[] getRequiredProjects(boolean includebinaries) throws CoreException {
631
	private IProject[] getRequiredProjects(boolean includebinaries) throws CoreException {
1058
		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
632
		IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot();
1059
		if (fCurrentProject == null || workspaceRoot == null) { 
633
		if (this.currentproject == null || workspaceRoot == null) { 
1060
			return new IProject[0];
634
			return new IProject[0];
1061
		}
635
		}
1062
		ArrayList projects = new ArrayList();
636
		ArrayList projects = new ArrayList();
1063
		try {
637
		try {
1064
			IJavaProject javaProject = JavaCore.create(fCurrentProject);
638
			IJavaProject javaProject = JavaCore.create(this.currentproject);
1065
			HashSet blocations = new HashSet();
639
			HashSet blocations = new HashSet();
1066
			blocations.add(javaProject.getOutputLocation());
640
			blocations.add(javaProject.getOutputLocation());
1067
			fProjectToOutputLocations.put(fCurrentProject, blocations);
641
			projecttooutputlocations.put(this.currentproject, blocations);
1068
			IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
642
			IClasspathEntry[] entries = javaProject.getResolvedClasspath(true);
1069
			for (int i = 0, l = entries.length; i < l; i++) {
643
			for (int i = 0, l = entries.length; i < l; i++) {
1070
				IClasspathEntry entry = entries[i];
644
				IClasspathEntry entry = entries[i];
Lines 1112-1118 Link Here
1112
								}
686
								}
1113
							}
687
							}
1114
						}
688
						}
1115
						fProjectToOutputLocations.put(p, bins);
689
						this.projecttooutputlocations.put(p, bins);
1116
					}
690
					}
1117
				}
691
				}
1118
			}
692
			}
Lines 1128-1136 Link Here
1128
	/**
702
	/**
1129
	 * @return the workspace {@link IApiProfile}
703
	 * @return the workspace {@link IApiProfile}
1130
	 */
704
	 */
1131
	private IApiBaseline getWorkspaceProfile() throws CoreException {
705
	IApiBaseline getWorkspaceProfile() throws CoreException {
1132
		return ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline();
706
		return ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline();
1133
	}
707
	}
708
	
709
	/**
710
	 * Returns the output paths of the given project or <code>null</code> if none have been computed
711
	 * @param project
712
	 * @return the output paths for the given project or <code>null</code>
713
	 */
714
	HashSet getProjectOutputPaths(IProject project) {
715
		return (HashSet) this.projecttooutputlocations.get(project);
716
	}
717
	
1134
	/**
718
	/**
1135
	 * Returns is the given classpath entry is optional or not
719
	 * Returns is the given classpath entry is optional or not
1136
	 * @param entry
720
	 * @param entry
Lines 1145-1183 Link Here
1145
		}
729
		}
1146
		return false;
730
		return false;
1147
	}
731
	}
1148
1149
	/**
1150
	 * Resolves the java path from the given resource
1151
	 * @param resource
1152
	 * @return the resolved path or <code>null</code> if the resource is not part of the java model
1153
	 */
1154
	private IPath resolveJavaPathFromResource(IResource resource) {
1155
		IJavaElement element = JavaCore.create(resource);
1156
		if(element != null) {
1157
			switch(element.getElementType()) {
1158
				case IJavaElement.CLASS_FILE: {
1159
					org.eclipse.jdt.core.IClassFile classfile = (org.eclipse.jdt.core.IClassFile) element;
1160
					IType type = classfile.getType();
1161
					HashSet paths = (HashSet) fProjectToOutputLocations.get(resource.getProject());
1162
					IPath prefix = null;
1163
					for(Iterator iter = paths.iterator(); iter.hasNext();) {
1164
						prefix = (IPath) iter.next();
1165
						if(prefix.isPrefixOf(type.getPath())) {
1166
							return type.getPath().removeFirstSegments(prefix.segmentCount()).removeFileExtension();
1167
						}
1168
					}
1169
					break;
1170
				}
1171
			}
1172
		}
1173
		return null;
1174
	}
1175
	
732
	
1176
	/* (non-Javadoc)
733
	/* (non-Javadoc)
1177
	 * @see java.lang.Object#toString()
734
	 * @see java.lang.Object#toString()
1178
	 */
735
	 */
1179
	public String toString() {
736
	public String toString() {
1180
		return "Builder for project: ["+fCurrentProject.getName()+"]"; //$NON-NLS-1$ //$NON-NLS-2$
737
		return NLS.bind(BuilderMessages.ApiAnalysisBuilder_builder_for_project, this.currentproject.getName());
1181
	}
738
	}
1182
	
739
	
1183
	/**
740
	/**
Lines 1194-1200 Link Here
1194
	/**
751
	/**
1195
	 * Reads the build state for the relevant project.
752
	 * Reads the build state for the relevant project.
1196
	 */
753
	 */
1197
	protected static BuildState readState(IProject project) throws CoreException {
754
	static BuildState readState(IProject project) throws CoreException {
1198
		File file = getSerializationFile(project);
755
		File file = getSerializationFile(project);
1199
		if (file != null && file.exists()) {
756
		if (file != null && file.exists()) {
1200
			try {
757
			try {
Lines 1245-1251 Link Here
1245
	/**
802
	/**
1246
	 * Returns the File to use for saving and restoring the last built state for the given project.
803
	 * Returns the File to use for saving and restoring the last built state for the given project.
1247
	 */
804
	 */
1248
	private static File getSerializationFile(IProject project) {
805
	static File getSerializationFile(IProject project) {
1249
		if (!project.exists()) {
806
		if (!project.exists()) {
1250
			return null;
807
			return null;
1251
		}
808
		}
Lines 1259-1265 Link Here
1259
	 * @param state
816
	 * @param state
1260
	 * @throws CoreException
817
	 * @throws CoreException
1261
	 */
818
	 */
1262
	private static void saveBuiltState(IProject project, BuildState state) throws CoreException {
819
	static void saveBuiltState(IProject project, BuildState state) throws CoreException {
1263
		if (DEBUG) {
820
		if (DEBUG) {
1264
			System.out.println("Saving build state for project: "+project.getName()); //$NON-NLS-1$
821
			System.out.println("Saving build state for project: "+project.getName()); //$NON-NLS-1$
1265
		}
822
		}
Lines 1305-1311 Link Here
1305
	 * Clears the last build state by setting it to <code>null</code>
862
	 * Clears the last build state by setting it to <code>null</code>
1306
	 * @throws CoreException
863
	 * @throws CoreException
1307
	 */
864
	 */
1308
	private void clearLastState() throws CoreException {
865
	void clearLastState() throws CoreException {
1309
		setLastBuiltState(fCurrentProject, null);
866
		setLastBuiltState(this.currentproject, null);
1310
	}
867
	}
1311
}
868
}
(-)src/org/eclipse/pde/api/tools/internal/builder/buildermessages.properties (-1 / +1 lines)
Lines 13-18 Link Here
13
building_workspace_profile=Building workspace API profile
13
building_workspace_profile=Building workspace API profile
14
checking_api_usage=Checking API use of ''{0}''
14
checking_api_usage=Checking API use of ''{0}''
15
AbstractTypeLeakDetector_vis_type_has_no_api_description=Visible type {0} has no API description
15
AbstractTypeLeakDetector_vis_type_has_no_api_description=Visible type {0} has no API description
16
ApiAnalysisBuilder_builder_for_project=Builder for project: [{0}]
16
ApiAnalysisBuilder_finding_affected_source_files=Finding affected source in ''{0}''
17
ApiAnalysisBuilder_finding_affected_source_files=Finding affected source in ''{0}''
17
ApiAnalysisBuilder_initializing_analyzer=Initializing analyzer for ''{0}''
18
ApiAnalysisBuilder_initializing_analyzer=Initializing analyzer for ''{0}''
18
ApiProblemFactory_problem_message_not_found=Message not found for id: {0}
19
ApiProblemFactory_problem_message_not_found=Message not found for id: {0}
Lines 25-31 Link Here
25
build_saveStateComplete = Saved in {0} ms
26
build_saveStateComplete = Saved in {0} ms
26
build_wrongFileFormat = Wrong file format
27
build_wrongFileFormat = Wrong file format
27
build_cannotSaveState = Error saving last build state for project {0}
28
build_cannotSaveState = Error saving last build state for project {0}
28
IllegalExtendsProblemDetector_an_anonymous_declaration=An anonymous declaration
29
ReferenceAnalyzer_analyzing_api_checking_use=Analyzing API: Checking API use
29
ReferenceAnalyzer_analyzing_api_checking_use=Analyzing API: Checking API use
30
ReferenceAnalyzer_analyzing_api=Analyzing API
30
ReferenceAnalyzer_analyzing_api=Analyzing API
31
ReferenceAnalyzer_api_analysis_error=API analysis error
31
ReferenceAnalyzer_api_analysis_error=API analysis error
(-)src/org/eclipse/pde/api/tools/internal/ApiBaselineManager.java (-2 lines)
Lines 56-62 Link Here
56
import org.eclipse.jdt.core.IPackageFragment;
56
import org.eclipse.jdt.core.IPackageFragment;
57
import org.eclipse.jdt.core.IPackageFragmentRoot;
57
import org.eclipse.jdt.core.IPackageFragmentRoot;
58
import org.eclipse.jdt.core.JavaCore;
58
import org.eclipse.jdt.core.JavaCore;
59
import org.eclipse.pde.api.tools.internal.builder.ApiAnalysisBuilder;
60
import org.eclipse.pde.api.tools.internal.model.ApiBaseline;
59
import org.eclipse.pde.api.tools.internal.model.ApiBaseline;
61
import org.eclipse.pde.api.tools.internal.model.ApiModelFactory;
60
import org.eclipse.pde.api.tools.internal.model.ApiModelFactory;
62
import org.eclipse.pde.api.tools.internal.model.StubApiComponent;
61
import org.eclipse.pde.api.tools.internal.model.StubApiComponent;
Lines 888-894 Link Here
888
									IJavaProject jp = JavaCore.create(project);
887
									IJavaProject jp = JavaCore.create(project);
889
									if (jp.exists()) {
888
									if (jp.exists()) {
890
										ApiDescriptionManager.getDefault().clean(jp, true, true);
889
										ApiDescriptionManager.getDefault().clean(jp, true, true);
891
										ApiAnalysisBuilder.cleanupMarkers(resource);
892
									}
890
									}
893
								}
891
								}
894
							} catch (CoreException e) {
892
							} catch (CoreException e) {
(-)src/org/eclipse/pde/api/tools/internal/ApiDescriptionManager.java (-5 / +5 lines)
Lines 139-146 Link Here
139
	 * Cleans the API description for the given project.
139
	 * Cleans the API description for the given project.
140
	 * 
140
	 * 
141
	 * @param project
141
	 * @param project
142
	 * @param whether to delete the file on disk
142
	 * @param delete whether to delete the file on disk
143
	 * @param whether to remove the cached API description
143
	 * @param remove whether to remove the cached API description
144
	 */
144
	 */
145
	public synchronized void clean(IJavaProject project, boolean delete, boolean remove) {
145
	public synchronized void clean(IJavaProject project, boolean delete, boolean remove) {
146
		ProjectApiDescription desc = null;
146
		ProjectApiDescription desc = null;
Lines 261-267 Link Here
261
					switch (delta.getKind()) {
261
					switch (delta.getKind()) {
262
						case IJavaElementDelta.CHANGED: {
262
						case IJavaElementDelta.CHANGED: {
263
							if ((flags & IJavaElementDelta.F_CONTENT) != 0
263
							if ((flags & IJavaElementDelta.F_CONTENT) != 0
264
									|| (flags & IJavaElementDelta.F_FINE_GRAINED) != 0) {
264
									|| (flags & IJavaElementDelta.F_FINE_GRAINED) != 0
265
									|| (flags & IJavaElementDelta.F_PRIMARY_RESOURCE) != 0){
265
								if (proj != null) {
266
								if (proj != null) {
266
									projectChanged(proj);
267
									projectChanged(proj);
267
									return true;
268
									return true;
Lines 502-507 Link Here
502
	private static void abort(String message, Throwable exception) throws CoreException {
503
	private static void abort(String message, Throwable exception) throws CoreException {
503
		IStatus status = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, exception);
504
		IStatus status = new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, exception);
504
		throw new CoreException(status);
505
		throw new CoreException(status);
505
	}	
506
	}
506
507
}
507
}
(-)src/org/eclipse/pde/api/tools/internal/CompilationUnit.java (+7 lines)
Lines 75-78 Link Here
75
		}
75
		}
76
		return new FileInputStream(new File(filepath));
76
		return new FileInputStream(new File(filepath));
77
	}
77
	}
78
	
79
	/* (non-Javadoc)
80
	 * @see java.lang.Object#toString()
81
	 */
82
	public String toString() {
83
		return getName();
84
	}
78
}
85
}
(-)src/org/eclipse/pde/api/tools/internal/builder/IncrementalApiBuilder.java (+523 lines)
Added Link Here
1
/*******************************************************************************
2
 * Copyright (c) 2009 IBM Corporation and others.
3
 * All rights reserved. This program and the accompanying materials
4
 * are made available under the terms of the Eclipse Public License v1.0
5
 * which accompanies this distribution, and is available at
6
 * http://www.eclipse.org/legal/epl-v10.html
7
 *
8
 * Contributors:
9
 *     IBM Corporation - initial API and implementation
10
 *******************************************************************************/
11
package org.eclipse.pde.api.tools.internal.builder;
12
13
import java.util.ArrayList;
14
import java.util.HashSet;
15
import java.util.Iterator;
16
import java.util.List;
17
import java.util.Set;
18
19
import org.eclipse.core.resources.IFile;
20
import org.eclipse.core.resources.IMarker;
21
import org.eclipse.core.resources.IProject;
22
import org.eclipse.core.resources.IResource;
23
import org.eclipse.core.resources.IResourceDelta;
24
import org.eclipse.core.resources.IResourceDeltaVisitor;
25
import org.eclipse.core.resources.IWorkspaceRoot;
26
import org.eclipse.core.resources.ResourcesPlugin;
27
import org.eclipse.core.runtime.CoreException;
28
import org.eclipse.core.runtime.IPath;
29
import org.eclipse.core.runtime.IProgressMonitor;
30
import org.eclipse.core.runtime.SubMonitor;
31
import org.eclipse.jdt.core.ICompilationUnit;
32
import org.eclipse.jdt.core.IJavaElement;
33
import org.eclipse.jdt.core.IType;
34
import org.eclipse.jdt.core.JavaCore;
35
import org.eclipse.jdt.core.JavaModelException;
36
import org.eclipse.jdt.internal.core.JavaModelManager;
37
import org.eclipse.jdt.internal.core.builder.ReferenceCollection;
38
import org.eclipse.jdt.internal.core.builder.State;
39
import org.eclipse.jdt.internal.core.builder.StringSet;
40
import org.eclipse.osgi.util.NLS;
41
import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
42
import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants;
43
import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
44
import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
45
import org.eclipse.pde.api.tools.internal.util.Util;
46
import org.eclipse.pde.core.plugin.IPluginModelBase;
47
48
/**
49
 * Used to incrementally build changed Java types
50
 * 
51
 * @since 3.5
52
 */
53
public class IncrementalApiBuilder {
54
55
	/**
56
	 * Visits a resource delta to collect changes that need to be built
57
	 */
58
	class ResourceDeltaVisitor implements IResourceDeltaVisitor {
59
		HashSet projects = null;
60
		IProject project = null;
61
		
62
		/**
63
		 * Constructor
64
		 * @param project
65
		 * @param projects
66
		 */
67
		public ResourceDeltaVisitor(IProject project, HashSet projects) {
68
			this.project = project;
69
			this.projects = projects;
70
		}
71
		/* (non-Javadoc)
72
		 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
73
		 */
74
		public boolean visit(IResourceDelta delta) throws CoreException {
75
			switch (delta.getResource().getType()) {
76
				case IResource.ROOT:
77
				case IResource.PROJECT:
78
				case IResource.FOLDER: {
79
					return true; 
80
				}
81
				case IResource.FILE: {
82
					IResource resource = delta.getResource();
83
					String fileName = resource.getName();
84
					if (Util.isClassFile(fileName)) {
85
						findAffectedSourceFiles(delta);
86
					} else if (Util.isJavaFileName(fileName)) {
87
						IProject project = resource.getProject();
88
						if (this.project.equals(project)) {
89
							if (delta.getKind() == IResourceDelta.ADDED) {
90
								IncrementalApiBuilder.this.addremovedeltas.add(delta);
91
							}
92
							IncrementalApiBuilder.this.changedtypes.add(resource);
93
						} 
94
						else if (this.projects != null && this.projects.contains(project)) {
95
							IncrementalApiBuilder.this.changedtypes.add(resource);
96
						}
97
					}
98
				}
99
			}
100
			return false;
101
		}
102
	}
103
104
	ApiAnalysisBuilder builder = null;
105
	//IProject project = null;
106
	HashSet changedtypes = new HashSet(16);
107
	HashSet addremovedeltas = new HashSet(8);
108
	StringSet typenames = new StringSet(16);
109
	StringSet packages = new StringSet(16);
110
	
111
	
112
	/**
113
	 * Constructor
114
	 * @param project the current project context being built
115
	 * @param delta the {@link IResourceDelta} from the build framework
116
	 * @param buildstate the current build state from the {@link org.eclipse.jdt.internal.core.builder.JavaBuilder}
117
	 */
118
	public IncrementalApiBuilder(ApiAnalysisBuilder builder) {
119
		this.builder = builder;
120
	}
121
	
122
	/**
123
	 * Incrementally builds using the {@link org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer}
124
	 * from the given {@link ApiAnalysisBuilder}
125
	 * 
126
	 * @param baseline
127
	 * @param deltas
128
	 * @param monitor
129
	 * @throws CoreException
130
	 */
131
	public void build(IApiBaseline baseline, IResourceDelta[] deltas, BuildState buildstate, IProgressMonitor monitor) throws CoreException {
132
		IProject project = this.builder.getProject();
133
		SubMonitor localmonitor = SubMonitor.convert(monitor, NLS.bind("API analysis: Incrementally building... {0}", project), 10);
134
		try {
135
			State state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(project, localmonitor.newChild(1));
136
			if(state == null) {
137
				this.builder.buildAll(baseline, localmonitor);
138
				return;
139
			}
140
			String[] projectNames = buildstate.getReexportedComponents();
141
			HashSet depprojects = null;
142
			if (projectNames.length != 0) {
143
				depprojects = new HashSet();
144
				IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
145
				IProject pj = null;
146
				for (int i = 0, max = projectNames.length; i < max; i++) {
147
					pj = root.getProject(projectNames[i]);
148
					if (pj.isAccessible()) {
149
						// select only projects that don't exist in the reference baseline
150
						if (baseline != null && baseline.getApiComponent(projectNames[i]) == null) {
151
							depprojects.add(pj);
152
						}
153
					}
154
				}
155
			}
156
			ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(project, depprojects);
157
			for (int i = 0; i < deltas.length; i++) {
158
				deltas[i].accept(visitor);
159
			}
160
			build(project, state, baseline, buildstate, localmonitor.newChild(1));
161
		}
162
		finally {
163
			if(!localmonitor.isCanceled()) {
164
				localmonitor.done();
165
			}
166
			this.changedtypes.clear();
167
			this.addremovedeltas.clear();
168
			this.typenames.clear();
169
			this.packages.clear();
170
		}
171
	}
172
	
173
	/**
174
	 * Builds an API delta using the default profile (from the workspace settings and the current
175
	 * @param project
176
	 * workspace profile
177
	 * @param state
178
	 * @param monitor
179
	 */
180
	private void build(final IProject project, final State state, IApiBaseline baseline, BuildState buildstate, IProgressMonitor monitor) throws CoreException {
181
		IApiBaseline wsprofile = null;
182
		try {
183
			// clear the old state so a full build will occur if this one is cancelled or terminates prematurely
184
			this.builder.clearLastState();
185
			SubMonitor localmonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 1);
186
			collectAffectedSourceFiles(project, state, this.changedtypes);
187
			int typesize = this.changedtypes.size();
188
			this.builder.updateMonitor(localmonitor, 1);
189
			localmonitor.setWorkRemaining(1+(typesize != 0 ? 5 : 0));
190
			localmonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, project.getName()));
191
			this.builder.updateMonitor(localmonitor, 0);
192
			if (typesize != 0) {
193
				IPluginModelBase currentModel = this.builder.getCurrentModel();
194
				if (currentModel != null) {
195
					wsprofile = this.builder.getWorkspaceProfile();
196
					if (wsprofile == null) {
197
						if (ApiAnalysisBuilder.DEBUG) {
198
							System.err.println("Could not retrieve a workspace profile"); //$NON-NLS-1$
199
						}
200
						return;
201
					}
202
					String id = currentModel.getBundleDescription().getSymbolicName();
203
					IApiComponent comp = wsprofile.getApiComponent(id);
204
					if(comp == null) {
205
						return;
206
					}
207
					List tnames = new ArrayList(typesize),
208
						 cnames = new ArrayList(typesize);
209
					collectAllQualifiedNames(project, this.changedtypes, tnames, cnames, localmonitor.newChild(1));
210
					this.builder.updateMonitor(localmonitor, 1);
211
					this.builder.getAnalyzer().analyzeComponent(buildstate, 
212
							null, 
213
							null, 
214
							baseline, 
215
							comp, 
216
							(String[])tnames.toArray(new String[tnames.size()]), 
217
							(String[])cnames.toArray(new String[cnames.size()]), 
218
							localmonitor.newChild(1));
219
					this.builder.updateMonitor(localmonitor, 1);
220
					this.builder.createMarkers();
221
					this.builder.updateMonitor(localmonitor, 1);
222
				}
223
			}
224
		}
225
		finally {
226
			if(wsprofile != null) {
227
				wsprofile.close();
228
			}
229
			if(monitor != null) {
230
				monitor.done();
231
			}
232
		}
233
	}
234
	
235
	/**
236
	 * Collects the complete set of affected source files from the current project context based on the current JDT build state.
237
	 * 
238
	 * @param project
239
	 * @param state
240
	 * @param typesToCheck
241
	 */
242
	private void collectAffectedSourceFiles(final IProject project, State state, Set typesToCheck) {
243
		// the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
244
		char[][][] internedQualifiedNames = ReferenceCollection.internQualifiedNames(this.packages);
245
		// if a well known qualified name was found then we can skip over these
246
		if (internedQualifiedNames.length < this.packages.elementSize) {
247
			internedQualifiedNames = null;
248
		}
249
		char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(this.typenames, true);
250
		// if a well known name was found then we can skip over these
251
		if (internedSimpleNames.length < this.typenames.elementSize) {
252
			internedSimpleNames = null;
253
		}
254
		Object[] keyTable = state.getReferences().keyTable;
255
		Object[] valueTable = state.getReferences().valueTable;
256
		IFile file = null;
257
		String typeLocator = null;
258
		next : for (int i = 0, l = valueTable.length; i < l; i++) {
259
			typeLocator =  (String) keyTable[i];
260
			if (typeLocator != null) {
261
				ReferenceCollection refs = (ReferenceCollection) valueTable[i];
262
				if (refs.includes(internedQualifiedNames, internedSimpleNames, null)) {
263
					file = project.getFile(typeLocator);
264
					if (file == null) {
265
						continue next;
266
					}
267
					if (ApiAnalysisBuilder.DEBUG) {
268
						System.out.println("  adding affected source file " + typeLocator); //$NON-NLS-1$
269
					}
270
					typesToCheck.add(file);
271
				}
272
			}
273
		}
274
	}
275
	
276
	/**
277
	 * Finds affected source files for a resource that has changed that either contains class files or is itself a class file
278
	 * @param binaryDelta
279
	 */
280
	private void findAffectedSourceFiles(IResourceDelta binaryDelta) {
281
		IResource resource = binaryDelta.getResource();
282
		if(resource.getType() == IResource.FILE) {
283
			if (Util.isClassFile(resource.getName())) {
284
				switch (binaryDelta.getKind()) {
285
					case IResourceDelta.REMOVED :
286
						this.addremovedeltas.add(binaryDelta);
287
						//$FALL-THROUGH$
288
					case IResourceDelta.ADDED : {
289
						IPath typePath = resolveJavaPathFromResource(resource);
290
						if(typePath == null) {
291
							return;
292
						}
293
						if (ApiAnalysisBuilder.DEBUG) {
294
							System.out.println("Found added/removed class file " + typePath); //$NON-NLS-1$
295
						}
296
						addDependentsOf(typePath);
297
						return;
298
					}
299
					case IResourceDelta.CHANGED : {
300
						if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0) {
301
							return; // skip it since it really isn't changed
302
						}
303
						IPath typePath = resolveJavaPathFromResource(resource);
304
						if(typePath == null) {
305
							return;
306
						}
307
						if (ApiAnalysisBuilder.DEBUG) {
308
							System.out.println("Found changed class file " + typePath); //$NON-NLS-1$
309
						}
310
						addDependentsOf(typePath);
311
					}
312
				}
313
				return;
314
			}
315
		}
316
	}
317
	
318
	/**
319
	 * Adds a type to search for dependents of in considered projects for an incremental build
320
	 * 
321
	 * @param path
322
	 */
323
	void addDependentsOf(IPath path) {
324
		// the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
325
		path = path.setDevice(null);
326
		String packageName = path.removeLastSegments(1).toString();
327
		String typeName = path.lastSegment();
328
		int memberIndex = typeName.indexOf('$');
329
		if (memberIndex > 0) {
330
			typeName = typeName.substring(0, memberIndex);
331
		}
332
		if (this.typenames.add(typeName) && this.packages.add(packageName) && ApiAnalysisBuilder.DEBUG) {
333
			System.out.println("  will look for dependents of " + typeName + " in " + packageName); //$NON-NLS-1$ //$NON-NLS-2$
334
		}
335
	}
336
	
337
	/**
338
	 * Returns an array of type names, and cleans up markers for the specified resource
339
	 * @param alltypes the listing of {@link IFile}s to get qualified names from
340
	 * @param changedtypes the listing of {@link IFile}s that have actually changed (from the {@link IResourceDelta}
341
	 * @param tnames the list to collect all type names into (including inner member names)
342
	 * @param cnames the list to collect the changed type names into
343
	 * @param monitor
344
	 */
345
	private void collectAllQualifiedNames(final IProject project, final HashSet alltypes, List tnames, List cnames, final IProgressMonitor monitor) {
346
		IType[] types = null;
347
		IFile file = null;
348
		for (Iterator iterator = alltypes.iterator(); iterator.hasNext(); ) {
349
			file = (IFile) iterator.next();
350
			ICompilationUnit unit = (ICompilationUnit) JavaCore.create(file);
351
			if(!unit.exists()) {
352
				continue;
353
			}
354
			IType type = unit.findPrimaryType();
355
			if(type == null) {
356
				continue;
357
			}
358
			this.builder.updateMonitor(monitor, 0);
359
			this.builder.cleanupUnsupportedTagMarkers(file);
360
			this.builder.updateMonitor(monitor, 0);
361
			this.builder.cleanupCompatibilityMarkers(file);
362
			this.builder.updateMonitor(monitor, 0);
363
			cnames.add(type.getFullyQualifiedName());
364
			try {
365
				this.builder.cleanupUsageMarkers(file);
366
				this.builder.updateMonitor(monitor, 0);
367
				types = unit.getAllTypes();
368
				String tname = null;
369
				for (int i = 0; i < types.length; i++) {
370
					IType type2 = types[i];
371
					if (type2.isMember()) {
372
						tname = type2.getFullyQualifiedName('$');
373
					} else {
374
						tname = type2.getFullyQualifiedName();
375
					}
376
					tnames.add(tname);
377
				}
378
			} catch (JavaModelException e) {
379
				ApiPlugin.log(e.getStatus());
380
			}
381
			this.builder.updateMonitor(monitor, 0);
382
		}
383
		// inject removed types inside changed type names so that we can properly detect type removal
384
		IResourceDelta delta = null;
385
		for (Iterator iterator = this.addremovedeltas.iterator(); iterator.hasNext(); ) {
386
			delta = (IResourceDelta) iterator.next();
387
			if (delta.getKind() != IResourceDelta.REMOVED) {
388
				continue;
389
			}
390
			IResource resource = delta.getResource();
391
			IPath typePath = resolveJavaPathFromResource(resource);
392
			if(typePath == null) {
393
				continue;
394
			}
395
			// record removed type names (package + type)
396
			StringBuffer buffer = new StringBuffer();
397
			String[] segments = typePath.segments();
398
			for (int i = 0, max = segments.length; i < max; i++) {
399
				if (i > 0) {
400
					buffer.append('.');
401
				}
402
				buffer.append(segments[i]);
403
			}
404
			cnames.add(String.valueOf(buffer));
405
		}
406
		// clean up markers on added deltas
407
		if (!this.addremovedeltas.isEmpty()) {
408
			IResource manifestFile = Util.getManifestFile(project);
409
			if (manifestFile != null) {
410
				try {
411
					IMarker[] markers = manifestFile.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE);
412
					for (int i = 0, max = markers.length; i < max; i++) {
413
						IMarker marker = markers[i];
414
						String typeName = marker.getAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, null);
415
						if (typeName != null) {
416
							for (Iterator iterator = this.addremovedeltas.iterator(); iterator.hasNext(); ) {
417
								delta = (IResourceDelta) iterator.next();
418
								if (delta.getKind() != IResourceDelta.ADDED) {
419
									continue;
420
								}
421
								ICompilationUnit unit = (ICompilationUnit) JavaCore.create(delta.getResource());
422
								if(!unit.exists()) {
423
									continue;
424
								}
425
								IType type = unit.findPrimaryType();
426
								if(type == null) {
427
									continue;
428
								}
429
								if (typeName.equals(type.getFullyQualifiedName())) {
430
									marker.delete();
431
									return;
432
								} else {
433
									// check secondary types
434
									try {
435
										types = unit.getAllTypes();
436
										for (int j = 0; j < types.length; j++) {
437
											IType type2 = types[i];
438
											String fullyQualifiedName = null;
439
											if (type2.isMember()) {
440
												fullyQualifiedName = type2.getFullyQualifiedName('$');
441
											} else {
442
												fullyQualifiedName = type2.getFullyQualifiedName();
443
											}
444
											if (typeName.equals(fullyQualifiedName)) {
445
												marker.delete();
446
												return;
447
											}
448
										}
449
									} catch (JavaModelException e) {
450
										ApiPlugin.log(e.getStatus());
451
									}
452
								}
453
							}
454
						}
455
					}
456
				} catch (CoreException e) {
457
					ApiPlugin.log(e.getStatus());
458
				}
459
			}
460
		}
461
		IResource resource = project.findMember(ApiAnalysisBuilder.MANIFEST_PATH);
462
		if (resource != null) {
463
			try {
464
				IMarker[] markers = resource.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
465
				loop: for (int i = 0, max = markers.length; i < max; i++) {
466
					IMarker marker = markers[i];
467
					String typeNameFromMarker = Util.getTypeNameFromMarker(marker);
468
					for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) {
469
						String typeName = (String) iterator.next();
470
						if (typeName.equals(typeNameFromMarker)) {
471
							marker.delete();
472
							continue loop;
473
						}
474
					}
475
				}
476
				markers = resource.findMarkers(IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
477
				loop: for (int i = 0, max = markers.length; i < max; i++) {
478
					IMarker marker = markers[i];
479
					String typeNameFromMarker = Util.getTypeNameFromMarker(marker);
480
					for (Iterator iterator = tnames.iterator(); iterator.hasNext(); ) {
481
						String typeName = (String) iterator.next();
482
						if (typeName.equals(typeNameFromMarker)) {
483
							marker.delete();
484
							continue loop;
485
						}
486
					}
487
				}
488
			} catch (CoreException e) {
489
				ApiPlugin.log(e);
490
			}
491
		}
492
	}
493
	
494
	/**
495
	 * Resolves the java path from the given resource
496
	 * @param resource
497
	 * @return the resolved path or <code>null</code> if the resource is not part of the java model
498
	 */
499
	IPath resolveJavaPathFromResource(IResource resource) {
500
		IJavaElement element = JavaCore.create(resource);
501
		if(element != null) {
502
			switch(element.getElementType()) {
503
				case IJavaElement.CLASS_FILE: {
504
					org.eclipse.jdt.core.IClassFile classfile = (org.eclipse.jdt.core.IClassFile) element;
505
					IType type = classfile.getType();
506
					HashSet paths = this.builder.getProjectOutputPaths(resource.getProject());
507
					if(paths == null) {
508
						return null;
509
					}
510
					IPath prefix = null;
511
					for(Iterator iter = paths.iterator(); iter.hasNext();) {
512
						prefix = (IPath) iter.next();
513
						if(prefix.isPrefixOf(type.getPath())) {
514
							return type.getPath().removeFirstSegments(prefix.segmentCount()).removeFileExtension();
515
						}
516
					}
517
					break;
518
				}
519
			}
520
		}
521
		return null;
522
	}
523
}

Return to bug 233643