View Javadoc

1   package org.apache.maven.vsslib;
2   
3   /*
4    * ====================================================================
5    * Copyright 2001-2004 The Apache Software Foundation.
6    *
7    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
8    * use this file except in compliance with the License. You may obtain a copy of
9    * the License at
10   *
11   * http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
15   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
16   * License for the specific language governing permissions and limitations under
17   * the License.
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      // state machine constants for reading Starteam output
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         //Comment suite on a new line
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             //Comment suite on a new line
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         // do not add if entry is not populated
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 }