Skip to main content
summaryrefslogtreecommitdiffstats
blob: f57ff4530e2701301a494acdca6c96d76e2ee02a (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
/*******************************************************************************
 * Copyright (c) 2000, 2003 IBM Corporation and others.
 * All rights reserved. This program and the accompanying materials 
 * are made available under the terms of the Common Public License v1.0
 * which accompanies this distribution, and is available at
 * http://www.eclipse.org/legal/cpl-v10.html
 * 
 * Contributors:
 *     IBM Corporation - initial API and implementation
 *******************************************************************************/
package org.eclipse.team.internal.ccvs.core.client.listeners;

import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.team.internal.ccvs.core.CVSProviderPlugin;
import org.eclipse.team.internal.ccvs.core.CVSStatus;
import org.eclipse.team.internal.ccvs.core.ICVSFolder;
import org.eclipse.team.internal.ccvs.core.ICVSRepositoryLocation;
import org.eclipse.team.internal.ccvs.core.client.CommandOutputListener;
import org.eclipse.team.internal.ccvs.core.client.Update;

public class UpdateListener extends CommandOutputListener {

	IUpdateMessageListener updateMessageListener;
	boolean merging = false;

	public UpdateListener(IUpdateMessageListener updateMessageListener) {
		this.updateMessageListener = updateMessageListener;
	}
	
	public IStatus messageLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot,
		IProgressMonitor monitor) {
		if (updateMessageListener == null) return OK;
		if(line.startsWith("Merging differences")) { //$NON-NLS-1$
			merging = true;
		} else if(line.indexOf(' ')==1) {
			// We have a message that indicates the type of update. The possible messages are
			// defined by the prefix constants MLP_*.
			String path = line.substring(2);
			char changeType = line.charAt(0);
			
			// calculate change type
			int type = 0;
			switch(changeType) {
				case 'A': type = Update.STATE_ADDED_LOCAL; break; // new file locally that was added but not comitted to server yet
				case '?': type = Update.STATE_UNKOWN; break; // new file locally but not added to server
				case 'U': type = Update.STATE_REMOTE_CHANGES; break;  // remote changes to an unmodified local file
				case 'R': type = Update.STATE_DELETED; break; // removed locally but still exists on the server
				case 'M': type = Update.STATE_MODIFIED; break; // modified locally
				case 'C': type = Update.STATE_CONFLICT; break;  // modified locally and on the server but cannot be auto-merged
				case 'D': type = Update.STATE_DELETED; break;  // deleted locally but still exists on server
				default: type = Update.STATE_NONE;
			}
				
			if (merging) {
				// If we are merging the modified prefix is used both to show merges and
				// local changes. We have to detect this case and use a more specific change
				// type.
				if (type == Update.STATE_MODIFIED)
					type = Update.STATE_MERGEABLE_CONFLICT;
				merging = false;
			}
			updateMessageListener.fileInformation(type, commandRoot, path);
		}
		return OK;
	}

	/**
	 * This handler is used by the RemoteResource hierarchy to retrieve E messages
	 * from the CVS server in order to determine the folders contained in a parent folder.
	 * 
	 * WARNING: This class parses the message output to determine the state of files in the
	 * repository. Unfortunately, these messages seem to be customizable on a server by server basis.
	 * 
	 * Here's a list of responses we expect in various situations:
	 * 
	 * Directory exists remotely:
	 *    cvs server: Updating folder1/folder2
	 * Directory doesn't exist remotely:
	 *    cvs server: skipping directory folder1/folder2
	 * New (or unknown) remote directory
	 *    cvs server: New Directory folder1/folder2
	 * File removed remotely
	 *    cvs server: folder1/file.ext is no longer in the repository
	 *    cvs server: warning: folder1/file.ext is not (any longer) pertinent
	 * Locally added file was added remotely as well
	 *    cvs server: conflict: folder/file.ext created independently by second party 
	 * File removed locally and modified remotely
	 *    cvs server: conflict: removed file.txt was modified by second party
	 * File modified locally but removed remotely
	 *    cvs server: conflict: file.txt is modified but no longer in the repository
	 * Ignored Messages
	 *    cvs server: cannot open directory ...
	 *    cvs server: nothing known about ...
	 * Tag error that really means there are no files in a directory
	 *    cvs [server aborted]: no such tag
	 * Merge contained conflicts
	 *    rcsmerge: warning: conflicts during merge
	 */
	public IStatus errorLine(String line, ICVSRepositoryLocation location, ICVSFolder commandRoot,
		IProgressMonitor monitor) {
		
		try {
			String serverMessage = getServerMessage(line, location);
			if (serverMessage != null) {
				// Strip the prefix from the line
				String message = serverMessage;
				if (message.startsWith("Updating")) { //$NON-NLS-1$
					if (updateMessageListener != null) {
						String path = message.substring(9);
						updateMessageListener.directoryInformation(commandRoot, path, false);
					}
					return OK;
				} else if (message.startsWith("skipping directory")) { //$NON-NLS-1$
					if (updateMessageListener != null) {
						String path = message.substring(18).trim();
						updateMessageListener.directoryDoesNotExist(commandRoot, path);
					}
					return OK;
				} else if (message.startsWith("New directory")) { //$NON-NLS-1$
					if (updateMessageListener != null) {
						String path = message.substring(15, message.lastIndexOf('\''));
						updateMessageListener.directoryInformation(commandRoot, path, true);
					}
					return OK;
				} else if (message.endsWith("is no longer in the repository")) { //$NON-NLS-1$
					if (updateMessageListener != null) {
						String filename = message.substring(0, message.length() - 31);
						updateMessageListener.fileDoesNotExist(commandRoot, filename);
					}
					return OK;
				} else if (message.startsWith("conflict:")) { //$NON-NLS-1$
					/*
					 * We can get the following conflict warnings
					 *    cvs server: conflict: folder/file.ext created independently by second party 
					 *    cvs server: conflict: removed file.txt was modified by second party
					 *    cvs server: conflict: file.txt is modified but no longer in the repository
					 * If we get the above line, we have conflicting additions or deletions and we can expect a server error.
					 * We still get "C foler/file.ext" so we don't need to do anything else (except in the remotely deleted case)
					 */
					if (updateMessageListener != null) {
						if (message.endsWith("is modified but no longer in the repository")) { //$NON-NLS-1$
							// The "C foler/file.ext" will come after this so if whould be ignored!
							String filename = message.substring(10, message.length() - 44);
							updateMessageListener.fileDoesNotExist(commandRoot, filename);
						}
					}
					return new CVSStatus(CVSStatus.WARNING, CVSStatus.CONFLICT, commandRoot, line);
				} else if (message.startsWith("warning:")) { //$NON-NLS-1$
					/*
					 * We can get the following conflict warnings
					 *    cvs server: warning: folder1/file.ext is not (any longer) pertinent
					 * If we get the above line, we have local changes to a remotely deleted file.
					 */
					if (updateMessageListener != null) {
						if (message.endsWith("is not (any longer) pertinent")) { //$NON-NLS-1$
							String filename = message.substring(9, message.length() - 30);
							updateMessageListener.fileDoesNotExist(commandRoot, filename);
						}
					}
					return new CVSStatus(CVSStatus.WARNING, CVSStatus.CONFLICT, commandRoot, line);
				} else if (message.startsWith("conflicts")) { //$NON-NLS-1$
					// This line is info only. The server doesn't report an error.
					return new CVSStatus(IStatus.INFO, CVSStatus.CONFLICT, commandRoot, line);
				} else if (!message.startsWith("cannot open directory") //$NON-NLS-1$
						&& !message.startsWith("nothing known about")) { //$NON-NLS-1$
					return super.errorLine(line, location, commandRoot, monitor);
				}
			} else {
				String serverAbortedMessage = getServerAbortedMessage(line, location);
				if (serverAbortedMessage != null) {
					// Strip the prefix from the line
					String message = serverAbortedMessage;
					if (message.startsWith("no such tag")) { //$NON-NLS-1$
						// This is reported from CVS when a tag is used on the update there are no files in the directory
						// To get the folders, the update request should be re-issued for HEAD
						return new CVSStatus(CVSStatus.WARNING, CVSStatus.NO_SUCH_TAG, commandRoot, line);
					} else {
						return super.errorLine(line, location, commandRoot, monitor);
					}
				} else if (line.equals("rcsmerge: warning: conflicts during merge")) { //$NON-NLS-1$
					// There were conflicts in the merge
					return new CVSStatus(CVSStatus.WARNING, CVSStatus.CONFLICT, commandRoot, line);
				}
			}
		} catch (StringIndexOutOfBoundsException e) {
			// Something went wrong in the parsing of the message.
			// Return a status indicating the problem
			if (CVSProviderPlugin.getPlugin().isDebugging()) {
				System.out.println("Error parsing E line: " + line); //$NON-NLS-1$
			}
			return new CVSStatus(CVSStatus.ERROR, CVSStatus.ERROR_LINE_PARSE_FAILURE, commandRoot, line);
		}
		return super.errorLine(line, location, commandRoot, monitor);
	}

}

Back to the top