EMMA Coverage Report (generated Thu Nov 26 15:54:18 CST 2009)
[all classes][org.eclipse.pde.api.tools.internal.builder]

COVERAGE SUMMARY FOR SOURCE FILE [BaseApiAnalyzer.java]

nameclass, %method, %block, %line, %
BaseApiAnalyzer.java100% (2/2)95%  (41/43)73%  (2904/3978)73%  (741/1022)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class BaseApiAnalyzer100% (1/1)95%  (40/42)73%  (2895/3969)72%  (737/1018)
createApiComponentResolutionProblem (IApiComponent, String): void 0%   (0/1)0%   (0/37)0%   (0/9)
setDebug (boolean): void 0%   (0/1)0%   (0/9)0%   (0/2)
checkUnusedProblemFilters (IBuildContext, IApiComponent, IProgressMonitor): void 100% (1/1)26%  (31/120)32%  (9.2/29)
getBuildState (): BuildState 100% (1/1)41%  (11/27)40%  (4/10)
processDelta (IDelta, IApiComponent, IApiComponent): void 100% (1/1)47%  (107/226)61%  (36/59)
isProblemFiltered (IApiProblem): boolean 100% (1/1)58%  (43/74)56%  (14/25)
checkApiComponentVersion (IApiComponent, IApiComponent): void 100% (1/1)63%  (335/534)65%  (86.8/133)
checkCompatibility (String, IApiComponent, IApiComponent, IProgressMonitor): ... 100% (1/1)64%  (238/372)73%  (66.8/92)
analyzeComponent (BuildState, IApiFilterStore, Properties, IApiBaseline, IApi... 100% (1/1)65%  (187/289)60%  (44.1/74)
checkCompatibility (IApiComponent, IApiComponent, IProgressMonitor): void 100% (1/1)67%  (105/157)80%  (31/39)
createSinceTagProblem (int, String [], Delta, IMember, String): IApiProblem 100% (1/1)69%  (116/168)70%  (32/46)
checkApiUsage (IBuildContext, IApiComponent, IProgressMonitor): void 100% (1/1)78%  (132/170)77%  (28.6/37)
getProblems (): IApiProblem [] 100% (1/1)80%  (12/15)67%  (2/3)
collectDetails (IDelta []): String 100% (1/1)81%  (47/58)91%  (10/11)
checkSinceTags (Delta, IApiComponent): void 100% (1/1)82%  (270/328)67%  (56/83)
createVersionProblem (int, String [], String, String): IApiProblem 100% (1/1)82%  (187/227)87%  (52.8/61)
scanSource (IJavaElement, IProgressMonitor): void 100% (1/1)83%  (44/53)79%  (13.4/17)
checkTagValidation (IBuildContext, IApiComponent, IProgressMonitor): void 100% (1/1)84%  (95/113)73%  (20.5/28)
addProblem (IApiProblem): boolean 100% (1/1)85%  (11/13)67%  (2/3)
checkDefaultBaselineSet (): void 100% (1/1)85%  (34/40)86%  (12/14)
processType (String): void 100% (1/1)85%  (17/20)75%  (6/8)
getSearchScope (IApiComponent, String []): IApiTypeContainer 100% (1/1)87%  (13/15)80%  (4/5)
ignoreMajorVersionCheckWithoutBreakingChange (): boolean 100% (1/1)87%  (13/15)67%  (2/3)
ignoreMinorVersionCheckWithoutApiChange (): boolean 100% (1/1)87%  (13/15)67%  (2/3)
reportApiBreakageWhenMajorVersionIncremented (): boolean 100% (1/1)87%  (13/15)67%  (2/3)
ignoreDefaultBaselineCheck (): boolean 100% (1/1)88%  (15/17)67%  (2/3)
createCompatibilityProblem (IDelta, IApiComponent, IApiComponent): IApiProblem 100% (1/1)90%  (147/164)83%  (43/52)
createUnusedApiFilterProblems (IApiProblemFilter []): void 100% (1/1)92%  (142/154)83%  (39/47)
createAST (ITypeRoot, int): CompilationUnit 100% (1/1)95%  (35/37)90%  (9/10)
ignoreSinceTagCheck (String): boolean 100% (1/1)96%  (53/55)98%  (9.8/10)
getScopedElements (String []): IReferenceTypeDescriptor [] 100% (1/1)97%  (32/33)83%  (5/6)
processType (ICompilationUnit): void 100% (1/1)97%  (33/34)89%  (8/9)
checkBundleVersionsOfReexportedBundles (IApiComponent, IApiComponent): BaseAp... 100% (1/1)99%  (121/122)100% (34.9/35)
<static initializer> 100% (1/1)100% (3/3)100% (2/2)
BaseApiAnalyzer (): void 100% (1/1)100% (27/27)100% (8/8)
dispose (): void 100% (1/1)100% (25/25)100% (9/9)
extractVersion (String): String 100% (1/1)100% (14/14)100% (2/2)
getJavaProject (IApiComponent): IJavaProject 100% (1/1)100% (11/11)100% (4/4)
ignoreApiUsageScan (): boolean 100% (1/1)100% (114/114)100% (15/15)
ignoreComponentVersionCheck (): boolean 100% (1/1)100% (17/17)100% (3/3)
ignoreInvalidTagCheck (): boolean 100% (1/1)100% (16/16)100% (3/3)
ignoreUnusedProblemFilterCheck (): boolean 100% (1/1)100% (16/16)100% (3/3)
     
class BaseApiAnalyzer$ReexportedBundleVersionInfo100% (1/1)100% (1/1)100% (9/9)100% (4/4)
BaseApiAnalyzer$ReexportedBundleVersionInfo (String, int): void 100% (1/1)100% (9/9)100% (4/4)

1/*******************************************************************************
2 * Copyright (c) 2008, 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 *******************************************************************************/
11package org.eclipse.pde.api.tools.internal.builder;
12 
13import java.io.BufferedReader;
14import java.io.IOException;
15import java.io.InputStream;
16import java.io.LineNumberReader;
17import java.io.PrintWriter;
18import java.io.StringReader;
19import java.io.StringWriter;
20import java.util.ArrayList;
21import java.util.HashSet;
22import java.util.Iterator;
23import java.util.List;
24import java.util.Map;
25import java.util.Properties;
26import java.util.jar.JarFile;
27 
28import org.eclipse.core.resources.IFile;
29import org.eclipse.core.resources.IProject;
30import org.eclipse.core.resources.IResource;
31import org.eclipse.core.runtime.CoreException;
32import org.eclipse.core.runtime.IProgressMonitor;
33import org.eclipse.core.runtime.NullProgressMonitor;
34import org.eclipse.core.runtime.Path;
35import org.eclipse.core.runtime.SubMonitor;
36import org.eclipse.jdt.core.Flags;
37import org.eclipse.jdt.core.ICompilationUnit;
38import org.eclipse.jdt.core.IJavaElement;
39import org.eclipse.jdt.core.IJavaProject;
40import org.eclipse.jdt.core.IMember;
41import org.eclipse.jdt.core.IPackageFragmentRoot;
42import org.eclipse.jdt.core.IParent;
43import org.eclipse.jdt.core.ISourceRange;
44import org.eclipse.jdt.core.IType;
45import org.eclipse.jdt.core.ITypeRoot;
46import org.eclipse.jdt.core.JavaCore;
47import org.eclipse.jdt.core.JavaModelException;
48import org.eclipse.jdt.core.compiler.CharOperation;
49import org.eclipse.jdt.core.dom.AST;
50import org.eclipse.jdt.core.dom.ASTParser;
51import org.eclipse.jdt.core.dom.CompilationUnit;
52import org.eclipse.jface.text.BadLocationException;
53import org.eclipse.jface.text.IDocument;
54import org.eclipse.osgi.service.resolver.ResolverError;
55import org.eclipse.osgi.service.resolver.VersionConstraint;
56import org.eclipse.osgi.service.resolver.VersionRange;
57import org.eclipse.osgi.util.NLS;
58import org.eclipse.pde.api.tools.internal.ApiBaselineManager;
59import org.eclipse.pde.api.tools.internal.ApiFilterStore;
60import org.eclipse.pde.api.tools.internal.IApiCoreConstants;
61import org.eclipse.pde.api.tools.internal.comparator.Delta;
62import org.eclipse.pde.api.tools.internal.model.PluginProjectApiComponent;
63import org.eclipse.pde.api.tools.internal.problems.ApiProblemFactory;
64import org.eclipse.pde.api.tools.internal.problems.ApiProblemFilter;
65import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
66import org.eclipse.pde.api.tools.internal.provisional.Factory;
67import org.eclipse.pde.api.tools.internal.provisional.IApiAnnotations;
68import org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager;
69import org.eclipse.pde.api.tools.internal.provisional.IApiDescription;
70import org.eclipse.pde.api.tools.internal.provisional.IApiFilterStore;
71import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants;
72import org.eclipse.pde.api.tools.internal.provisional.IRequiredComponentDescription;
73import org.eclipse.pde.api.tools.internal.provisional.IVersionRange;
74import org.eclipse.pde.api.tools.internal.provisional.RestrictionModifiers;
75import org.eclipse.pde.api.tools.internal.provisional.VisibilityModifiers;
76import org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer;
77import org.eclipse.pde.api.tools.internal.provisional.builder.IBuildContext;
78import org.eclipse.pde.api.tools.internal.provisional.comparator.ApiComparator;
79import org.eclipse.pde.api.tools.internal.provisional.comparator.DeltaProcessor;
80import org.eclipse.pde.api.tools.internal.provisional.comparator.IDelta;
81import org.eclipse.pde.api.tools.internal.provisional.descriptors.IElementDescriptor;
82import org.eclipse.pde.api.tools.internal.provisional.descriptors.IReferenceTypeDescriptor;
83import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
84import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
85import org.eclipse.pde.api.tools.internal.provisional.model.IApiType;
86import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeContainer;
87import org.eclipse.pde.api.tools.internal.provisional.model.IApiTypeRoot;
88import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblem;
89import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemFilter;
90import org.eclipse.pde.api.tools.internal.provisional.problems.IApiProblemTypes;
91import org.eclipse.pde.api.tools.internal.util.Signatures;
92import org.eclipse.pde.api.tools.internal.util.SinceTagVersion;
93import org.eclipse.pde.api.tools.internal.util.Util;
94import org.osgi.framework.Constants;
95import org.osgi.framework.Version;
96 
97import com.ibm.icu.text.MessageFormat;
98 
99/**
100 * Base implementation of the analyzer used in the {@link ApiAnalysisBuilder}
101 * 
102 * @since 1.0.0
103 */
104public class BaseApiAnalyzer implements IApiAnalyzer {
105        private static final String QUALIFIER = "qualifier"; //$NON-NLS-1$
106        private static class ReexportedBundleVersionInfo {
107                String componentID;
108                int kind;
109                
110                ReexportedBundleVersionInfo(String componentID, int kind) {
111                        this.componentID = componentID;
112                        this.kind = kind;
113                }
114        }
115        
116        /**
117         * Constant used for controlling tracing in the API tool builder
118         */
119        private static boolean DEBUG = Util.DEBUG;
120        
121        /**
122         * The backing list of problems found so far
123         */
124        private ArrayList fProblems = new ArrayList(25);
125        
126        /**
127         * List of pending deltas for which the @since tags should be checked
128         */
129        private List fPendingDeltaInfos = new ArrayList(3);
130                
131        /**
132         * The current build state to use
133         */
134        private BuildState fBuildState = null;
135        /**
136         * The current filter store to use
137         */
138        private IApiFilterStore fFilterStore = null;
139        /**
140         * The associated {@link IJavaProject}, if there is one
141         */
142        private IJavaProject fJavaProject = null;
143        /**
144         * The current preferences to use when the platform is not running.
145         */
146        private Properties fPreferences = null;
147        /**
148         * Method used for initializing tracing in the API tool builder
149         */
150        public static void setDebug(boolean debugValue) {
151                DEBUG = debugValue || Util.DEBUG;
152        }
153        
154        /**
155         * Constructs an API analyzer
156         */
157        public BaseApiAnalyzer() {
158        }
159        
160        /* (non-Javadoc)
161         * @see org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer#analyzeComponent(..)
162         */
163        public void analyzeComponent(
164                        final BuildState state,
165                        final IApiFilterStore filterStore,
166                        final Properties preferences,
167                        final IApiBaseline baseline,
168                        final IApiComponent component,
169                        final IBuildContext context,
170                        IProgressMonitor monitor) {
171                SubMonitor localMonitor = SubMonitor.convert(monitor, BuilderMessages.BaseApiAnalyzer_analyzing_api, 7);
172                try {
173                        fJavaProject = getJavaProject(component);
174                        this.fFilterStore = filterStore;
175                        this.fPreferences = preferences;
176                        if(!ignoreUnusedProblemFilterCheck()) {
177                                ((ApiFilterStore)component.getFilterStore()).recordFilterUsage();
178                        }
179                        ResolverError[] errors = component.getErrors();
180                        if (errors != null) {
181                                // check if all errors have a constraint
182                                StringBuffer buffer = null;
183                                for (int i = 0, max = errors.length; i < max; i++) {
184                                        ResolverError error = errors[i];
185                                        VersionConstraint constraint = error.getUnsatisfiedConstraint();
186                                        if (constraint == null) continue;
187                                        VersionRange versionRange = constraint.getVersionRange();
188                                        String minimum = versionRange == null ? BuilderMessages.undefinedRange : versionRange.getMinimum().toString();
189                                        String maximum = versionRange == null ? BuilderMessages.undefinedRange : versionRange.getMaximum().toString();
190                                        if (buffer == null) {
191                                                buffer = new StringBuffer();
192                                        }
193                                        if (i > 0) {
194                                                buffer.append(',');
195                                        }
196                                        buffer.append(
197                                                NLS.bind(
198                                                                BuilderMessages.reportUnsatisfiedConstraint,
199                                                                new String[] {
200                                                                                constraint.getName(),
201                                                                                minimum,
202                                                                                maximum
203                                                                }
204                                                ));
205                                }
206                                if (buffer != null) {
207                                        // api component has errors that should be reported
208                                        createApiComponentResolutionProblem(component, String.valueOf(buffer));
209                                        if (baseline == null) {
210                                                checkDefaultBaselineSet();
211                                        }
212                                        return;
213                                }
214                        }
215                        IBuildContext bcontext = context;
216                        if(bcontext == null) {
217                                bcontext = new BuildContext();
218                        }
219                        boolean checkfilters = false;
220                        if(baseline != null) {
221                                IApiComponent reference = baseline.getApiComponent(component.getId());
222                                this.fBuildState = state;
223                                if(fBuildState == null) {
224                                        fBuildState = getBuildState();
225                                }
226                                //compatibility checks
227                                if(reference != null) {
228                                        localMonitor.subTask(NLS.bind(BuilderMessages.BaseApiAnalyzer_comparing_api_profiles, new String[] {reference.getId(), baseline.getName()}));
229                                        if(bcontext.hasChangedTypes()) {
230                                                String[] changedtypes = bcontext.getStructurallyChangedTypes();
231                                                for(int i = 0; i < changedtypes.length; i++) {
232                                                        if(changedtypes[i] == null) {
233                                                                continue;
234                                                        }
235                                                        checkCompatibility(changedtypes[i], reference, component, localMonitor.newChild(1));
236                                                        Util.updateMonitor(localMonitor);
237                                                }
238                                        } else {
239                                                // store re-exported bundle into the build state
240                                                checkCompatibility(reference, component, localMonitor.newChild(1));
241                                                Util.updateMonitor(localMonitor);
242                                        }
243                                        this.fBuildState.setReexportedComponents(Util.getReexportedComponents(component));
244                                } else {
245                                        localMonitor.subTask(NLS.bind(BuilderMessages.BaseApiAnalyzer_comparing_api_profiles, new String[] {component.getId(), baseline.getName()}));
246                                        checkCompatibility(null, component, localMonitor.newChild(1));
247                                        Util.updateMonitor(localMonitor);
248                                }
249                                //version checks
250                                checkApiComponentVersion(reference, component);
251                                Util.updateMonitor(localMonitor);
252                                checkfilters = true;
253                        }
254                        else {
255                                //check default baseline
256                                checkDefaultBaselineSet();
257                                Util.updateMonitor(localMonitor);
258                        }
259                        //usage checks
260                        checkApiUsage(bcontext, component, localMonitor.newChild(1));
261                        Util.updateMonitor(localMonitor);
262                        //tag validation
263                        checkTagValidation(bcontext, component, localMonitor.newChild(1));
264                        Util.updateMonitor(localMonitor);
265                        if(checkfilters) {
266                                //check for unused filters only if the scans have been done
267                                checkUnusedProblemFilters(bcontext, component, localMonitor.newChild(1));
268                        }
269                        Util.updateMonitor(localMonitor);
270                } catch(CoreException e) {
271                        ApiPlugin.log(e);
272                }
273                finally {
274                        localMonitor.done();
275                }
276        }
277 
278        /**
279         * Checks for unused API problem filters
280         * @param context the current build context
281         * @param reference
282         * @param monitor
283         */
284        private void checkUnusedProblemFilters(final IBuildContext context, IApiComponent reference, IProgressMonitor monitor) {
285                if(ignoreUnusedProblemFilterCheck()) {
286                        if(DEBUG) {
287                                System.out.println("Ignoring unused problem filter check"); //$NON-NLS-1$
288                        }
289                        Util.updateMonitor(monitor, 1);
290                        return;
291                }
292                try {
293                        ApiFilterStore store = (ApiFilterStore)reference.getFilterStore();
294                        IProject project = fJavaProject.getProject(); 
295                        if(context.hasTypes()) {
296                                IResource resource = null;
297                                String[] types = context.getStructurallyChangedTypes();
298                                for (int i = 0; i < types.length; i++) {
299                                        if(types[i] == null) {
300                                                continue;
301                                        }
302                                        resource = Util.getResource(project, fJavaProject.findType(Signatures.getPrimaryTypeName(types[i])));
303                                        if(resource != null) {
304                                                createUnusedApiFilterProblems(store.getUnusedFilters(resource, types[i]));
305                                        }
306                                }
307                                types = context.getDependentTypes();
308                                for (int i = 0; i < types.length; i++) {
309                                        if(types[i] == null) {
310                                                continue;
311                                        }
312                                        resource = Util.getResource(project, fJavaProject.findType(Signatures.getPrimaryTypeName(types[i])));
313                                        if(resource != null) {
314                                                createUnusedApiFilterProblems(store.getUnusedFilters(resource, types[i]));
315                                        }
316                                }
317                        } else {
318                                //full build, clean up all old markers
319                                createUnusedApiFilterProblems(store.getUnusedFilters(null, null));
320                        }
321                }
322                catch(CoreException ce) {
323                        //ignore, just don't create problems
324                }
325                finally {
326                        Util.updateMonitor(monitor, 1);
327                }
328        }
329 
330        /**
331         * Creates a new unused {@link IApiProblemFilter} problem
332         * @param filters the filters to create the problems for
333         * @return a new {@link IApiProblem} for unused problem filters or <code>null</code>
334         */
335        private void createUnusedApiFilterProblems(IApiProblemFilter[] filters) {
336                if(fJavaProject == null) {
337                        return;
338                }
339                IApiProblemFilter filter = null;
340                IApiProblem problem = null;
341                for (int i = 0; i < filters.length; i++) {
342                        filter = filters[i];
343                        problem = filter.getUnderlyingProblem();
344                        if(problem == null) {
345                                return;
346                        }
347                        IResource resource = null;
348                        IType type = null;
349                        // retrieve line number, char start and char end
350                        int lineNumber = 0;
351                        int charStart = -1;
352                        int charEnd = 1;
353                        if (fJavaProject != null) {
354                                try {
355                                        String typeName = problem.getTypeName();
356                                        if (typeName != null) {
357                                                type = fJavaProject.findType(typeName.replace('$', '.'));
358                                        }
359                                        IProject project = fJavaProject.getProject();
360                                        resource = Util.getResource(project, type);
361                                        if (resource == null) {
362                                                return;
363                                        }
364                                        if (!Util.isManifest(resource.getProjectRelativePath()) && !type.isBinary()) {
365                                                ISourceRange range = type.getNameRange();
366                                                charStart = range.getOffset();
367                                                charEnd = charStart + range.getLength();
368                                                try {
369                                                        IDocument document = Util.getDocument(type.getCompilationUnit());
370                                                        lineNumber = document.getLineOfOffset(charStart);
371                                                } catch (BadLocationException e) {
372                                                        // ignore
373                                                }
374                                        }
375                                } catch (JavaModelException e) {
376                                        ApiPlugin.log(e);
377                                } catch(CoreException e) {
378                                        ApiPlugin.log(e);
379                                }
380                        }
381                        String path = null;
382                        if (resource != null) {
383                                path = resource.getProjectRelativePath().toPortableString();
384                        }
385                        addProblem(ApiProblemFactory.newApiUsageProblem(path,
386                                        problem.getTypeName(),
387                                        new String[] {filter.getUnderlyingProblem().getMessage()}, //message args
388                                        new String[] {IApiMarkerConstants.MARKER_ATTR_FILTER_HANDLE_ID, IApiMarkerConstants.API_MARKER_ATTR_ID},
389                                        new Object[] {((ApiProblemFilter)filter).getHandle(), new Integer(IApiMarkerConstants.UNUSED_PROBLEM_FILTER_MARKER_ID)},
390                                        lineNumber,
391                                        charStart,
392                                        charEnd,
393                                        problem.getElementKind(),
394                                        IApiProblem.UNUSED_PROBLEM_FILTERS));
395                }
396        }
397        
398        /**
399         * Check the version changes of re-exported bundles to make sure that the given component
400         * version is modified accordingly.
401         * 
402         * @param reference the given reference api profile
403         * @param component the given component
404         */
405        private ReexportedBundleVersionInfo checkBundleVersionsOfReexportedBundles(
406                        IApiComponent reference, IApiComponent component) throws CoreException {
407                IRequiredComponentDescription[] requiredComponents = component.getRequiredComponents();
408                int length = requiredComponents.length;
409                ReexportedBundleVersionInfo info = null;
410                if (length != 0) {
411                        loop: for (int i = 0; i < length; i++) {
412                                IRequiredComponentDescription description = requiredComponents[i];
413                                if (description.isExported()) {
414                                        // get the corresponding IRequiredComponentDescription for the component from the reference baseline
415                                        String id = description.getId();
416                                        IRequiredComponentDescription[] requiredComponents2 = reference.getRequiredComponents();
417                                        // get the corresponding exported bundle
418                                        IRequiredComponentDescription referenceDescription = null;
419                                        int length2 = requiredComponents2.length;
420                                        loop2: for (int j = 0; j < length2; j++) {
421                                                IRequiredComponentDescription description2 = requiredComponents2[j];
422                                                if (description2.getId().equals(id)) {
423                                                        if (description2.isExported()) {
424                                                                referenceDescription = description2;
425                                                                break loop2;
426                                                        }
427                                                }
428                                        }
429                                        if (referenceDescription == null) {
430                                                continue loop;
431                                        }
432                                        IVersionRange versionRange = description.getVersionRange();
433                                        IVersionRange versionRange2 = referenceDescription.getVersionRange();
434                                        
435                                        Version currentLowerBound = new Version(versionRange.getMinimumVersion());
436                                        Version referenceLowerBound = new Version(versionRange2.getMinimumVersion());
437                                        
438                                        int currentLowerMajorVersion = currentLowerBound.getMajor();
439                                        int referenceLowerMajorVersion = referenceLowerBound.getMajor();
440                                        int currentLowerMinorVersion = currentLowerBound.getMinor();
441                                        int referenceLowerMinorVersion = referenceLowerBound.getMinor();
442                                        
443                                        if (currentLowerMajorVersion < referenceLowerMajorVersion
444                                                        || currentLowerMinorVersion < referenceLowerMinorVersion) {
445                                                return new ReexportedBundleVersionInfo(id , IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE);
446                                        }
447                                        
448                                        if (currentLowerMajorVersion > referenceLowerMajorVersion) {
449                                                return new ReexportedBundleVersionInfo(id , IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE);
450                                        }
451                                        if (currentLowerMinorVersion > referenceLowerMinorVersion) {
452                                                info = new ReexportedBundleVersionInfo(id , IApiProblem.REEXPORTED_MINOR_VERSION_CHANGE);
453                                        }
454                                }
455                        }
456                }
457                return info;
458        }
459 
460        /**
461         * Creates and AST for the given {@link ITypeRoot} at the given offset
462         * @param root
463         * @param offset
464         * @return
465         */
466        private CompilationUnit createAST(ITypeRoot root, int offset) {
467                if(fJavaProject == null) {
468                        return null;
469                }
470                ASTParser parser = ASTParser.newParser(AST.JLS3);
471                parser.setFocalPosition(offset);
472                parser.setResolveBindings(false);
473                parser.setSource(root);
474                Map options = fJavaProject.getOptions(true);
475                options.put(JavaCore.COMPILER_DOC_COMMENT_SUPPORT, JavaCore.ENABLED);
476                parser.setCompilerOptions(options);
477                return (CompilationUnit) parser.createAST(new NullProgressMonitor());
478        }
479        
480        /**
481         * @return the build state to use.
482         */
483        private BuildState getBuildState() {
484                IProject project = null;
485                if (fJavaProject != null) {
486                        project = fJavaProject.getProject();
487                }
488                if(project == null) {
489                        return new BuildState();
490                }
491                try {
492                        BuildState state = BuildState.getLastBuiltState(project);
493                        if(state != null) {
494                                return state;
495                        }
496                } 
497                catch (CoreException e) {}
498                return new BuildState();
499        }
500        
501        /**
502         * Returns an {@link IApiTypeContainer} given the component and type names context
503         * @param component
504         * @param types
505         * @return a new {@link IApiTypeContainer} for the component and type names context
506         */
507        private IApiTypeContainer getSearchScope(final IApiComponent component, final String[] typenames) {
508                if(typenames == null) {
509                        return component;
510                }
511                if(typenames.length == 0) {
512                        return component;
513                }
514                else {
515                        return Factory.newTypeScope(component, getScopedElements(typenames));
516                }
517        }
518        
519        /**
520         * Returns a listing of {@link IReferenceTypeDescriptor}s given the listing of type names
521         * @param typenames
522         * @return
523         */
524        private IReferenceTypeDescriptor[] getScopedElements(final String[] typenames) {
525                ArrayList types = new ArrayList(typenames.length);
526                for(int i = 0; i < typenames.length; i++) {
527                        if(typenames[i] == null) {
528                                continue;
529                        }
530                        types.add(Util.getType(typenames[i]));
531                }
532                return (IReferenceTypeDescriptor[]) types.toArray(new IReferenceTypeDescriptor[types.size()]);
533        }
534        
535        /* (non-Javadoc)
536         * @see org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer#getProblems()
537         */
538        public IApiProblem[] getProblems() {
539                if(fProblems == null) {
540                        return new IApiProblem[0];
541                }
542                return (IApiProblem[]) fProblems.toArray(new IApiProblem[fProblems.size()]);
543        }
544 
545        /* (non-Javadoc)
546         * @see org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer#dispose()
547         */
548        public void dispose() {
549                if(fProblems != null) {
550                        fProblems.clear();
551                        fProblems = null;
552                }
553                if(fPendingDeltaInfos != null) {
554                        fPendingDeltaInfos.clear();
555                        fPendingDeltaInfos = null;
556                }
557                if(fBuildState != null) {
558                        fBuildState = null;
559                }
560        }
561        
562        /**
563         * @return if the API usage scan should be ignored
564         */
565        private boolean ignoreApiUsageScan() {
566                if (fJavaProject == null) {
567                        // do the API use scan for binary bundles in non-OSGi mode
568                        return false;
569                }
570                IProject project = fJavaProject.getProject();
571                boolean ignore = true;
572                ApiPlugin plugin = ApiPlugin.getDefault();
573                ignore &= plugin.getSeverityLevel(IApiProblemTypes.ILLEGAL_EXTEND, project) == ApiPlugin.SEVERITY_IGNORE;
574                ignore &= plugin.getSeverityLevel(IApiProblemTypes.ILLEGAL_IMPLEMENT, project) == ApiPlugin.SEVERITY_IGNORE;
575                ignore &= plugin.getSeverityLevel(IApiProblemTypes.ILLEGAL_INSTANTIATE, project) == ApiPlugin.SEVERITY_IGNORE;
576                ignore &= plugin.getSeverityLevel(IApiProblemTypes.ILLEGAL_REFERENCE, project) == ApiPlugin.SEVERITY_IGNORE;
577                ignore &= plugin.getSeverityLevel(IApiProblemTypes.LEAK_EXTEND, project) == ApiPlugin.SEVERITY_IGNORE;
578                ignore &= plugin.getSeverityLevel(IApiProblemTypes.LEAK_FIELD_DECL, project) == ApiPlugin.SEVERITY_IGNORE;
579                ignore &= plugin.getSeverityLevel(IApiProblemTypes.LEAK_IMPLEMENT, project) == ApiPlugin.SEVERITY_IGNORE;
580                ignore &= plugin.getSeverityLevel(IApiProblemTypes.LEAK_METHOD_PARAM, project) == ApiPlugin.SEVERITY_IGNORE;
581                ignore &= plugin.getSeverityLevel(IApiProblemTypes.LEAK_METHOD_RETURN_TYPE, project) == ApiPlugin.SEVERITY_IGNORE;
582                return ignore;
583        }
584        /**
585         * @return if the API usage scan should be ignored
586         */
587        private boolean reportApiBreakageWhenMajorVersionIncremented() {
588                if (fJavaProject == null) {
589                        // we ignore it for non-OSGi case
590                        return false;
591                }
592                return ApiPlugin.getDefault().getEnableState(IApiProblemTypes.REPORT_API_BREAKAGE_WHEN_MAJOR_VERSION_INCREMENTED, fJavaProject.getProject().getProject()).equals(ApiPlugin.VALUE_ENABLED);
593        }
594        
595        /**
596         * @return if the default API baseline check should be ignored or not
597         */
598        private boolean ignoreDefaultBaselineCheck() {
599                if(fJavaProject == null) {
600                        return true;
601                }
602                return ApiPlugin.getDefault().getSeverityLevel(IApiProblemTypes.MISSING_DEFAULT_API_BASELINE, fJavaProject.getProject().getProject()) == ApiPlugin.SEVERITY_IGNORE;
603        }
604        /**
605         * Whether to ignore since tag checks. If <code>null</code> is passed in we are asking if all since tag checks should be ignored,
606         * if a pref is specified we only want to know if that kind should be ignored
607         * @param pref
608         * @return
609         */
610        private boolean ignoreSinceTagCheck(String pref) {
611                if (fJavaProject == null) {
612                        return true;
613                }
614                IProject project = fJavaProject.getProject();
615                ApiPlugin plugin = ApiPlugin.getDefault();
616                if(pref == null) {
617                        boolean ignore = plugin.getSeverityLevel(IApiProblemTypes.MALFORMED_SINCE_TAG, project) == ApiPlugin.SEVERITY_IGNORE;
618                        ignore &= plugin.getSeverityLevel(IApiProblemTypes.INVALID_SINCE_TAG_VERSION, project) == ApiPlugin.SEVERITY_IGNORE;
619                        ignore &= plugin.getSeverityLevel(IApiProblemTypes.MISSING_SINCE_TAG, project) == ApiPlugin.SEVERITY_IGNORE;
620                        return ignore;
621                }
622                else {
623                        return plugin.getSeverityLevel(pref, project) == ApiPlugin.SEVERITY_IGNORE;
624                }
625        }
626        
627        /**
628         * @return if the component version checks should be ignored or not
629         */
630        private boolean ignoreComponentVersionCheck() {
631                if (fJavaProject == null) {
632                        // still do version checks for non-OSGi case
633                        return false;
634                }
635                return ApiPlugin.getDefault().getSeverityLevel(IApiProblemTypes.INCOMPATIBLE_API_COMPONENT_VERSION, fJavaProject.getProject().getProject()) == ApiPlugin.SEVERITY_IGNORE;
636        }
637        private boolean ignoreMinorVersionCheckWithoutApiChange() {
638                if (fJavaProject == null) {
639                        // we ignore it for non-OSGi case
640                        return true;
641                }
642                return ApiPlugin.getDefault().getEnableState(IApiProblemTypes.INCOMPATIBLE_API_COMPONENT_VERSION_INCLUDE_INCLUDE_MINOR_WITHOUT_API_CHANGE, fJavaProject.getProject().getProject()).equals(ApiPlugin.VALUE_DISABLED);
643        }
644        private boolean ignoreMajorVersionCheckWithoutBreakingChange() {
645                if (fJavaProject == null) {
646                        // we ignore it for non-OSGi case
647                        return true;
648                }
649                return ApiPlugin.getDefault().getEnableState(IApiProblemTypes.INCOMPATIBLE_API_COMPONENT_VERSION_INCLUDE_INCLUDE_MAJOR_WITHOUT_BREAKING_CHANGE, fJavaProject.getProject().getProject()).equals(ApiPlugin.VALUE_DISABLED);
650        }
651        /**
652         * @return if the invalid tag check should be ignored
653         */
654        private boolean ignoreInvalidTagCheck() {
655                if(fJavaProject == null) {
656                        return true;
657                }
658                return ApiPlugin.getDefault().getSeverityLevel(IApiProblemTypes.INVALID_JAVADOC_TAG, fJavaProject.getProject()) == ApiPlugin.SEVERITY_IGNORE;
659        }
660        /**
661         * @return if the unused problem filter check should be ignored or not
662         */
663        private boolean ignoreUnusedProblemFilterCheck() {
664                if(fJavaProject == null) {
665                        return true;
666                }
667                return ApiPlugin.getDefault().getSeverityLevel(IApiProblemTypes.UNUSED_PROBLEM_FILTERS, fJavaProject.getProject()) == ApiPlugin.SEVERITY_IGNORE;
668        }
669        /**
670         * Checks the validation of tags for the given {@link IApiComponent}
671         * @param context
672         * @param component
673         * @param monitor
674         */
675        private void checkTagValidation(final IBuildContext context, final IApiComponent component, IProgressMonitor monitor) {
676                if(ignoreInvalidTagCheck()) {
677                        return;
678                }
679                SubMonitor localMonitor = null;
680                try {
681                        
682                        localMonitor = SubMonitor.convert(monitor, BuilderMessages.BaseApiAnalyzer_validating_javadoc_tags, 1 + component.getApiTypeContainers().length);
683                        if(context.hasTypes()) {
684                                String[] typenames = context.getStructurallyChangedTypes();
685                                for(int i = 0; i < typenames.length; i++) {
686                                        if(typenames[i] == null) {
687                                                continue;
688                                        }
689                                        localMonitor.subTask(NLS.bind(BuilderMessages.BaseApiAnalyzer_scanning_0, typenames[i]));
690                                        processType(typenames[i]);
691                                        Util.updateMonitor(localMonitor);
692                                }
693                        }
694                        else {
695                                try {
696                                        IPackageFragmentRoot[] roots = fJavaProject.getPackageFragmentRoots();
697                                        for(int i = 0; i < roots.length; i++) {
698                                                if(roots[i].getKind() == IPackageFragmentRoot.K_SOURCE) {
699                                                        localMonitor.subTask(NLS.bind(BuilderMessages.BaseApiAnalyzer_scanning_0, roots[i].getPath().toOSString()));
700                                                        scanSource(roots[i], localMonitor.newChild(1));
701                                                        Util.updateMonitor(localMonitor);
702                                                }
703                                        }
704                                }
705                                catch(JavaModelException jme) {
706                                        ApiPlugin.log(jme);
707                                }
708                        }
709                        Util.updateMonitor(localMonitor);
710                } catch(CoreException e) {
711                        ApiPlugin.log(e);
712                }
713                finally {
714                        if(localMonitor != null) {
715                                localMonitor.done();
716                        }
717                }
718        }
719        
720        /**
721         * Recursively finds all source in the given project and scans it for invalid tags 
722         * @param element
723         * @param monitor
724         * @throws JavaModelException
725         */
726        private void scanSource(IJavaElement element, IProgressMonitor monitor) throws JavaModelException {
727                try {
728                        switch(element.getElementType()) {
729                                case IJavaElement.PACKAGE_FRAGMENT_ROOT:
730                                case IJavaElement.PACKAGE_FRAGMENT: {
731                                        IParent parent = (IParent) element;
732                                        IJavaElement[] children = parent.getChildren();
733                                        for (int i = 0; i < children.length; i++) {
734                                                scanSource(children[i], monitor);
735                                                Util.updateMonitor(monitor, 0);
736                                        }
737                                        break;
738                                }
739                                case IJavaElement.COMPILATION_UNIT: {
740                                        ICompilationUnit unit = (ICompilationUnit) element;
741                                        processType(unit);
742                                        Util.updateMonitor(monitor, 0);
743                                        break;
744                                }
745                        }
746                }
747                finally {
748                        if(monitor != null) {
749                                Util.updateMonitor(monitor);
750                                monitor.done();
751                        }
752                }
753        }
754        
755        /**
756         * Processes the given type name for invalid Javadoc tags
757         * @param typename
758         */
759        private void processType(String typename) {
760                try {
761                        IMember type = fJavaProject.findType(typename);
762                        if(type != null) {
763                                ICompilationUnit cunit = type.getCompilationUnit();
764                                if(cunit != null) {
765                                        processType(cunit);
766                                }
767                        }
768                } 
769                catch (JavaModelException e) {
770                        e.printStackTrace();
771                }
772        }
773        
774        /**
775         * Processes the given {@link ICompilationUnit} for invalid tags
776         * @param cunit
777         */
778        private void processType(ICompilationUnit cunit) {
779                TagValidator tv = new TagValidator(cunit);
780                CompilationUnit comp = createAST(cunit, 0);
781                if(comp == null) {
782                        return;
783                }
784                comp.accept(tv);
785                IApiProblem[] tagProblems = tv.getTagProblems();
786                for (int i = 0; i < tagProblems.length; i++) {
787                        addProblem(tagProblems[i]);
788                }
789        }
790        /**
791         * Checks for illegal API usage in the specified component, creating problem
792         * markers as required.
793         * 
794         * @param context the current build context
795         * @param component component being built
796         * @param monitor progress monitor
797         */
798        private void checkApiUsage(final IBuildContext context, final IApiComponent component, IProgressMonitor monitor) throws CoreException {
799                if(ignoreApiUsageScan()) {
800                        if(DEBUG) {
801                                System.out.println("Ignoring API usage scan"); //$NON-NLS-1$
802                        }
803                        return;
804                } 
805                IApiTypeContainer scope = getSearchScope(component, null);
806                if(context.hasTypes()) {
807                        String[] deptypes = context.getDependentTypes();
808                        String[] structtypes = context.getStructurallyChangedTypes();
809                        //TODO do any special processing here to prune types prior
810                        //to doing the scan
811                        HashSet typenames = new HashSet(deptypes.length + structtypes.length);
812                        for (int i = 0; i < deptypes.length; i++) {
813                                if(deptypes[i] == null) {
814                                        continue;
815                                }
816                                typenames.add(deptypes[i]);
817                        }
818                        for (int i = 0; i < structtypes.length; i++) {
819                                if(structtypes[i] == null) {
820                                        continue;
821                                }
822                                typenames.add(structtypes[i]);
823                        }
824                        scope = getSearchScope(component, (String[]) typenames.toArray(new String[typenames.size()]));
825                }
826                SubMonitor localMonitor = SubMonitor.convert(monitor, MessageFormat.format(BuilderMessages.checking_api_usage, new String[] {component.getId()}), 2);
827                ReferenceAnalyzer analyzer = new ReferenceAnalyzer();
828                try {
829                        long start = System.currentTimeMillis();
830                        IApiProblem[] illegal = analyzer.analyze(component, scope, localMonitor.newChild(2));
831                        Util.updateMonitor(localMonitor);
832                        long end = System.currentTimeMillis();
833                        if (DEBUG) {
834                                System.out.println("API usage scan: " + (end- start) + " ms\t" + illegal.length + " problems"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
835                        }                
836                        for (int i = 0; i < illegal.length; i++) {
837                                addProblem(illegal[i]);
838                        }
839                        Util.updateMonitor(localMonitor);
840                } 
841                catch (CoreException ce) {
842                        if(DEBUG) {
843                                ApiPlugin.log(ce);
844                        }
845                }
846                finally {
847                        if(monitor != null) {
848                                monitor.done();
849                        }
850                }
851        }
852        
853        /**
854         * Compares the given type between the two API components
855         * @param typeName the type to check in each component
856         * @param reference 
857         * @param component
858         * @param monitor
859         */
860        private void checkCompatibility(final String typeName, final IApiComponent reference, final IApiComponent component, IProgressMonitor monitor) throws CoreException {
861                String id = component.getId();
862                if (DEBUG) {
863                        System.out.println("comparing profiles ["+reference.getId()+"] and ["+id+"] for type ["+typeName+"]"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ //$NON-NLS-4$
864                }
865                IApiTypeRoot classFile = null;
866                try {
867                        if (Util.ORG_ECLIPSE_SWT.equals(id)) {
868                                classFile = component.findTypeRoot(typeName);
869                        } else {
870                                classFile = component.findTypeRoot(typeName, id);
871                        }
872                } catch (CoreException e) {
873                        ApiPlugin.log(e);
874                }
875                SubMonitor localmonitor = SubMonitor.convert(monitor, BuilderMessages.BaseApiAnalyzer_checking_compat, 4);
876                try {
877                        IDelta delta = null;
878                        IApiComponent provider = null;
879                        if (classFile == null) {
880                                String packageName = Signatures.getPackageName(typeName);
881                                // check if the type is provided by a required component (it could have been moved/re-exported)
882                                IApiComponent[] providers = component.getBaseline().resolvePackage(component, packageName);
883                                int index = 0;
884                                while (classFile == null && index < providers.length) {
885                                        IApiComponent p = providers[index];
886                                        if (!p.equals(component)) {
887                                                String id2 = p.getId();
888                                                if (Util.ORG_ECLIPSE_SWT.equals(id2)) {
889                                                        classFile = p.findTypeRoot(typeName);
890                                                } else {
891                                                        classFile = p.findTypeRoot(typeName, id2);
892                                                }
893                                                if (classFile != null) {
894                                                        provider = p;
895                                                }
896                                        }
897                                        index++;
898                                }
899                        } else {
900                                provider = component;
901                        }
902                        Util.updateMonitor(localmonitor, 1);
903                        if (classFile == null) {
904                                // this indicates a removed type
905                                // we should try to get the class file from the reference
906                                IApiTypeRoot referenceClassFile = null;
907                                try {
908                                        referenceClassFile = reference.findTypeRoot(typeName);
909                                } catch (CoreException e) {
910                                        ApiPlugin.log(e);
911                                }
912                                if (referenceClassFile != null) {
913                                        try {
914                                                IApiType type = referenceClassFile.getStructure();
915                                                if(type == null) {
916                                                        return;
917                                                }
918                                                final IApiDescription referenceApiDescription = reference.getApiDescription();
919                                                IApiAnnotations elementDescription = referenceApiDescription.resolveAnnotations(type.getHandle());
920                                                int restrictions = RestrictionModifiers.NO_RESTRICTIONS;
921                                                if (!type.isMemberType() && !type.isAnonymous() && !type.isLocal()) {
922                                                        int visibility = VisibilityModifiers.ALL_VISIBILITIES;
923                                                        // we skip nested types (member, local and anonymous)
924                                                        if (elementDescription != null) {
925                                                                restrictions = elementDescription.getRestrictions();
926                                                                visibility = elementDescription.getVisibility();
927                                                        }
928                                                        // if the visibility is API, we only consider public and protected types
929                                                        if (Util.isDefault(type.getModifiers())
930                                                                                || Flags.isPrivate(type.getModifiers())) {
931                                                                return;
932                                                        }
933                                                        if (VisibilityModifiers.isAPI(visibility)) {
934                                                                String deltaComponentID = Util.getDeltaComponentVersionsId(reference);
935                                                                delta = new Delta(
936                                                                                deltaComponentID,
937                                                                                IDelta.API_COMPONENT_ELEMENT_TYPE,
938                                                                                IDelta.REMOVED,
939                                                                                IDelta.TYPE,
940                                                                                restrictions,
941                                                                                type.getModifiers(),
942                                                                                0,
943                                                                                typeName,
944                                                                                typeName,
945                                                                                new String[] { typeName, Util.getComponentVersionsId(reference)});
946                                                        }
947                                                }
948                                        } catch (CoreException e) {
949                                                ApiPlugin.log(e);
950                                        }
951                                }
952                                Util.updateMonitor(localmonitor, 1);
953                        } else {
954                                fBuildState.cleanup(typeName);
955                                long time = System.currentTimeMillis();
956                                try {
957                                        delta = ApiComparator.compare(classFile, reference, provider, reference.getBaseline(), provider.getBaseline(), VisibilityModifiers.API, localmonitor.newChild(1));
958                                } catch(Exception e) {
959                                        ApiPlugin.log(e);
960                                } finally {
961                                        if (DEBUG) {
962                                                System.out.println("Time spent for " + typeName + " : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
963                                        }
964                                        fPendingDeltaInfos.clear();
965                                }
966                        }
967                        if (delta == null) {
968                                return;
969                        }
970                        if (delta != ApiComparator.NO_DELTA) {
971                                List allDeltas = Util.collectAllDeltas(delta);
972                                localmonitor.subTask(BuilderMessages.BaseApiAnalyzer_processing_deltas);
973                                for (Iterator iterator = allDeltas.iterator(); iterator.hasNext();) {
974                                        processDelta((IDelta) iterator.next(), reference, component);
975                                }
976                                Util.updateMonitor(localmonitor, 1);
977                                if (!fPendingDeltaInfos.isEmpty()) {
978                                        localmonitor.subTask(BuilderMessages.BaseApiAnalyzer_checking_since_tags);
979                                        for (Iterator iterator = fPendingDeltaInfos.iterator(); iterator.hasNext();) {
980                                                checkSinceTags((Delta) iterator.next(), component);
981                                        }
982                                }
983                                Util.updateMonitor(localmonitor, 1);
984                        }
985                        else {
986                                Util.updateMonitor(localmonitor, 2);
987                        }
988                }
989                finally {
990                        localmonitor.done();
991                }
992        }
993        /**
994         * Compares the two given profiles and generates an {@link IDelta}
995         * 
996         * @param jproject
997         * @param reference
998         * @param component
999         * @param monitor
1000         */
1001        private void checkCompatibility(final IApiComponent reference, final IApiComponent component, IProgressMonitor monitor) throws CoreException {
1002                long time = System.currentTimeMillis();
1003                SubMonitor localmonitor = SubMonitor.convert(monitor, BuilderMessages.BaseApiAnalyzer_checking_compat, 3);
1004                try {
1005                        IDelta delta = null;
1006                        if (reference == null) {
1007                                delta =
1008                                        new Delta(
1009                                                null,
1010                                                IDelta.API_PROFILE_ELEMENT_TYPE,
1011                                                IDelta.ADDED,
1012                                                IDelta.API_COMPONENT,
1013                                                null,
1014                                                component.getId(),
1015                                                component.getId());
1016                                Util.updateMonitor(localmonitor, 5);
1017                        } else {
1018                                try {
1019                                        delta = ApiComparator.compare(reference, component, VisibilityModifiers.API, localmonitor.newChild(1));
1020                                } finally {
1021                                        if (DEBUG) {
1022                                                System.out.println("Time spent for " + component.getId() + " : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
1023                                        }
1024                                        fPendingDeltaInfos.clear();
1025                                }
1026                        }
1027                        if (delta == null) {
1028                                return;
1029                        }
1030                        if (delta != ApiComparator.NO_DELTA) {
1031                                List allDeltas = Util.collectAllDeltas(delta);
1032                                if (allDeltas.size() != 0) {
1033                                        localmonitor.subTask(BuilderMessages.BaseApiAnalyzer_processing_deltas);
1034                                        for (Iterator iterator = allDeltas.iterator(); iterator.hasNext();) {
1035                                                processDelta((IDelta) iterator.next(), reference, component);
1036                                        }
1037                                        Util.updateMonitor(localmonitor, 1);
1038                                        localmonitor.subTask(BuilderMessages.BaseApiAnalyzer_checking_since_tags);
1039                                        if (!fPendingDeltaInfos.isEmpty()) {
1040                                                for (Iterator iterator = fPendingDeltaInfos.iterator(); iterator.hasNext();) {
1041                                                        checkSinceTags((Delta) iterator.next(), component);
1042                                                }
1043                                        }
1044                                        Util.updateMonitor(localmonitor, 1);
1045                                }
1046                        }
1047                        else {
1048                                Util.updateMonitor(localmonitor, 2);
1049                        }
1050                }
1051                finally {
1052                        localmonitor.done();
1053                }
1054        }
1055        
1056        /**
1057         * Processes delta to determine if it needs an @since tag. If it does and one
1058         * is not present or the version of the tag is incorrect, a marker is created
1059         * @param jproject
1060         * @param delta
1061         * @param component
1062         */
1063        private void checkSinceTags(final Delta delta, final IApiComponent component) throws CoreException {
1064                if(ignoreSinceTagCheck(null)) {
1065                        return;
1066                }
1067                IMember member = Util.getIMember(delta, fJavaProject);
1068                if (member == null || member.isBinary()) {
1069                        return;
1070                }
1071                ICompilationUnit cunit = member.getCompilationUnit();
1072                if (cunit == null) {
1073                        return;
1074                }
1075                try {
1076                        if (! cunit.isConsistent()) {
1077                                cunit.makeConsistent(null);
1078                        }
1079                } catch (JavaModelException e) {
1080                        e.printStackTrace();
1081                }
1082                IApiProblem problem = null;
1083                ISourceRange nameRange = null;
1084                try {
1085                        nameRange = member.getNameRange();
1086                } catch (JavaModelException e) {
1087                        ApiPlugin.log(e);
1088                        return;
1089                }
1090                if (nameRange == null) {
1091                        return;
1092                }
1093                try {
1094                        int offset = nameRange.getOffset();
1095                        CompilationUnit comp = createAST(cunit, offset);
1096                        if(comp == null) {
1097                                return;
1098                        }
1099                        SinceTagChecker visitor = new SinceTagChecker(offset);
1100                        comp.accept(visitor);
1101                        // we must retrieve the component version from the delta component id
1102                        String componentVersionId = delta.getComponentVersionId();
1103                        String componentVersionString = null;
1104                        if (componentVersionId == null) {
1105                                componentVersionString = component.getVersion();
1106                        } else {
1107                                componentVersionString = extractVersion(componentVersionId);
1108                        }
1109                        try {
1110                                if (visitor.hasNoComment() || visitor.isMissing()) {
1111                                        if(ignoreSinceTagCheck(IApiProblemTypes.MISSING_SINCE_TAG)) {
1112                                                if(DEBUG) {
1113                                                        System.out.println("Ignoring missing since tag problem"); //$NON-NLS-1$
1114                                                }
1115                                                return;
1116                                        }
1117                                        StringBuffer buffer = new StringBuffer();
1118                                        Version componentVersion = new Version(componentVersionString);
1119                                        buffer.append(componentVersion.getMajor()).append('.').append(componentVersion.getMinor());
1120                                        problem = createSinceTagProblem(IApiProblem.SINCE_TAG_MISSING, new String[] { Util.getDeltaArgumentString(delta) }, delta, member, String.valueOf(buffer));
1121                                } else if (visitor.hasJavadocComment()) {
1122                                        // we don't want to flag block comment
1123                                        String sinceVersion = visitor.getSinceVersion();
1124                                        if (sinceVersion != null) {
1125                                                SinceTagVersion tagVersion = new SinceTagVersion(sinceVersion);
1126                                                String postfixString = tagVersion.postfixString();
1127                                                if (tagVersion.getVersion() == null || Util.getFragmentNumber(tagVersion.getVersionString()) > 2) {
1128                                                        if(ignoreSinceTagCheck(IApiProblemTypes.MALFORMED_SINCE_TAG)) {
1129                                                                if(DEBUG) {
1130                                                                        System.out.println("Ignoring malformed since tag problem"); //$NON-NLS-1$
1131                                                                }
1132                                                                return;
1133                                                        }
1134                                                        StringBuffer buffer = new StringBuffer();
1135                                                        if (tagVersion.prefixString() != null) {
1136                                                                buffer.append(tagVersion.prefixString());
1137                                                        }
1138                                                        Version componentVersion = new Version(componentVersionString);
1139                                                        buffer.append(componentVersion.getMajor()).append('.').append(componentVersion.getMinor());
1140                                                        if (postfixString != null) {
1141                                                                buffer.append(postfixString);
1142                                                        }
1143                                                        problem = createSinceTagProblem(IApiProblem.SINCE_TAG_MALFORMED, new String[] {sinceVersion, Util.getDeltaArgumentString(delta)}, delta, member, String.valueOf(buffer));
1144                                                } else {
1145                                                        if(ignoreSinceTagCheck(IApiProblemTypes.INVALID_SINCE_TAG_VERSION)) {
1146                                                                if(DEBUG) {
1147                                                                        System.out.println("Ignoring invalid tag version problem"); //$NON-NLS-1$
1148                                                                }
1149                                                                return;
1150                                                        }
1151                                                        StringBuffer accurateVersionBuffer = new StringBuffer();
1152                                                        Version componentVersion = new Version(componentVersionString);
1153                                                        accurateVersionBuffer.append(componentVersion.getMajor()).append('.').append(componentVersion.getMinor());
1154                                                        String accurateVersion = String.valueOf(accurateVersionBuffer);
1155                                                        if (Util.isDifferentVersion(sinceVersion, accurateVersion)) {
1156                                                                // report invalid version number
1157                                                                StringBuffer buffer = new StringBuffer();
1158                                                                if (tagVersion.prefixString() != null) {
1159                                                                        buffer.append(tagVersion.prefixString());
1160                                                                }
1161                                                                Version version = new Version(accurateVersion);
1162                                                                buffer.append(version.getMajor()).append('.').append(version.getMinor());
1163                                                                if (postfixString != null) {
1164                                                                        buffer.append(postfixString);
1165                                                                }
1166                                                                String accurateSinceTagValue = String.valueOf(buffer);
1167                                                                problem = createSinceTagProblem(IApiProblem.SINCE_TAG_INVALID, new String[] {sinceVersion, accurateSinceTagValue, Util.getDeltaArgumentString(delta)}, delta, member, accurateSinceTagValue);
1168                                                        }
1169                                                }
1170                                        }
1171                                }
1172                        } catch (IllegalArgumentException e) {
1173                                ApiPlugin.log(e);
1174                        }
1175                } catch (RuntimeException e) {
1176                        ApiPlugin.log(e);
1177                }
1178                if(problem != null) {
1179                        addProblem(problem);
1180                }
1181        }
1182        
1183        private String extractVersion(String componentVersionId) {
1184                // extract the version from the delta component id. It is located between parenthesis
1185                int indexOfOpen = componentVersionId.lastIndexOf('(');
1186                return componentVersionId.substring(indexOfOpen + 1, componentVersionId.length() - 1);
1187        }
1188 
1189        /**
1190         * Creates a marker to denote a problem with the since tag (existence or correctness) for a member
1191         * and returns it, or <code>null</code>
1192         * @param kind
1193         * @param messageargs
1194         * @param compilationUnit
1195         * @param member
1196         * @param version
1197         * @return a new {@link IApiProblem} or <code>null</code>
1198         */
1199        private IApiProblem createSinceTagProblem(int kind, final String[] messageargs, final Delta info, final IMember member, final String version) {
1200                try {
1201                        // create a marker on the member for missing @since tag
1202                        IType declaringType = null;
1203                        if (member.getElementType() == IJavaElement.TYPE) {
1204                                declaringType = (IType) member;
1205                        } else {
1206                                declaringType = member.getDeclaringType();
1207                        }
1208                        IResource resource = Util.getResource(this.fJavaProject.getProject(), declaringType);
1209                        if (resource == null) {
1210                                return null;
1211                        }
1212                        int lineNumber = 1;
1213                        int charStart = 0;
1214                        int charEnd = 1;
1215                        String qtn = null;
1216                        if (member instanceof IType) {
1217                                qtn = ((IType)member).getFullyQualifiedName();
1218                        } else {
1219                                qtn = declaringType.getFullyQualifiedName();
1220                        }
1221                        String[] messageArguments = null;
1222                        if (!Util.isManifest(resource.getProjectRelativePath())) {
1223                                messageArguments = messageargs;
1224                                ICompilationUnit unit = member.getCompilationUnit();
1225                                ISourceRange range = member.getNameRange();
1226                                charStart = range.getOffset();
1227                                charEnd = charStart + range.getLength();
1228                                try {
1229                                        // unit cannot be null
1230                                        IDocument document = Util.getDocument(unit);
1231                                        lineNumber = document.getLineOfOffset(charStart);
1232                                } catch (BadLocationException e) {
1233                                        ApiPlugin.log(e);
1234                                }
1235                        } else {
1236                                // update the last entry in the message arguments 
1237                                if (!(member instanceof IType)) {
1238                                        // insert the declaring type
1239                                        int length = messageargs.length;
1240                                        messageArguments = new String[length];
1241                                        System.arraycopy(messageargs, 0, messageArguments, 0, length);
1242                                        StringBuffer buffer = new StringBuffer();
1243                                        buffer.append(qtn).append('.').append(messageargs[length - 1]);
1244                                        messageArguments[length - 1] = String.valueOf(buffer);
1245                                } else {
1246                                        messageArguments = messageargs;
1247                                }
1248                        }
1249                        return ApiProblemFactory.newApiSinceTagProblem(resource.getProjectRelativePath().toPortableString(),
1250                                        qtn,
1251                                        messageArguments,
1252                                        new String[] {IApiMarkerConstants.MARKER_ATTR_VERSION, IApiMarkerConstants.API_MARKER_ATTR_ID, IApiMarkerConstants.MARKER_ATTR_HANDLE_ID},
1253                                        new Object[] {version, new Integer(IApiMarkerConstants.SINCE_TAG_MARKER_ID), member.getHandleIdentifier()},
1254                                        lineNumber,
1255                                        charStart,
1256                                        charEnd,
1257                                        info.getElementType(),
1258                                        kind);
1259                } catch (CoreException e) {
1260                        ApiPlugin.log(e);
1261                }
1262                return null;
1263        }
1264        
1265        /**
1266         * Creates an {@link IApiProblem} for the given compatibility delta 
1267         * @param delta
1268         * @param jproject
1269         * @param reference
1270         * @param component
1271         * @return a new compatibility problem or <code>null</code>
1272         */
1273        private IApiProblem createCompatibilityProblem(final IDelta delta, final IApiComponent reference, final IApiComponent component) {
1274                try {
1275                        Version referenceVersion = new Version(reference.getVersion());
1276                        Version componentVersion = new Version(component.getVersion());
1277                        if ((referenceVersion.getMajor() < componentVersion.getMajor())
1278                                        && !reportApiBreakageWhenMajorVersionIncremented()) {
1279                                // API breakage are ok in this case and we don't want them to be reported
1280                                fBuildState.addBreakingChange(delta);
1281                                return null;
1282                        }
1283                        IResource resource = null;
1284                        IType type = null;
1285                        // retrieve line number, char start and char end
1286                        int lineNumber = 0;
1287                        int charStart = -1;
1288                        int charEnd = 1;
1289                        IMember member = null;
1290                        if (fJavaProject != null) {
1291                                try {
1292                                        type = fJavaProject.findType(delta.getTypeName().replace('$', '.'));
1293                                } catch (JavaModelException e) {
1294                                        ApiPlugin.log(e);
1295                                }
1296                                IProject project = fJavaProject.getProject();
1297                                resource = Util.getResource(project, type);
1298                                if (resource == null) {
1299                                        return null;
1300                                }
1301                                if (!Util.isManifest(resource.getProjectRelativePath())) {
1302                                        member = Util.getIMember(delta, fJavaProject);
1303                                }
1304                                if (member != null && !member.isBinary() && member.exists()) {
1305                                        ISourceRange range = member.getNameRange();
1306                                        charStart = range.getOffset();
1307                                        charEnd = charStart + range.getLength();
1308                                        try {
1309                                                IDocument document = Util.getDocument(member.getCompilationUnit());
1310                                                lineNumber = document.getLineOfOffset(charStart);
1311                                        } catch (BadLocationException e) {
1312                                                // ignore
1313                                        }
1314                                }
1315                        }
1316                        String path = null;
1317                        if (resource != null) {
1318                                path = resource.getProjectRelativePath().toPortableString();
1319                        }
1320                        IApiProblem apiProblem = ApiProblemFactory.newApiProblem(path,
1321                                        delta.getTypeName(),
1322                                        delta.getArguments(),
1323                                        new String[] {
1324                                                IApiMarkerConstants.MARKER_ATTR_HANDLE_ID,
1325                                                IApiMarkerConstants.API_MARKER_ATTR_ID
1326                                        },
1327                                        new Object[] {
1328                                                member == null ? null : member.getHandleIdentifier(),
1329                                                new Integer(IApiMarkerConstants.COMPATIBILITY_MARKER_ID),
1330                                        },
1331                                        lineNumber,
1332                                        charStart,
1333                                        charEnd,
1334                                        IApiProblem.CATEGORY_COMPATIBILITY,
1335                                        delta.getElementType(),
1336                                        delta.getKind(),
1337                                        delta.getFlags());
1338                        return apiProblem;
1339                        
1340                } catch (CoreException e) {
1341                        ApiPlugin.log(e);
1342                }
1343                return null;
1344        }
1345        /**
1346         * Creates an {@link IApiProblem} for the given api component
1347         * @param component
1348         * @return a new api component resolution problem or <code>null</code>
1349         */
1350        private void createApiComponentResolutionProblem(final IApiComponent component, final String message) throws CoreException {
1351                IApiProblem problem = ApiProblemFactory.newApiComponentResolutionProblem(
1352                                Path.EMPTY.toPortableString(),
1353                                new String[] {component.getId(), message },
1354                                new String[] {IApiMarkerConstants.API_MARKER_ATTR_ID},
1355                                new Object[] {new Integer(IApiMarkerConstants.API_COMPONENT_RESOLUTION_MARKER_ID)},
1356                                IElementDescriptor.RESOURCE,
1357                                IApiProblem.API_COMPONENT_RESOLUTION);
1358                addProblem(problem);
1359        }
1360        /**
1361         * Processes a delta to know if we need to check for since tag or version numbering problems
1362         * @param jproject
1363         * @param delta
1364         * @param reference
1365         * @param component
1366         */
1367        private void processDelta(final IDelta delta, final IApiComponent reference, final IApiComponent component) {
1368                int flags = delta.getFlags();
1369                int kind = delta.getKind();
1370                int modifiers = delta.getNewModifiers();
1371                if (DeltaProcessor.isCompatible(delta)) {
1372                        if (!RestrictionModifiers.isReferenceRestriction(delta.getRestrictions())) {
1373                                if (Util.isVisible(modifiers)) {
1374                                        if (Flags.isProtected(modifiers)) {
1375                                                String typeName = delta.getTypeName();
1376                                                if (typeName != null) {
1377                                                        IApiTypeRoot typeRoot = null;
1378                                                        IApiType type = null;
1379                                                        try {
1380                                                                String id = component.getId();
1381                                                                if (Util.ORG_ECLIPSE_SWT.equals(id)) {
1382                                                                        typeRoot = component.findTypeRoot(typeName);
1383                                                                } else {
1384                                                                        typeRoot = component.findTypeRoot(typeName, id);
1385                                                                }
1386                                                                if (typeRoot == null) {
1387                                                                        String packageName = Signatures.getPackageName(typeName);
1388                                                                        // check if the type is provided by a required component (it could have been moved/re-exported)
1389                                                                        IApiComponent[] providers = component.getBaseline().resolvePackage(component, packageName);
1390                                                                        int index = 0;
1391                                                                        while (typeRoot == null && index < providers.length) {
1392                                                                                IApiComponent p = providers[index];
1393                                                                                if (!p.equals(component)) {
1394                                                                                        String id2 = p.getId();
1395                                                                                        if (Util.ORG_ECLIPSE_SWT.equals(id2)) {
1396                                                                                                typeRoot = p.findTypeRoot(typeName);
1397                                                                                        } else {
1398                                                                                                typeRoot = p.findTypeRoot(typeName, id2);
1399                                                                                        }
1400                                                                                }
1401                                                                                index++;
1402                                                                        }
1403                                                                }
1404                                                                if (typeRoot == null) {
1405                                                                        return;
1406                                                                }
1407                                                                type = typeRoot.getStructure();
1408                                                        } catch (CoreException e) {
1409                                                                // ignore
1410                                                        }
1411                                                        if (type == null || Flags.isFinal(type.getModifiers())) {
1412                                                                // no @since tag to report for new protected methods inside a final class
1413                                                                return;
1414                                                        }
1415                                                }
1416                                        }
1417                                        // if protected, we only want to check @since tags if the enclosing class can be subclassed
1418                                        switch(kind) {
1419                                                case IDelta.ADDED :
1420                                                        // if public, we always want to check @since tags
1421                                                        switch(flags) {
1422                                                                case IDelta.TYPE_MEMBER :
1423                                                                case IDelta.METHOD :
1424                                                                case IDelta.CONSTRUCTOR :
1425                                                                case IDelta.ENUM_CONSTANT :
1426                                                                case IDelta.METHOD_WITH_DEFAULT_VALUE :
1427                                                                case IDelta.METHOD_WITHOUT_DEFAULT_VALUE :
1428                                                                case IDelta.FIELD :
1429                                                                case IDelta.TYPE :
1430                                                                        if (DEBUG) {
1431                                                                                String deltaDetails = "Delta : " + Util.getDetail(delta); //$NON-NLS-1$
1432                                                                                System.out.println(deltaDetails + " is compatible"); //$NON-NLS-1$
1433                                                                        }
1434                                                                        this.fBuildState.addCompatibleChange(delta);
1435                                                                        fPendingDeltaInfos.add(delta);
1436                                                                        break;
1437                                                        }
1438                                                        break;
1439                                                case IDelta.CHANGED :
1440                                                        if (flags == IDelta.INCREASE_ACCESS) {
1441                                                                if (DEBUG) {
1442                                                                        String deltaDetails = "Delta : " + Util.getDetail(delta); //$NON-NLS-1$
1443                                                                        System.out.println(deltaDetails + " is compatible"); //$NON-NLS-1$
1444                                                                }
1445                                                                this.fBuildState.addCompatibleChange(delta);
1446                                                                fPendingDeltaInfos.add(delta);
1447                                                        }
1448                                        }
1449                                }
1450                        }
1451                } else {
1452                        switch(kind) {
1453                                case IDelta.ADDED :
1454                                        // if public, we always want to check @since tags
1455                                        switch(flags) {
1456                                                case IDelta.TYPE_MEMBER :
1457                                                case IDelta.METHOD :
1458                                                case IDelta.CONSTRUCTOR :
1459                                                case IDelta.ENUM_CONSTANT :
1460                                                case IDelta.METHOD_WITH_DEFAULT_VALUE :
1461                                                case IDelta.METHOD_WITHOUT_DEFAULT_VALUE :
1462                                                case IDelta.FIELD :
1463                                                        // ensure that there is a @since tag for the corresponding member
1464                                                        if (Util.isVisible(modifiers)) {
1465                                                                if (DEBUG) {
1466                                                                        String deltaDetails = "Delta : " + Util.getDetail(delta); //$NON-NLS-1$
1467                                                                        System.err.println(deltaDetails + " is not compatible"); //$NON-NLS-1$
1468                                                                }
1469                                                                fPendingDeltaInfos.add(delta);
1470                                                        }
1471                                        }
1472                                        break;
1473                        }
1474                        IApiProblem problem = createCompatibilityProblem(delta, reference, component);
1475                        if(addProblem(problem)) {
1476                                fBuildState.addBreakingChange(delta);
1477                        }
1478                }
1479        }
1480        /**
1481         * Checks the version number of the API component and creates a problem markers as needed
1482         * @param reference
1483         * @param component
1484         */
1485        private void checkApiComponentVersion(final IApiComponent reference, final IApiComponent component) throws CoreException {
1486                if(ignoreComponentVersionCheck() || reference == null || component == null) {
1487                        if(DEBUG) {
1488                                System.out.println("Ignoring component version check"); //$NON-NLS-1$
1489                        }
1490                        return;
1491                }
1492                IApiProblem problem = null;
1493                String refversionval = reference.getVersion();
1494                String compversionval = component.getVersion();
1495                Version refversion = new Version(refversionval);
1496                Version compversion = new Version(compversionval);
1497                Version newversion = null;
1498                if (DEBUG) {
1499                        System.out.println("reference version of " + reference.getId() + " : " + refversion); //$NON-NLS-1$ //$NON-NLS-2$
1500                        System.out.println("component version of " + component.getId() + " : " + compversion); //$NON-NLS-1$ //$NON-NLS-2$
1501                }
1502                IDelta[] breakingChanges = fBuildState.getBreakingChanges();
1503                if (breakingChanges.length != 0) {
1504                        // make sure that the major version has been incremented
1505                        if (compversion.getMajor() <= refversion.getMajor()) {
1506                                newversion = new Version(compversion.getMajor() + 1, 0, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1507                                problem = createVersionProblem(
1508                                                IApiProblem.MAJOR_VERSION_CHANGE,
1509                                                new String[] {
1510                                                        compversionval,
1511                                                        refversionval
1512                                                },
1513                                                String.valueOf(newversion),
1514                                                collectDetails(breakingChanges));
1515                        }
1516                } else {
1517                        IDelta[] compatibleChanges = fBuildState.getCompatibleChanges();
1518                        if (compatibleChanges.length != 0) {
1519                                // only new API have been added
1520                                if (compversion.getMajor() != refversion.getMajor()) {
1521                                        if (!ignoreMajorVersionCheckWithoutBreakingChange()) {
1522                                                // major version should be identical
1523                                                newversion = new Version(refversion.getMajor(), refversion.getMinor() + 1, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1524                                                problem = createVersionProblem(
1525                                                                IApiProblem.MAJOR_VERSION_CHANGE_NO_BREAKAGE,
1526                                                                new String[] {
1527                                                                        compversionval,
1528                                                                        refversionval
1529                                                                },
1530                                                                String.valueOf(newversion),
1531                                                                collectDetails(compatibleChanges));
1532                                        }
1533                                } else if (compversion.getMinor() <= refversion.getMinor()) {
1534                                        // the minor version should be incremented
1535                                        newversion = new Version(compversion.getMajor(), compversion.getMinor() + 1, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1536                                        problem = createVersionProblem(
1537                                                        IApiProblem.MINOR_VERSION_CHANGE,
1538                                                        new String[] {
1539                                                                compversionval,
1540                                                                refversionval
1541                                                        },
1542                                                        String.valueOf(newversion),
1543                                                        collectDetails(compatibleChanges));
1544                                }
1545                        } else if (compversion.getMajor() != refversion.getMajor()) {
1546                                if (!ignoreMajorVersionCheckWithoutBreakingChange()) {
1547                                        // major version should be identical
1548                                        newversion = new Version(refversion.getMajor(), refversion.getMinor(), refversion.getMicro(), refversion.getQualifier() != null ? QUALIFIER : null);
1549                                        problem = createVersionProblem(
1550                                                        IApiProblem.MAJOR_VERSION_CHANGE_NO_BREAKAGE,
1551                                                        new String[] {
1552                                                                compversionval,
1553                                                                refversionval
1554                                                        },
1555                                                        String.valueOf(newversion),
1556                                                        Util.EMPTY_STRING);
1557                                }
1558                        } else if (compversion.getMinor() > refversion.getMinor()) {
1559                                // the minor version should not be incremented
1560                                if (!ignoreMinorVersionCheckWithoutApiChange()) {
1561                                        newversion = new Version(refversion.getMajor(), refversion.getMinor(), refversion.getMicro(), refversion.getQualifier() != null ? QUALIFIER : null);
1562                                        problem = createVersionProblem(
1563                                                        IApiProblem.MINOR_VERSION_CHANGE_NO_NEW_API,
1564                                                        new String[] {
1565                                                                compversionval,
1566                                                                refversionval
1567                                                        },
1568                                                        String.valueOf(newversion),
1569                                                        Util.EMPTY_STRING);
1570                                }
1571                        }
1572                        // analyze version of required components
1573                        ReexportedBundleVersionInfo info = null;
1574                        if (problem != null) {
1575                                switch (problem.getKind()) {
1576                                        case IApiProblem.MAJOR_VERSION_CHANGE_NO_BREAKAGE :
1577                                                // check if there is a version change required due to re-exported bundles
1578                                                info = checkBundleVersionsOfReexportedBundles(reference, component);
1579                                                if (info != null) {
1580                                                        switch(info.kind) {
1581                                                                case IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE :
1582                                                                        /* we don't do anything since the major version is already incremented
1583                                                                         * we cancel the previous issue. No need to report that the major version
1584                                                                         * should not be incremented */
1585                                                                        problem = null;
1586                                                                        break;
1587                                                                case IApiProblem.REEXPORTED_MINOR_VERSION_CHANGE :
1588                                                                        // we should reset the major version and increment only the minor version
1589                                                                        newversion = new Version(refversion.getMajor(), refversion.getMinor() + 1, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1590                                                                        problem = createVersionProblem(
1591                                                                                        IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE,
1592                                                                                        new String[] {
1593                                                                                                compversionval,
1594                                                                                                info.componentID,
1595                                                                                        },
1596                                                                                        String.valueOf(newversion),
1597                                                                                        Util.EMPTY_STRING);
1598                                                        }
1599                                                }
1600                                                break;
1601                                        case IApiProblem.MINOR_VERSION_CHANGE :
1602                                                // check if there is a version change required due to re-exported bundles
1603                                                info = checkBundleVersionsOfReexportedBundles(reference, component);
1604                                                if (info != null) {
1605                                                        switch(info.kind) {
1606                                                                case IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE :
1607                                                                        // we keep this problem
1608                                                                        newversion = new Version(compversion.getMajor() + 1, 0, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1609                                                                        problem = createVersionProblem(
1610                                                                                        info.kind,
1611                                                                                        new String[] {
1612                                                                                                compversionval,
1613                                                                                                info.componentID,
1614                                                                                        },
1615                                                                                        String.valueOf(newversion),
1616                                                                                        Util.EMPTY_STRING);
1617                                                                        break;
1618                                                                case IApiProblem.REEXPORTED_MINOR_VERSION_CHANGE :
1619                                                                        // we don't do anything since we should already increment the minor version
1620                                                        }
1621                                                }
1622                                                break;
1623                                        case IApiProblem.MINOR_VERSION_CHANGE_NO_NEW_API :
1624                                                // check if there is a version change required due to re-exported bundles
1625                                                info = checkBundleVersionsOfReexportedBundles(reference, component);
1626                                                if (info != null) {
1627                                                        switch(info.kind) {
1628                                                                case IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE :
1629                                                                        // we return this one
1630                                                                        newversion = new Version(compversion.getMajor() + 1, 0, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1631                                                                        problem = createVersionProblem(
1632                                                                                        info.kind,
1633                                                                                        new String[] {
1634                                                                                                compversionval,
1635                                                                                                info.componentID,
1636                                                                                        },
1637                                                                                        String.valueOf(newversion),
1638                                                                                        Util.EMPTY_STRING);
1639                                                                        break;
1640                                                                case IApiProblem.REEXPORTED_MINOR_VERSION_CHANGE :
1641                                                                        // we don't do anything since we already incremented the minor version
1642                                                                        // we get rid of the previous problem
1643                                                                        problem = null;
1644                                                        }
1645                                                }
1646                                }
1647                        } else {
1648                                info = checkBundleVersionsOfReexportedBundles(reference, component);
1649                                if (info != null) {
1650                                        switch(info.kind) {
1651                                                case IApiProblem.REEXPORTED_MAJOR_VERSION_CHANGE :
1652                                                        // major version change
1653                                                        if (compversion.getMajor() <= refversion.getMajor()) {
1654                                                                newversion = new Version(compversion.getMajor() + 1, 0, 0, compversion.getQualifier() != null ? QUALIFIER : null);
1655                                                                problem = createVersionProblem(
1656                                                                                info.kind,
1657                                                                                new String[] {
1658                                                                                        compversionval,
1659                                                                                        info.componentID,
1660                                                                                },
1661                                                                                String.valueOf(newversion),
1662                                                                                Util.EMPTY_STRING);
1663                                                        }
1664                                                        break;
1665                                                case IApiProblem.REEXPORTED_MINOR_VERSION_CHANGE :
1666                                                        // minor version change
1667                                                        if (compversion.getMinor() <= refversion.getMinor()) {
1668                                                                newversion = new Version(compversion.getMajor(), compversion.getMinor() + 1, 0, compversion.getQualifier());
1669                                                                problem = createVersionProblem(
1670                                                                        info.kind,
1671                                                                        new String[] {
1672                                                                                        compversionval,
1673                                                                                        info.componentID,
1674                                                                        },
1675                                                                        String.valueOf(newversion),
1676                                                                        Util.EMPTY_STRING);
1677                                                        }
1678                                        }
1679                                }
1680                        }
1681                }
1682                if(problem != null) {
1683                        addProblem(problem);
1684                }
1685        }
1686        
1687        /**
1688         * Collects details from the given delta listing for version problems
1689         * @param deltas
1690         * @return a {@link String} of the details why the version number should be changed
1691         */
1692        private String collectDetails(final IDelta[] deltas) {
1693                StringWriter writer = new StringWriter();
1694                PrintWriter printWriter = new PrintWriter(writer);
1695                //TODO contrived default for https://bugs.eclipse.org/bugs/show_bug.cgi?id=251313
1696                int max = Math.min(20, deltas.length);
1697                for (int i = 0; i < max; i++) {
1698                        printWriter.print("- "); //$NON-NLS-1$
1699                        printWriter.println(deltas[i].getMessage());
1700                        if(i == max-1 && max < deltas.length) {
1701                                printWriter.println(NLS.bind(BuilderMessages.BaseApiAnalyzer_more_version_problems, new Integer(deltas.length - max)));
1702                        }
1703                }
1704                printWriter.flush();
1705                printWriter.close();
1706                return String.valueOf(writer.getBuffer());
1707        }
1708        
1709        /**
1710         * Creates a marker on a manifest file for a version numbering problem and returns it
1711         * or <code>null</code> 
1712         * @param kind
1713         * @param messageargs
1714         * @param version
1715         * @param description the description of details
1716         * @return a new {@link IApiProblem} or <code>null</code>
1717         */
1718        private IApiProblem createVersionProblem(int kind, final String[] messageargs, String version, String description) {
1719                IResource manifestFile = null;
1720                String path = JarFile.MANIFEST_NAME;
1721                if (fJavaProject != null) {
1722                        manifestFile = Util.getManifestFile(fJavaProject.getProject());
1723                }
1724                // this error should be located on the manifest.mf file
1725                // first of all we check how many api breakage marker are there
1726                int lineNumber = -1;
1727                int charStart = 0;
1728                int charEnd = 1;
1729                char[] contents = null;
1730                if (manifestFile!= null && manifestFile.getType() == IResource.FILE) {
1731                        path = manifestFile.getProjectRelativePath().toPortableString();
1732                        IFile file = (IFile) manifestFile;
1733                        InputStream inputStream = null;
1734                        LineNumberReader reader = null;
1735                        try {
1736                                inputStream = file.getContents(true);
1737                                contents = Util.getInputStreamAsCharArray(inputStream, -1, IApiCoreConstants.UTF_8);
1738                                reader = new LineNumberReader(new BufferedReader(new StringReader(new String(contents))));
1739                                int lineCounter = 0;
1740                                String line = null;
1741                                loop: while ((line = reader.readLine()) != null) {
1742                                        lineCounter++;
1743                                        if (line.startsWith(Constants.BUNDLE_VERSION)) {
1744                                                lineNumber = lineCounter;
1745                                                break loop;
1746                                        }
1747                                }
1748                        } catch (CoreException e) {
1749                                // ignore
1750                        } catch (IOException e) {
1751                                // ignore
1752                        } finally {
1753                                try {
1754                                        if (inputStream != null) {
1755                                                inputStream.close();
1756                                        }
1757                                        if (reader != null) {
1758                                                reader.close();
1759                                        }
1760                                } catch (IOException e) {
1761                                        // ignore
1762                                }
1763                        }
1764                }
1765                if (lineNumber != -1 && contents != null) {
1766                        // initialize char start, char end
1767                        int index = CharOperation.indexOf(Constants.BUNDLE_VERSION.toCharArray(), contents, true);
1768                        loop: for (int i = index + Constants.BUNDLE_VERSION.length() + 1, max = contents.length; i < max; i++) {
1769                                char currentCharacter = contents[i];
1770                                if (CharOperation.isWhitespace(currentCharacter)) {
1771                                        continue;
1772                                }
1773                                charStart = i;
1774                                break loop;
1775                        }
1776                        loop: for (int i = charStart + 1, max = contents.length; i < max; i++) {
1777                                switch(contents[i]) {
1778                                        case '\r' :
1779                                        case '\n' :
1780                                                charEnd = i;
1781                                                break loop;
1782                                }
1783                        }
1784                } else {
1785                        lineNumber = 1;
1786                }
1787                return ApiProblemFactory.newApiVersionNumberProblem(path,
1788                                null,
1789                                messageargs, 
1790                                new String[] {
1791                                        IApiMarkerConstants.MARKER_ATTR_VERSION,
1792                                        IApiMarkerConstants.API_MARKER_ATTR_ID,
1793                                        IApiMarkerConstants.VERSION_NUMBERING_ATTR_DESCRIPTION,
1794                                }, 
1795                                new Object[] {
1796                                        version,
1797                                        new Integer(IApiMarkerConstants.VERSION_NUMBERING_MARKER_ID),
1798                                        description
1799                                }, 
1800                                lineNumber, 
1801                                charStart, 
1802                                charEnd, 
1803                                IElementDescriptor.RESOURCE, 
1804                                kind);
1805        }
1806        
1807        /**
1808         * Checks to see if there is a default API profile set in the workspace,
1809         * if not create a marker
1810         */
1811        private void checkDefaultBaselineSet() {
1812                if(ignoreDefaultBaselineCheck()) {
1813                        if(DEBUG) {
1814                                System.out.println("Ignoring check for default API baseline"); //$NON-NLS-1$
1815                        }
1816                        return;
1817                }
1818                if(DEBUG) {
1819                        System.out.println("Checking if the default api baseline is set"); //$NON-NLS-1$
1820                }
1821                IApiProblem problem = ApiProblemFactory.newApiBaselineProblem(
1822                                Path.EMPTY.toPortableString(),
1823                                new String[] {IApiMarkerConstants.API_MARKER_ATTR_ID},
1824                                new Object[] {new Integer(IApiMarkerConstants.DEFAULT_API_BASELINE_MARKER_ID)},
1825                                IElementDescriptor.RESOURCE,
1826                                IApiProblem.API_BASELINE_MISSING);
1827                addProblem(problem);
1828        }
1829        
1830        /**
1831         * Returns the Java project associated with the given API component, or <code>null</code>
1832         * if none.
1833         * 
1834         *@param component API component
1835         * @return Java project or <code>null</code>
1836         */
1837        private IJavaProject getJavaProject(IApiComponent component) {
1838                if (component instanceof PluginProjectApiComponent) {
1839                        PluginProjectApiComponent pp = (PluginProjectApiComponent) component;
1840                        return pp.getJavaProject();
1841                }
1842                return null;
1843        }
1844        
1845        /**
1846         * Adds the problem to the list of problems iff it is not <code>null</code> and not filtered
1847         * @param problem
1848         * @return
1849         */
1850        private boolean addProblem(IApiProblem problem) {
1851                if (problem == null || isProblemFiltered(problem)) {
1852                        return false;
1853                }
1854                return fProblems.add(problem);
1855        }
1856        /**
1857         * Returns if the given {@link IApiProblem} should be filtered from having a problem marker created for it
1858         * 
1859         * @param problem the problem that may or may not be filtered
1860         * @return true if the {@link IApiProblem} should not have a marker created, false otherwise
1861         */
1862        private boolean isProblemFiltered(IApiProblem problem) {
1863                if (fJavaProject == null) {
1864                        if (this.fFilterStore != null) {
1865                                boolean filtered = this.fFilterStore.isFiltered(problem);
1866                                if (filtered) {
1867                                        return true;
1868                                }
1869                        }
1870                        if (this.fPreferences != null) {
1871                                String key = ApiProblemFactory.getProblemSeverityId(problem);
1872                                if (key != null) {
1873                                        String value = this.fPreferences.getProperty(key, null);
1874                                        return ApiPlugin.VALUE_IGNORE.equals(value);
1875                                }
1876                        }
1877                        return false;
1878                }
1879 
1880                IProject project = fJavaProject.getProject();
1881                // first the severity is checked
1882                if (ApiPlugin.getDefault().getSeverityLevel(ApiProblemFactory.getProblemSeverityId(problem), project) == ApiPlugin.SEVERITY_IGNORE) {
1883                        return true;
1884                }
1885 
1886                IApiBaselineManager manager = ApiBaselineManager.getManager();
1887                IApiBaseline profile = manager.getWorkspaceBaseline();
1888                if(profile == null) {
1889                        return false;
1890                }
1891                IApiComponent component = profile.getApiComponent(project);
1892                if(component != null) {
1893                        try {
1894                                IApiFilterStore filterStore = component.getFilterStore();
1895                                if (filterStore != null) {
1896                                        return filterStore.isFiltered(problem);
1897                                }
1898                        }
1899                        catch(CoreException e) {}
1900                }
1901                return false;
1902        }
1903}

[all classes][org.eclipse.pde.api.tools.internal.builder]
EMMA 2.0.5312 EclEmma Fix 1 (C) Vladimir Roubtsov