1 | /******************************************************************************* |
2 | * Copyright (c) 2007, 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.Date; |
15 | import java.util.HashMap; |
16 | import java.util.HashSet; |
17 | import java.util.Map; |
18 | import java.util.Set; |
19 | import java.util.jar.JarFile; |
20 | |
21 | import org.eclipse.core.resources.IMarker; |
22 | import org.eclipse.core.resources.IProject; |
23 | import org.eclipse.core.resources.IResource; |
24 | import org.eclipse.core.resources.IResourceDelta; |
25 | import org.eclipse.core.resources.IWorkspaceRoot; |
26 | import org.eclipse.core.resources.IncrementalProjectBuilder; |
27 | import org.eclipse.core.resources.ResourcesPlugin; |
28 | import org.eclipse.core.runtime.CoreException; |
29 | import org.eclipse.core.runtime.IPath; |
30 | import org.eclipse.core.runtime.IProgressMonitor; |
31 | import org.eclipse.core.runtime.IStatus; |
32 | import org.eclipse.core.runtime.OperationCanceledException; |
33 | import org.eclipse.core.runtime.Path; |
34 | import org.eclipse.core.runtime.SubMonitor; |
35 | import org.eclipse.jdt.core.IClasspathAttribute; |
36 | import org.eclipse.jdt.core.IClasspathEntry; |
37 | import org.eclipse.jdt.core.IJavaProject; |
38 | import org.eclipse.jdt.core.IPackageFragmentRoot; |
39 | import org.eclipse.jdt.core.JavaCore; |
40 | import org.eclipse.jdt.core.JavaModelException; |
41 | import org.eclipse.jdt.internal.core.JavaModelManager; |
42 | import org.eclipse.jdt.internal.core.builder.State; |
43 | import org.eclipse.osgi.service.resolver.BundleDescription; |
44 | import org.eclipse.osgi.util.NLS; |
45 | import org.eclipse.pde.api.tools.internal.ApiDescriptionManager; |
46 | import org.eclipse.pde.api.tools.internal.IApiCoreConstants; |
47 | import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory; |
48 | import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin; |
49 | import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants; |
50 | import org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer; |
51 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline; |
52 | import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent; |
53 | import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem; |
54 | import org.eclipse.pde.api.tools.internal.util.Util; |
55 | import org.eclipse.pde.core.plugin.IPluginModelBase; |
56 | import org.eclipse.pde.core.plugin.PluginRegistry; |
57 | |
58 | import com.ibm.icu.text.MessageFormat; |
59 | |
60 | /** |
61 | * Builder for creating API tooling resource markers |
62 | * @since 1.0.0 |
63 | */ |
64 | public class ApiAnalysisBuilder extends IncrementalProjectBuilder { |
65 | /** |
66 | * Constant used for controlling tracing in the API tool builder |
67 | */ |
68 | static boolean DEBUG = Util.DEBUG; |
69 | |
70 | /** |
71 | * Project relative path to the .settings folder |
72 | * @since 1.0.1 |
73 | */ |
74 | static final IPath SETTINGS_PATH = new Path(".settings"); //$NON-NLS-1$ |
75 | |
76 | /** |
77 | * Project relative path to the manifest file. |
78 | */ |
79 | static final IPath MANIFEST_PATH = new Path(JarFile.MANIFEST_NAME); |
80 | |
81 | /** |
82 | * Project relative path to the .api_filters file |
83 | */ |
84 | static final IPath FILTER_PATH = SETTINGS_PATH.append(IApiCoreConstants.API_FILTERS_XML_NAME); |
85 | |
86 | /** |
87 | * Empty listing of projects to be returned by the builder if there is nothing to do |
88 | */ |
89 | static final IProject[] NO_PROJECTS = new IProject[0]; |
90 | |
91 | /** |
92 | * Constant representing the name of the 'source' attribute on API tooling markers. |
93 | * Value is <code>Api Tooling</code> |
94 | */ |
95 | static final String SOURCE = "Api Tooling"; //$NON-NLS-1$ |
96 | |
97 | /** |
98 | * Method used for initializing tracing in the API tool builder |
99 | */ |
100 | public static void setDebug(boolean debugValue) { |
101 | DEBUG = debugValue || Util.DEBUG; |
102 | } |
103 | |
104 | /** |
105 | * The current project for which this builder was defined |
106 | */ |
107 | private IProject currentproject = null; |
108 | |
109 | /** |
110 | * The API analyzer for this builder |
111 | */ |
112 | private IApiAnalyzer analyzer = null; |
113 | |
114 | /** |
115 | * Maps prerequisite projects to their output location(s) |
116 | */ |
117 | HashMap output_locs = new HashMap(); |
118 | |
119 | /** |
120 | * Maps pre-requisite projects to their source locations |
121 | */ |
122 | HashMap src_locs = new HashMap(); |
123 | |
124 | /** |
125 | * Current build state |
126 | */ |
127 | private BuildState buildstate = null; |
128 | |
129 | /** |
130 | * Cleans up markers associated with API tooling on the given resource. |
131 | * |
132 | * @param resource |
133 | */ |
134 | void cleanupMarkers(IResource resource) { |
135 | cleanUnusedFilterMarkers(resource); |
136 | cleanupUsageMarkers(resource); |
137 | cleanupCompatibilityMarkers(resource); |
138 | cleanupUnsupportedTagMarkers(resource); |
139 | } |
140 | |
141 | /** |
142 | * Cleans up unsupported Javadoc tag markers on the specified resource |
143 | * @param resource |
144 | */ |
145 | void cleanupUnsupportedTagMarkers(IResource resource) { |
146 | try { |
147 | if(DEBUG) { |
148 | System.out.println("cleaning unsupported tag problems"); //$NON-NLS-1$ |
149 | } |
150 | resource.deleteMarkers(IApiMarkerConstants.UNSUPPORTED_TAG_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
151 | } catch (CoreException e) { |
152 | ApiPlugin.log(e.getStatus()); |
153 | } |
154 | } |
155 | |
156 | /** |
157 | * Cleans up only API compatibility markers on the given {@link IResource} |
158 | * @param resource the given resource |
159 | */ |
160 | void cleanupCompatibilityMarkers(IResource resource) { |
161 | try { |
162 | if (resource != null && resource.isAccessible()) { |
163 | resource.deleteMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
164 | resource.deleteMarkers(IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
165 | if (resource.getType() == IResource.PROJECT) { |
166 | // on full builds |
167 | resource.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
168 | resource.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); |
169 | resource.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, true, IResource.DEPTH_ZERO); |
170 | } |
171 | } |
172 | } catch(CoreException e) { |
173 | ApiPlugin.log(e.getStatus()); |
174 | } |
175 | } |
176 | |
177 | /** |
178 | * cleans up only API usage markers from the given {@link IResource} |
179 | * @param resource |
180 | */ |
181 | void cleanupUsageMarkers(IResource resource) { |
182 | try { |
183 | if (resource != null && resource.isAccessible()) { |
184 | resource.deleteMarkers(IApiMarkerConstants.API_USAGE_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
185 | } |
186 | } catch(CoreException e) { |
187 | ApiPlugin.log(e.getStatus()); |
188 | } |
189 | } |
190 | |
191 | /** |
192 | * Cleans up the unused API filter problems from the given resource |
193 | * @param resource |
194 | */ |
195 | void cleanUnusedFilterMarkers(IResource resource) { |
196 | try { |
197 | if(resource != null && resource.isAccessible()) { |
198 | resource.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
199 | } |
200 | } |
201 | catch(CoreException ce) { |
202 | ApiPlugin.log(ce.getStatus()); |
203 | } |
204 | } |
205 | |
206 | /* (non-Javadoc) |
207 | * @see org.eclipse.core.resources.IncrementalProjectBuilder#build(int, java.util.Map, org.eclipse.core.runtime.IProgressMonitor) |
208 | */ |
209 | protected IProject[] build(int kind, Map args, IProgressMonitor monitor) throws CoreException { |
210 | this.currentproject = getProject(); |
211 | if (!this.currentproject.isAccessible() || !this.currentproject.hasNature(ApiPlugin.NATURE_ID) || hasBeenBuilt(this.currentproject)) { |
212 | return NO_PROJECTS; |
213 | } |
214 | if (DEBUG) { |
215 | System.out.println("\nApiAnalysis builder - Starting build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ |
216 | } |
217 | SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_builder, 8); |
218 | IApiBaseline wbaseline = ApiPlugin.getDefault().getApiBaselineManager().getWorkspaceBaseline(); |
219 | if (wbaseline == null) { |
220 | if (DEBUG) { |
221 | System.err.println("Could not retrieve a workspace profile"); //$NON-NLS-1$ |
222 | } |
223 | return NO_PROJECTS; |
224 | } |
225 | final IProject[] projects = getRequiredProjects(true); |
226 | IApiBaseline baseline = ApiPlugin.getDefault().getApiBaselineManager().getDefaultApiBaseline(); |
227 | try { |
228 | switch(kind) { |
229 | case FULL_BUILD : { |
230 | if (DEBUG) { |
231 | System.out.println("Performing full build as requested by user"); //$NON-NLS-1$ |
232 | } |
233 | buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
234 | break; |
235 | } |
236 | case AUTO_BUILD : |
237 | case INCREMENTAL_BUILD : { |
238 | this.buildstate = BuildState.getLastBuiltState(currentproject); |
239 | if (this.buildstate == null) { |
240 | buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
241 | break; |
242 | } |
243 | else if(worthDoingFullBuild(projects)) { |
244 | buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
245 | break; |
246 | } |
247 | else { |
248 | IResourceDelta[] deltas = getDeltas(projects); |
249 | if(deltas.length < 1) { |
250 | buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
251 | } |
252 | else { |
253 | IResourceDelta manifest = null; |
254 | IResourceDelta filters = null; |
255 | boolean filterbuild = false; |
256 | for (int i = 0; i < deltas.length; i++) { |
257 | manifest = deltas[i].findMember(MANIFEST_PATH); |
258 | if(manifest != null) { |
259 | break; |
260 | } |
261 | filters = deltas[i].findMember(FILTER_PATH); |
262 | if(filters != null){ |
263 | switch(filters.getKind()) { |
264 | case IResourceDelta.ADDED: |
265 | case IResourceDelta.REMOVED: { |
266 | filterbuild = true; |
267 | break; |
268 | } |
269 | case IResourceDelta.CHANGED: { |
270 | filterbuild = (filters.getFlags() & IResourceDelta.REPLACED) > 0; |
271 | break; |
272 | } |
273 | } |
274 | if(filterbuild) { |
275 | break; |
276 | } |
277 | } |
278 | } |
279 | if (manifest != null || filterbuild) { |
280 | if (DEBUG) { |
281 | System.out.println("Performing full build since MANIFEST.MF or .api_filters was modified"); //$NON-NLS-1$ |
282 | } |
283 | buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
284 | } |
285 | else { |
286 | State state = (State)JavaModelManager.getJavaModelManager().getLastBuiltState(this.currentproject, localMonitor.newChild(1)); |
287 | if(state == null) { |
288 | buildAll(baseline, wbaseline, localMonitor.newChild(1)); |
289 | break; |
290 | } |
291 | BuildState.setLastBuiltState(this.currentproject, null); |
292 | IncrementalApiBuilder builder = new IncrementalApiBuilder(this); |
293 | builder.build(baseline, wbaseline, deltas, state, this.buildstate, localMonitor.newChild(1)); |
294 | } |
295 | } |
296 | } |
297 | } |
298 | } |
299 | Util.updateMonitor(localMonitor, 0); |
300 | |
301 | } |
302 | catch (OperationCanceledException oce) { |
303 | //do nothing, just end and clean up |
304 | } |
305 | catch(CoreException e) { |
306 | IStatus status = e.getStatus(); |
307 | if (status == null || status.getCode() != ApiPlugin.REPORT_BASELINE_IS_DISPOSED) { |
308 | throw e; |
309 | } |
310 | ApiPlugin.log(e); |
311 | } finally { |
312 | Util.updateMonitor(localMonitor, 0); |
313 | if(this.analyzer != null) { |
314 | this.analyzer.dispose(); |
315 | this.analyzer = null; |
316 | } |
317 | if(projects.length < 1) { |
318 | //if this build cycle indicates that more projects need to be built do not close |
319 | //the baselines yet, they might be re-read by another build cycle |
320 | if(baseline != null) { |
321 | baseline.close(); |
322 | } |
323 | } |
324 | Util.updateMonitor(localMonitor, 0); |
325 | if (this.buildstate != null) { |
326 | for(int i = 0, max = projects.length; i < max; i++) { |
327 | IProject project = projects[i]; |
328 | if (Util.isApiProject(project)) { |
329 | this.buildstate.addApiToolingDependentProject(project.getName()); |
330 | } |
331 | } |
332 | BuildState.saveBuiltState(this.currentproject, this.buildstate); |
333 | this.buildstate = null; |
334 | Util.updateMonitor(monitor, 0); |
335 | } |
336 | if(localMonitor != null) { |
337 | localMonitor.done(); |
338 | } |
339 | } |
340 | if (DEBUG) { |
341 | System.out.println("Finished build of " + this.currentproject.getName() + " @ " + new Date(System.currentTimeMillis())); //$NON-NLS-1$ //$NON-NLS-2$ |
342 | } |
343 | return projects; |
344 | } |
345 | |
346 | /** |
347 | * if its worth doing a full build considering the given set if projects |
348 | * @param projects projects to check the build state for |
349 | * @return true if a full build should take place, false otherwise |
350 | */ |
351 | boolean worthDoingFullBuild(IProject[] projects) { |
352 | Set apiToolingDependentProjects = this.buildstate.getApiToolingDependentProjects(); |
353 | for (int i = 0, max = projects.length; i < max; i++) { |
354 | IProject currentProject = projects[i]; |
355 | if (Util.isApiProject(currentProject)) { |
356 | if (apiToolingDependentProjects.contains(currentProject.getName())) { |
357 | continue; |
358 | } |
359 | return true; |
360 | } else if (apiToolingDependentProjects.contains(currentProject.getName())) { |
361 | return true; |
362 | } |
363 | } |
364 | return false; |
365 | } |
366 | |
367 | /** |
368 | * Performs a full build for the project |
369 | * @param baseline the default baseline |
370 | * @param wbaseline the workspace baseline |
371 | * @param monitor |
372 | */ |
373 | void buildAll(IApiBaseline baseline, IApiBaseline wbaseline, IProgressMonitor monitor) throws CoreException { |
374 | SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 4); |
375 | try { |
376 | BuildState.setLastBuiltState(this.currentproject, null); |
377 | this.buildstate = new BuildState(); |
378 | localMonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_initializing_analyzer, currentproject.getName())); |
379 | cleanupMarkers(this.currentproject); |
380 | IPluginModelBase currentModel = getCurrentModel(); |
381 | if (currentModel != null) { |
382 | localMonitor.subTask(BuilderMessages.building_workspace_profile); |
383 | Util.updateMonitor(localMonitor, 1); |
384 | String id = currentModel.getBundleDescription().getSymbolicName(); |
385 | // Compatibility checks |
386 | IApiComponent apiComponent = wbaseline.getApiComponent(id); |
387 | if(apiComponent != null) { |
388 | getAnalyzer().analyzeComponent(this.buildstate, null, null, baseline, apiComponent, new BuildContext(), localMonitor.newChild(1)); |
389 | Util.updateMonitor(localMonitor, 1); |
390 | createMarkers(); |
391 | Util.updateMonitor(localMonitor, 1); |
392 | } |
393 | } |
394 | } |
395 | finally { |
396 | if(localMonitor != null) { |
397 | localMonitor.done(); |
398 | } |
399 | } |
400 | } |
401 | |
402 | /** |
403 | * Creates new markers are for the listing of problems added to this reporter. |
404 | * If no problems have been added to this reporter, or we are not running in the framework, |
405 | * no work is done. |
406 | */ |
407 | protected void createMarkers() { |
408 | try { |
409 | IResource manifest = Util.getManifestFile(this.currentproject); |
410 | if(manifest != null) { |
411 | manifest.deleteMarkers(IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); |
412 | } |
413 | this.currentproject.deleteMarkers(IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); |
414 | this.currentproject.deleteMarkers(IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER, false, IResource.DEPTH_ZERO); |
415 | } catch (CoreException e) { |
416 | ApiPlugin.log(e); |
417 | } |
418 | IApiProblem[] problems = getAnalyzer().getProblems(); |
419 | String type = null; |
420 | for(int i = 0; i < problems.length; i++) { |
421 | int category = problems[i].getCategory(); |
422 | type = getProblemTypeFromCategory(category, problems[i].getKind()); |
423 | if(type == null) { |
424 | continue; |
425 | } |
426 | if(DEBUG) { |
427 | System.out.println("creating marker for: "+problems[i].toString()); //$NON-NLS-1$ |
428 | } |
429 | createMarkerForProblem(category, type, problems[i]); |
430 | } |
431 | } |
432 | |
433 | /** |
434 | * Returns the {@link IApiMarkerConstants} problem type given the |
435 | * problem category |
436 | * @param category the problem category - see {@link IApiProblem} for problem categories |
437 | * @param kind the kind of the problem - see {@link IApiProblem} for problem kinds |
438 | * @return the problem type or <code>null</code> |
439 | */ |
440 | String getProblemTypeFromCategory(int category, int kind) { |
441 | switch(category) { |
442 | case IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION : { |
443 | return IApiMarkerConstants.API_COMPONENT_RESOLUTION_PROBLEM_MARKER; |
444 | } |
445 | case IApiProblem.CATEGORY_API_BASELINE: { |
446 | return IApiMarkerConstants.DEFAULT_API_BASELINE_PROBLEM_MARKER; |
447 | } |
448 | case IApiProblem.CATEGORY_COMPATIBILITY: { |
449 | return IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER; |
450 | } |
451 | case IApiProblem.CATEGORY_SINCETAGS: { |
452 | return IApiMarkerConstants.SINCE_TAGS_PROBLEM_MARKER; |
453 | } |
454 | case IApiProblem.CATEGORY_USAGE: { |
455 | if(kind == IApiProblem.UNSUPPORTED_TAG_USE) { |
456 | return IApiMarkerConstants.UNSUPPORTED_TAG_PROBLEM_MARKER; |
457 | } |
458 | if(kind == IApiProblem.UNUSED_PROBLEM_FILTERS) { |
459 | return IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER; |
460 | } |
461 | return IApiMarkerConstants.API_USAGE_PROBLEM_MARKER; |
462 | } |
463 | case IApiProblem.CATEGORY_VERSION: { |
464 | return IApiMarkerConstants.VERSION_NUMBERING_PROBLEM_MARKER; |
465 | } |
466 | } |
467 | return null; |
468 | } |
469 | |
470 | /** |
471 | * Creates an {@link IMarker} on the resource specified |
472 | * in the problem (via its path) with the given problem |
473 | * attributes |
474 | * @param category the category of the problem - see {@link IApiProblem} for categories |
475 | * @param type the marker type to create - see {@link IApiMarkerConstants} for types |
476 | * @param problem the problem to create a marker from |
477 | */ |
478 | void createMarkerForProblem(int category, String type, IApiProblem problem) { |
479 | IResource resource = resolveResource(problem); |
480 | if(resource == null) { |
481 | return; |
482 | } |
483 | try { |
484 | IMarker marker = resource.createMarker(type); |
485 | int line = problem.getLineNumber(); |
486 | switch(category) { |
487 | case IApiProblem.CATEGORY_VERSION : |
488 | case IApiProblem.CATEGORY_API_BASELINE : |
489 | case IApiProblem.CATEGORY_API_COMPONENT_RESOLUTION : { |
490 | break; |
491 | } |
492 | default : { |
493 | line++; |
494 | } |
495 | } |
496 | marker.setAttributes( |
497 | new String[] { |
498 | IMarker.MESSAGE, |
499 | IMarker.SEVERITY, |
500 | IMarker.LINE_NUMBER, |
501 | IMarker.CHAR_START, |
502 | IMarker.CHAR_END, |
503 | IMarker.SOURCE_ID, |
504 | IApiMarkerConstants.MARKER_ATTR_PROBLEM_ID}, |
505 | new Object[] { |
506 | problem.getMessage(), |
507 | new Integer(ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), this.currentproject)), |
508 | new Integer(line), |
509 | new Integer(problem.getCharStart()), |
510 | new Integer(problem.getCharEnd()), |
511 | ApiAnalysisBuilder.SOURCE, |
512 | new Integer(problem.getId()) |
513 | } |
514 | ); |
515 | //add message arguments, if any |
516 | String[] args = problem.getMessageArguments(); |
517 | if(args.length > 0) { |
518 | marker.setAttribute(IApiMarkerConstants.MARKER_ATTR_MESSAGE_ARGUMENTS, createArgAttribute(args)); |
519 | } |
520 | String typeName = problem.getTypeName(); |
521 | if (typeName != null) { |
522 | marker.setAttribute(IApiMarkerConstants.MARKER_ATTR_PROBLEM_TYPE_NAME, typeName); |
523 | } |
524 | //add all other extra arguments, if any |
525 | if(problem.getExtraMarkerAttributeIds().length > 0) { |
526 | marker.setAttributes(problem.getExtraMarkerAttributeIds(), problem.getExtraMarkerAttributeValues()); |
527 | } |
528 | } catch (CoreException e) { |
529 | //ignore and continue |
530 | return; |
531 | } |
532 | } |
533 | |
534 | /** |
535 | * Resolves the resource from the path in the problem, returns <code>null</code> in |
536 | * the following cases: |
537 | * <ul> |
538 | * <li>The resource is not found in the parent project (findMember() returns null)</li> |
539 | * <li>The resource is not accessible (isAccessible() returns false</li> |
540 | * </ul> |
541 | * @param problem the problem to get the resource for |
542 | * @return the resource or <code>null</code> |
543 | */ |
544 | IResource resolveResource(IApiProblem problem) { |
545 | String resourcePath = problem.getResourcePath(); |
546 | if (resourcePath == null) { |
547 | return null; |
548 | } |
549 | IResource resource = currentproject.findMember(new Path(resourcePath)); |
550 | if(resource == null) { |
551 | return null; |
552 | } |
553 | if(!resource.isAccessible()) { |
554 | return null; |
555 | } |
556 | return resource; |
557 | } |
558 | |
559 | /** |
560 | * Creates a single string attribute from an array of strings. Uses the '#' char as |
561 | * a delimiter |
562 | * @param args |
563 | * @return a single string attribute from an array or arguments |
564 | */ |
565 | String createArgAttribute(String[] args) { |
566 | StringBuffer buff = new StringBuffer(); |
567 | for(int i = 0; i < args.length; i++) { |
568 | buff.append(args[i]); |
569 | if(i < args.length-1) { |
570 | buff.append("#"); //$NON-NLS-1$ |
571 | } |
572 | } |
573 | return buff.toString(); |
574 | } |
575 | |
576 | /* (non-Javadoc) |
577 | * @see org.eclipse.core.resources.IncrementalProjectBuilder#clean(org.eclipse.core.runtime.IProgressMonitor) |
578 | */ |
579 | protected void clean(IProgressMonitor monitor) throws CoreException { |
580 | this.currentproject = getProject(); |
581 | SubMonitor localmonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.CleaningAPIDescription, new String[] {this.currentproject.getName()}), 2); |
582 | try { |
583 | // clean up all existing markers |
584 | cleanupUsageMarkers(this.currentproject); |
585 | cleanupCompatibilityMarkers(this.currentproject); |
586 | cleanupUnsupportedTagMarkers(this.currentproject); |
587 | this.currentproject.deleteMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_INFINITE); |
588 | Util.updateMonitor(localmonitor, 1); |
589 | //clean up the .api_settings |
590 | cleanupApiDescription(this.currentproject); |
591 | Util.updateMonitor(localmonitor, 1); |
592 | } |
593 | finally { |
594 | BuildState.setLastBuiltState(this.currentproject, null); |
595 | localmonitor.done(); |
596 | } |
597 | } |
598 | |
599 | /** |
600 | * Cleans the .api_settings file for the given project |
601 | * @param project |
602 | */ |
603 | void cleanupApiDescription(IProject project) { |
604 | if(project != null && project.exists()) { |
605 | ApiDescriptionManager.getDefault().clean(JavaCore.create(project), true, false); |
606 | } |
607 | } |
608 | |
609 | /** |
610 | * @return the current {@link IPluginModelBase} based on the current project for this builder |
611 | */ |
612 | IPluginModelBase getCurrentModel() { |
613 | IPluginModelBase[] workspaceModels = PluginRegistry.getWorkspaceModels(); |
614 | IPath location = this.currentproject.getLocation(); |
615 | IPluginModelBase currentModel = null; |
616 | BundleDescription desc = null; |
617 | loop: for (int i = 0, max = workspaceModels.length; i < max; i++) { |
618 | desc = workspaceModels[i].getBundleDescription(); |
619 | if(desc != null) { |
620 | Path path = new Path(desc.getLocation()); |
621 | if (path.equals(location)) { |
622 | currentModel = workspaceModels[i]; |
623 | break loop; |
624 | } |
625 | } |
626 | else if(DEBUG) { |
627 | System.out.println("Tried to look up bundle description for: " + workspaceModels[i].toString()); //$NON-NLS-1$ |
628 | } |
629 | } |
630 | return currentModel; |
631 | } |
632 | |
633 | /** |
634 | * Returns a listing of deltas for this project and for dependent projects |
635 | * @param projects |
636 | * @return |
637 | */ |
638 | IResourceDelta[] getDeltas(IProject[] projects) { |
639 | if(DEBUG) { |
640 | System.out.println("Searching for deltas for build of project: "+this.currentproject.getName()); //$NON-NLS-1$ |
641 | } |
642 | ArrayList deltas = new ArrayList(); |
643 | IResourceDelta delta = getDelta(this.currentproject); |
644 | if(delta != null) { |
645 | if (DEBUG) { |
646 | System.out.println("Found a delta: " + delta); //$NON-NLS-1$ |
647 | } |
648 | deltas.add(delta); |
649 | } |
650 | for(int i = 0; i < projects.length; i++) { |
651 | delta = getDelta(projects[i]); |
652 | if(delta != null) { |
653 | if (DEBUG) { |
654 | System.out.println("Found a delta: " + delta); //$NON-NLS-1$ |
655 | } |
656 | deltas.add(delta); |
657 | } |
658 | } |
659 | return (IResourceDelta[]) deltas.toArray(new IResourceDelta[deltas.size()]); |
660 | } |
661 | |
662 | /** |
663 | * Returns the API analyzer to use with this instance of the builder |
664 | * @return the API analyzer to use |
665 | */ |
666 | protected synchronized IApiAnalyzer getAnalyzer() { |
667 | if(this.analyzer == null) { |
668 | this.analyzer = new BaseApiAnalyzer(); |
669 | } |
670 | return this.analyzer; |
671 | } |
672 | |
673 | /** |
674 | * Returns the complete listing of required projects from the classpath of the backing project |
675 | * @param includeBinaryPrerequisites |
676 | * @return the list of projects required |
677 | * @throws CoreException |
678 | */ |
679 | IProject[] getRequiredProjects(boolean includebinaries) throws CoreException { |
680 | IWorkspaceRoot workspaceRoot = ResourcesPlugin.getWorkspace().getRoot(); |
681 | if (this.currentproject == null || workspaceRoot == null) { |
682 | return new IProject[0]; |
683 | } |
684 | ArrayList projects = new ArrayList(); |
685 | try { |
686 | IJavaProject javaProject = JavaCore.create(this.currentproject); |
687 | HashSet blocations = new HashSet(); |
688 | blocations.add(javaProject.getOutputLocation()); |
689 | this.output_locs.put(this.currentproject, blocations); |
690 | HashSet slocations = new HashSet(); |
691 | IPackageFragmentRoot[] roots = javaProject.getPackageFragmentRoots(); |
692 | for (int i = 0; i < roots.length; i++) { |
693 | if(roots[i].isArchive()) { |
694 | continue; |
695 | } |
696 | slocations.add(roots[i].getPath()); |
697 | } |
698 | this.src_locs.put(this.currentproject, slocations); |
699 | IClasspathEntry[] entries = javaProject.getResolvedClasspath(true); |
700 | for (int i = 0, l = entries.length; i < l; i++) { |
701 | IClasspathEntry entry = entries[i]; |
702 | IPath path = entry.getPath(); |
703 | IProject p = null; |
704 | switch (entry.getEntryKind()) { |
705 | case IClasspathEntry.CPE_PROJECT : { |
706 | p = workspaceRoot.getProject(path.lastSegment()); // missing projects are considered too |
707 | if (isOptional(entry) && !p.hasNature(ApiPlugin.NATURE_ID)) {// except if entry is optional |
708 | p = null; |
709 | } |
710 | break; |
711 | } |
712 | case IClasspathEntry.CPE_LIBRARY : { |
713 | if (includebinaries && path.segmentCount() > 1) { |
714 | // some binary resources on the class path can come from projects that are not included in the project references |
715 | IResource resource = workspaceRoot.findMember(path.segment(0)); |
716 | if (resource instanceof IProject) { |
717 | p = (IProject) resource; |
718 | } |
719 | } |
720 | break; |
721 | } |
722 | case IClasspathEntry.CPE_SOURCE: { |
723 | IPath entrypath = entry.getOutputLocation(); |
724 | if(entrypath != null) { |
725 | blocations.add(entrypath); |
726 | } |
727 | } |
728 | } |
729 | if (p != null && !projects.contains(p)) { |
730 | projects.add(p); |
731 | //try to derive all of the output locations for each of the projects |
732 | javaProject = JavaCore.create(p); |
733 | HashSet bins = new HashSet(); |
734 | HashSet srcs = new HashSet(); |
735 | if(javaProject.exists()) { |
736 | bins.add(javaProject.getOutputLocation()); |
737 | IClasspathEntry[] source = javaProject.getRawClasspath(); |
738 | IPath entrypath = null; |
739 | for(int j = 0; j < source.length; j++) { |
740 | if(source[j].getEntryKind() == IClasspathEntry.CPE_SOURCE) { |
741 | srcs.add(source[j].getPath()); |
742 | entrypath = source[j].getOutputLocation(); |
743 | if(entrypath != null) { |
744 | bins.add(entrypath); |
745 | } |
746 | } |
747 | } |
748 | this.output_locs.put(p, bins); |
749 | this.src_locs.put(p, srcs); |
750 | } |
751 | } |
752 | } |
753 | } |
754 | catch(JavaModelException e) { |
755 | return new IProject[0]; |
756 | } |
757 | IProject[] result = new IProject[projects.size()]; |
758 | projects.toArray(result); |
759 | return result; |
760 | } |
761 | |
762 | /** |
763 | * Returns the output paths of the given project or <code>null</code> if none have been computed |
764 | * @param project |
765 | * @return the output paths for the given project or <code>null</code> |
766 | */ |
767 | HashSet getProjectOutputPaths(IProject project) { |
768 | return (HashSet) this.output_locs.get(project); |
769 | } |
770 | |
771 | /** |
772 | * Returns is the given classpath entry is optional or not |
773 | * @param entry |
774 | * @return true if the specified {@link IClasspathEntry} is optional, false otherwise |
775 | */ |
776 | boolean isOptional(IClasspathEntry entry) { |
777 | IClasspathAttribute[] attribs = entry.getExtraAttributes(); |
778 | for (int i = 0, length = attribs.length; i < length; i++) { |
779 | IClasspathAttribute attribute = attribs[i]; |
780 | if (IClasspathAttribute.OPTIONAL.equals(attribute.getName()) && "true".equals(attribute.getValue())) //$NON-NLS-1$ |
781 | return true; |
782 | } |
783 | return false; |
784 | } |
785 | |
786 | /* (non-Javadoc) |
787 | * @see java.lang.Object#toString() |
788 | */ |
789 | public String toString() { |
790 | return NLS.bind(BuilderMessages.ApiAnalysisBuilder_builder_for_project, this.currentproject.getName()); |
791 | } |
792 | } |