Skip to main content
aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
Diffstat (limited to 'bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/CompareInputChangeNotifier.java')
-rw-r--r--bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/CompareInputChangeNotifier.java402
1 files changed, 402 insertions, 0 deletions
diff --git a/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/CompareInputChangeNotifier.java b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/CompareInputChangeNotifier.java
new file mode 100644
index 000000000..45f53e73d
--- /dev/null
+++ b/bundles/org.eclipse.team.ui/src/org/eclipse/team/internal/ui/mapping/CompareInputChangeNotifier.java
@@ -0,0 +1,402 @@
+/*******************************************************************************
+ * Copyright (c) 2006, 2017 IBM Corporation and others.
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * which accompanies this distribution, and is available at
+ * http://www.eclipse.org/legal/epl-v10.html
+ *
+ * Contributors:
+ * IBM Corporation - initial API and implementation
+ *******************************************************************************/
+package org.eclipse.team.internal.ui.mapping;
+
+import java.util.*;
+
+import org.eclipse.compare.structuremergeviewer.ICompareInput;
+import org.eclipse.core.resources.*;
+import org.eclipse.core.runtime.CoreException;
+import org.eclipse.core.runtime.IProgressMonitor;
+import org.eclipse.core.runtime.jobs.Job;
+import org.eclipse.swt.widgets.Display;
+import org.eclipse.team.core.TeamException;
+import org.eclipse.team.internal.core.BackgroundEventHandler;
+import org.eclipse.team.internal.core.BackgroundEventHandler.Event;
+import org.eclipse.team.internal.ui.Policy;
+import org.eclipse.team.internal.ui.TeamUIMessages;
+
+/**
+ * An abstract class that
+ * listens to resource changes and synchronization context changes.
+ * <p>
+ * This class can be subclassed by clients.
+ * <p>
+ * <strong>EXPERIMENTAL</strong>. This class or interface has been added as
+ * part of a work in progress. There is a guarantee neither that this API will
+ * work nor that it will remain the same. Please do not use this API without
+ * consulting with the Platform/Team team.
+ * </p>
+ * @since 3.3
+ */
+public abstract class CompareInputChangeNotifier implements
+ IResourceChangeListener {
+
+ private Map<ICompareInput, CompareInputConnecton> inputs = new HashMap<>();
+ private InputChangeEventHandler eventHandler;
+
+ private class CompareInputConnecton {
+ private int connections;
+ public void increment() {
+ connections++;
+ }
+ public void decrement() {
+ if (connections > 0)
+ connections--;
+
+ }
+ public boolean isDisconnected() {
+ return connections == 0;
+ }
+ }
+
+ private static final int COMPARE_INPUT_CHANGE = 1;
+
+ private static class InputChangeEvent extends Event {
+ private final ICompareInput[] inputs;
+ public InputChangeEvent(ICompareInput[] inputs) {
+ super(COMPARE_INPUT_CHANGE);
+ this.inputs = inputs;
+
+ }
+ public ICompareInput[] getChangedInputs() {
+ return inputs;
+ }
+ }
+
+ private class InputChangeEventHandler extends BackgroundEventHandler {
+
+ private final Set<ICompareInput> changedInputs = new HashSet<>();
+ private final List pendingRunnables = new ArrayList();
+
+ protected InputChangeEventHandler() {
+ super(TeamUIMessages.CompareInputChangeNotifier_0, TeamUIMessages.CompareInputChangeNotifier_1);
+ }
+
+ @Override
+ protected boolean doDispatchEvents(IProgressMonitor monitor)
+ throws TeamException {
+ ICompareInput[] toDispatch;
+ RunnableEvent[] events;
+ synchronized (pendingRunnables) {
+ synchronized (changedInputs) {
+ if (changedInputs.isEmpty() && pendingRunnables.isEmpty())
+ return false;
+ toDispatch = changedInputs.toArray(new ICompareInput[changedInputs.size()]);
+ events = (RunnableEvent[]) pendingRunnables.toArray(new RunnableEvent[pendingRunnables.size()]);
+ changedInputs.clear();
+ pendingRunnables.clear();
+ }
+ }
+ dispatchChanges(toDispatch, monitor);
+ for (int i = 0; i < events.length; i++) {
+ RunnableEvent event = events[i];
+ executeRunnableNow(event, monitor);
+ }
+ return true;
+ }
+
+ @Override
+ protected void processEvent(Event event, IProgressMonitor monitor)
+ throws CoreException {
+ int type = event.getType();
+ switch (type) {
+ case BackgroundEventHandler.RUNNABLE_EVENT :
+ RunnableEvent runnableEvent = ((RunnableEvent)event);
+ if (runnableEvent.isPreemtive())
+ executeRunnableNow(event, monitor);
+ else
+ executeRunnableDuringDispatch(event);
+ break;
+ case COMPARE_INPUT_CHANGE :
+ if (event instanceof InputChangeEvent) {
+ InputChangeEvent changeEvent = (InputChangeEvent) event;
+ ICompareInput[] inputs = changeEvent.getChangedInputs();
+ synchronized (changedInputs) {
+ for (int i = 0; i < inputs.length; i++) {
+ ICompareInput input = inputs[i];
+ changedInputs.add(input);
+ }
+ }
+ }
+ break;
+ }
+ }
+
+ private void executeRunnableDuringDispatch(Event event) {
+ synchronized (pendingRunnables) {
+ pendingRunnables.add(event);
+ }
+ }
+
+ private void executeRunnableNow(Event event, IProgressMonitor monitor) {
+ try {
+ // Dispatch any queued results to clear pending output events
+ dispatchEvents(Policy.subMonitorFor(monitor, 1));
+ } catch (TeamException e) {
+ handleException(e);
+ }
+ try {
+ ((RunnableEvent)event).run(Policy.subMonitorFor(monitor, 1));
+ } catch (CoreException e) {
+ handleException(e);
+ }
+ }
+
+ protected synchronized void queueEvent(Event event) {
+ super.queueEvent(event, false);
+ }
+
+ @Override
+ protected long getShortDispatchDelay() {
+ // Only wait 250 for additional changes to come in
+ return 250;
+ }
+
+ @Override
+ protected boolean belongsTo(Object family) {
+ return CompareInputChangeNotifier.this.belongsTo(family);
+ }
+ }
+
+ /**
+ * Create a change notifier for the given synchronization context.
+ */
+ public CompareInputChangeNotifier() {
+ super();
+ }
+
+ /**
+ * Initialize the change notifier. This method is called from the
+ * constructor and registers a listener with the workspace and the
+ * synchronization context. It also registers a listener with the context
+ * cache which will unregister the listeners when the context is disposed.
+ * Subclasses may extend this method.
+ */
+ public void initialize() {
+ ResourcesPlugin.getWorkspace().addResourceChangeListener(this, IResourceChangeEvent.POST_CHANGE);
+ eventHandler = new InputChangeEventHandler();
+ }
+
+ /**
+ * Dispose of the change notifier. This method is invoked when the context
+ * to which the change notifier is associated is disposed.
+ * Subclasses may extend this method.
+ */
+ public void dispose() {
+ ResourcesPlugin.getWorkspace().removeResourceChangeListener(this);
+ if (eventHandler != null)
+ eventHandler.shutdown();
+ }
+
+ /**
+ * Connect the input to this change notifier. Once connected, the change notifier will issue
+ * change events for the given input. When change notification is no longer desired, the
+ * input should be disconnected. The number of calls to {@link #connect(ICompareInput)} needs to
+ * be matched by the same number of calls to {@link #disconnect(ICompareInput)}.
+ * @param input the compare input
+ */
+ public void connect(ICompareInput input) {
+ CompareInputConnecton con = inputs.get(input);
+ if (con == null) {
+ con = new CompareInputConnecton();
+ inputs.put(input, con);
+ }
+ con.increment();
+ }
+
+ /**
+ * Disconnect the input from this change notifier.
+ * @param input the compare input
+ * @see #connect(ICompareInput)
+ */
+ public void disconnect(ICompareInput input) {
+ CompareInputConnecton con = inputs.get(input);
+ if (con != null) {
+ con.decrement();
+ if (con.isDisconnected()) {
+ inputs.remove(input);
+ }
+ }
+ }
+
+ /**
+ * Return the array of inputs that have connections.
+ * @return the array of inputs that have connections
+ */
+ protected ICompareInput[] getConnectedInputs() {
+ return inputs.keySet().toArray(new ICompareInput[inputs.size()]);
+ }
+
+ /**
+ * Send out notification that the given compare inputs have changed.
+ * @param inputs the changed inputs
+ */
+ protected void inputsChanged(ICompareInput[] inputs) {
+ InputChangeEvent event = new InputChangeEvent(inputs);
+ eventHandler.queueEvent(event);
+ }
+
+ /**
+ * Dispatch the changes to the given inputs.
+ * @param inputs the changed compare inputs
+ * @param monitor a progress monitor
+ */
+ protected void dispatchChanges(final ICompareInput[] inputs, IProgressMonitor monitor) {
+ prepareInputs(inputs, monitor);
+ Display.getDefault().syncExec(() -> fireChanges(inputs));
+ }
+
+ /**
+ * Prepare the inputs in the background before firing the compare input change event.
+ * This allows for the caching of contents etc. before the input change event is fired.
+ * @param inputs the changed inputs
+ * @param monitor a progress monitor
+ */
+ protected void prepareInputs(ICompareInput[] inputs, IProgressMonitor monitor) {
+ monitor.beginTask(null, inputs.length * 100);
+ for (int i = 0; i < inputs.length; i++) {
+ ICompareInput input = inputs[i];
+ prepareInput(input, Policy.subMonitorFor(monitor, 100));
+ }
+ monitor.done();
+ }
+
+ /**
+ * Prepare the input before firing the compare input change event.
+ * This allows for the caching of contents etc. before the input change event is fired.
+ * This method is called from {@link #prepareInputs(ICompareInput[], IProgressMonitor)}
+ * for each input. By default, nothing is done, subclasses may override.
+ * @param input the compare input
+ * @param monitor a progress monitor
+ */
+ protected void prepareInput(ICompareInput input, IProgressMonitor monitor) {
+ // Default is to do nothing
+ }
+
+ /**
+ * Update the compare inputs and fire the change events.
+ * This method is called from the UI thread after the inputs have
+ * been prepared in a background thread
+ * (see {@link #prepareInputs(ICompareInput[], IProgressMonitor)})
+ * @param inputs the changed inputs
+ */
+ protected void fireChanges(ICompareInput[] inputs) {
+ for (int i = 0; i < inputs.length; i++) {
+ ICompareInput input = inputs[i];
+ fireChange(input);
+ }
+ }
+
+ /**
+ * Run the given runnable in the background.
+ * @param runnable the runnable
+ */
+ protected void runInBackground(IWorkspaceRunnable runnable) {
+ eventHandler.queueEvent(new BackgroundEventHandler.RunnableEvent(runnable, false));
+ }
+
+ @Override
+ public void resourceChanged(IResourceChangeEvent event) {
+ List<ICompareInput> changedInputs = new ArrayList<>();
+ ICompareInput[] inputs = getConnectedInputs();
+ for (int i = 0; i < inputs.length; i++) {
+ ICompareInput input = inputs[i];
+ IResource[] resources = getResources(input);
+ for (int j = 0; j < resources.length; j++) {
+ IResource resource = resources[j];
+ if (resource != null) {
+ IResourceDelta delta = event.getDelta().findMember(resource.getFullPath());
+ if (delta != null) {
+ if ((delta.getKind() & (IResourceDelta.ADDED | IResourceDelta.REMOVED)) > 0
+ || (delta.getKind() & (IResourceDelta.CHANGED)) > 0
+ && (delta.getFlags() & (IResourceDelta.CONTENT | IResourceDelta.REPLACED)) > 0) {
+ changedInputs.add(input);
+ break;
+ }
+ }
+ }
+ }
+ }
+ if (!changedInputs.isEmpty())
+ handleInputChanges(changedInputs.toArray(new ICompareInput[changedInputs.size()]), true);
+ }
+
+ /**
+ * Return the resources covered by the given compare input.
+ * This method is used by the {@link #resourceChanged(IResourceChangeEvent)}
+ * method to determine if a workspace change affects the compare input.
+ * @param input the compare input
+ * @return the resources covered by the given compare input
+ */
+ protected abstract IResource[] getResources(ICompareInput input);
+
+ /**
+ * Handle the input changes by notifying any listeners of the changed inputs.
+ * @param inputs the changed inputs
+ */
+ protected void handleInputChanges(ICompareInput[] inputs, boolean force) {
+ ICompareInput[] realChanges;
+ if (force) {
+ realChanges = inputs;
+ } else {
+ List<ICompareInput> result = new ArrayList<>();
+ for (int i = 0; i < inputs.length; i++) {
+ ICompareInput input = inputs[i];
+ if (isChanged(input)) {
+ result.add(input);
+ }
+ }
+ realChanges = result.toArray(new ICompareInput[result.size()]);
+ }
+ if (realChanges.length > 0)
+ inputsChanged(realChanges);
+ }
+
+ /**
+ * Return whether the given compare input has changed and requires
+ * a compare input change event to be fired.
+ * @param input the compare input
+ * @return whether the given compare input has changed
+ */
+ protected boolean isChanged(ICompareInput input) {
+ if (input instanceof AbstractCompareInput) {
+ AbstractCompareInput ci = (AbstractCompareInput) input;
+ return ci.needsUpdate();
+ }
+ return false;
+ }
+
+ /**
+ * Update the compare input and fire the change event.
+ * This method is called from {@link #fireChanges(ICompareInput[])}
+ * for each changed input.
+ * @param input the changed compare input
+ */
+ protected void fireChange(ICompareInput input) {
+ if (input instanceof AbstractCompareInput) {
+ AbstractCompareInput ci = (AbstractCompareInput) input;
+ ci.update();
+ }
+ }
+
+ /**
+ * Return whether the background handler for this notifier belongs to the
+ * given job family.
+ * @param family the job family
+ * @return whether the background handler belongs to the given job family.
+ * @see Job#belongsTo(Object)
+ */
+ protected boolean belongsTo(Object family) {
+ return false;
+ }
+
+}

Back to the top