Skip to main content
aboutsummaryrefslogtreecommitdiffstats
blob: 715916bd57079928ae2397a0e06bd1ede8150019 (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
299
/*******************************************************************************
 * Copyright (c) 2000, 2007 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
 *     Matt McCutchen <hashproduct+eclipse@gmail.com> - Bug 178874 Test failure against CVS 1.11.22
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.core.client;

import java.util.*;

import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.osgi.util.NLS;
import org.eclipse.team.internal.ccvs.core.*;
import org.eclipse.team.internal.ccvs.core.client.Command.LocalOption;
import org.eclipse.team.internal.ccvs.core.resources.CVSEntryLineTag;
import org.eclipse.team.internal.ccvs.core.syncinfo.*;
import org.eclipse.team.internal.ccvs.core.util.Util;

/**
 * An ICVSResourceVisitor that is superclass to all ICVSResourceVisitor's used
 * by Command and it's subclasses.
 * Provides helper methods to send files and folders with modifications
 * to the server.
 * 
 * This class does not perform a beginTask of done on the provided monitor.
 * It is used only to signal worl and subTask.
 */
abstract class AbstractStructureVisitor implements ICVSResourceVisitor {
	
	protected Session session;
	private ICVSFolder lastFolderSent;
	protected IProgressMonitor monitor;
	protected boolean sendQuestionable;
	protected boolean sendModifiedContents;
	private boolean sendBinary;
    
    private boolean recurse = true;

	public AbstractStructureVisitor(Session session, LocalOption[] localOptions, boolean sendQuestionable, boolean sendModifiedContents) {
		this(session, localOptions, sendQuestionable, sendModifiedContents, true);
	}

	public AbstractStructureVisitor(Session session, LocalOption[] localOptions, boolean sendQuestionable, boolean sendModifiedContents, boolean sendBinary) {
		this.session = session;
		this.sendQuestionable = sendQuestionable;
		this.sendModifiedContents = sendModifiedContents;
		this.sendBinary = sendBinary;
        if (Command.DO_NOT_RECURSE.isElementOf(localOptions))
            recurse = false;
	}
		
	/** 
	 * Helper method to indicate if a directory has already been sent to the server
	 */
	protected boolean isLastSent(ICVSFolder folder) {
		return folder.equals(lastFolderSent);
	}
	
	/** 
	 * Helper method to record if a directory has already been sent to the server
	 */
	protected void recordLastSent(ICVSFolder folder) {
		lastFolderSent = folder;
	}
	
	/** 
	 * Helper which indicates if a folder is an orphaned subtree. 
	 * That is, a directory which contains a CVS subdirectory but is
	 * not managed by its parent. The root directory of the session
	 * is not considered orphaned even if it is not managed by its
	 * parent.
	 */
	protected boolean isOrphanedSubtree(ICVSFolder mFolder) throws CVSException {
		return mFolder.isCVSFolder() && ! mFolder.isManaged() && ! mFolder.equals(session.getLocalRoot()) && mFolder.getParent().isCVSFolder();
	}
	
	/**
	 * Send the folder relative to the root to the server. Send all 
	 * appropiate modifier like Sticky, Questionable, Static-directory.
	 * <br>
	 * Folders will only be sent once.
	 */
	protected void sendFolder(ICVSFolder mFolder) throws CVSException {

		Policy.checkCanceled(monitor);
		
		boolean exists = mFolder.exists();
		FolderSyncInfo info = mFolder.getFolderSyncInfo();
		boolean isCVSFolder = info != null;
		
		// We are only interested in folders that exist or are CVS folders
		// A folder could be a non-existant CVS folder if it is a holder for outgoing file deletions
		if ( ! exists && ! isCVSFolder) return;
		
		// Do not send the same folder twice
		if (isLastSent(mFolder)) return;
		
		// Do not send virtual directories
        if (isCVSFolder && info.isVirtualDirectory()) {
			return;
		}

		String localPath = mFolder.getRelativePath(session.getLocalRoot());
		
		monitor.subTask(NLS.bind(CVSMessages.AbstractStructureVisitor_sendingFolder, new String[] { Util.toTruncatedPath(mFolder, session.getLocalRoot(), 3) })); 
		
		// Deal with questionable directories
		boolean isQuestionable = exists && (! isCVSFolder || isOrphanedSubtree(mFolder));
		if (isQuestionable) {
			if (sendQuestionable) {
				// We need to make sure the parent folder was sent 
				sendFolder(mFolder.getParent());
				session.sendQuestionable(mFolder);
			}
			return;
		}

		// Send the directory to the server
		String remotePath = mFolder.getRemoteLocation(session.getLocalRoot());
		if (remotePath == null) {
			IStatus status = new CVSStatus(IStatus.ERROR,CVSStatus.ERROR, CVSMessages.AbstractStructureVisitor_noRemote, session.getLocalRoot());
			throw new CVSException(status); 
		}
		session.sendDirectory(localPath, remotePath);

		// Send any directory properties to the server
		if (info != null) {

			if (info.getIsStatic()) {
				session.sendStaticDirectory();
			}

			CVSEntryLineTag tag = info.getTag();

			if (tag != null && tag.getType() != CVSTag.HEAD) {
				session.sendSticky(tag.toEntryLineFormat(false));
			}
		}

		// Record that we sent this folder
		recordLastSent(mFolder);
		
		monitor.worked(1);
	}

	/**
	 * Send the information about the file to the server.
	 * 
	 * If the file is modified, its contents are sent as well.
	 */
	protected void sendFile(ICVSFile mFile) throws CVSException {

		Policy.checkCanceled(monitor);

		// Send the parent folder if it hasn't been sent already
		sendFolder(mFile.getParent());
		
		// Send the file's entry line to the server
		byte[] syncBytes = mFile.getSyncBytes();
		boolean isManaged = syncBytes != null;
		
		if (isManaged) {
		    sendPendingNotification(mFile);
		} else {
			// If the file is not managed, send a questionable to the server if the file exists locally
			// A unmanaged, locally non-existant file results from the explicit use of the file name as a command argument
			if (sendQuestionable) {
				if (mFile.exists()) {
					session.sendQuestionable(mFile);
				}
				return;
			}
			// else we are probably doing an import so send the file contents below
		}
		
		// Determine if we need to send the contents.
		// If the file is unmodified since a conflict, we need to not send the
		// contents so that the server rejects the file (bug 178874).
		boolean sendContents = mFile.exists() && mFile.isModified(monitor)
			&& !mFile.getSyncInfo().isNeedsMerge(mFile.getTimeStamp());
		if (ResourceSyncInfo.isDeletion(syncBytes)) {
		    sendEntryLineToServer(mFile, syncBytes);
		} else if (sendContents) {
		    // Perform the send of modified contents in a sheduling rule to ensure that
		    // the contents are not modified while we are sending them
		    final IResource resource = mFile.getIResource();
            try {
                if (resource != null)
                    Job.getJobManager().beginRule(resource, monitor);
		        
				sendEntryLineToServer(mFile, syncBytes);
				if (mFile.exists() && mFile.isModified(null)) {
					boolean binary = ResourceSyncInfo.isBinary(syncBytes);
					if (sendModifiedContents) {
						session.sendModified(mFile, binary, sendBinary, monitor);
					} else {
						session.sendIsModified(mFile, binary, monitor);
					}
				} else {
					session.sendUnchanged(mFile);
				}
		    } finally {
		        if (resource != null)
		            Job.getJobManager().endRule(resource);
		    }
		} else {
		    sendEntryLineToServer(mFile, syncBytes);
			session.sendUnchanged(mFile);
		}
		
		monitor.worked(1);
	}

    private void sendEntryLineToServer(ICVSFile mFile, byte[] syncBytes) throws CVSException {
        if (syncBytes != null) {
            String syncBytesToServer = ResourceSyncInfo.getTimestampToServer(syncBytes, mFile.getTimeStamp());
            session.sendEntry(syncBytes, syncBytesToServer);
        }
    }

    protected void sendPendingNotification(ICVSFile mFile) throws CVSException {
		NotifyInfo notify = mFile.getPendingNotification();
		if (notify != null) {
			sendFolder(mFile.getParent());
			session.sendNotify(mFile.getParent(), notify);
		}
	}
	
	/**
	 * This method is used to visit a set of ICVSResources. Using it ensures
	 * that a common parent between the set of resources is only sent once
	 */
	public void visit(Session session, ICVSResource[] resources, IProgressMonitor monitor) throws CVSException {
		
		// Sort the resources to avoid sending the same directory multiple times
		List<ICVSResource> resourceList = new ArrayList<>(resources.length);
		resourceList.addAll(Arrays.asList(resources));
		final ICVSFolder localRoot = session.getLocalRoot();
		Collections.sort(resourceList, (resource1, resource2) -> {
			try {
				String path1 = resource1.getParent().getRelativePath(localRoot);
				String path2 = resource2.getParent().getRelativePath(localRoot);
				int pathCompare = path1.compareTo(path2);
				if (pathCompare == 0) {
					if (resource1.isFolder() == resource2.isFolder()) {
						return resource1.getName().compareTo(resource2.getName());
					} else if (resource1.isFolder()) {
						return 1;
					} else {
						return -1;
					}
				} else {
					return pathCompare;
				}
			} catch (CVSException e) {
				return resource1.getName().compareTo(resource2.getName());
			}
		});

		// Create a progress monitor suitable for the visit
		int resourceHint = 64;
		monitor.beginTask(null, resourceHint);
		this.monitor = Policy.infiniteSubMonitorFor(monitor, resourceHint);
		try {
			// Visit all the resources
			this.monitor.beginTask(null, resourceHint);
			session.setSendFileTitleKey(getSendFileMessage());
			for (int i = 0; i < resourceList.size(); i++) {
				((ICVSResource)resourceList.get(i)).accept(this);
			}
		} finally {
			monitor.done();
		}
	}
	
    /**
     * Return a send file message that contains one argument slot
     * for the file name.
     * @return a send file message that contains one argument slot
     * for the file name
     */
	protected String getSendFileMessage() {
		return CVSMessages.AbstractStructureVisitor_sendingFile;
	}
    public boolean isRecurse() {
        return recurse;
    }
}

Back to the top