1 package org.apache.maven.starteamlib;
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
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 import org.apache.maven.changelog.ChangeLogParser;
42
43
44 /**
45 * A class to parse starteam log output
46 *
47 * @author <a href="mailto:evenisse@ifrance">Emmanuel Venisse</a>
48 * @version $Id: StarteamChangeLogParser.java 532339 2007-04-25 12:28:56Z ltheussl $
49 */
50 public class StarteamChangeLogParser implements ChangeLogParser
51 {
52 /**
53 * Custom date/time formatter. Rounds ChangeLogEntry times to the nearest
54 * minute.
55 */
56 private static final SimpleDateFormat ENTRY_KEY_TIMESTAMP_FORMAT =
57 new SimpleDateFormat( "yyyyMMddHHmm" );
58
59 /** Log */
60 private static final Log LOG =
61 LogFactory.getLog( StarteamChangeLogGenerator.class );
62
63
64
65 /** expecting file information */
66 private static final int GET_FILE = 1;
67
68 /** expecting date */
69 private static final int GET_AUTHOR = 2;
70
71 /** expecting comments */
72 private static final int GET_COMMENT = 3;
73
74 /** expecting revision */
75 private static final int GET_REVISION = 4;
76
77 /** Marks start of file data*/
78 private static final String START_FILE = "History for: ";
79
80 /** Marks end of file */
81 private static final String END_FILE =
82 "==================================="
83 + "==========================================";
84
85 /** Marks start of revision */
86 private static final String START_REVISION = "----------------------------";
87
88 /** Marks revision data */
89 private static final String REVISION_TAG = "Branch Revision: ";
90
91 /** Marks author data */
92 private static final String AUTHOR_TAG = "Author: ";
93
94 /** Marks date data */
95 private static final String DATE_TAG = " Date: ";
96 private SimpleDateFormat localFormat = new SimpleDateFormat();
97
98 /**
99 * rcs entries, in reverse (date, time, author, comment) order
100 */
101 private Map entries = new TreeMap( Collections.reverseOrder() );
102
103 /** current status of the parser */
104 private int status = GET_FILE;
105
106 /** the current log entry being processed by the parser*/
107 private ChangeLogEntry currentLogEntry = null;
108
109 /** the current file being processed by the parser */
110 private ChangeLogFile currentFile = null;
111
112 /** the controlling task */
113 private ChangeLog changeLog = null;
114
115 /** the before date */
116 private Date beforeDate;
117
118 /** the to date */
119 private Date toDate;
120
121 /** the test mode (In test mode, we dont't use the date range*/
122 private boolean testMode = false;
123
124 /**
125 * Create a new ChangeLogParser.
126 */
127 public StarteamChangeLogParser()
128 {
129 }
130
131 /**
132 * initialize the parser from the change log
133 * @param changeLog the controlling task
134 * @see ChangeLogParser#init(ChangeLog)
135 */
136 public void init( ChangeLog changeLog )
137 {
138 this.changeLog = changeLog;
139
140 if ( ( changeLog.getRange() != null )
141 && ( changeLog.getRange().length() != 0 ) )
142 {
143 setDateRange( changeLog.getRange() );
144 }
145
146 if ( changeLog.getDateFormat() != null )
147 {
148 localFormat = new SimpleDateFormat( changeLog.getDateFormat() );
149 }
150 }
151
152 /**
153 * Clean up any parser resources
154 * @see ChangeLogParser#cleanup()
155 */
156 public void cleanup()
157 {
158 }
159
160 /**
161 * Use only with StarteamChangeLogParserTest class
162 */
163 public void setTestMode( boolean testMode )
164 {
165 this.testMode = testMode;
166 }
167
168 /**
169 * Set the date formatter for parse starteam stream
170 * @param aSimpleDateFormat a simpleDateFormat for replace the local format
171 */
172 public void setDateFormatInFile( String dateFormat )
173 {
174 localFormat = new SimpleDateFormat( dateFormat );
175 }
176
177 /**
178 * Parse the input stream into a collection.
179 * @param anInputStream an input stream containing Starteam log output
180 * @return a collection of ChangeLogEntry's
181 * @throws IOException when there are errors reading the provided stream
182 */
183 public Collection parse( InputStream anInputStream )
184 throws IOException
185 {
186 BufferedReader stream =
187 new BufferedReader( new InputStreamReader( anInputStream ) );
188
189
190
191
192
193
194 String line = null;
195
196 while ( ( line = stream.readLine() ) != null )
197 {
198 switch ( getStatus() )
199 {
200 case GET_FILE :
201 processGetFile( line );
202
203 break;
204
205 case GET_REVISION :
206 processGetRevision( line );
207
208 break;
209
210 case GET_AUTHOR :
211 processGetAuthor( line );
212
213 break;
214
215 case GET_COMMENT :
216 processGetComment( line );
217
218 break;
219
220 default :
221 throw new IllegalStateException( "Unknown state: " + status );
222 }
223 }
224
225 return entries.values();
226 }
227
228 /**
229 * Add a change log entry to the list (if it's not already there)
230 * with the given file.
231 * @param entry a {@link ChangeLogEntry} to be added to the list if another
232 * with the same key doesn't exist already. If the entry's author
233 * is null, the entry wont be added
234 * @param file a {@link ChangeLogFile} to be added to the entry
235 */
236 private void addEntry( ChangeLogEntry entry, ChangeLogFile file )
237 {
238
239 if ( entry.getAuthor() == null )
240 {
241 return;
242 }
243
244
245 if ( !testMode )
246 {
247 if ( ( beforeDate != null ) && ( toDate != null ) )
248 {
249 if ( entry.getDate().before( beforeDate )
250 || entry.getDate().after( toDate ) )
251 {
252 return;
253 }
254 }
255 }
256
257 String key =
258 ENTRY_KEY_TIMESTAMP_FORMAT.format( entry.getDate() )
259 + entry.getAuthor() + entry.getComment();
260
261 if ( !entries.containsKey( key ) )
262 {
263 entry.addFile( file );
264 entries.put( key, entry );
265 }
266 else
267 {
268 ChangeLogEntry existingEntry = (ChangeLogEntry) entries.get( key );
269
270 existingEntry.addFile( file );
271 }
272 }
273
274 /**
275 * Process the current input line in the Get File state.
276 * @param line a line of text from the Starteam log output
277 */
278 private void processGetFile( String line )
279 {
280 if ( line.startsWith( START_FILE ) )
281 {
282 setCurrentLogEntry( new ChangeLogEntry() );
283 setCurrentFile( new ChangeLogFile( line.substring(
284 START_FILE.length(), line.length() ) ) );
285 setStatus( GET_REVISION );
286 }
287 }
288
289 /**
290 * Process the current input line in the Get Revision state.
291 * @param line a line of text from the Starteam log output
292 */
293 private void processGetRevision( String line )
294 {
295 int pos;
296
297 if ( ( pos = line.indexOf( REVISION_TAG ) ) != -1 )
298 {
299 getCurrentFile().setRevision( line.substring( pos
300 + REVISION_TAG.length() ) );
301 setStatus( GET_AUTHOR );
302 }
303 else if ( line.startsWith( END_FILE ) )
304 {
305
306
307
308 setStatus( GET_FILE );
309 addEntry( getCurrentLogEntry(), getCurrentFile() );
310 }
311 }
312
313 /**
314 * Process the current input line in the Get Author/Date state.
315 * @param line a line of text from the Starteam log output
316 */
317 private void processGetAuthor( String line )
318 {
319 if ( line.startsWith( AUTHOR_TAG ) )
320 {
321 int posDateTag = line.indexOf( DATE_TAG );
322 String author = line.substring( AUTHOR_TAG.length(), posDateTag );
323
324 getCurrentLogEntry().setAuthor( author );
325
326 String date = line.substring( posDateTag + DATE_TAG.length() );
327
328 getCurrentLogEntry().setDate( parseDate( date ) );
329 setStatus( GET_COMMENT );
330 }
331 }
332
333 /**
334 * Process the current input line in the Get Comment state.
335 * @param line a line of text from the Starteam log output
336 */
337 private void processGetComment( String line )
338 {
339 if ( line.startsWith( START_REVISION ) )
340 {
341
342 addEntry( getCurrentLogEntry(), getCurrentFile() );
343
344
345 setCurrentLogEntry( new ChangeLogEntry() );
346
347
348 setCurrentFile( new ChangeLogFile( getCurrentFile().getName() ) );
349 setStatus( GET_REVISION );
350 }
351 else if ( line.startsWith( END_FILE ) )
352 {
353 addEntry( getCurrentLogEntry(), getCurrentFile() );
354 setStatus( GET_FILE );
355 }
356 else
357 {
358
359 getCurrentLogEntry().setComment( getCurrentLogEntry()
360 .getComment() + line
361 + "\n" );
362 }
363 }
364
365 /**
366 * Getter for property currentFile.
367 * @return Value of property currentFile.
368 */
369 private ChangeLogFile getCurrentFile()
370 {
371 return currentFile;
372 }
373
374 /**
375 * Setter for property currentFile.
376 * @param currentFile New value of property currentFile.
377 */
378 private void setCurrentFile( ChangeLogFile currentFile )
379 {
380 this.currentFile = currentFile;
381 }
382
383 /**
384 * Getter for property currentLogEntry.
385 * @return Value of property currentLogEntry.
386 */
387 private ChangeLogEntry getCurrentLogEntry()
388 {
389 return currentLogEntry;
390 }
391
392 /**
393 * Setter for property currentLogEntry.
394 * @param currentLogEntry New value of property currentLogEntry.
395 */
396 private void setCurrentLogEntry( ChangeLogEntry currentLogEntry )
397 {
398 this.currentLogEntry = currentLogEntry;
399 }
400
401 /**
402 * Getter for property status.
403 * @return Value of property status.
404 */
405 private int getStatus()
406 {
407 return status;
408 }
409
410 /**
411 * Setter for property status.
412 * @param status New value of property status.
413 */
414 private void setStatus( int status )
415 {
416 this.status = status;
417 }
418
419 /**
420 * Converts the date timestamp from the svn output into a date
421 * object.
422 *
423 * @return A date representing the timestamp of the log entry.
424 */
425 private Date parseDate( String date )
426 {
427 try
428 {
429 return localFormat.parse( date.toString() );
430 }
431 catch ( ParseException e )
432 {
433 LOG.error( "ParseException Caught", e );
434
435 return null;
436 }
437 }
438
439 /**
440 * Set the beforeDate and toDate member based on the number of days
441 * obtained from the ChangeLog.
442 *
443 * @param numDaysString The number of days of log output to
444 * generate.
445 */
446 private void setDateRange( String numDaysString )
447 {
448 int days = Integer.parseInt( numDaysString );
449
450 beforeDate =
451 new Date( System.currentTimeMillis()
452 - ( (long) days * 24 * 60 * 60 * 1000 ) );
453 toDate =
454 new Date( System.currentTimeMillis()
455 + ( (long) 1 * 24 * 60 * 60 * 1000 ) );
456 }
457 }