StatusHandler.java
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- String.valueOf(workingRevision)
249             .length());
250     offsets[1= offset.substring(0- 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 }