Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: c062b6a193491d066bed837123fd59260d356109 (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
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
/*******************************************************************************
 * Copyright (c) 2000, 2006 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.ccvs.core.resources;

import java.util.*;

import org.eclipse.core.resources.IProject;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.core.RepositoryProvider;
import org.eclipse.team.core.TeamException;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.client.listeners.*;
import org.eclipse.team.internal.ccvs.core.connection.CVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.connection.CVSServerException;
import org.eclipse.team.internal.ccvs.core.syncinfo.FolderSyncInfo;
import org.eclipse.team.internal.ccvs.core.syncinfo.ResourceSyncInfo;

/**
 * This class can be used to fetch and cache file contents for remote files.
 */
public class UpdateContentCachingService implements IUpdateMessageListener {

	private CVSRepositoryLocation repository;
	private ICVSFolder remoteRoot;
	private final CVSTag tag;
	private final int depth;
	private boolean fetchAbsentDirectories = true;
	private ArrayList<ICVSResource> removed = new ArrayList<>();

	public class SandboxUpdate extends Update {
		
		@Override
		protected boolean shouldRetrieveAbsentDirectories(Session session) {
			return fetchAbsentDirectories;
		}
		
		@Override
		protected IStatus commandFinished(Session session, GlobalOption[] globalOptions, LocalOption[] localOptions, ICVSResource[] resources, IProgressMonitor monitor, IStatus status) throws CVSException {
			// Don't do anything (i.e. don't prune)
			return status;
		}

		@Override
		protected IStatus doExecute(Session session, GlobalOption[] globalOptions, LocalOption[] localOptions, String[] arguments, ICommandOutputListener listener, IProgressMonitor monitor) throws CVSException {
			session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_CREATED));
			session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_MERGED));
			session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_UPDATE_EXISTING));
			session.registerResponseHandler(new SandboxUpdatedHandler(UpdatedHandler.HANDLE_UPDATED));
			return super.doExecute(session, globalOptions, localOptions, arguments, listener, monitor);
		}
	}
	
	/**
	 * This class overrides the "Created" handler in order to configure the remote file
	 * to receive and cache the contents
	 */
	public class SandboxUpdatedHandler extends UpdatedHandler {
		public SandboxUpdatedHandler(int type) {
			super(type);
		}
		@Override
		protected void receiveTargetFile(
			Session session,
			ICVSFile mFile,
			String entryLine,
			Date modTime,
			boolean binary,
			boolean readOnly,
			boolean executable,
			IProgressMonitor monitor)
			throws CVSException {
			
			// Set the sync info first so that the contents are cached properly
			ResourceSyncInfo info = new ResourceSyncInfo(entryLine, modTime);
			// We're always excepting new revisions so the file is clean
			mFile.setSyncInfo(info, ICVSFile.CLEAN);
			
			// receive the file contents from the server
			session.receiveFile(mFile, binary, getHandlerType(), monitor);
			
			// Handle execute
			try {
				if (executable) mFile.setExecutable(true);
			} catch (CVSException e) {
				// Just log and keep going
				CVSProviderPlugin.log(e);
			}
		}
	}
	
	public static RemoteFolder buildRemoteTree(final CVSRepositoryLocation repository, ICVSFolder root, CVSTag tag, int depth, IProgressMonitor monitor) throws CVSException {
		monitor.beginTask(null, 100);
		try {
			RemoteFolder tree = buildBaseTree(repository, root, tag, Policy.subMonitorFor(monitor, 50));
			UpdateContentCachingService service = new UpdateContentCachingService(repository, tree, tag, depth);
			service.setFetchAbsentDirectories(getFetchAbsentDirectories(root));
			if (!service.cacheFileContents(Policy.subMonitorFor(monitor, 50)))
				return null;
			return tree;
		} finally {
			monitor.done();
		}
	}
	
	private void setFetchAbsentDirectories(boolean fetchAbsentDirectories) {
		this.fetchAbsentDirectories = fetchAbsentDirectories;
	}

	private static boolean getFetchAbsentDirectories(ICVSFolder root) {
		IResource resource = root.getIResource();
		if (resource != null) {
			IProject project = resource.getProject();
			RepositoryProvider provider = RepositoryProvider.getProvider(project, CVSProviderPlugin.getTypeId());
			if (provider instanceof CVSTeamProvider) {
				CVSTeamProvider cp = (CVSTeamProvider) provider;
				try {
					return cp.getFetchAbsentDirectories();
				} catch (CVSException e) {
					CVSProviderPlugin.log(e);
				}
			}
		}
		return CVSProviderPlugin.getPlugin().getFetchAbsentDirectories();
	}

	private static RemoteFolder buildBaseTree(final CVSRepositoryLocation repository, ICVSFolder root, CVSTag tag, IProgressMonitor progress) throws CVSException {
		try {
			RemoteFolderTreeBuilder builder = new RemoteFolderTreeBuilder(repository, root, tag) {
				protected RemoteFolder createRemoteFolder(ICVSFolder local, RemoteFolder parent, FolderSyncInfo folderSyncInfo) {
					return new RemoteFolderSandbox(parent, local.getName(), repository, folderSyncInfo.getRepository(), folderSyncInfo.getTag(), folderSyncInfo.getIsStatic());
				}
				protected RemoteFile createRemoteFile(RemoteFolder remote, byte[] syncBytes) throws CVSException {
					return new RemoteFile(remote, syncBytes){
						public boolean isModified(IProgressMonitor monitor) throws CVSException {
							return false;
						}	
						public void delete() {
							RemoteFolderSandbox parent = (RemoteFolderSandbox)getParent();
							parent.remove(this);
						}
					};
				}
				protected boolean isPruneEmptyDirectories() {
					return true;
				}
			};
			progress.beginTask(null, 100);
			IProgressMonitor subProgress = Policy.infiniteSubMonitorFor(progress, 100);
			subProgress.beginTask(null, 512);  
			subProgress.subTask(NLS.bind(CVSMessages.RemoteFolderTreeBuilder_buildingBase, new String[] { root.getName() })); 
			RemoteFolder tree = builder.buildBaseTree(null, root, subProgress);
			if (tree == null) {
				// The local tree is empty and was pruned.
				// Return the root folder so that the operation can proceed
				FolderSyncInfo folderSyncInfo = root.getFolderSyncInfo();
				if (folderSyncInfo == null) return null;
				return new RemoteFolderSandbox(null, root.getName(), repository, folderSyncInfo.getRepository(), folderSyncInfo.getTag(), folderSyncInfo.getIsStatic());
			}
			return tree;
		} finally {
			progress.done();
		}
	}
	
	public static RemoteFile buildRemoteTree(CVSRepositoryLocation repository, ICVSFile file, CVSTag tag, IProgressMonitor monitor) throws CVSException {
		monitor.beginTask(null, 100);
		try {
			RemoteFolderTreeBuilder builder = new RemoteFolderTreeBuilder(repository, file.getParent(), tag);
			RemoteFile remote =  builder.buildTree(file, Policy.subMonitorFor(monitor, 10));
			
			if (remote == null)
				return null;
			
			byte[] syncBytes = remote.getSyncBytes();
			if (builder.getFileDiffs().length > 0) {
				// Getting the storage of the file will cache the contents
				remote.getStorage(Policy.subMonitorFor(monitor, 90));
			}
			// We need to set the sync bytes back because the content fetch
			// makes the handle sticky
			remote.setSyncBytes(syncBytes, ICVSFile.CLEAN);
			return remote;
		} catch (TeamException e) {
			throw CVSException.wrapException(e);
		} finally {
			monitor.done();
		}
	}
	
	public UpdateContentCachingService(CVSRepositoryLocation repository, RemoteFolder tree, CVSTag tag, int depth) {
		this.repository = repository;
		this.remoteRoot = tree;
		this.tag = tag;
		this.depth = depth;
	}
	
	private boolean cacheFileContents(IProgressMonitor monitor) throws CVSException {
		// Fetch the file contents for all out-of-sync files by running an update
		// on the remote tree passing the known changed files as arguments
		monitor.beginTask(null, 100);
		Policy.checkCanceled(monitor);
		Session session = new Session(repository, remoteRoot, false);
		session.open(Policy.subMonitorFor(monitor, 10), false /* read-only */);
		try {
			Policy.checkCanceled(monitor);
			IStatus status = new SandboxUpdate().execute(session,
				Command.NO_GLOBAL_OPTIONS,
				getLocalOptions(),
				new String[] { Session.CURRENT_LOCAL_FOLDER },
				new UpdateListener(this),
				Policy.subMonitorFor(monitor, 90));
			if (!status.isOK()) {
				if (status.getCode() == CVSStatus.SERVER_ERROR) {
					CVSServerException e = new CVSServerException(status);
					if ( ! e.isNoTagException() && e.containsErrors())
						throw e;
					return false;
				} else if (status.getSeverity() == IStatus.ERROR && isReportableError(status)) {
					throw new CVSException(status);
				}
			}
			for (Iterator iterator = removed.iterator(); iterator.hasNext();) {
				ICVSResource resource = (ICVSResource) iterator.next();
				if (resource.exists())
					resource.delete();
			}
		} finally {
			session.close();
			monitor.done();
		}
		return true;
	}

	private boolean isReportableError(IStatus status) {
		return CVSStatus.isInternalError(status) 
			|| status.getCode() == TeamException.UNABLE
			|| status.getCode() == CVSStatus.INVALID_LOCAL_RESOURCE_PATH
			|| status.getCode() == CVSStatus.RESPONSE_HANDLING_FAILURE;
	}

	private LocalOption[] getLocalOptions() {
		ArrayList<LocalOption> options = new ArrayList<>();
		if (tag != null)
			options.add(Update.makeTagOption(tag));
		
		if (depth != IResource.DEPTH_INFINITE )
			options.add(Command.DO_NOT_RECURSE);
		
		if (fetchAbsentDirectories)
			options.add(Update.RETRIEVE_ABSENT_DIRECTORIES);
		
		if (!options.isEmpty())
			return options.toArray(new LocalOption[options.size()]);
		
		return Command.NO_LOCAL_OPTIONS;
	}

	public void directoryDoesNotExist(ICVSFolder commandRoot, String path) {
		try {
			removed.add(commandRoot.getChild(path));
		} catch (CVSException e) {
			CVSProviderPlugin.log(e);
		}
	}

	public void directoryInformation(ICVSFolder commandRoot, String path,
			boolean newDirectory) {
		// Nothing to do
	}

	public void fileDoesNotExist(ICVSFolder parent, String filename) {
		try {
			removed.add(parent.getChild(filename));
		} catch (CVSException e) {
			CVSProviderPlugin.log(e);
		}
	}

	public void fileInformation(int type, ICVSFolder parent, String filename) {
		// Nothing to do
	}
}

Back to the top