KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > core > diff > provider > DiffTree


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  *******************************************************************************/

11 package org.eclipse.team.core.diff.provider;
12
13 import java.util.*;
14
15 import org.eclipse.core.resources.IResource;
16 import org.eclipse.core.runtime.*;
17 import org.eclipse.core.runtime.jobs.ILock;
18 import org.eclipse.core.runtime.jobs.Job;
19 import org.eclipse.team.core.diff.*;
20 import org.eclipse.team.internal.core.Policy;
21 import org.eclipse.team.internal.core.mapping.DiffChangeEvent;
22 import org.eclipse.team.internal.core.mapping.PathTree;
23 import org.eclipse.team.internal.core.subscribers.DiffTreeStatistics;
24
25 /**
26  * Implementation of {@link IDiffTree}.
27  * <p>
28  * This class is not intended to be subclassed by clients.
29  * Clients can instead use {@link DiffTree}.
30  *
31  * @since 3.2
32  */

33 public class DiffTree implements IDiffTree {
34     
35     /**
36      * Constant that indicates the start of the property value
37      * range that clients can use when storing properties in this tree.
38      */

39     public static final int START_CLIENT_PROPERTY_RANGE = 1024;
40     
41     private ListenerList listeners = new ListenerList();
42     
43     private PathTree pathTree = new PathTree();
44     
45     private ILock lock = Job.getJobManager().newLock();
46     
47     private DiffTreeStatistics statistics = new DiffTreeStatistics();
48     
49     private DiffChangeEvent changes;
50
51     private boolean lockedForModification;
52
53     private Map propertyChanges = new HashMap();
54
55     /**
56      * Create an empty diff tree.
57      */

58     public DiffTree() {
59         resetChanges();
60     }
61     
62     /* (non-Javadoc)
63      * @see org.eclipse.team.core.synchronize.ISyncDeltaTree#addSyncDeltaChangeListener(org.eclipse.team.core.synchronize.ISyncDeltaChangeListener)
64      */

65     public void addDiffChangeListener(IDiffChangeListener listener) {
66         listeners.add(listener);
67     }
68
69     /* (non-Javadoc)
70      * @see org.eclipse.team.core.synchronize.ISyncDeltaTree#removeSyncDeltaChangeListener(org.eclipse.team.core.synchronize.ISyncDeltaChangeListener)
71      */

72     public void removeDiffChangeListener(IDiffChangeListener listener) {
73         listeners.remove(listener);
74     }
75
76     /* (non-Javadoc)
77      * @see org.eclipse.team.core.synchronize.ISyncDeltaTree#accept(org.eclipse.core.runtime.IPath, org.eclipse.team.core.synchronize.ISyncDeltaVisitor)
78      */

79     public void accept(IPath path, IDiffVisitor visitor, int depth) {
80         IDiff delta = getDiff(path);
81         if (delta == null || visitor.visit(delta)) {
82             if (depth == IResource.DEPTH_ZERO)
83                 return;
84             IPath[] children = getChildren(path);
85             for (int i = 0; i < children.length; i++) {
86                 IPath child = children[i];
87                 accept(child, visitor, depth == IResource.DEPTH_ONE ? IResource.DEPTH_ZERO : IResource.DEPTH_INFINITE);
88             }
89         }
90     }
91
92     /* (non-Javadoc)
93      * @see org.eclipse.team.core.synchronize.ISyncDeltaTree#findMember(org.eclipse.core.runtime.IPath)
94      */

95     public IDiff getDiff(IPath path) {
96         return (IDiff)pathTree.get(path);
97     }
98
99     /* (non-Javadoc)
100      * @see org.eclipse.team.core.synchronize.ISyncDeltaTree#getAffectedChildren(org.eclipse.core.runtime.IPath)
101      */

102     public IPath[] getChildren(IPath path) {
103         return pathTree.getChildren(path);
104     }
105
106     /* (non-Javadoc)
107      * @see org.eclipse.team.core.delta.ISyncDeltaTree#isEmpty()
108      */

109     public boolean isEmpty() {
110         return pathTree.isEmpty();
111     }
112
113     /**
114      * Add the given {@link IDiff} to the tree. A change event will
115      * be generated unless the call to this method is nested in between calls
116      * to <code>beginInput()</code> and <code>endInput(IProgressMonitor)</code>
117      * in which case the event for this addition and any other sync set
118      * change will be fired in a batched event when <code>endInput</code>
119      * is invoked.
120      * <p>
121      * Invoking this method outside of the above mentioned block will result
122      * in the <code>endInput(IProgressMonitor)</code> being invoked with a null
123      * progress monitor. If responsiveness is required, the client should always
124      * nest sync set modifications within <code>beginInput/endInput</code>.
125      * </p>
126      * @param delta the delta to be added to this set.
127      */

128     public void add(IDiff delta) {
129         try {
130             beginInput();
131             IDiff oldDiff = getDiff(delta.getPath());
132             internalAdd(delta);
133             if (oldDiff != null) {
134                 internalChanged(delta);
135             } else {
136                 internalAdded(delta);
137             }
138         } finally {
139             endInput(null);
140         }
141     }
142
143     /**
144      * Remove the given local resource from the set. A change event will
145      * be generated unless the call to this method is nested in between calls
146      * to <code>beginInput()</code> and <code>endInput(IProgressMonitor)</code>
147      * in which case the event for this removal and any other sync set
148      * change will be fired in a batched event when <code>endInput</code>
149      * is invoked.
150      * <p>
151      * Invoking this method outside of the above mentioned block will result
152      * in the <code>endInput(IProgressMonitor)</code> being invoked with a null
153      * progress monitor. If responsiveness is required, the client should always
154      * nest sync set modifications within <code>beginInput/endInput</code>.
155      * </p>
156      *
157      * @param path the path to remove
158      */

159     public void remove(IPath path) {
160         try {
161             beginInput();
162             IDiff delta = getDiff(path);
163             if (delta != null) {
164                 internalRemove(delta);
165                 internalRemoved(path, delta);
166             }
167         } finally {
168             endInput(null);
169         }
170     }
171
172     /**
173      * Clear the contents of the set
174      */

175     public void clear() {
176         try {
177             beginInput();
178             pathTree.clear();
179             statistics.clear();
180             internalReset();
181         } finally {
182             endInput(null);
183         }
184     }
185     
186     /**
187      * This method is used to obtain a lock on the set which ensures thread safety
188      * and batches change notification. If the set is locked by another thread,
189      * the calling thread will block until the lock
190      * becomes available. This method uses an <code>org.eclipse.core.runtime.jobs.ILock</code>.
191      * <p>
192      * It is important that the lock is released after it is obtained. Calls to <code>endInput</code>
193      * should be done in a finally block as illustrated in the following code snippet.
194      * <pre>
195      * try {
196      * set.beginInput();
197      * // do stuff
198      * } finally {
199      * set.endInput(progress);
200      * }
201      * </pre>
202      * </p><p>
203      * Calls to <code>beginInput</code> and <code>endInput</code> can be nested and must be matched.
204      * </p>
205      */

206     public void beginInput() {
207         lock.acquire();
208     }
209
210     /**
211      * This method is used to release the lock on this set. The progress monitor is needed to allow
212      * listeners to perform long-running operations is response to the set change. The lock is held
213      * while the listeners are notified so listeners must be cautious in order to avoid deadlock.
214      * @param monitor a progress monitor
215      * @see #beginInput()
216      */

217     public void endInput(IProgressMonitor monitor) {
218         try {
219             if (lock.getDepth() == 1) {
220                 // Remain locked while firing the events so the handlers
221
// can expect the set to remain constant while they process the events
222
fireChanges(Policy.monitorFor(monitor));
223             }
224         } finally {
225             lock.release();
226         }
227     }
228
229     private void fireChanges(final IProgressMonitor monitor) {
230         
231         final DiffChangeEvent event = getChangeEvent();
232         resetChanges();
233         final Map propertyChanges = this.propertyChanges;
234         this.propertyChanges = new HashMap();
235         
236         if(event.isEmpty() && ! event.isReset() && propertyChanges.isEmpty()) return;
237         Object JavaDoc[] listeners = this.listeners.getListeners();
238         for (int i = 0; i < listeners.length; i++) {
239             final IDiffChangeListener listener = (IDiffChangeListener)listeners[i];
240             SafeRunner.run(new ISafeRunnable() {
241                 public void handleException(Throwable JavaDoc exception) {
242                     // don't log the exception....it is already being logged in Platform#run
243
}
244                 public void run() throws Exception JavaDoc {
245                     try {
246                         lockedForModification = true;
247                         if (!event.isEmpty() || event.isReset())
248                             listener.diffsChanged(event, Policy.subMonitorFor(monitor, 100));
249                         for (Iterator iter = propertyChanges.keySet().iterator(); iter.hasNext();) {
250                             Integer JavaDoc key = (Integer JavaDoc) iter.next();
251                             Set paths = (Set)propertyChanges.get(key);
252                             listener.propertyChanged(DiffTree.this, key.intValue(), (IPath[]) paths.toArray(new IPath[paths
253                                     .size()]));
254                         }
255                         
256                     } finally {
257                         lockedForModification = false;
258                     }
259                 }
260             });
261         }
262         monitor.done();
263     }
264
265     private DiffChangeEvent getChangeEvent() {
266         return changes;
267     }
268     
269     private void resetChanges() {
270         changes = createEmptyChangeEvent();
271     }
272
273     private DiffChangeEvent createEmptyChangeEvent() {
274         return new DiffChangeEvent(this);
275     }
276
277     private void internalAdd(IDiff delta) {
278         Assert.isTrue(!lockedForModification);
279         IDiff oldDiff = (IDiff)pathTree.get(delta.getPath());
280         pathTree.put(delta.getPath(), delta);
281         if(oldDiff == null) {
282             statistics.add(delta);
283         } else {
284             statistics.remove(oldDiff);
285             statistics.add(delta);
286         }
287         boolean isConflict = false;
288         if (delta instanceof IThreeWayDiff) {
289             IThreeWayDiff twd = (IThreeWayDiff) delta;
290             isConflict = twd.getDirection() == IThreeWayDiff.CONFLICTING;
291         }
292         setPropertyToRoot(delta, P_HAS_DESCENDANT_CONFLICTS, isConflict);
293     }
294     
295     private void internalRemove(IDiff delta) {
296         Assert.isTrue(!lockedForModification);
297         statistics.remove(delta);
298         setPropertyToRoot(delta, P_HAS_DESCENDANT_CONFLICTS, false);
299         setPropertyToRoot(delta, P_BUSY_HINT, false);
300         pathTree.remove(delta.getPath());
301     }
302     
303     private void internalAdded(IDiff delta) {
304         changes.added(delta);
305     }
306     
307     private void internalChanged(IDiff delta) {
308         changes.changed(delta);
309     }
310     private void internalRemoved(IPath path, IDiff delta) {
311         changes.removed(path, delta);
312     }
313     
314     private void internalReset() {
315         changes.reset();
316     }
317
318     /**
319      * Return the paths in this tree that contain diffs.
320      * @return the paths in this tree that contain diffs.
321      */

322     public IPath[] getPaths() {
323         return pathTree.getPaths();
324     }
325     
326     /**
327      * Return all the diffs contained in this diff tree.
328      * @return all the diffs contained in this diff tree
329      */

330     public IDiff[] getDiffs() {
331         return (IDiff[]) pathTree.values().toArray(new IDiff[pathTree.size()]);
332     }
333     
334     /* (non-Javadoc)
335      * @see org.eclipse.team.core.diff.IDiffTree#countFor(int, int)
336      */

337     public long countFor(int state, int mask) {
338         if (state == 0)
339             return size();
340         return statistics.countFor(state, mask);
341     }
342
343     /* (non-Javadoc)
344      * @see org.eclipse.team.core.diff.IDiffTree#size()
345      */

346     public int size() {
347         return pathTree.size();
348     }
349
350     /* (non-Javadoc)
351      * @see org.eclipse.team.core.diff.IDiffTree#setPropertyToRoot(org.eclipse.core.runtime.IPath, int, boolean)
352      */

353     public void setPropertyToRoot(IDiff node, int property, boolean value) {
354         try {
355             beginInput();
356             IPath[] paths = pathTree.setPropogatedProperty(node.getPath(), property, value);
357             accumulatePropertyChanges(property, paths);
358         } finally {
359             endInput(null);
360         }
361     }
362     
363     private void accumulatePropertyChanges(int property, IPath[] paths) {
364         Integer JavaDoc key = new Integer JavaDoc(property);
365         Set changes = (Set)propertyChanges.get(key);
366         if (changes == null) {
367             changes = new HashSet();
368             propertyChanges.put(key, changes);
369         }
370         for (int i = 0; i < paths.length; i++) {
371             IPath path = paths[i];
372             changes.add(path);
373         }
374     }
375
376     /* (non-Javadoc)
377      * @see org.eclipse.team.core.diff.IDiffTree#getProperty(org.eclipse.core.runtime.IPath, int)
378      */

379     public boolean getProperty(IPath path, int property) {
380         return pathTree.getProperty(path, property);
381     }
382
383     /* (non-Javadoc)
384      * @see org.eclipse.team.core.diff.IDiffTree#setBusy(org.eclipse.team.core.diff.IDiffNode[], org.eclipse.core.runtime.IProgressMonitor)
385      */

386     public void setBusy(IDiff[] diffs, IProgressMonitor monitor) {
387         try {
388             beginInput();
389             for (int i = 0; i < diffs.length; i++) {
390                 IDiff node = diffs[i];
391                 setPropertyToRoot(node, P_BUSY_HINT, true);
392             }
393         } finally {
394             endInput(monitor);
395         }
396     }
397
398     /* (non-Javadoc)
399      * @see org.eclipse.team.core.diff.IDiffTree#clearBusy(org.eclipse.core.runtime.IProgressMonitor)
400      */

401     public void clearBusy(IProgressMonitor monitor) {
402         try {
403             beginInput();
404             IPath[] paths = pathTree.getPaths();
405             for (int i = 0; i < paths.length; i++) {
406                 IPath path = paths[i];
407                 IPath[] changed = pathTree.setPropogatedProperty(path, P_BUSY_HINT, false);
408                 accumulatePropertyChanges(P_BUSY_HINT, changed);
409             }
410         } finally {
411             endInput(monitor);
412         }
413     }
414     
415     /* (non-Javadoc)
416      * @see org.eclipse.team.core.diff.IDiffTree#hasDiffsMatching(org.eclipse.core.runtime.IPath, org.eclipse.team.core.diff.FastDiffFilter)
417      */

418     public boolean hasMatchingDiffs(IPath path, final FastDiffFilter filter) {
419         final RuntimeException JavaDoc found = new RuntimeException JavaDoc();
420         try {
421             accept(path, new IDiffVisitor() {
422                 public boolean visit(IDiff delta) {
423                     if (filter.select(delta)) {
424                         throw found;
425                     }
426                     return false;
427                 }
428             
429             }, IResource.DEPTH_INFINITE);
430         } catch (RuntimeException JavaDoc e) {
431             if (e == found)
432                 return true;
433             throw e;
434         }
435         return false;
436     }
437     
438     /**
439      * Report to any listeners that an error has occurred while populating the
440      * set. Listeners will be notified that an error occurred and can react
441      * accordingly.
442      * </p>
443      *
444      * @param status
445      * the status that describes the error that occurred.
446      */

447     public void reportError(IStatus status) {
448         try {
449             beginInput();
450             getChangeEvent().errorOccurred(status);
451         } finally {
452             endInput(null);
453         }
454     }
455 }
456
Popular Tags