001 /*
002 * StatusHandler.java
003 * Copyright (c) 1998-2008, The University of Sheffield.
004 *
005 * This code is from the GATE project (http://gate.ac.uk/) and is free
006 * software licenced under the GNU General Public License version 3. It is
007 * distributed without any warranty. For more details see COPYING.txt in the
008 * top level directory (or at http://gatewiki.sf.net/COPYING.txt).
009 *
010 * Hamish Cunningham 8th January 2007
011 *
012 * Based on example code from SVNKit in the
013 * org.tmatesoft.svn.examples.wc
014 * package. Thanks TMate Software Ltd.
015 */
016 package gate.versioning.svnkit;
017
018 import org.tmatesoft.svn.core.SVNCancelException;
019 import org.tmatesoft.svn.core.SVNLock;
020 import org.tmatesoft.svn.core.wc.ISVNStatusHandler;
021 import org.tmatesoft.svn.core.wc.SVNStatus;
022 import org.tmatesoft.svn.core.wc.SVNStatusType;
023 import org.tmatesoft.svn.core.wc.SVNEvent;
024 import org.tmatesoft.svn.core.wc.ISVNEventHandler;
025 import org.tmatesoft.svn.core.wc.SVNEventAction;
026
027 import org.apache.log4j.Logger;
028
029 /*
030 * This is an implementation of ISVNStatusHandler & ISVNEventHandler that is
031 * used in Sandbox.java to display status information. This implementation is
032 * passed to
033 *
034 * SVNStatusClient.doStatus(File path, boolean recursive, boolean remote,
035 * boolean reportAll, boolean includeIgnored, boolean collectParentExternals,
036 * ISVNStatusHandler handler)
037 *
038 * For each item to be processed doStatus(..) collects status information and
039 * creates an SVNStatus object which holds that information. Then doStatus(..)
040 * calls an implementor's handler.handleStatus(SVNStatus) passing it the status
041 * info collected.
042 *
043 * StatusHandler will be also provided to an SVNStatusClient object as a handler
044 * of events generated by a doStatus(..) method. For example, if the status is
045 * invoked with the flag remote=true (like 'svn status -u' command), so then the
046 * status operation will be finished with dispatching an SVNEvent to
047 * ISVNEventHandler that will 'say' that the status is performed against the
048 * youngest revision (the event holds that revision number).
049 */
050 public class StatusHandler implements ISVNStatusHandler, ISVNEventHandler {
051 /** Logger. */
052 static Logger lgr = Logger.getLogger(StatusHandler.class);
053
054 private boolean myIsRemote;
055
056 public StatusHandler(boolean isRemote) {
057 myIsRemote = isRemote;
058 }
059
060 /*
061 * This is an implementation of ISVNStatusHandler.handleStatus(SVNStatus
062 * status)
063 */
064 public void handleStatus(SVNStatus status) {
065 lgr.info(buildStatusReport(status, myIsRemote));
066 }
067
068 /** Build up a status report. */
069 static public String buildStatusReport(SVNStatus status, boolean myIsRemote) {
070 /*
071 * Gets the status of file/directory/symbolic link text contents. It is
072 * SVNStatusType who contains information on the state of an item.
073 */
074 SVNStatusType contentsStatus = status.getContentsStatus();
075 String pathChangeType = " ";
076 boolean isAddedWithHistory = status.isCopied();
077 if(contentsStatus == SVNStatusType.STATUS_MODIFIED) {
078 /*
079 * The contents of the file have been Modified.
080 */
081 pathChangeType = "M";
082 } else if(contentsStatus == SVNStatusType.STATUS_CONFLICTED) {
083 /*
084 * The file item is in a state of Conflict. That is, changes received from
085 * the server during an update overlap with local changes the user has in
086 * his working copy.
087 */
088 pathChangeType = "C";
089 } else if(contentsStatus == SVNStatusType.STATUS_DELETED) {
090 /*
091 * The file, directory or symbolic link item has been scheduled for
092 * Deletion from the repository.
093 */
094 pathChangeType = "D";
095 } else if(contentsStatus == SVNStatusType.STATUS_ADDED) {
096 /*
097 * The file, directory or symbolic link item has been scheduled for
098 * Addition to the repository.
099 */
100 pathChangeType = "A";
101 } else if(contentsStatus == SVNStatusType.STATUS_UNVERSIONED) {
102 /*
103 * The file, directory or symbolic link item is not under version control.
104 */
105 pathChangeType = "?";
106 } else if(contentsStatus == SVNStatusType.STATUS_EXTERNAL) {
107 /*
108 * The item is unversioned, but is used by an eXternals definition.
109 */
110 pathChangeType = "X";
111 } else if(contentsStatus == SVNStatusType.STATUS_IGNORED) {
112 /*
113 * The file, directory or symbolic link item is not under version control,
114 * and is configured to be Ignored during 'add', 'import' and 'status'
115 * operations.
116 */
117 pathChangeType = "I";
118 } else if(contentsStatus == SVNStatusType.STATUS_MISSING
119 || contentsStatus == SVNStatusType.STATUS_INCOMPLETE) {
120 /*
121 * The file, directory or symbolic link item is under version control but
122 * is missing or somehow incomplete. The item can be missing if it is
123 * removed using a command incompatible with the native Subversion command
124 * line client (for example, just removed from the filesystem). In the
125 * case the item is a directory, it can be incomplete if the user happened
126 * to interrupt a checkout or update.
127 */
128 pathChangeType = "!";
129 } else if(contentsStatus == SVNStatusType.STATUS_OBSTRUCTED) {
130 /*
131 * The file, directory or symbolic link item is in the repository as one
132 * kind of object, but what's actually in the user's working copy is some
133 * other kind. For example, Subversion might have a file in the
134 * repository, but the user removed the file and created a directory in
135 * its place, without using the 'svn delete' or 'svn add' command (or
136 * SVNKit analogues for them).
137 */
138 pathChangeType = "~";
139 } else if(contentsStatus == SVNStatusType.STATUS_REPLACED) {
140 /*
141 * The file, directory or symbolic link item was Replaced in the user's
142 * working copy; that is, the item was deleted, and a new item with the
143 * same name was added (within a single revision). While they may have the
144 * same name, the repository considers them to be distinct objects with
145 * distinct histories.
146 */
147 pathChangeType = "R";
148 } else if(contentsStatus == SVNStatusType.STATUS_NONE
149 || contentsStatus == SVNStatusType.STATUS_NORMAL) {
150 /*
151 * The item was not modified (normal).
152 */
153 pathChangeType = " ";
154 }
155 /*
156 * If SVNStatusClient.doStatus(..) was invoked with remote = true the
157 * following code finds out whether the current item had been changed in the
158 * repository
159 */
160 String remoteChangeType = " ";
161 if(status.getRemotePropertiesStatus() != SVNStatusType.STATUS_NONE
162 || status.getRemoteContentsStatus() != SVNStatusType.STATUS_NONE) {
163 /*
164 * the local item is out of date
165 */
166 remoteChangeType = "*";
167 }
168 /*
169 * Now getting the status of properties of an item. SVNStatusType also
170 * contains information on the properties state.
171 */
172 SVNStatusType propertiesStatus = status.getPropertiesStatus();
173 /*
174 * Default - properties are normal (unmodified).
175 */
176 String propertiesChangeType = " ";
177 if(propertiesStatus == SVNStatusType.STATUS_MODIFIED) {
178 /*
179 * Properties were modified.
180 */
181 propertiesChangeType = "M";
182 } else if(propertiesStatus == SVNStatusType.STATUS_CONFLICTED) {
183 /*
184 * Properties are in conflict with the repository.
185 */
186 propertiesChangeType = "C";
187 }
188 /*
189 * Whether the item was locked in the .svn working area (for example, during
190 * a commit or maybe the previous operation was interrupted, in this case
191 * the lock needs to be cleaned up).
192 */
193 boolean isLocked = status.isLocked();
194 /*
195 * Whether the item is switched to a different URL (branch).
196 */
197 boolean isSwitched = status.isSwitched();
198 /*
199 * If the item is a file it may be locked.
200 */
201 SVNLock localLock = status.getLocalLock();
202 /*
203 * If doStatus() was run with remote=true and the item is a file, checks
204 * whether a remote lock presents.
205 */
206 SVNLock remoteLock = status.getRemoteLock();
207 String lockLabel = " ";
208 if(localLock != null) {
209 /*
210 * at first suppose the file is locKed
211 */
212 lockLabel = "K";
213 if(remoteLock != null) {
214 /*
215 * if the lock-token of the local lock differs from the lock- token of
216 * the remote lock - the lock was sTolen!
217 */
218 if(!remoteLock.getID().equals(localLock.getID())) {
219 lockLabel = "T";
220 }
221 } else {
222 if(myIsRemote) {
223 /*
224 * the local lock presents but there's no lock in the repository - the
225 * lock was Broken. This is true only if doStatus() was invoked with
226 * remote=true.
227 */
228 lockLabel = "B";
229 }
230 }
231 } else if(remoteLock != null) {
232 /*
233 * the file is not locally locked but locked in the repository - the lock
234 * token is in some Other working copy.
235 */
236 lockLabel = "O";
237 }
238 /*
239 * Obtains the working revision number of the item.
240 */
241 long workingRevision = status.getRevision().getNumber();
242 /*
243 * Obtains the number of the revision when the item was last changed.
244 */
245 long lastChangedRevision = status.getCommittedRevision().getNumber();
246 String offset = " ";
247 String[] offsets = new String[3];
248 offsets[0] = offset.substring(0, 6 - String.valueOf(workingRevision)
249 .length());
250 offsets[1] = offset.substring(0, 6 - String.valueOf(lastChangedRevision)
251 .length());
252 // status
253 offsets[2] = offset.substring(0, offset.length()
254 - (status.getAuthor() != null ? status.getAuthor().length() : 1));
255 /*
256 * status is shown in the manner of the native Subversion command line
257 * client's command "svn status"
258 */
259 return pathChangeType
260 + propertiesChangeType
261 + (isLocked ? "L" : " ")
262 + (isAddedWithHistory ? "+" : " ")
263 + (isSwitched ? "S" : " ")
264 + lockLabel
265 + " "
266 + remoteChangeType
267 + " "
268 + workingRevision
269 + offsets[0]
270 + (lastChangedRevision >= 0
271 ? String.valueOf(lastChangedRevision)
272 : "?") + offsets[1]
273 + (status.getAuthor() != null ? status.getAuthor() : "?")
274 + offsets[2] + status.getFile().getPath();
275 } // buildStatusReport
276
277 /*
278 * This is an implementation for ISVNEventHandler.handleEvent(SVNEvent event,
279 * double progress)
280 */
281 public void handleEvent(SVNEvent event, double progress) {
282 /*
283 * Gets the current action. An action is represented by SVNEventAction. In
284 * case of a status operation a current action can be determined via
285 * SVNEvent.getAction() and SVNEventAction.STATUS_-like constants.
286 */
287 SVNEventAction action = event.getAction();
288 /*
289 * Print out the revision against which the status was performed. This event
290 * is dispatched when the SVNStatusClient.doStatus() was invoked with the
291 * flag remote set to true - that is for a local status it won't be
292 * dispatched.
293 */
294 if(action == SVNEventAction.STATUS_COMPLETED) {
295 lgr.info("Status against revision: " + event.getRevision());
296 }
297 }
298
299 /*
300 * Should be implemented to check if the current operation is cancelled. If it
301 * is, this method should throw an SVNCancelException.
302 */
303 public void checkCancelled() throws SVNCancelException {
304 }
305 }
|