KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ui > mapping > CompareInputChangeNotifier


1 /*******************************************************************************
2  * Copyright (c) 2006 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.internal.ui.mapping;
12
13 import java.util.*;
14
15 import org.eclipse.compare.structuremergeviewer.ICompareInput;
16 import org.eclipse.core.resources.*;
17 import org.eclipse.core.runtime.CoreException;
18 import org.eclipse.core.runtime.IProgressMonitor;
19 import org.eclipse.core.runtime.jobs.Job;
20 import org.eclipse.swt.widgets.Display;
21 import org.eclipse.team.core.TeamException;
22 import org.eclipse.team.internal.core.BackgroundEventHandler;
23 import org.eclipse.team.internal.core.BackgroundEventHandler.Event;
24 import org.eclipse.team.internal.ui.Policy;
25 import org.eclipse.team.internal.ui.TeamUIMessages;
26
27 /**
28  * An abstract class that
29  * listens to resource changes and synchronization context changes.
30  * <p>
31  * This class can be subclassed by clients.
32  * <p>
33  * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
34  * part of a work in progress. There is a guarantee neither that this API will
35  * work nor that it will remain the same. Please do not use this API without
36  * consulting with the Platform/Team team.
37  * </p>
38  * @since 3.3
39  */

40 public abstract class CompareInputChangeNotifier implements
41         IResourceChangeListener {
42
43     private Map inputs = new HashMap();
44     private InputChangeEventHandler eventHandler;
45
46     private class CompareInputConnecton {
47         private ICompareInput input;
48         private int connections;
49         public CompareInputConnecton(ICompareInput input) {
50             this.input = input;
51         }
52         public void increment() {
53             connections++;
54         }
55         public void decrement() {
56             if (connections > 0)
57                 connections--;
58             
59         }
60         public boolean isDisconnected() {
61             return connections == 0;
62         }
63         public ICompareInput getInput() {
64             return input;
65         }
66     }
67     
68     private static final int COMPARE_INPUT_CHANGE = 1;
69     
70     private static class InputChangeEvent extends Event {
71         private final ICompareInput[] inputs;
72         public InputChangeEvent(ICompareInput[] inputs) {
73             super(COMPARE_INPUT_CHANGE);
74             this.inputs = inputs;
75             
76         }
77         public ICompareInput[] getChangedInputs() {
78             return inputs;
79         }
80     }
81     
82     private class InputChangeEventHandler extends BackgroundEventHandler {
83
84         private final Set changedInputs = new HashSet();
85         private final List pendingRunnables = new ArrayList();
86         
87         protected InputChangeEventHandler() {
88             super(TeamUIMessages.CompareInputChangeNotifier_0, TeamUIMessages.CompareInputChangeNotifier_1);
89         }
90
91         protected boolean doDispatchEvents(IProgressMonitor monitor)
92                 throws TeamException {
93             ICompareInput[] toDispatch;
94             RunnableEvent[] events;
95             synchronized (pendingRunnables) {
96                 synchronized (changedInputs) {
97                     if (changedInputs.isEmpty() && pendingRunnables.isEmpty())
98                         return false;
99                     toDispatch = (ICompareInput[]) changedInputs.toArray(new ICompareInput[changedInputs.size()]);
100                     events = (RunnableEvent[]) pendingRunnables.toArray(new RunnableEvent[pendingRunnables.size()]);
101                     changedInputs.clear();
102                     pendingRunnables.clear();
103                 }
104             }
105             dispatchChanges(toDispatch, monitor);
106             for (int i = 0; i < events.length; i++) {
107                 RunnableEvent event = events[i];
108                 executeRunnableNow(event, monitor);
109             }
110             return true;
111         }
112
113         protected void processEvent(Event event, IProgressMonitor monitor)
114                 throws CoreException {
115             int type = event.getType();
116             switch (type) {
117                 case BackgroundEventHandler.RUNNABLE_EVENT :
118                     RunnableEvent runnableEvent = ((RunnableEvent)event);
119                     if (runnableEvent.isPreemtive())
120                         executeRunnableNow(event, monitor);
121                     else
122                         executeRunnableDuringDispatch(event);
123                     break;
124                 case COMPARE_INPUT_CHANGE :
125                     if (event instanceof InputChangeEvent) {
126                         InputChangeEvent changeEvent = (InputChangeEvent) event;
127                         ICompareInput[] inputs = changeEvent.getChangedInputs();
128                         synchronized (changedInputs) {
129                             for (int i = 0; i < inputs.length; i++) {
130                                 ICompareInput input = inputs[i];
131                                 changedInputs.add(input);
132                             }
133                         }
134                     }
135                     break;
136             }
137         }
138         
139         private void executeRunnableDuringDispatch(Event event) {
140             synchronized (pendingRunnables) {
141                 pendingRunnables.add(event);
142             }
143         }
144
145         private void executeRunnableNow(Event event, IProgressMonitor monitor) {
146             try {
147                 // Dispatch any queued results to clear pending output events
148
dispatchEvents(Policy.subMonitorFor(monitor, 1));
149             } catch (TeamException e) {
150                 handleException(e);
151             }
152             try {
153                 ((RunnableEvent)event).run(Policy.subMonitorFor(monitor, 1));
154             } catch (CoreException e) {
155                 handleException(e);
156             }
157         }
158         
159         protected synchronized void queueEvent(Event event) {
160             super.queueEvent(event, false);
161         }
162         
163         protected long getShortDispatchDelay() {
164             // Only wait 250 for additional changes to come in
165
return 250;
166         }
167         
168         protected boolean belongsTo(Object JavaDoc family) {
169             return CompareInputChangeNotifier.this.belongsTo(family);
170         }
171     }
172     
173     /**
174      * Create a change notifier for the given synchronization context.
175      */

176     public CompareInputChangeNotifier() {
177         super();
178     }
179
180     /**
181      * Initialize the change notifier. This method is called from the
182      * constructor and registers a listener with the workspace and the
183      * synchronization context. It also registers a listener with the context
184      * cache which will unregister the listeners when the context is disposed.
185      * Subclasses may extend this method.
186      */

187     public void initialize() {
188         ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
189         eventHandler = new InputChangeEventHandler();
190     }
191     
192     /**
193      * Dispose of the change notifier. This method is invoked when the context
194      * to which the change notifier is associated is disposed.
195      * Subclasses may extend this method.
196      */

197     public void dispose() {
198         ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
199         eventHandler.shutdown();
200     }
201
202     /**
203      * Connect the input to this change notifier. Once connected, the change notifier will issue
204      * change events for the given input. When change notification is no longer desired, the
205      * input should be disconnected. The number of calls to {@link #connect(ICompareInput)} needs to
206      * be matched by the same number of calls to {@link #disconnect(ICompareInput)}.
207      * @param input the compare input
208      */

209     public void connect(ICompareInput input) {
210         CompareInputConnecton con = (CompareInputConnecton)inputs.get(input);
211         if (con == null) {
212             con = new CompareInputConnecton(input);
213             inputs.put(input, con);
214         }
215         con.increment();
216     }
217
218     /**
219      * Disconnect the input from this change notifier.
220      * @param input the compare input
221      * @see #connect(ICompareInput)
222      */

223     public void disconnect(ICompareInput input) {
224         CompareInputConnecton con = (CompareInputConnecton)inputs.get(input);
225         if (con != null) {
226             con.decrement();
227             if (con.isDisconnected()) {
228                 inputs.remove(input);
229             }
230         }
231     }
232     
233     /**
234      * Return the array of inputs that have connections.
235      * @return the array of inputs that have connections
236      */

237     protected ICompareInput[] getConnectedInputs() {
238         return (ICompareInput[])inputs.keySet().toArray(new ICompareInput[inputs.size()]);
239     }
240     
241     /**
242      * Send out notification that the given compare inputs have changed.
243      * @param inputs the changed inputs
244      */

245     protected void inputsChanged(ICompareInput[] inputs) {
246         InputChangeEvent event = new InputChangeEvent(inputs);
247         eventHandler.queueEvent(event);
248     }
249     
250     /**
251      * Dispatch the changes to the given inputs.
252      * @param inputs the changed compare inputs
253      * @param monitor a progress monitor
254      */

255     protected void dispatchChanges(final ICompareInput[] inputs, IProgressMonitor monitor) {
256         prepareInputs(inputs, monitor);
257         Display.getDefault().syncExec(new Runnable JavaDoc() {
258             public void run() {
259                 fireChanges(inputs);
260             }
261         });
262     }
263
264     /**
265      * Prepare the inputs in the background before firing the compare input change event.
266      * This allows for the caching of contents etc. before the input change event is fired.
267      * @param inputs the changed inputs
268      * @param monitor a progress monitor
269      */

270     protected void prepareInputs(ICompareInput[] inputs, IProgressMonitor monitor) {
271         monitor.beginTask(null, inputs.length * 100);
272         for (int i = 0; i < inputs.length; i++) {
273             ICompareInput input = inputs[i];
274             prepareInput(input, Policy.subMonitorFor(monitor, 100));
275         }
276         monitor.done();
277     }
278
279     /**
280      * Prepare the input before firing the compare input change event.
281      * This allows for the caching of contents etc. before the input change event is fired.
282      * This method is called from {@link #prepareInputs(ICompareInput[], IProgressMonitor)}
283      * for each input. By default, nothing is done, subclasses may override.
284      * @param input the compare input
285      * @param monitor a progress monitor
286      */

287     protected void prepareInput(ICompareInput input, IProgressMonitor monitor) {
288         // Default is to do nothing
289
}
290     
291     /**
292      * Update the compare inputs and fire the change events.
293      * This method is called from the UI thread after the inputs have
294      * been prepared in a background thread
295      * (see {@link #prepareInputs(ICompareInput[], IProgressMonitor)})
296      * @param inputs the changed inputs
297      */

298     protected void fireChanges(ICompareInput[] inputs) {
299         for (int i = 0; i < inputs.length; i++) {
300             ICompareInput input = inputs[i];
301             fireChange(input);
302         }
303     }
304     
305     /**
306      * Run the given runnable in the background.
307      * @param runnable the runnable
308      */

309     protected void runInBackground(IWorkspaceRunnable runnable) {
310         eventHandler.queueEvent(new BackgroundEventHandler.RunnableEvent(runnable, false));
311     }
312     
313     /* (non-Javadoc)
314      * @see org.eclipse.core.resources.IResourceChangeListener#resourceChanged(org.eclipse.core.resources.IResourceChangeEvent)
315      */

316     public void resourceChanged(IResourceChangeEvent event) {
317         List changedInputs = new ArrayList();
318         ICompareInput[] inputs = getConnectedInputs();
319         for (int i = 0; i < inputs.length; i++) {
320             ICompareInput input = inputs[i];
321             IResource[] resources = getResources(input);
322             for (int j = 0; j < resources.length; j++) {
323                 IResource resource = resources[j];
324                 if (resource != null) {
325                     IResourceDelta delta = event.getDelta().findMember(resource.getFullPath());
326                     if (delta != null) {
327                         if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.REMOVED)) > 0
328                                 || (delta.getKind() & (IResourceDelta.CHANGED)) > 0
329                                 && (delta.getFlags() & (IResourceDelta.CONTENT | IResourceDelta.REPLACED)) > 0) {
330                             changedInputs.add(input);
331                             break;
332                         }
333                     }
334                 }
335             }
336         }
337         if (!changedInputs.isEmpty())
338             handleInputChanges((ICompareInput[]) changedInputs.toArray(new ICompareInput[changedInputs.size()]), true);
339     }
340     
341     /**
342      * Return the resources covered by the given compare input.
343      * This method is used by the {@link #resourceChanged(IResourceChangeEvent)}
344      * method to determine if a workspace change affects the compare input.
345      * @param input the compare input
346      * @return the resources covered by the given compare input
347      */

348     protected abstract IResource[] getResources(ICompareInput input);
349
350     /**
351      * Handle the input changes by notifying any listeners of the changed inputs.
352      * @param inputs the changed inputs
353      */

354     protected void handleInputChanges(ICompareInput[] inputs, boolean force) {
355         ICompareInput[] realChanges;
356         if (force) {
357             realChanges = inputs;
358         } else {
359             List result = new ArrayList();
360             for (int i = 0; i < inputs.length; i++) {
361                 ICompareInput input = inputs[i];
362                 if (isChanged(input)) {
363                     result.add(input);
364                 }
365             }
366             realChanges = (ICompareInput[]) result.toArray(new ICompareInput[result.size()]);
367         }
368         if (realChanges.length > 0)
369             inputsChanged(realChanges);
370     }
371     
372     /**
373      * Return whether the given compare input has changed and requires
374      * a compare input change event to be fired.
375      * @param input the compare input
376      * @return whether the given compare input has changed
377      */

378     protected boolean isChanged(ICompareInput input) {
379         if (input instanceof AbstractCompareInput) {
380             AbstractCompareInput ci = (AbstractCompareInput) input;
381             return ci.needsUpdate();
382         }
383         return false;
384     }
385     
386     /**
387      * Update the compare input and fire the change event.
388      * This method is called from {@link #fireChanges(ICompareInput[])}
389      * for each changed input.
390      * @param input the changed compare input
391      */

392     protected void fireChange(ICompareInput input) {
393         if (input instanceof AbstractCompareInput) {
394             AbstractCompareInput ci = (AbstractCompareInput) input;
395             ci.update();
396         }
397     }
398     
399     /**
400      * Return whether the background handler for this notifier belongs to the
401      * given job family.
402      * @param family the job family
403      * @return whether the background handler belongs to the given job family.
404      * @see Job#belongsTo(Object)
405      */

406     protected boolean belongsTo(Object JavaDoc family) {
407         return false;
408     }
409
410 }
411
Popular Tags