Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 9f070444a5a58bc867a21487d3a1f27cac8b8cf4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
/*******************************************************************************
 * Copyright (c) 2000, 2017 IBM Corporation and others.
 *
 * This program and the accompanying materials
 * are made available under the terms of the Eclipse Public License 2.0
 * which accompanies this distribution, and is available at
 * https://www.eclipse.org/legal/epl-2.0/
 *
 * SPDX-License-Identifier: EPL-2.0
 *
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.internal.ui.synchronize;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.resources.IWorkspaceRunnable;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.team.core.ITeamStatus;
import org.eclipse.team.core.synchronize.ISyncInfoSetChangeEvent;
import org.eclipse.team.core.synchronize.ISyncInfoSetChangeListener;
import org.eclipse.team.core.synchronize.SyncInfo;
import org.eclipse.team.core.synchronize.SyncInfoSet;
import org.eclipse.team.core.synchronize.SyncInfoTree;
import org.eclipse.team.internal.core.subscribers.ChangeSet;
import org.eclipse.team.internal.core.subscribers.ChangeSetManager;
import org.eclipse.team.internal.core.subscribers.CheckedInChangeSet;
import org.eclipse.team.ui.synchronize.ISynchronizePageConfiguration;

/**
 * This abstract class provides API for accumulating the <code>SyncInfo</code>
 * from a seed <code>SyncInfoSet</code> into a set of <code>ChangeSet</code>
 * instances. It is used to provide the input to a synchronize page when
 * change sets are enabled.
 * <p>
 * This class does not register as a change listener with the seed set. It
 * is up to clients to invoke either the <code>reset</code> or <code>handleChange</code>
 * methods in response to seed set changes.
 * @since 3.1
 */
public abstract class SyncInfoSetChangeSetCollector extends ChangeSetManager {

    private final ISynchronizePageConfiguration configuration;
    private ChangeSetModelProvider provider;

    /*
     * Listener that will remove sets when they become empty.
     * The sets in this collector are only modified from either the
     * UI thread or the provider's event handler thread so updates
     * done by this listener will update the view properly.
     */
    ISyncInfoSetChangeListener changeSetListener = new ISyncInfoSetChangeListener() {

        @Override
		public void syncInfoSetReset(SyncInfoSet set, IProgressMonitor monitor) {
            handleChangeEvent(set);
        }

        @Override
		public void syncInfoChanged(ISyncInfoSetChangeEvent event, IProgressMonitor monitor) {
            handleChangeEvent(event.getSet());
        }

        @Override
		public void syncInfoSetErrors(SyncInfoSet set, ITeamStatus[] errors, IProgressMonitor monitor) {
            // TODO Auto-generated method stub
        }

        /*
         * The collector removes change sets once they are empty
         */
        private void handleChangeEvent(SyncInfoSet set) {
            if (set.isEmpty()) {
                ChangeSet changeSet = getChangeSet(set);
                if (changeSet != null) {
                    remove(changeSet);
                }
            }
        }
    };

    /**
     * Create a collector that contains the sync info from the given seed set
     * @param configuration the set used to determine which sync info
     * should be included in the change sets.
     */
    public SyncInfoSetChangeSetCollector(ISynchronizePageConfiguration configuration) {
        this.configuration = configuration;
    }

    /**
     * Add the given resource sync info nodes to the appropriate
     * change sets, adding them if necessary.
     * This method is invoked by the <code>handleChanges</code>
     * and <code>reset</code> methods
     * when the model provider changes state. Updates done to the collector
     * from within this thread will be thread-safe and update the view
     * properly. Updates done from other threads should perform adds
     * within a runnable passed to the
     * <code>performUpdate</code> method to ensure the view is
     * updated properly.
     * <p>
     * Subclasses must override this method.
     * @param infos the sync infos to add
     */
    protected abstract void add(SyncInfo[] infos);

    /**
     * Remove the given resources from all sets of this collector.
     * This method is invoked by the <code>handleChanges</code> method
     * when the model provider changes state. It should not
     * be invoked by other clients. The model provider
     * will invoke this method from a particular thread (which may
     * or may not be the UI thread).
     * Updates done from other threads should perform removes
     * within a runnable passed to the
     * <code>performUpdate</code> method to ensure the view is
     * updated properly.
     * <p>
     * Subclasses may override this method.
     * @param resources the resources to be removed
     */
    protected void remove(IResource[] resources) {
    	ChangeSet[] sets = getSets();
        for (int i = 0; i < sets.length; i++) {
        	ChangeSet set = sets[i];
            set.remove(resources);
        }
    }

    protected ISyncInfoSetChangeListener getChangeSetChangeListener() {
        return changeSetListener;
    }

    /**
     * Re-populate the change sets from the seed set.
     * If <code>null</code> is passed, clear any state
     * but do not re-populate.
     * <p>
     * This method is invoked by the model provider when the
     * model provider changes state. It should not
     * be invoked by other clients. The model provider
     * will invoke this method from a particular thread (which may
     * or may not be the UI thread). Updates done to the collector
     * from within this thread will be thread-safe and update the view
     * properly. Updates done from other threads should use the
     * <code>performUpdate</code> method to ensure the view is
     * updated properly.
     * <p>
     * Subclasses may override this method.
     * @param seedSet
     */
    public void reset(SyncInfoSet seedSet) {
        // First, remove all the sets
        ChangeSet[] sets = getSets();
        for (int i = 0; i < sets.length; i++) {
        	ChangeSet set2 = sets[i];
            remove(set2);
        }
        if (seedSet != null) {
            add(seedSet.getSyncInfos());
        }
    }

    /**
     * This method is invoked by the model provider when the
     * seed <code>SyncInfoSet</code> changes. It should not
     * be invoked by other clients. The model provider
     * will invoke this method from a particular thread (which may
     * or may not be the UI thread). Updates done to the collector
     * from within this thread will be thread-safe and update the view
     * properly. Updates done from other threads should use the
     * <code>performUpdate</code> method to ensure the view is
     * updated properly.
     * <p>
     * Subclasses may override this method.
     * @param event the set change event.
     */
    public void handleChange(ISyncInfoSetChangeEvent event) {
        List<IResource> removals = new ArrayList<>();
        List<SyncInfo> additions = new ArrayList<>();
        removals.addAll(Arrays.asList(event.getRemovedResources()));
        additions.addAll(Arrays.asList(event.getAddedResources()));
        SyncInfo[] changed = event.getChangedResources();
        for (int i = 0; i < changed.length; i++) {
            SyncInfo info = changed[i];
            additions.add(info);
            removals.add(info.getLocal());
        }
        if (!removals.isEmpty()) {
            remove(removals.toArray(new IResource[removals.size()]));
        }
        if (!additions.isEmpty()) {
            add(additions.toArray(new SyncInfo[additions.size()]));
        }
    }

    /**
     * Return the configuration for the page that is displaying the model created
     * using this collector.
     * @return the configuration for the page that is displaying the model created
     * using this collector
     */
    public final ISynchronizePageConfiguration getConfiguration() {
        return configuration;
    }

    /**
     * Execute the given runnable which updates the sync sets contained
     * in this collector. This method should be used by subclasses when they
     * are populating or modifying sets from another thread. In other words,
     * if the sets of this collector are updated directly in the <code>add</code>
     * method then this method is not required. However, if sets are created
     * or modified by another thread, that thread must use this method to ensure
     * the updates occur in the proper thread in order to ensure thread safety.
     * <p>
     * The update may be run in a different thread then the caller.
     * However, regardless of which thread the update is run in, the view
     * will be updated once the update is completed.
     * @param runnable the workspace runnable that updates the sync sets.
     * @param preserveExpansion whether the expansed items in the view should
     * remain expanded after the update is performed.
     * @param monitor a progress monitor
     */
    protected final void performUpdate(IWorkspaceRunnable runnable, boolean preserveExpansion, IProgressMonitor monitor) {
        provider.performUpdate(runnable, preserveExpansion, false /* run in the handler thread and refresh at the end */);
    }

    /* (non-javadoc)
     * Sets the provider for this collector. This method is for internal use only.
     */
    public final void setProvider(ChangeSetModelProvider provider) {
        this.provider = provider;
    }

    /**
     * This method should wait until any background processing is
     * completed. It is for testing purposes. By default, it does not wait at all.
     * Subclasses that perform work in the background should override.
     * @param monitor a progress monitor
     */
    public void waitUntilDone(IProgressMonitor monitor) {
        // Do nothing, by default
    }

    @Override
	protected void handleSetAdded(ChangeSet set) {
    	((CheckedInChangeSet)set).getSyncInfoSet().addSyncSetChangedListener(getChangeSetChangeListener());
    	super.handleSetAdded(set);
    }

    @Override
	protected void handleSetRemoved(ChangeSet set) {
    	((CheckedInChangeSet)set).getSyncInfoSet().removeSyncSetChangedListener(getChangeSetChangeListener());
    	super.handleSetRemoved(set);
    }

    /**
     * Return the Change Set whose sync info set is the
     * one given.
     * @param set a sync info set
     * @return the change set for the given sync info set
     */
    protected ChangeSet getChangeSet(SyncInfoSet set) {
        ChangeSet[] sets = getSets();
        for (int i = 0; i < sets.length; i++) {
        	ChangeSet changeSet = sets[i];
            if (((CheckedInChangeSet)changeSet).getSyncInfoSet() == set) {
                return changeSet;
            }
        }
        return null;
    }

	public SyncInfoTree getSyncInfoSet(ChangeSet set) {
		return ((CheckedInChangeSet)set).getSyncInfoSet();
	}
}

Back to the top