KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > eclipse > team > internal > ccvs > core > client > listeners > LogListener


1 /*******************************************************************************
2  * Copyright (c) 2000, 2007 IBM Corporation and others.
3  * All rights reserved. This program and the accompanying materials
4  * are made available under the terms of the Eclipse Public License v1.0
5  * which accompanies this distribution, and is available at
6  * http://www.eclipse.org/legal/epl-v10.html
7  *
8  * Contributors:
9  * IBM Corporation - initial API and implementation
10  * Brock Janiczak <brockj@tpg.com.au> - Bug 179977 CVS log command doesn't scale well with lots of tags and versions
11  *******************************************************************************/

12 package org.eclipse.team.internal.ccvs.core.client.listeners;
13
14 import java.text.*;
15 import java.util.*;
16
17 import org.eclipse.core.runtime.IProgressMonitor;
18 import org.eclipse.core.runtime.IStatus;
19 import org.eclipse.osgi.util.NLS;
20 import org.eclipse.team.internal.ccvs.core.*;
21 import org.eclipse.team.internal.ccvs.core.client.CommandOutputListener;
22 import org.eclipse.team.internal.ccvs.core.resources.RemoteFile;
23 import org.eclipse.team.internal.ccvs.core.util.Util;
24
25 /**
26  * Log listener that parses the log entries returned from the
27  * server but delegates the handling of the entries to a subclass.
28  */

29 public class LogListener extends CommandOutputListener {
30     
31     /*
32      * A new format for log dates was introduced in 1.12.9
33      */

34     private static final String JavaDoc LOG_TIMESTAMP_FORMAT_OLD= "yyyy/MM/dd HH:mm:ss zzz";//$NON-NLS-1$
35
private static final String JavaDoc LOG_TIMESTAMP_FORMAT= "yyyy-MM-dd HH:mm:ss zzz";//$NON-NLS-1$
36
private static final Locale LOG_TIMESTAMP_LOCALE= Locale.US;
37     private final DateFormat LOG_DATE_FORMATTER_OLD = new SimpleDateFormat(LOG_TIMESTAMP_FORMAT_OLD, LOG_TIMESTAMP_LOCALE);
38     private final DateFormat LOG_DATE_FORMATTER = new SimpleDateFormat(LOG_TIMESTAMP_FORMAT, LOG_TIMESTAMP_LOCALE);
39     
40     // Server message prefix used for error detection
41
private static final String JavaDoc NOTHING_KNOWN_ABOUT = "nothing known about "; //$NON-NLS-1$
42

43     // States of log accumulation.
44
private final int DONE = 4;
45     private final int COMMENT = 3;
46     private final int REVISION = 2;
47     private final int SYMBOLIC_NAMES = 1;
48     private final int BEGIN = 0;
49     
50     //Tag used for accumulating all of a branch's revision info
51
public final static String JavaDoc BRANCH_REVISION = "branchRevision"; //$NON-NLS-1$
52

53     // Instance variables for accumulating Log information
54
private RemoteFile currentFile;
55     private int state = BEGIN;
56     private StringBuffer JavaDoc comment;
57     private String JavaDoc fileState;
58     private String JavaDoc revision;
59     private String JavaDoc author;
60     private String JavaDoc creationDate;
61     private List versions = new ArrayList();
62     
63     private final ILogEntryListener listener;
64     
65     /**
66      * Create a log listener for receiving entries for one or more files.
67      */

68     public LogListener(ILogEntryListener listener) {
69         this.listener = listener;
70     }
71
72     public LogListener(RemoteFile file, ILogEntryListener listener) {
73         this(listener);
74         this.currentFile = file;
75     }
76
77     private String JavaDoc getRelativeFilePath(ICVSRepositoryLocation location, String JavaDoc fileName) {
78         if (fileName.endsWith(",v")) { //$NON-NLS-1$
79
fileName = fileName.substring(0, fileName.length() - 2);
80         }
81         fileName = Util.removeAtticSegment(fileName);
82         String JavaDoc rootDirectory = location.getRootDirectory();
83         if (fileName.startsWith(rootDirectory)) {
84             try {
85                 fileName = Util.getRelativePath(rootDirectory, fileName);
86             } catch (CVSException e) {
87                 CVSProviderPlugin.log(e);
88                 return null;
89             }
90         }
91         return fileName;
92     }
93
94     public IStatus errorLine(String JavaDoc line, ICVSRepositoryLocation location, ICVSFolder commandRoot, IProgressMonitor monitor) {
95         String JavaDoc serverMessage = getServerMessage(line, location);
96         if (serverMessage != null) {
97             // look for the following condition
98
// E cvs server: nothing known about fileName
99
if (serverMessage.startsWith(NOTHING_KNOWN_ABOUT)) {
100                 return new CVSStatus(IStatus.ERROR, CVSStatus.DOES_NOT_EXIST, line, commandRoot);
101             }
102         }
103         return OK;
104     }
105
106     public IStatus messageLine(String JavaDoc line, ICVSRepositoryLocation location, ICVSFolder commandRoot, IProgressMonitor monitor) {
107         // Fields we will find in the log for a file
108
// keys = String (tag name), values = String (tag revision number) */
109
switch (state) {
110             case BEGIN:
111                 if (line.startsWith("RCS file: ")) { //$NON-NLS-1$
112
// We are starting to recieve the log for a file
113
String JavaDoc fileName = getRelativeFilePath(location, line.substring(10).trim());
114                     if (fileName == null) {
115                         currentFile = null;
116                         handleInvalidFileName(location, fileName);
117                     } else {
118                         if (currentFile == null || !currentFile.getRepositoryRelativePath().equals(fileName)) {
119                             // We are starting another file
120
beginFile(location, fileName);
121                         }
122                     }
123                 } else if (line.startsWith("symbolic names:")) { //$NON-NLS-1$
124
state = SYMBOLIC_NAMES;
125                 } else if (line.startsWith("revision ")) { //$NON-NLS-1$
126
revision = line.substring(9);
127                     state = REVISION;
128                 } else if (line.startsWith("total revisions:")){ //$NON-NLS-1$
129
//if there are no current revision selected and this is a branch then we are in the
130
//case where there have been no changes made on the branch since the initial branching
131
//and we need to get the revision that the branch was made from
132
int indexOfSelectedRevisions = line.lastIndexOf("selected revisions: "); //$NON-NLS-1$
133
//20 for length of "selected revisions: "
134
String JavaDoc selectedRevisions = line.substring(indexOfSelectedRevisions + 20).trim();
135                     if (selectedRevisions.equals("0")){ //$NON-NLS-1$
136
//ok put into comment state to await ======= and add info to log
137
state = COMMENT;
138                         revision = BRANCH_REVISION;
139                         comment = new StringBuffer JavaDoc();
140                     }
141                 }
142                 break;
143             case SYMBOLIC_NAMES:
144                 if (line.startsWith("keyword substitution:")) { //$NON-NLS-1$
145
state = BEGIN;
146                 } else {
147                     int firstColon = line.indexOf(':');
148                     String JavaDoc tagName = line.substring(1, firstColon);
149                     String JavaDoc tagRevision = line.substring(firstColon + 2);
150                     versions.add(new VersionInfo(tagRevision, tagName));
151                 }
152                 break;
153             case REVISION:
154                 // date: 2000/06/19 04:56:21; author: somebody; state: Exp; lines: +114 -45
155
// get the creation date
156
int endOfDateIndex = line.indexOf(';', 6);
157                 creationDate = line.substring(6, endOfDateIndex) + " GMT"; //$NON-NLS-1$
158

159                 // get the author name
160
int endOfAuthorIndex = line.indexOf(';', endOfDateIndex + 1);
161                 author = line.substring(endOfDateIndex + 11, endOfAuthorIndex);
162     
163                 // get the file state (because this revision might be "dead")
164
fileState = line.substring(endOfAuthorIndex + 10, line.indexOf(';', endOfAuthorIndex + 1));
165                 comment = new StringBuffer JavaDoc();
166                 state = COMMENT;
167                 break;
168             case COMMENT:
169                 // skip next line (info about branches) if it exists, if not then it is a comment line.
170
if (line.startsWith("branches:")) break; //$NON-NLS-1$
171
if (line.equals("=============================================================================") //$NON-NLS-1$
172
|| line.equals("----------------------------")) { //$NON-NLS-1$
173
state = DONE;
174                     break;
175                 }
176                 //check for null if we are in the waiting to finish case (brought on by branches)
177
if (comment == null)
178                     break;
179                 
180                 if (comment.length() != 0) comment.append('\n');
181                 comment.append(line);
182                 break;
183         }
184         if (state == DONE) {
185             // we are only interested in tag names for this revision, remove all others.
186
List thisRevisionTags = new ArrayList(3);
187             //a parallel lists for revision tags (used only for branches with no commits on them)
188
List revisionVersions = new ArrayList(3);
189             for (Iterator i = versions.iterator(); i.hasNext();) {
190                 VersionInfo version = (VersionInfo) i.next();
191                 String JavaDoc tagName = version.getTagName();
192                 String JavaDoc tagRevision = version.getTagRevision();
193                 
194                 if (tagRevision.equals(revision) ||
195                     revision.equals(BRANCH_REVISION)) {
196                     int type = version.isBranch() ? CVSTag.BRANCH : CVSTag.VERSION;
197                     thisRevisionTags.add(new CVSTag(tagName, type));
198                     if (revision.equals(BRANCH_REVISION)){
199                         //also record the tag revision
200
revisionVersions.add(tagRevision);
201                     }
202                 }
203             }
204             Date date = null;
205             if (creationDate != null)
206                 date = convertFromLogTime(creationDate);
207             
208             if (currentFile != null) {
209                 LogEntry entry = new LogEntry(currentFile, revision, author, date,
210                     comment.toString(), fileState, (CVSTag[]) thisRevisionTags.toArray(new CVSTag[thisRevisionTags.size()]), (String JavaDoc[]) revisionVersions.toArray(new String JavaDoc[revisionVersions.size()]));
211                 addEntry(entry);
212             }
213             state = BEGIN;
214         }
215         return OK;
216     }
217
218     protected void beginFile(ICVSRepositoryLocation location, String JavaDoc fileName) {
219         currentFile = RemoteFile.create(fileName, location);
220         versions.clear();
221     }
222
223     protected void addEntry(LogEntry entry) {
224         listener.handleLogEntryReceived(entry);
225     }
226
227     protected void handleInvalidFileName(ICVSRepositoryLocation location, String JavaDoc badFilePath) {
228         CVSProviderPlugin.log(IStatus.WARNING, "Invalid file path '" + badFilePath + "' received from " + location.toString(), null); //$NON-NLS-1$ //$NON-NLS-2$
229
}
230
231     /**
232      * Converts a time stamp as sent from a cvs server for a "log" command into a
233      * <code>Date</code>.
234      */

235     private Date convertFromLogTime(String JavaDoc modTime) {
236         DateFormat format = LOG_DATE_FORMATTER;
237         // Compatibility for older cvs version (pre 1.12.9)
238
if (modTime.length() > 4 && modTime.charAt(4) == '/')
239             format = LOG_DATE_FORMATTER_OLD;
240         
241         try {
242             return format.parse(modTime);
243         } catch (ParseException e) {
244             // fallback is to return null
245
return null;
246         }
247     }
248     
249     private static class VersionInfo {
250         private final String JavaDoc version;
251         private final boolean isBranch;
252         private String JavaDoc tagRevision;
253         private final String JavaDoc tagName;
254         
255         public VersionInfo(String JavaDoc version, String JavaDoc tagName) {
256             this.version = version;
257             this.tagName = tagName;
258             this.isBranch = isBranchTag(version);
259             tagRevision = version;
260             if (isBranch) {
261                 int lastDot = version.lastIndexOf('.');
262                 if (lastDot == -1) {
263                     CVSProviderPlugin.log(IStatus.ERROR,
264                         NLS.bind(CVSMessages.LogListener_invalidRevisionFormat, new String JavaDoc[] { tagName, version }), null);
265                 } else {
266                     if (version.charAt(lastDot - 1) == '0' && version.charAt(lastDot - 2) == '.') {
267                         lastDot = lastDot - 2;
268                     }
269                     tagRevision = version.substring(0, lastDot);
270                 }
271             }
272         }
273         
274         public String JavaDoc getVersion() {
275             return this.version;
276         }
277         
278         public String JavaDoc getTagName() {
279             return this.tagName;
280         }
281         
282         public String JavaDoc getTagRevision() {
283             return this.tagRevision;
284         }
285         
286         public boolean isBranch() {
287             return isBranch;
288         }
289         
290         /** branch tags have odd number of segments or have
291          * an even number with a zero as the second last segment
292          * e.g: 1.1.1, 1.26.0.2 are branch revision numbers */

293         private boolean isBranchTag(String JavaDoc tagName) {
294             // First check if we have an odd number of segments (i.e. even number of dots)
295
int numberOfDots = 0;
296             int lastDot = 0;
297             for (int i = 0; i < tagName.length(); i++) {
298                 if (tagName.charAt(i) == '.') {
299                     numberOfDots++;
300                     lastDot = i;
301                 }
302             }
303             if ((numberOfDots % 2) == 0) return true;
304             if (numberOfDots == 1) return false;
305             
306             // If not, check if the second lat segment is a zero
307
if (tagName.charAt(lastDot - 1) == '0' && tagName.charAt(lastDot - 2) == '.') return true;
308             return false;
309         }
310     }
311 }
312
Popular Tags