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 [IncrementalApiBuilder.java]

nameclass, %method, %block, %line, %
IncrementalApiBuilder.java100% (2/2)100% (13/13)79%  (628/792)81%  (158.2/196)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class IncrementalApiBuilder100% (1/1)100% (11/11)79%  (568/722)80%  (140.9/177)
build (IApiBaseline, IApiBaseline, IResourceDelta [], State, BuildState, IPro... 100% (1/1)55%  (71/128)51%  (13.3/26)
findAffectedSourceFiles (IResourceDelta): void 100% (1/1)59%  (43/73)71%  (15/21)
extClean (IProject, BuildState, IProgressMonitor): void 100% (1/1)78%  (82/105)77%  (16.9/22)
addDependentsOf (String): void 100% (1/1)79%  (49/62)89%  (8/9)
collectAffectedSourceFiles (IProject, State): void 100% (1/1)84%  (74/88)86%  (19/22)
build (IProject, IApiBaseline, IApiBaseline, State, BuildState, IProgressMoni... 100% (1/1)87%  (80/92)85%  (24.6/29)
addDependentTypeToContext (IFile): void 100% (1/1)96%  (23/24)88%  (7/8)
addTypeToContext (IFile): void 100% (1/1)96%  (23/24)88%  (7/8)
resolveTypeName (IResource): String 100% (1/1)97%  (61/63)93%  (13/14)
collectInnerTypes (IFile): void 100% (1/1)97%  (38/39)91%  (10/11)
IncrementalApiBuilder (ApiAnalysisBuilder): void 100% (1/1)100% (24/24)100% (7/7)
     
class IncrementalApiBuilder$ResourceDeltaVisitor100% (1/1)100% (2/2)86%  (60/70)91%  (17.4/19)
visit (IResourceDelta): boolean 100% (1/1)81%  (42/52)87%  (11.4/13)
IncrementalApiBuilder$ResourceDeltaVisitor (IncrementalApiBuilder, IProject, ... 100% (1/1)100% (18/18)100% (6/6)

1/*******************************************************************************
2 * Copyright (c) 2009 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *     IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.pde.api.tools.internal.builder;
12 
13import java.util.HashSet;
14import java.util.Iterator;
15 
16import org.eclipse.core.resources.IFile;
17import org.eclipse.core.resources.IMarker;
18import org.eclipse.core.resources.IProject;
19import org.eclipse.core.resources.IResource;
20import org.eclipse.core.resources.IResourceDelta;
21import org.eclipse.core.resources.IResourceDeltaVisitor;
22import org.eclipse.core.resources.IWorkspaceRoot;
23import org.eclipse.core.resources.ResourcesPlugin;
24import org.eclipse.core.runtime.CoreException;
25import org.eclipse.core.runtime.IPath;
26import org.eclipse.core.runtime.IProgressMonitor;
27import org.eclipse.core.runtime.SubMonitor;
28import org.eclipse.jdt.core.ICompilationUnit;
29import org.eclipse.jdt.core.IType;
30import org.eclipse.jdt.core.JavaCore;
31import org.eclipse.jdt.core.JavaModelException;
32import org.eclipse.jdt.internal.core.builder.ReferenceCollection;
33import org.eclipse.jdt.internal.core.builder.State;
34import org.eclipse.jdt.internal.core.builder.StringSet;
35import org.eclipse.osgi.util.NLS;
36import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
37import org.eclipse.pde.api.tools.internal.provisional.IApiMarkerConstants;
38import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
39import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
40import org.eclipse.pde.api.tools.internal.util.Util;
41import org.eclipse.pde.core.plugin.IPluginModelBase;
42 
43/**
44 * Used to incrementally build changed Java types
45 * 
46 * @since 3.5
47 */
48public class IncrementalApiBuilder {
49 
50        /**
51         * Visits a resource delta to collect changes that need to be built
52         */
53        class ResourceDeltaVisitor implements IResourceDeltaVisitor {
54                HashSet projects = null;
55                IProject project = null;
56                
57                /**
58                 * Constructor
59                 * @param project
60                 * @param projects
61                 */
62                public ResourceDeltaVisitor(IProject project, HashSet projects) {
63                        this.project = project;
64                        this.projects = projects;
65                }
66                /* (non-Javadoc)
67                 * @see org.eclipse.core.resources.IResourceDeltaVisitor#visit(org.eclipse.core.resources.IResourceDelta)
68                 */
69                public boolean visit(IResourceDelta delta) throws CoreException {
70                        switch (delta.getResource().getType()) {
71                                case IResource.ROOT:
72                                case IResource.PROJECT:
73                                case IResource.FOLDER: {
74                                        return true; 
75                                }
76                                case IResource.FILE: {
77                                        IResource resource = delta.getResource();
78                                        String fileName = resource.getName();
79                                        if (Util.isClassFile(fileName)) {
80                                                findAffectedSourceFiles(delta);
81                                        } else if (Util.isJavaFileName(fileName)) {
82                                                IProject project = resource.getProject();
83                                                if (this.project.equals(project)) {
84                                                        addTypeToContext((IFile) resource);
85                                                } 
86                                                else if (this.projects != null && this.projects.contains(project)) {
87                                                        addTypeToContext((IFile) resource);
88                                                }
89                                        }
90                                }
91                        }
92                        return false;
93                }
94        }
95 
96        ApiAnalysisBuilder builder = null;
97        BuildContext context = null;
98        StringSet typenames = new StringSet(16);
99        StringSet packages = new StringSet(16);
100        
101        
102        /**
103         * Constructor
104         * @param project the current project context being built
105         * @param delta the {@link IResourceDelta} from the build framework
106         * @param buildstate the current build state from the {@link org.eclipse.jdt.internal.core.builder.JavaBuilder}
107         */
108        public IncrementalApiBuilder(ApiAnalysisBuilder builder) {
109                this.builder = builder;
110        }
111        
112        /**
113         * Incrementally builds using the {@link org.eclipse.pde.api.tools.internal.provisional.builder.IApiAnalyzer}
114         * from the given {@link ApiAnalysisBuilder}
115         * 
116         * @param baseline the baseline to compare with
117         * @param wbaseline the workspace baseline
118         * @param deltas the deltas to be built
119         * @param state the current JDT build state
120         * @param buildstate the current API tools build state
121         * @param monitor
122         * @throws CoreException
123         */
124        public void build(IApiBaseline baseline, IApiBaseline wbaseline, IResourceDelta[] deltas, State state, BuildState buildstate, IProgressMonitor monitor) throws CoreException {
125                IProject project = this.builder.getProject();
126                SubMonitor localmonitor = SubMonitor.convert(monitor, NLS.bind(BuilderMessages.IncrementalBuilder_builder_for_project, project.getName()), 1);
127                this.context = new BuildContext();
128                try {
129                        String[] projectNames = buildstate.getReexportedComponents();
130                        HashSet depprojects = null;
131                        if (projectNames.length != 0) {
132                                depprojects = new HashSet();
133                                IWorkspaceRoot root = ResourcesPlugin.getWorkspace().getRoot();
134                                IProject pj = null;
135                                for (int i = 0, max = projectNames.length; i < max; i++) {
136                                        pj = root.getProject(projectNames[i]);
137                                        if (pj.isAccessible()) {
138                                                // select only projects that don't exist in the reference baseline
139                                                if (baseline != null && baseline.getApiComponent(projectNames[i]) == null) {
140                                                        depprojects.add(pj);
141                                                }
142                                        }
143                                }
144                        }
145                        
146                        ResourceDeltaVisitor visitor = new ResourceDeltaVisitor(project, depprojects);
147                        for (int i = 0; i < deltas.length; i++) {
148                                deltas[i].accept(visitor);
149                        }
150                        build(project, baseline, wbaseline, state, buildstate, localmonitor.newChild(1));
151                }
152                finally {
153                        if(!localmonitor.isCanceled()) {
154                                localmonitor.done();
155                        }
156                        this.context.dispose();
157                        this.typenames.clear();
158                        this.packages.clear();
159                }
160        }
161        
162        /**
163         * Builds an API delta using the default profile (from the workspace settings and the current
164         * @param project
165         * @param baseline the baseline to compare to
166         * @param wbaseline the current workspace baseline
167         * @param state the current JDT build state
168         * @param buildstate the current API tools build state
169         * @param monitor
170         */
171        void build(final IProject project, final IApiBaseline baseline, final IApiBaseline wbaseline, final State state, BuildState buildstate, IProgressMonitor monitor) throws CoreException {
172                SubMonitor localmonitor = SubMonitor.convert(monitor, BuilderMessages.api_analysis_on_0, 6);
173                try {
174                        collectAffectedSourceFiles(project, state);
175                        Util.updateMonitor(localmonitor, 1);
176                        localmonitor.subTask(NLS.bind(BuilderMessages.ApiAnalysisBuilder_finding_affected_source_files, project.getName()));
177                        Util.updateMonitor(localmonitor, 0);
178                        if (this.context.hasTypes()) {
179                                IPluginModelBase currentModel = this.builder.getCurrentModel();
180                                if (currentModel != null) {
181                                        String id = currentModel.getBundleDescription().getSymbolicName();
182                                        IApiComponent comp = wbaseline.getApiComponent(id);
183                                        if(comp == null) {
184                                                return;
185                                        }
186                                        extClean(project, buildstate, localmonitor.newChild(1));
187                                        Util.updateMonitor(localmonitor, 1);
188                                        this.builder.getAnalyzer().analyzeComponent(buildstate, 
189                                                        null, 
190                                                        null, 
191                                                        baseline, 
192                                                        comp, 
193                                                        this.context, 
194                                                        localmonitor.newChild(1));
195                                        Util.updateMonitor(localmonitor, 1);
196                                        this.builder.createMarkers();
197                                        Util.updateMonitor(localmonitor, 1);
198                                }
199                        }
200                }
201                finally {
202                        if(localmonitor != null) {
203                                localmonitor.done();
204                        }
205                }
206        }
207        
208        /**
209         * Records the type name from the given IFile as a changed type 
210         * in the given build context
211         * @param file
212         */
213        void addTypeToContext(IFile file) {
214                String type = resolveTypeName(file);
215                if(type == null) {
216                        return;
217                }
218                if(!this.context.containsChangedType(type)) {
219                        this.builder.cleanupMarkers(file);
220                        //TODO implement detecting description changed types
221                        this.context.recordStructurallyChangedType(type);
222                        collectInnerTypes(file);
223                }
224        }
225        
226        /**
227         * Records the type name from the given IFile as a dependent type in the
228         * given build context
229         * @param file
230         */
231        private void addDependentTypeToContext(IFile file) {
232                String type = resolveTypeName(file);
233                if(type == null) {
234                        return;
235                }
236                if(!this.context.containsDependentType(type)) {
237                        this.builder.cleanupMarkers(file);
238                        this.context.recordDependentType(type);
239                        collectInnerTypes(file);
240                }
241        }
242        
243        /**
244         * Collects the inner types from the compilation unit
245         * @param file
246         */
247        private void collectInnerTypes(IFile file) {
248                ICompilationUnit unit = (ICompilationUnit) JavaCore.create(file);
249                IType[] types = null;
250                try {
251                        types = unit.getAllTypes();
252                        String typename = null;
253                        for (int i = 0; i < types.length; i++) {
254                                typename = types[i].getFullyQualifiedName('$');
255                                if(this.context.containsChangedType(typename)) {
256                                        continue;
257                                }
258                                this.context.recordDependentType(typename);
259                        }
260                }
261                catch(JavaModelException jme) {
262                        //do nothing, just don't consider types
263                }
264        }
265        
266        /**
267         * Collects the complete set of affected source files from the current project context based on the current JDT build state.
268         * 
269         * @param project the current project being built
270         * @param state the current JDT build state
271         */
272        void collectAffectedSourceFiles(final IProject project, State state) {
273                // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
274                char[][][] internedQualifiedNames = ReferenceCollection.internQualifiedNames(this.packages);
275                // if a well known qualified name was found then we can skip over these
276                if (internedQualifiedNames.length < this.packages.elementSize) {
277                        internedQualifiedNames = null;
278                }
279                char[][] internedSimpleNames = ReferenceCollection.internSimpleNames(this.typenames, true);
280                // if a well known name was found then we can skip over these
281                if (internedSimpleNames.length < this.typenames.elementSize) {
282                        internedSimpleNames = null;
283                }
284                Object[] keyTable = state.getReferences().keyTable;
285                Object[] valueTable = state.getReferences().valueTable;
286                IFile file = null;
287                String typeLocator = null;
288                for (int i = 0; i < valueTable.length; i++) {
289                        typeLocator =  (String) keyTable[i];
290                        if (typeLocator != null) {
291                                ReferenceCollection refs = (ReferenceCollection) valueTable[i];
292                                if (refs.includes(internedQualifiedNames, internedSimpleNames, null)) {
293                                        file = project.getFile(typeLocator);
294                                        if (file == null) {
295                                                continue;
296                                        }
297                                        if (ApiAnalysisBuilder.DEBUG) {
298                                                System.out.println("  adding affected source file " + file.getName()); //$NON-NLS-1$
299                                        }
300                                        addDependentTypeToContext(file);
301                                }
302                        }
303                }
304        }
305        
306        /**
307         * Finds affected source files for a resource that has changed that either contains class files or is itself a class file
308         * @param binaryDelta
309         */
310        void findAffectedSourceFiles(IResourceDelta binaryDelta) {
311                IResource resource = binaryDelta.getResource();
312                if(resource.getType() == IResource.FILE) {
313                        String typename = resolveTypeName(resource);
314                        if(typename == null) {
315                                return;
316                        }
317                        switch (binaryDelta.getKind()) {
318                                case IResourceDelta.REMOVED : {
319                                        if (ApiAnalysisBuilder.DEBUG) {
320                                                System.out.println("Found removed class file " + typename); //$NON-NLS-1$
321                                        }
322                                        //directly add the removed type
323                                        this.context.recordStructurallyChangedType(typename);
324                                        this.context.recordRemovedType(typename);
325                                }
326                                        //$FALL-THROUGH$
327                                case IResourceDelta.ADDED : {
328                                        if (ApiAnalysisBuilder.DEBUG) {
329                                                System.out.println("Found added class file " + typename); //$NON-NLS-1$
330                                        }
331                                        addDependentsOf(typename);
332                                        return;
333                                }
334                                case IResourceDelta.CHANGED : {
335                                        if ((binaryDelta.getFlags() & IResourceDelta.CONTENT) == 0) {
336                                                return; // skip it since it really isn't changed
337                                        }
338                                        if (ApiAnalysisBuilder.DEBUG) {
339                                                System.out.println("Found changed class file " + typename); //$NON-NLS-1$
340                                        }
341                                        addDependentsOf(typename);
342                                }
343                        }
344                        return;
345                }
346        }
347        
348        /**
349         * Adds a type to search for dependents of in considered projects for an incremental build
350         * 
351         * @param path
352         */
353        void addDependentsOf(String typename) {
354                // the qualifiedStrings are of the form 'p1/p2' & the simpleStrings are just 'X'
355                int idx = typename.lastIndexOf('.');
356                String packageName = (idx < 0 ? Util.EMPTY_STRING : typename.substring(0, idx));
357                String typeName = (idx < 0 ? typename : typename.substring(idx+1, typename.length()));
358                idx = typeName.indexOf('$');
359                if (idx > 0) {
360                        typeName = typeName.substring(0, idx);
361                }
362                if (this.typenames.add(typeName) && this.packages.add(packageName) && ApiAnalysisBuilder.DEBUG) {
363                        System.out.println("  will look for dependents of " + typeName + " in " + packageName);  //$NON-NLS-1$ //$NON-NLS-2$
364                }
365        }
366        
367        /**
368         * Returns an array of type names, and cleans up markers for the specified resource
369         * @param project the project being built
370         * @param state the current build state for the given project
371         * @param monitor
372         */
373        void extClean(final IProject project, BuildState state, IProgressMonitor monitor) throws CoreException {
374                //clean up the state - https://bugs.eclipse.org/bugs/show_bug.cgi?id=271110
375                String[] types = this.context.getRemovedTypes();
376                for (int i = 0; i < types.length; i++) {
377                        state.cleanup(types[i]);
378                }
379                Util.updateMonitor(monitor, 0);
380                IResource resource = project.findMember(ApiAnalysisBuilder.MANIFEST_PATH);
381                if (resource != null) {
382                        try {
383                                //TODO we should find a way to cache markers to type names, that way to get all
384                                //the manifest markers for a given type name is time of O(1)
385                                IMarker[] markers = resource.findMarkers(IApiMarkerConstants.COMPATIBILITY_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
386                                String tname = null; 
387                                for (int i = 0; i < markers.length; i++) {
388                                        tname = Util.getTypeNameFromMarker(markers[i]);
389                                        if(this.context.containsDependentType(tname) || this.context.containsChangedType(tname)) {
390                                                markers[i].delete();
391                                        }
392                                }
393                                Util.updateMonitor(monitor, 0);
394                                //TODO we should find a way to cache markers to type names, that way to get all
395                                //the manifest markers for a given type name is time of O(1)
396                                markers = resource.findMarkers(IApiMarkerConstants.UNUSED_FILTER_PROBLEM_MARKER, false, IResource.DEPTH_ZERO);
397                                for (int i = 0; i < markers.length; i++) {
398                                        tname = Util.getTypeNameFromMarker(markers[i]);
399                                        if(this.context.containsDependentType(tname) || this.context.containsChangedType(tname)) {
400                                                markers[i].delete();
401                                        }
402                                }
403                                Util.updateMonitor(monitor, 0);
404                        } catch (CoreException e) {
405                                ApiPlugin.log(e);
406                        }
407                }
408        }
409 
410        /**
411         * Resolves the java path from the given resource
412         * @param resource
413         * @return the resolved path or <code>null</code> if the resource is not part of the java model
414         */
415        String resolveTypeName(IResource resource) {
416                IPath typepath = resource.getFullPath();
417                HashSet paths = null;
418                if(Util.isClassFile(resource.getName())) {
419                        paths = (HashSet) this.builder.output_locs.get(resource.getProject());
420                }
421                else if(Util.isJavaFileName(resource.getName())) {
422                        paths = (HashSet) this.builder.src_locs.get(resource.getProject());
423                }
424                if(paths != null) {
425                        IPath path = null;
426                        for (Iterator iterator = paths.iterator(); iterator.hasNext();) {
427                                path = (IPath) iterator.next();
428                                if(path.isPrefixOf(typepath)) {
429                                        typepath = typepath.removeFirstSegments(path.segmentCount()).removeFileExtension();
430                                        return typepath.toString().replace('/', '.');
431                                }
432                        }
433                }
434                return null;
435        }
436}

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