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

COVERAGE SUMMARY FOR SOURCE FILE [ApiBaselineManager.java]

nameclass, %method, %block, %line, %
ApiBaselineManager.java100% (2/2)89%  (31/35)62%  (917/1488)66%  (248.2/376)

COVERAGE BREAKDOWN BY CLASS AND METHOD

nameclass, %method, %block, %line, %
     
class ApiBaselineManager$1100% (1/1)50%  (1/2)55%  (6/11)67%  (2/3)
accept (File): boolean 0%   (0/1)0%   (0/5)0%   (0/1)
ApiBaselineManager$1 (ApiBaselineManager): void 100% (1/1)100% (6/6)100% (2/2)
     
class ApiBaselineManager100% (1/1)91%  (30/33)62%  (911/1477)66%  (247.2/374)
abort (String, Throwable): void 0%   (0/1)0%   (0/11)0%   (0/1)
restoreBaseline (IApiBaseline, InputStream): void 0%   (0/1)0%   (0/253)0%   (0/59)
setDebug (boolean): void 0%   (0/1)0%   (0/9)0%   (0/2)
loadBaselineInfos (IApiBaseline): void 100% (1/1)26%  (18/70)26%  (5/19)
handlePackageRemoval (IProject, IPackageFragment): void 100% (1/1)43%  (9/21)75%  (3/4)
initializeStateCache (): void 100% (1/1)48%  (54/112)63%  (13.9/22)
writeBaselineDescription (IApiBaseline, OutputStream): void 100% (1/1)55%  (11/20)43%  (3/7)
createWorkspaceBaseline (): IApiBaseline 100% (1/1)61%  (53/87)69%  (12.4/18)
processJavaElementDeltas (IJavaElementDelta [], IJavaProject): void 100% (1/1)67%  (146/217)80%  (40/50)
acceptProject (IProject): boolean 100% (1/1)79%  (11/14)33%  (1/3)
stop (): void 100% (1/1)79%  (59/75)78%  (15.7/20)
resourceChanged (IResourceChangeEvent): void 100% (1/1)80%  (66/82)80%  (20/25)
saving (ISaveContext): void 100% (1/1)81%  (13/16)75%  (6/8)
isExistingProfileName (String): boolean 100% (1/1)82%  (9/11)67%  (2/3)
getWorkspaceBaseline (): IApiBaseline 100% (1/1)83%  (15/18)71%  (5/7)
disposeWorkspaceBaseline (IProject): void 100% (1/1)88%  (22/25)88%  (7/8)
getProfileXML (IApiBaseline): String 100% (1/1)95%  (76/80)95%  (18/19)
persistStateCache (): void 100% (1/1)96%  (109/114)91%  (26.3/29)
removeApiBaseline (String): boolean 100% (1/1)96%  (51/53)93%  (14/15)
<static initializer> 100% (1/1)100% (5/5)100% (3/3)
ApiBaselineManager (boolean): void 100% (1/1)100% (43/43)100% (14/14)
addApiBaseline (IApiBaseline): void 100% (1/1)100% (25/25)100% (7/7)
cleanStateCache (): void 100% (1/1)100% (36/36)100% (8/8)
doneSaving (ISaveContext): void 100% (1/1)100% (1/1)100% (1/1)
elementChanged (ElementChangedEvent): void 100% (1/1)100% (16/16)100% (6/6)
getApiBaseline (String): IApiBaseline 100% (1/1)100% (8/8)100% (2/2)
getApiBaselines (): IApiBaseline [] 100% (1/1)100% (12/12)100% (2/2)
getDefaultApiBaseline (): IApiBaseline 100% (1/1)100% (9/9)100% (2/2)
getDefaultProfilePref (): String 100% (1/1)100% (16/16)100% (2/2)
getManager (): ApiBaselineManager 100% (1/1)100% (9/9)100% (3/3)
prepareToSave (ISaveContext): void 100% (1/1)100% (1/1)100% (1/1)
rollback (ISaveContext): void 100% (1/1)100% (1/1)100% (1/1)
setDefaultApiBaseline (String): void 100% (1/1)100% (7/7)100% (3/3)

1/*******************************************************************************
2 * Copyright (c) 2007, 2009 IBM Corporation and others.
3 * All rights reserved. This program and the accompanying materials
4 * are made available under the terms of the Eclipse Public License v1.0
5 * which accompanies this distribution, and is available at
6 * http://www.eclipse.org/legal/epl-v10.html
7 *
8 * Contributors:
9 *     IBM Corporation - initial API and implementation
10 *******************************************************************************/
11package org.eclipse.pde.api.tools.internal;
12 
13import java.io.File;
14import java.io.FileFilter;
15import java.io.FileInputStream;
16import java.io.FileOutputStream;
17import java.io.IOException;
18import java.io.InputStream;
19import java.io.OutputStream;
20import java.io.UnsupportedEncodingException;
21import java.util.ArrayList;
22import java.util.HashMap;
23import java.util.HashSet;
24import java.util.Iterator;
25import java.util.List;
26 
27import javax.xml.parsers.DocumentBuilder;
28import javax.xml.parsers.DocumentBuilderFactory;
29import javax.xml.parsers.FactoryConfigurationError;
30import javax.xml.parsers.ParserConfigurationException;
31 
32import org.eclipse.core.resources.IProject;
33import org.eclipse.core.resources.IResource;
34import org.eclipse.core.resources.IResourceChangeEvent;
35import org.eclipse.core.resources.IResourceChangeListener;
36import org.eclipse.core.resources.IResourceDelta;
37import org.eclipse.core.resources.ISaveContext;
38import org.eclipse.core.resources.ISaveParticipant;
39import org.eclipse.core.resources.ResourcesPlugin;
40import org.eclipse.core.runtime.CoreException;
41import org.eclipse.core.runtime.IPath;
42import org.eclipse.core.runtime.IStatus;
43import org.eclipse.core.runtime.Path;
44import org.eclipse.core.runtime.Platform;
45import org.eclipse.core.runtime.Status;
46import org.eclipse.core.runtime.preferences.IEclipsePreferences;
47import org.eclipse.core.runtime.preferences.IPreferencesService;
48import org.eclipse.core.runtime.preferences.IScopeContext;
49import org.eclipse.core.runtime.preferences.InstanceScope;
50import org.eclipse.jdt.core.ElementChangedEvent;
51import org.eclipse.jdt.core.IElementChangedListener;
52import org.eclipse.jdt.core.IJavaElement;
53import org.eclipse.jdt.core.IJavaElementDelta;
54import org.eclipse.jdt.core.IJavaProject;
55import org.eclipse.jdt.core.IPackageFragment;
56import org.eclipse.jdt.core.IPackageFragmentRoot;
57import org.eclipse.jdt.core.JavaCore;
58import org.eclipse.pde.api.tools.internal.model.ApiBaseline;
59import org.eclipse.pde.api.tools.internal.model.ApiModelCache;
60import org.eclipse.pde.api.tools.internal.model.ApiModelFactory;
61import org.eclipse.pde.api.tools.internal.model.StubApiComponent;
62import org.eclipse.pde.api.tools.internal.provisional.ApiPlugin;
63import org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager;
64import org.eclipse.pde.api.tools.internal.provisional.model.IApiBaseline;
65import org.eclipse.pde.api.tools.internal.provisional.model.IApiComponent;
66import org.eclipse.pde.api.tools.internal.util.Util;
67import org.eclipse.pde.core.plugin.IPluginModelBase;
68import org.eclipse.pde.core.plugin.PluginRegistry;
69import org.w3c.dom.Document;
70import org.w3c.dom.Element;
71import org.w3c.dom.NodeList;
72import org.xml.sax.SAXException;
73import org.xml.sax.helpers.DefaultHandler;
74 
75/**
76 * This manager is used to maintain (persist, restore, access, update) Api baselines.
77 * This manager is lazy, in that caches are built and maintained when requests
78 * are made for information, nothing is pre-loaded when the manager is initialized.
79 * 
80 * @since 1.0.0
81 * @noinstantiate This class is not intended to be instantiated by clients.
82 */
83public final class ApiBaselineManager implements IApiBaselineManager, ISaveParticipant, IElementChangedListener, IResourceChangeListener {
84        
85        /**
86         * Constant used for controlling tracing in the API tool builder
87         */
88        private static boolean DEBUG = Util.DEBUG;
89        
90        /**
91         * Method used for initializing tracing in the API tool builder
92         */
93        public static void setDebug(boolean debugValue) {
94                DEBUG = debugValue || Util.DEBUG;
95        }
96        
97        /**
98         * Constant for the default API baseline.
99         * Value is: <code>default_api_profile</code>
100         */
101        private static final String DEFAULT_BASELINE = "default_api_profile"; //$NON-NLS-1$
102        
103        /**
104         * Constant representing the id of the workspace {@link IApiBaseline}.
105         * Value is: <code>workspace</code>
106         */
107        public static final String WORKSPACE_API_BASELINE_ID = "workspace"; //$NON-NLS-1$
108        
109        /**
110         * Constant representing the file extension for a baseline file.
111         * Value is: <code>.profile</code>
112         */
113        private static final String BASELINE_FILE_EXTENSION = ".profile"; //$NON-NLS-1$
114        
115        /**
116         * The main cache for the manager.
117         * The form of the cache is: 
118         * <pre>
119         * HashMap<String(baselineid), {@link IApiBaseline}>
120         * </pre>
121         */
122        private HashMap baselinecache = null;
123        
124        /**
125         * Cache of baseline names to the location with their infos in it
126         */
127        private HashMap handlecache = null;
128        
129        private HashSet hasinfos = null;
130        
131        /**
132         * The current default {@link IApiBaseline}
133         */
134        private String defaultbaseline = null;
135        
136        /**
137         * The current workspace baseline
138         */
139        private IApiBaseline workspacebaseline = null;
140        
141        /**
142         * The default save location for persisting the cache from this manager.
143         */
144        private IPath savelocation = null;
145        
146        /**
147         * If the cache of profiles needs to be saved or not.
148         */
149        private boolean fNeedsSaving = false;
150        
151        /**
152         * The singleton instance
153         */
154        private static ApiBaselineManager fInstance = null;
155        
156        /**
157         * Constructor
158         */
159        private ApiBaselineManager(boolean framework) {
160                if(framework) {
161                        ApiPlugin.getDefault().addSaveParticipant(this);
162                        JavaCore.addElementChangedListener(this, ElementChangedEvent.POST_CHANGE);
163                        ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_BUILD);
164                        savelocation = ApiPlugin.getDefault().getStateLocation().append(".api_profiles").addTrailingSeparator(); //$NON-NLS-1$
165                }
166        }
167        
168        /**
169         * Returns the singleton instance of the manager
170         * @return the singleton instance of the manager
171         */
172        public static synchronized ApiBaselineManager getManager() {
173                if(fInstance == null) {
174                        fInstance = new ApiBaselineManager(ApiPlugin.isRunningInFramework());
175                }
176                return fInstance;
177        }
178        
179        /* (non-Javadoc)
180         * @see org.eclipse.pde.api.tools.IApiBaselineManager#getApiProfile(java.lang.String)
181         */
182        public synchronized IApiBaseline getApiBaseline(String name) {
183                initializeStateCache();
184                return (ApiBaseline) baselinecache.get(name);
185        }
186 
187        /* (non-Javadoc)
188         * @see org.eclipse.pde.api.tools.IApiBaselineManager#getApiProfiles()
189         */
190        public synchronized IApiBaseline[] getApiBaselines() {
191                initializeStateCache();
192                return (IApiBaseline[]) baselinecache.values().toArray(new IApiBaseline[baselinecache.size()]);
193        }
194        
195        /* (non-Javadoc)
196         * @see org.eclipse.pde.api.tools.IApiBaselineManager#addApiProfile(org.eclipse.pde.api.tools.model.component.IApiBaseline)
197         */
198        public synchronized void addApiBaseline(IApiBaseline newbaseline) {
199                if(newbaseline != null) {
200                        initializeStateCache();
201                        baselinecache.put(newbaseline.getName(), newbaseline);
202                        if(((ApiBaseline)newbaseline).peekInfos()) {
203                                hasinfos.add(newbaseline.getName());
204                        }
205                        fNeedsSaving = true;
206                }
207        }
208        
209        /* (non-Javadoc)
210         * @see org.eclipse.pde.api.tools.IApiBaselineManager#removeApiProfile(java.lang.String)
211         */
212        public synchronized boolean removeApiBaseline(String name) {
213                if(name != null) {
214                        initializeStateCache();
215                        IApiBaseline profile = (IApiBaseline) baselinecache.remove(name);
216                        if(profile != null) {
217                                profile.dispose();
218                                boolean success = true;
219                                if(savelocation == null) {
220                                        return success;
221                                }
222                                //remove from filesystem
223                                File file = savelocation.append(name+BASELINE_FILE_EXTENSION).toFile();
224                                if(file.exists()) {
225                                        success &= file.delete();
226                                }
227                                fNeedsSaving = true;
228                                
229                                //flush the model cache
230                                ApiModelCache.getCache().removeElementInfo(profile);
231                                return success;
232                        }
233                }
234                return false;
235        }
236        
237        /**
238         * Loads the infos for the given baseline from persisted storage (the *.profile file)
239         * @param baseline
240         * @throws CoreException
241         */
242        public void loadBaselineInfos(IApiBaseline baseline) throws CoreException {
243                initializeStateCache();
244                if(hasinfos.contains(baseline.getName())) {
245                        return;
246                }
247                String filename = (String) handlecache.get(baseline.getName());
248                if(filename != null) {
249                        File file = new File(filename);
250                        if(file.exists()) {
251                                FileInputStream inputStream = null;
252                                try {
253                                        inputStream = new FileInputStream(file);
254                                        restoreBaseline(baseline, inputStream);
255                                } catch (IOException e) {
256                                        ApiPlugin.log(e);
257                                } finally {
258                                        if (inputStream != null) {
259                                                try {
260                                                        inputStream.close();
261                                                } catch(IOException e) {
262                                                        // ignore
263                                                }
264                                        }
265                                }
266                                hasinfos.add(baseline.getName());
267                        }
268                }
269        }
270        
271        /**
272         * Initializes the profile cache lazily. Only performs work
273         * if the current cache has not been created yet
274         * @throws FactoryConfigurationError 
275         * @throws ParserConfigurationException 
276         */
277        private synchronized void initializeStateCache() {
278                long time = System.currentTimeMillis();
279                if(baselinecache == null) {
280                        handlecache = new HashMap(8);
281                        hasinfos = new HashSet(8);
282                        baselinecache = new HashMap(8);
283                        if(!ApiPlugin.isRunningInFramework()) {
284                                return;
285                        }
286                        File[] baselines = savelocation.toFile().listFiles(new FileFilter() {
287                                public boolean accept(File pathname) {
288                                        return pathname.getName().endsWith(BASELINE_FILE_EXTENSION);
289                                }
290                        });
291                        if(baselines != null) {
292                                IApiBaseline newbaseline = null;
293                                for(int i = 0; i < baselines.length; i++) {
294                                        File baseline = baselines[i];
295                                        if(baseline.exists()) {
296                                                newbaseline = new ApiBaseline(new Path(baseline.getName()).removeFileExtension().toString());
297                                                handlecache.put(newbaseline.getName(), baseline.getAbsolutePath());
298                                                baselinecache.put(newbaseline.getName(), newbaseline);
299                                        }
300                                }
301                        }
302                        String def = getDefaultProfilePref();
303                        IApiBaseline baseline = (IApiBaseline) baselinecache.get(def);
304                        defaultbaseline = (baseline != null ? def : null);
305                        if(DEBUG) {
306                                System.out.println("Time to initialize state cache: " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
307                        }
308                }
309        }
310        
311        /**
312         * @return the default API baseline saved in the preferences, or <code>null</code> if there isn't one
313         */
314        private String getDefaultProfilePref() {
315                IPreferencesService service = Platform.getPreferencesService();
316                return service.getString(ApiPlugin.PLUGIN_ID, DEFAULT_BASELINE, null, new IScopeContext[] {new InstanceScope()});
317        }
318        
319        /**
320         * Persists all of the cached elements to individual xml files named 
321         * with the id of the API profile
322         * @throws IOException 
323         */
324        private void persistStateCache() throws CoreException, IOException {
325                if(savelocation == null) {
326                        return;
327                }
328                IEclipsePreferences node = new InstanceScope().getNode(ApiPlugin.PLUGIN_ID);
329                if(defaultbaseline != null) {
330                        node.put(DEFAULT_BASELINE, defaultbaseline);
331                }
332                else {
333                        node.remove(DEFAULT_BASELINE);
334                }
335                if(baselinecache != null && hasinfos != null) {
336                        File dir = new File(savelocation.toOSString());
337                        if(!dir.exists()) {
338                                dir.mkdirs();
339                        }
340                        String id = null;
341                        File file = null;
342                        FileOutputStream fout = null;
343                        IApiBaseline baseline = null;
344                        for(Iterator iter = baselinecache.keySet().iterator(); iter.hasNext();) {
345                                id = (String) iter.next();
346                                baseline = (IApiBaseline) baselinecache.get(id);
347                                if(!hasinfos.contains(baseline.getName())) {
348                                        continue;
349                                }
350                                file = savelocation.append(id+BASELINE_FILE_EXTENSION).toFile();
351                                if(!file.exists()) {
352                                        file.createNewFile();
353                                }
354                                try {
355                                        fout = new FileOutputStream(file);
356                                        writeBaselineDescription(baseline, fout);
357                                        fout.flush();
358                                }
359                                finally {
360                                        fout.close();
361                                }
362                        }
363                }
364        }        
365        
366        /**
367         * Writes out the current state of the {@link IApiBaseline} as XML
368         * to the given output stream
369         * @param stream
370         * @throws CoreException
371         */
372        private void writeBaselineDescription(IApiBaseline baseline, OutputStream stream) throws CoreException {
373                String xml = getProfileXML(baseline);
374                try {
375                        stream.write(xml.getBytes(IApiCoreConstants.UTF_8));
376                } catch (UnsupportedEncodingException e) {
377                        abort("Error writing pofile descrition", e); //$NON-NLS-1$
378                } catch (IOException e) {
379                        abort("Error writing pofile descrition", e); //$NON-NLS-1$
380                }
381        }
382 
383        /**
384         * Returns an XML description of the given profile.
385         * 
386         * @param profile API profile
387         * @return XML
388         * @throws CoreException
389         */
390        private String getProfileXML(IApiBaseline profile) throws CoreException {
391                Document document = Util.newDocument();
392                Element root = document.createElement(IApiXmlConstants.ELEMENT_APIPROFILE);
393                document.appendChild(root);
394                root.setAttribute(IApiXmlConstants.ATTR_NAME, profile.getName());
395                root.setAttribute(IApiXmlConstants.ATTR_VERSION, IApiXmlConstants.API_PROFILE_CURRENT_VERSION);
396                String location = profile.getLocation();
397                if (location != null) {
398                        root.setAttribute(IApiXmlConstants.ATTR_LOCATION, location);
399                }
400                Element celement = null;
401                IApiComponent[] components = profile.getApiComponents();
402                for(int i = 0, max = components.length; i < max; i++) {
403                        IApiComponent comp = components[i];
404                        if (!comp.isSystemComponent()) {
405                                celement = document.createElement(IApiXmlConstants.ELEMENT_APICOMPONENT);
406                                celement.setAttribute(IApiXmlConstants.ATTR_ID, comp.getId());
407                                celement.setAttribute(IApiXmlConstants.ATTR_VERSION, comp.getVersion());
408                                celement.setAttribute(IApiXmlConstants.ATTR_LOCATION, new Path(comp.getLocation()).toPortableString());
409                                root.appendChild(celement);
410                        }
411                }
412                return Util.serializeDocument(document);
413        }
414        
415        /**
416         * Throws a core exception with the given message and underlying exception,
417         * if any.
418         * 
419         * @param message error message
420         * @param e underlying exception or <code>null</code>
421         * @throws CoreException
422         */
423        private static void abort(String message, Throwable e) throws CoreException {
424                throw new CoreException(new Status(IStatus.ERROR, ApiPlugin.PLUGIN_ID, message, e));
425        }        
426        
427        /**
428         * Constructs and returns a profile from the given input stream (persisted profile).
429         * 
430         * @param stream input stream
431         * @return API profile
432         * @throws CoreException if unable to restore the profile
433         */
434        private void restoreBaseline(IApiBaseline baseline, InputStream stream) throws CoreException {
435                long start = System.currentTimeMillis();
436                DocumentBuilder parser = null;
437                try {
438                        parser = DocumentBuilderFactory.newInstance().newDocumentBuilder();
439                        parser.setErrorHandler(new DefaultHandler());
440                } catch (ParserConfigurationException e) {
441                        abort("Error restoring API baseline", e); //$NON-NLS-1$
442                } catch (FactoryConfigurationError e) {
443                        abort("Error restoring API baseline", e); //$NON-NLS-1$
444                }
445                try {
446                        Document document = parser.parse(stream);
447                        Element root = document.getDocumentElement();
448                        if(root.getNodeName().equals(IApiXmlConstants.ELEMENT_APIPROFILE)) {
449                                String baselineLocation = root.getAttribute(IApiXmlConstants.ATTR_LOCATION);
450                                if (baselineLocation != null && !baselineLocation.equals(Util.EMPTY_STRING)) {
451                                        baseline.setLocation(Path.fromPortableString(baselineLocation).toOSString());
452                                }
453                                // un-pooled components
454                                NodeList children = root.getElementsByTagName(IApiXmlConstants.ELEMENT_APICOMPONENT);
455                                List components = new ArrayList();
456                                for(int j = 0; j < children.getLength(); j++) {
457                                        Element componentNode = (Element) children.item(j);
458                                        // this also contains components in pools, so don't process them
459                                        if (componentNode.getParentNode().equals(root)) {
460                                                String location = componentNode.getAttribute(IApiXmlConstants.ATTR_LOCATION);
461                                                IApiComponent component = ApiModelFactory.newApiComponent(baseline, Path.fromPortableString(location).toOSString());
462                                                if(component != null) {
463                                                        components.add(component);
464                                                }
465                                        }
466                                }
467                                // pooled components - only for xml file with version <= 1
468                                // since version 2, pools have been removed
469                                children = root.getElementsByTagName(IApiXmlConstants.ELEMENT_POOL);
470                                IApiComponent component = null;
471                                for(int j = 0; j < children.getLength(); j++) {
472                                        String location = ((Element) children.item(j)).getAttribute(IApiXmlConstants.ATTR_LOCATION);
473                                        IPath poolPath = Path.fromPortableString(location);
474                                        NodeList componentNodes = root.getElementsByTagName(IApiXmlConstants.ELEMENT_APICOMPONENT);
475                                        for (int i = 0; i < componentNodes.getLength(); i++) {
476                                                Element compElement = (Element) componentNodes.item(i);
477                                                String id = compElement.getAttribute(IApiXmlConstants.ATTR_ID);
478                                                String ver = compElement.getAttribute(IApiXmlConstants.ATTR_VERSION);
479                                                StringBuffer name = new StringBuffer();
480                                                name.append(id);
481                                                name.append('_');
482                                                name.append(ver);
483                                                File file = poolPath.append(name.toString()).toFile();
484                                                if (!file.exists()) {
485                                                        name.append(".jar"); //$NON-NLS-1$
486                                                        file = poolPath.append(name.toString()).toFile();
487                                                }
488                                                component = ApiModelFactory.newApiComponent(baseline, file.getAbsolutePath());
489                                                if(component != null) {
490                                                        components.add(component);
491                                                }
492                                        }
493                                }
494                                baseline.addApiComponents((IApiComponent[]) components.toArray(new IApiComponent[components.size()]));
495                        }
496                } catch (IOException e) {
497                        abort("Error restoring API baseline", e); //$NON-NLS-1$
498                } catch(SAXException e) {
499                        abort("Error restoring API baseline", e); //$NON-NLS-1$
500                } finally {
501                        try {
502                                stream.close();
503                        } catch (IOException io) {
504                                ApiPlugin.log(io);
505                        }
506                }
507                if (baseline == null) {
508                        abort("Invalid profile description", null); //$NON-NLS-1$
509                }
510                if(DEBUG) {
511                        System.out.println("Time to restore a persisted profile : " + (System.currentTimeMillis() - start) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
512                }
513        }
514        
515        /* (non-Javadoc)
516         * @see org.eclipse.core.resources.ISaveParticipant#saving(org.eclipse.core.resources.ISaveContext)
517         */
518        public void saving(ISaveContext context) throws CoreException {
519                if(!fNeedsSaving) {
520                        return;
521                }
522                try {
523                        persistStateCache();
524                        cleanStateCache();
525                        fNeedsSaving = false;
526                } catch (IOException e) {
527                        ApiPlugin.log(e);
528                }
529        }
530        
531        /**
532         * Cleans out all but the default baseline from the in-memory cache of baselines
533         */
534        private void cleanStateCache() {
535                if(baselinecache != null) {
536                        IApiBaseline baseline = null;
537                        for(Iterator iter = baselinecache.keySet().iterator(); iter.hasNext();) {
538                                baseline = (IApiBaseline) baselinecache.get(iter.next());
539                                if(!baseline.getName().equals(defaultbaseline)) {
540                                        baseline.dispose();
541                                        hasinfos.remove(baseline.getName());
542                                        //iter.remove();
543                                }
544                        }
545                }
546        }
547        
548        /**
549         * Returns if the given name is an existing profile name
550         * @param name
551         * @return true if the given name is an existing profile name, false otherwise
552         */
553        public boolean isExistingProfileName(String name) {
554                if(baselinecache == null) {
555                        return false;
556                }
557                return baselinecache.keySet().contains(name);
558        }
559        
560        /**
561         * Cleans up the manager
562         */
563        public void stop() {
564                try {
565                        if(baselinecache != null) {
566                                // we should first dispose all existing profiles
567                                for (Iterator iterator = this.baselinecache.values().iterator(); iterator.hasNext();) {
568                                        IApiBaseline profile = (IApiBaseline) iterator.next();
569                                        profile.dispose();
570                                }
571                                this.baselinecache.clear();
572                        }
573                        synchronized (this) {
574                                if(this.workspacebaseline != null) {
575                                        this.workspacebaseline.dispose();
576                                }
577                        }
578                        if(this.handlecache != null) {
579                                this.handlecache.clear();
580                        }
581                        if(hasinfos != null) {
582                                hasinfos.clear();
583                        }
584                        StubApiComponent.disposeAllCaches();
585                }
586                finally {
587                        if(ApiPlugin.isRunningInFramework()) {
588                                ApiPlugin.getDefault().removeSaveParticipant(this);
589                                JavaCore.removeElementChangedListener(this);
590                                ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
591                        }
592                }
593        }
594 
595        /* (non-Javadoc)
596         * @see org.eclipse.core.resources.ISaveParticipant#doneSaving(org.eclipse.core.resources.ISaveContext)
597         */
598        public void doneSaving(ISaveContext context) {}
599 
600        /* (non-Javadoc)
601         * @see org.eclipse.core.resources.ISaveParticipant#prepareToSave(org.eclipse.core.resources.ISaveContext)
602         */
603        public void prepareToSave(ISaveContext context) throws CoreException {}
604 
605        /* (non-Javadoc)
606         * @see org.eclipse.core.resources.ISaveParticipant#rollback(org.eclipse.core.resources.ISaveContext)
607         */
608        public void rollback(ISaveContext context) {}
609 
610        /* (non-Javadoc)
611         * @see org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager#getDefaultApiBaseline()
612         */
613        public synchronized IApiBaseline getDefaultApiBaseline() {
614                initializeStateCache();
615                return (IApiBaseline) baselinecache.get(defaultbaseline);
616        }
617 
618        /* (non-Javadoc)
619         * @see org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager#setDefaultApiBaseline(java.lang.String)
620         */
621        public void setDefaultApiBaseline(String name) {
622                fNeedsSaving = true;
623                defaultbaseline = name;
624        }
625        
626        /* (non-Javadoc)
627         * @see org.eclipse.pde.api.tools.internal.provisional.IApiBaselineManager#getWorkspaceBaseline()
628         */
629        public synchronized IApiBaseline getWorkspaceBaseline() {
630                if(ApiPlugin.isRunningInFramework()) {
631                        if(this.workspacebaseline == null) {
632                                try {
633                                        this.workspacebaseline = createWorkspaceBaseline();
634                                } catch (CoreException e) {
635                                        ApiPlugin.log(e);
636                                }
637                        }
638                        return this.workspacebaseline;
639                }
640                return null;
641        }        
642        
643        /**
644         * Disposes the workspace profile such that a new one will be created
645         * on the next request.
646         */
647        private synchronized void disposeWorkspaceBaseline(IProject project) {
648                if (workspacebaseline != null) {
649                        if (acceptProject(project) || workspacebaseline.getApiComponent(project) != null) {
650                                if(DEBUG) {
651                                        System.out.println("disposing workspace baseline"); //$NON-NLS-1$
652                                }
653                                workspacebaseline.dispose();
654                                StubApiComponent.disposeAllCaches();
655                                workspacebaseline = null;
656                        }
657                }
658        }
659 
660        /**
661         * Creates a workspace {@link IApiBaseline}
662         * @return a new workspace {@link IApiBaseline} or <code>null</code>
663         */
664        private IApiBaseline createWorkspaceBaseline() throws CoreException {
665                long time = System.currentTimeMillis();
666                IApiBaseline baseline = null; 
667                try {
668                        baseline = ApiModelFactory.newApiBaseline(ApiBaselineManager.WORKSPACE_API_BASELINE_ID);
669                        // populate it with only projects that are API aware
670                        IPluginModelBase[] models = PluginRegistry.getActiveModels();
671                        List componentsList = new ArrayList(models.length);
672                        IApiComponent apiComponent = null;
673                        for (int i = 0, length = models.length; i < length; i++) {
674                                try {
675                                        apiComponent = ApiModelFactory.newApiComponent(baseline, models[i]);
676                                        if (apiComponent != null) {
677                                                componentsList.add(apiComponent);
678                                        }
679                                } catch (CoreException e) {
680                                        ApiPlugin.log(e);
681                                }
682                        }
683                        baseline.addApiComponents((IApiComponent[]) componentsList.toArray(new IApiComponent[componentsList.size()]));
684                } finally {
685                        if (DEBUG) {
686                                System.out.println("Time to create a workspace profile : " + (System.currentTimeMillis() - time) + "ms"); //$NON-NLS-1$ //$NON-NLS-2$
687                        }
688                }
689                return baseline;
690        }
691        
692        /* (non-Javadoc)
693         * @see org.eclipse.jdt.core.IElementChangedListener#elementChanged(org.eclipse.jdt.core.ElementChangedEvent)
694         */
695        public void elementChanged(ElementChangedEvent event) {
696                if(!ApiPlugin.isRunningInFramework()) {
697                        return;
698                }
699                Object obj = event.getSource();
700                if(obj instanceof IJavaElementDelta) {
701                        processJavaElementDeltas(((IJavaElementDelta)obj).getAffectedChildren(), null);
702                }
703        }
704        
705        /**
706         * Processes the java element deltas of interest
707         * @param deltas
708         */
709        private synchronized void processJavaElementDeltas(IJavaElementDelta[] deltas, IJavaProject project) {
710                try {
711                        for(int i = 0; i < deltas.length; i++) {
712                                IJavaElementDelta delta = deltas[i];
713                                switch(delta.getElement().getElementType()) {
714                                        case IJavaElement.JAVA_PROJECT: {
715                                                IJavaProject proj = (IJavaProject) delta.getElement();
716                                                IProject pj = proj.getProject();
717                                                int flags = delta.getFlags();
718                                                switch (delta.getKind()) {
719                                                        case IJavaElementDelta.CHANGED: {
720                                                                if( (flags & IJavaElementDelta.F_RESOLVED_CLASSPATH_CHANGED) != 0 ||
721                                                                        (flags & IJavaElementDelta.F_CLASSPATH_CHANGED) != 0 ||
722                                                                        (flags & IJavaElementDelta.F_CLOSED) != 0 ||
723                                                                        (flags & IJavaElementDelta.F_OPENED) != 0) {
724                                                                                if(DEBUG) {
725                                                                                        System.out.println("--> processing CLASSPATH CHANGE/CLOSE/OPEN project: ["+proj.getElementName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$
726                                                                                }
727                                                                                disposeWorkspaceBaseline(pj);
728                                                                }
729                                                                if (!acceptProject(pj)) {
730                                                                        return;
731                                                                }
732                                                                if((flags & IJavaElementDelta.F_CHILDREN) != 0) {
733                                                                        if(DEBUG) {
734                                                                                System.out.println("--> processing child deltas of project: ["+proj.getElementName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$
735                                                                        }
736                                                                        processJavaElementDeltas(delta.getAffectedChildren(), proj);
737                                                                } else {
738                                                                        IResourceDelta[] resourcedeltas = delta.getResourceDeltas();
739                                                                        if(resourcedeltas != null) {
740                                                                                IResourceDelta rdelta = null;
741                                                                                for (int j = 0; j < resourcedeltas.length; j++) {
742                                                                                        rdelta = resourcedeltas[j].findMember(new Path(Util.MANIFEST_NAME));
743                                                                                        if(rdelta!= null && rdelta.getKind() == IResourceDelta.CHANGED && (rdelta.getFlags() & IResourceDelta.CONTENT) > 0) {
744                                                                                                if(DEBUG) {
745                                                                                                        System.out.println("--> processing manifest delta"); //$NON-NLS-1$
746                                                                                                }
747                                                                                                disposeWorkspaceBaseline(pj);
748                                                                                                break;
749                                                                                        }
750                                                                                }
751                                                                        }
752                                                                }
753                                                                break;
754                                                        }
755                                                        case IJavaElementDelta.REMOVED: {
756                                                                if((flags & IJavaElementDelta.F_MOVED_TO) != 0) {
757                                                                        if(DEBUG) {
758                                                                                System.out.println("--> processing PROJECT RENAME: ["+proj.getElementName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$
759                                                                        }
760                                                                        disposeWorkspaceBaseline(pj);
761                                                                }
762                                                                break;
763                                                        }
764                                                }
765                                                break;
766                                        }
767                                        case IJavaElement.PACKAGE_FRAGMENT_ROOT: {
768                                                IPackageFragmentRoot root = (IPackageFragmentRoot) delta.getElement();
769                                                if(DEBUG) {
770                                                        System.out.println("processed package fragment root delta: ["+root.getElementName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$
771                                                }
772                                                switch(delta.getKind()) {
773                                                        case IJavaElementDelta.CHANGED: {
774                                                                if(DEBUG) {
775                                                                        System.out.println("processed children of CHANGED package fragment root: ["+root.getElementName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$
776                                                                }
777                                                                processJavaElementDeltas(delta.getAffectedChildren(), project);
778                                                                break;
779                                                        }
780                                                }
781                                                break;
782                                        }
783                                        case IJavaElement.PACKAGE_FRAGMENT: {
784                                                IPackageFragment fragment = (IPackageFragment) delta.getElement();
785                                                if(delta.getKind() == IJavaElementDelta.REMOVED) {
786                                                        handlePackageRemoval(project.getProject(), fragment);
787                                                }
788                                                break;
789                                        }
790                                }
791                        }
792                } catch (CoreException e) {
793                        ApiPlugin.log(e);
794                }
795        }
796                
797        /**
798         * Handles the specified {@link IPackageFragment} being removed.
799         * When a packaged is removed, we:
800         * <ol>
801         * <li>Remove the package from the cache of resolved providers
802         *         of that package (in the API baseline)</li>
803         * </ol>
804         * @param project
805         * @param fragment
806         * @throws CoreException
807         */
808        private void handlePackageRemoval(IProject project, IPackageFragment fragment) throws CoreException {
809                if(DEBUG) {
810                        System.out.println("processed package fragment REMOVE delta: ["+fragment.getElementName()+"]"); //$NON-NLS-1$ //$NON-NLS-2$
811                }
812                ((ApiBaseline)getWorkspaceBaseline()).clearPackage(fragment.getElementName());
813        }
814        
815        /**
816         * Returns if we should care about the specified project
817         * @param project
818         * @return true if the project is an 'API aware' project, false otherwise
819         */
820        private boolean acceptProject(IProject project) {
821                try {
822                        return project.isAccessible() && project.hasNature(ApiPlugin.NATURE_ID);
823                }
824                catch(CoreException e) {
825                        return false;
826                }
827        }
828 
829        /* (non-Javadoc)
830         * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
831         */
832        public void resourceChanged(IResourceChangeEvent event) {
833                if(!ApiPlugin.isRunningInFramework()) {
834                        return;
835                }
836                // clean all API errors when a project description changes
837                IResourceDelta delta = event.getDelta();
838                if (delta != null) {
839                        IResourceDelta[] children = delta.getAffectedChildren(IResourceDelta.CHANGED);
840                        boolean dispose = false;
841                        IResource resource = null;
842                        IProject modifiedProject = null;
843                        for (int i = 0; i < children.length; i++) {
844                                resource = children[i].getResource();
845                                if (children[i].getResource().getType() == IResource.PROJECT) {
846                                        if ((children[i].getFlags() & IResourceDelta.DESCRIPTION) != 0) {
847                                                IProject project = (IProject)resource;
848                                                if (project.isAccessible()) {
849                                                        try {
850                                                                if (!project.getDescription().hasNature(ApiPlugin.NATURE_ID)) {
851                                                                        IJavaProject jp = JavaCore.create(project);
852                                                                        if (jp.exists()) {
853                                                                                ApiDescriptionManager.getDefault().clean(jp, true, true);
854                                                                        }
855                                                                }
856                                                        } catch (CoreException e) {
857                                                                ApiPlugin.log(e.getStatus());
858                                                        }
859                                                        modifiedProject = project;
860                                                        dispose = true;
861                                                }
862                                        }
863                                }
864                        }
865                        if(dispose) {
866                                disposeWorkspaceBaseline(modifiedProject);
867                        }
868                }
869        }
870 
871}

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