1 package org.apache.maven.vsslib;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21 import java.io.BufferedReader;
22 import java.io.IOException;
23 import java.io.InputStream;
24 import java.io.InputStreamReader;
25
26 import java.text.ParseException;
27 import java.text.SimpleDateFormat;
28
29 import java.util.Collection;
30 import java.util.Collections;
31 import java.util.Date;
32 import java.util.Map;
33 import java.util.TreeMap;
34 import java.util.Vector;
35
36 import org.apache.commons.logging.Log;
37 import org.apache.commons.logging.LogFactory;
38 import org.apache.maven.changelog.ChangeLog;
39 import org.apache.maven.changelog.ChangeLogEntry;
40 import org.apache.maven.changelog.ChangeLogFile;
41
42
43 /**
44 * Visual Source Safe specific implementation of ChangeLogParser interface.
45 *
46 * @author Freddy Mallet
47 */
48 public class VssChangeLogParser
49 implements org.apache.maven.changelog.ChangeLogParser
50 {
51 /** * Log */
52 private static final Log LOG =
53 LogFactory.getLog( VssChangeLogParser.class );
54
55 /**
56 * Custom date/time formatter. Rounds ChangeLogEntry times to the nearest
57 * minute.
58 */
59 private static final SimpleDateFormat ENTRY_KEY_TIMESTAMP_FORMAT =
60 new SimpleDateFormat( "yyyyMMddHHmm" );
61
62
63
64 /** * expecting file information */
65 private static final int GET_FILE = 1;
66
67 /** * expecting file path information */
68 private static final int GET_FILE_PATH = 2;
69
70 /** * expecting date */
71 private static final int GET_AUTHOR = 3;
72
73 /** * expecting comments */
74 private static final int GET_COMMENT = 4;
75
76 /** * expecting revision */
77 private static final int GET_REVISION = 5;
78
79 /** * unknown vss history line status */
80 private static final int GET_UNKNOWN = 6;
81
82 /** * Marks start of file data */
83 private static final String START_FILE = "***** ";
84
85 /** * Marks start of file data */
86 private static String START_FILE_PATH = "$/";
87
88 /** * Marks start of revision */
89 private static final String START_REVISION = "Version";
90
91 /** * Marks author data */
92 private static final String START_AUTHOR = "User: ";
93
94 /** * Marks comment data */
95 private static final String START_COMMENT = "Comment: ";
96
97 /**
98 * rcs entries, in reverse (date, time, author, comment) order
99 */
100 private Map entries = new TreeMap( Collections.reverseOrder() );
101
102 /** * last status of the parser */
103 private int lastStatus = GET_FILE;
104
105 /** * the current log entry being processed by the parser */
106 private ChangeLogEntry currentLogEntry = null;
107
108 /** * the current file being processed by the parser */
109 private ChangeLogFile currentFile = null;
110
111 /** * the bean representation of the vss connection string */
112 private VssConnection vssConnection;
113
114 /**
115 * Create a new ChangeLogParser.
116 */
117 public VssChangeLogParser()
118 {
119 }
120
121 /**
122 * initialize the parser from the change log
123 *
124 * @param changeLog
125 * the controlling task
126 * @see ChangeLogParser#init(ChangeLog)
127 */
128 public void init( ChangeLog changeLog )
129 {
130 String connection = changeLog.getRepositoryConnection();
131
132 try
133 {
134 vssConnection = new VssConnection( connection );
135 }
136 catch ( Exception e )
137 {
138 String message =
139 "Unable to parse vss connection string : " + connection;
140
141 throw new IllegalArgumentException( message );
142 }
143 }
144
145 /**
146 * Parse the input stream into a collection.
147 *
148 * @param inputStream
149 * an input stream containing clearcase log output
150 * @return a collection of ChangeLogEntry's
151 * @throws IOException
152 * when there are errors reading the provided stream
153 */
154 public Collection parse( InputStream inputStream )
155 throws IOException
156 {
157 BufferedReader stream =
158 new BufferedReader( new InputStreamReader( inputStream ) );
159
160 String line;
161
162 while ( ( line = stream.readLine() ) != null )
163 {
164 switch ( getLineStatus( line ) )
165 {
166 case GET_FILE :
167 processGetFile( line );
168
169 break;
170
171 case GET_REVISION :
172 processGetRevision( line );
173
174 break;
175
176 case GET_AUTHOR :
177 processGetAuthor( line );
178
179 break;
180
181 case GET_FILE_PATH :
182 processGetFilePath( line );
183
184 break;
185
186 case GET_COMMENT :
187 processGetComment( line );
188
189 break;
190
191 default :
192 break;
193 }
194 }
195
196 return entries.values();
197 }
198
199 /**
200 * Process the current input line in the Get Comment state.
201 *
202 * @param line
203 * a line of text from the VSS log output
204 */
205 private void processGetComment( String line )
206 {
207 String[] commentLine = line.split( ":" );
208
209 if ( commentLine.length == 2 )
210 {
211 getCurrentLogEntry().setComment( commentLine[1] );
212 }
213
214
215 else
216 {
217 String comment = getCurrentLogEntry().getComment();
218
219 comment = comment + " " + line;
220 getCurrentLogEntry().setComment( comment );
221 }
222 }
223
224 /**
225 * Process the current input line in the Get Author state.
226 *
227 * @param line
228 * a line of text from the VSS log output
229 */
230 private void processGetAuthor( String line )
231 {
232 String[] result = line.split( "\\s" );
233 Vector vector = new Vector();
234
235 for ( int i = 0; i < result.length; i++ )
236 {
237 if ( !result[i].equals( "" ) )
238 {
239 vector.add( result[i] );
240 }
241 }
242
243 ChangeLogEntry entry = getCurrentLogEntry();
244
245 entry.setAuthor( (String) vector.get( 1 ) );
246 entry.setDate( parseDate( (String) vector.get( 3 ) + " "
247 + (String) vector.get( 5 ) ) );
248 }
249
250 /**
251 * Process the current input line in the Get File state.
252 *
253 * @param line
254 * a line of text from the VSS log output
255 */
256 private void processGetFile( String line )
257 {
258 setCurrentLogEntry( new ChangeLogEntry() );
259
260 String[] fileLine = line.split( " " );
261
262 setCurrentFile( new ChangeLogFile( fileLine[2] ) );
263 }
264
265 /**
266 * Process the current input line in the Get File Path state.
267 *
268 * @param line
269 * a line of text from the VSS log output
270 */
271 private void processGetFilePath( String line )
272 {
273 if ( getCurrentFile() != null )
274 {
275 String fileName = getCurrentFile().getName();
276
277 String path =
278 line.substring( line.indexOf( "$" ), line.length() );
279 String longPath =
280 path.substring( vssConnection.getVssProject().length() + 1,
281 path.length() ) + "/" + fileName;
282
283 getCurrentFile().setName( longPath );
284 addEntry( getCurrentLogEntry(), getCurrentFile() );
285 }
286 }
287
288 /**
289 * Process the current input line in the Get Revision state.
290 *
291 * @param line
292 * a line of text from the VSS log output
293 */
294 private void processGetRevision( String line )
295 {
296 String[] revisionLine = line.split( " " );
297
298 getCurrentFile().setRevision( revisionLine[1] );
299 }
300
301 /**
302 * Identify the status of a vss history line
303 *
304 * @param the
305 * line to process
306 * @return status
307 */
308 private int getLineStatus( String line )
309 {
310 int argument = GET_UNKNOWN;
311
312 if ( line.startsWith( START_FILE ) )
313 {
314 argument = GET_FILE;
315 }
316 else if ( line.startsWith( START_REVISION ) )
317 {
318 argument = GET_REVISION;
319 }
320 else if ( line.startsWith( START_AUTHOR ) )
321 {
322 argument = GET_AUTHOR;
323 }
324 else if ( line.indexOf( START_FILE_PATH ) != -1 )
325 {
326 argument = GET_FILE_PATH;
327 }
328 else if ( line.startsWith( START_COMMENT ) )
329 {
330 argument = GET_COMMENT;
331 }
332 else if ( getLastStatus() == GET_COMMENT )
333 {
334
335 argument = getLastStatus();
336 }
337
338 setLastStatus( argument );
339
340 return argument;
341 }
342
343 /**
344 * Add a change log entry to the list (if it's not already there) with the
345 * given file.
346 *
347 * @param entry
348 * a {@link ChangeLogEntry}to be added to the list if another
349 * with the same key doesn't exist already. If the entry's author
350 * is null, the entry wont be added
351 * @param file
352 * a {@link ChangeLogFile}to be added to the entry
353 */
354 private void addEntry( ChangeLogEntry entry, ChangeLogFile file )
355 {
356
357 if ( entry.getAuthor() == null )
358 {
359 return;
360 }
361
362 String key =
363 ENTRY_KEY_TIMESTAMP_FORMAT.format( entry.getDate() )
364 + entry.getAuthor() + entry.getComment();
365
366 if ( !entries.containsKey( key ) )
367 {
368 entry.addFile( file );
369 entries.put( key, entry );
370 }
371 else
372 {
373 ChangeLogEntry existingEntry = (ChangeLogEntry) entries.get( key );
374
375 existingEntry.addFile( file );
376 }
377 }
378
379 /**
380 * Converts the date timestamp from the svn output into a date object.
381 *
382 * @return A date representing the timestamp of the log entry.
383 */
384 private Date parseDate( String dateString )
385 {
386 try
387 {
388 SimpleDateFormat format = new SimpleDateFormat( "dd.MM.yy HH:mm" );
389 Date date = format.parse( dateString );
390
391 return date;
392 }
393 catch ( ParseException e )
394 {
395 LOG.error( "ParseException Caught", e );
396
397 return null;
398 }
399 }
400
401 /**
402 * Getter for property currentFile.
403 *
404 * @return Value of property currentFile.
405 */
406 private ChangeLogFile getCurrentFile()
407 {
408 return currentFile;
409 }
410
411 /**
412 * Setter for property currentFile.
413 *
414 * @param currentFile
415 * New value of property currentFile.
416 */
417 private void setCurrentFile( ChangeLogFile currentFile )
418 {
419 this.currentFile = currentFile;
420 }
421
422 /**
423 * Getter for property currentLogEntry.
424 *
425 * @return Value of property currentLogEntry.
426 */
427 private ChangeLogEntry getCurrentLogEntry()
428 {
429 return currentLogEntry;
430 }
431
432 /**
433 * Setter for property currentLogEntry.
434 *
435 * @param currentLogEntry
436 * New value of property currentLogEntry.
437 */
438 private void setCurrentLogEntry( ChangeLogEntry currentLogEntry )
439 {
440 this.currentLogEntry = currentLogEntry;
441 }
442
443 /**
444 * Getter for property status.
445 *
446 * @return Value of property status.
447 */
448 private int getLastStatus()
449 {
450 return lastStatus;
451 }
452
453 /**
454 * Setter for property status.
455 *
456 * @param status
457 * New value of property status.
458 */
459 private void setLastStatus( int status )
460 {
461 this.lastStatus = status;
462 }
463
464 /**
465 * Clean up any parser resources
466 *
467 * @see ChangeLogParser#cleanup()
468 */
469 public void cleanup()
470 {
471 }
472
473 /**
474 * Defined in ChangeLogParser interface
475 *
476 * @see ChangeLogParser
477 */
478 public void setDateFormatInFile( String s )
479 {
480 }
481 }