View Javadoc

1   package org.apache.maven.scm.provider.cvslib.command.changelog;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.scm.ChangeFile;
23  import org.apache.maven.scm.ChangeSet;
24  import org.apache.maven.scm.log.ScmLogger;
25  import org.apache.maven.scm.util.AbstractConsumer;
26  
27  import java.util.ArrayList;
28  import java.util.Collections;
29  import java.util.Comparator;
30  import java.util.Iterator;
31  import java.util.List;
32  import java.util.StringTokenizer;
33  
34  /**
35   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse </a>
36   * @author <a href="mailto:trygvis@inamo.no">Trygve Laugst&oslash;l</a>
37   * @version $Id: CvsChangeLogConsumer.java 527432 2007-04-11 09:35:23Z evenisse $
38   */
39  public class CvsChangeLogConsumer
40      extends AbstractConsumer
41  {
42      private List entries = new ArrayList();
43  
44      // state machine constants for reading cvs output
45  
46      /**
47       * expecting file information
48       */
49      private static final int GET_FILE = 1;
50  
51      /**
52       * expecting date
53       */
54      private static final int GET_DATE = 2;
55  
56      /**
57       * expecting comments
58       */
59      private static final int GET_COMMENT = 3;
60  
61      /**
62       * expecting revision
63       */
64      private static final int GET_REVISION = 4;
65  
66      /**
67       * Marks start of file data
68       */
69      private static final String START_FILE = "Working file: ";
70  
71      /**
72       * Marks end of file
73       */
74      private static final String END_FILE =
75          "===================================" + "==========================================";
76  
77      /**
78       * Marks start of revision
79       */
80      private static final String START_REVISION = "----------------------------";
81  
82      /**
83       * Marks revision data
84       */
85      private static final String REVISION_TAG = "revision ";
86  
87      /**
88       * Marks date data
89       */
90      private static final String DATE_TAG = "date: ";
91  
92      /**
93       * current status of the parser
94       */
95      private int status = GET_FILE;
96  
97      /**
98       * the current log entry being processed by the parser
99       */
100     private ChangeSet currentChange = null;
101 
102     /**
103      * the current file being processed by the parser
104      */
105     private ChangeFile currentFile = null;
106 
107     private String userDatePattern;
108 
109     public CvsChangeLogConsumer( ScmLogger logger, String userDatePattern )
110     {
111         super( logger );
112 
113         this.userDatePattern = userDatePattern;
114     }
115 
116     public List getModifications()
117     {
118         Collections.sort( entries, new Comparator()
119         {
120             public int compare( Object entry1, Object entry2 )
121             {
122                 ChangeSet set1 = (ChangeSet) entry1;
123                 ChangeSet set2 = (ChangeSet) entry2;
124                 return set1.getDate().compareTo( set2.getDate() );
125             }
126         } );
127         List fixedModifications = new ArrayList();
128         ChangeSet currentEntry = null;
129         for ( Iterator entryIterator = entries.iterator(); entryIterator.hasNext(); )
130         {
131             ChangeSet entry = (ChangeSet) entryIterator.next();
132             if ( currentEntry == null )
133             {
134                 currentEntry = entry;
135             }
136             else if ( areEqual( currentEntry, entry ) )
137             {
138                 currentEntry.addFile( (ChangeFile) entry.getFiles().get( 0 ) );
139             }
140             else
141             {
142                 fixedModifications.add( currentEntry );
143                 currentEntry = entry;
144             }
145         }
146         if ( currentEntry != null )
147         {
148             fixedModifications.add( currentEntry );
149         }
150         return fixedModifications;
151     }
152 
153     private boolean areEqual( ChangeSet set1, ChangeSet set2 )
154     {
155         if ( set1.getAuthor().equals( set2.getAuthor() ) && set1.getComment().equals( set2.getComment() ) &&
156             set1.getDate().equals( set2.getDate() ) )
157         {
158             return true;
159         }
160         return false;
161     }
162 
163     public void consumeLine( String line )
164     {
165         try
166         {
167             switch ( getStatus() )
168             {
169                 case GET_FILE:
170                     processGetFile( line );
171                     break;
172                 case GET_REVISION:
173                     processGetRevision( line );
174                     break;
175                 case GET_DATE:
176                     processGetDate( line );
177                     break;
178                 case GET_COMMENT:
179                     processGetComment( line );
180                     break;
181                 default:
182                     throw new IllegalStateException( "Unknown state: " + status );
183             }
184         }
185         catch ( Throwable ex )
186         {
187             getLogger().warn( "Exception in the cvs changelog consumer.", ex );
188         }
189     }
190 
191     /**
192      * Add a change log entry to the list (if it's not already there) with the
193      * given file.
194      *
195      * @param entry a {@link ChangeSet}to be added to the list if another
196      *              with the same key doesn't exist already. If the entry's author
197      *              is null, the entry wont be added
198      * @param file  a {@link ChangeFile}to be added to the entry
199      */
200     private void addEntry( ChangeSet entry, ChangeFile file )
201     {
202         // do not add if entry is not populated
203         if ( entry.getAuthor() == null )
204         {
205             return;
206         }
207 
208         entry.addFile( file );
209 
210         entries.add( entry );
211     }
212 
213     /**
214      * Process the current input line in the Get File state.
215      *
216      * @param line a line of text from the cvs log output
217      */
218     private void processGetFile( String line )
219     {
220         if ( line.startsWith( START_FILE ) )
221         {
222             setCurrentChange( new ChangeSet() );
223             setCurrentFile( new ChangeFile( line.substring( START_FILE.length(), line.length() ) ) );
224             setStatus( GET_REVISION );
225         }
226     }
227 
228     /**
229      * Process the current input line in the Get Revision state.
230      *
231      * @param line a line of text from the cvs log output
232      */
233     private void processGetRevision( String line )
234     {
235         if ( line.startsWith( REVISION_TAG ) )
236         {
237             getCurrentFile().setRevision( line.substring( REVISION_TAG.length() ) );
238             setStatus( GET_DATE );
239         }
240         else if ( line.startsWith( END_FILE ) )
241         {
242             // If we encounter an end of file line, it means there
243             // are no more revisions for the current file.
244             // there could also be a file still being processed.
245             setStatus( GET_FILE );
246             addEntry( getCurrentChange(), getCurrentFile() );
247         }
248     }
249 
250     /**
251      * Process the current input line in the Get Date state.
252      *
253      * @param line a line of text from the cvs log output
254      */
255     private void processGetDate( String line )
256     {
257         if ( line.startsWith( DATE_TAG ) )
258         {
259             StringTokenizer tokenizer = new StringTokenizer( line, ";" );
260             // date: YYYY/mm/dd HH:mm:ss [Z]; author: name;...
261 
262             String datePart = tokenizer.nextToken().trim();
263             String dateTime = datePart.substring( "date: ".length() );
264             StringTokenizer dateTokenizer = new StringTokenizer( dateTime, " " );
265             if ( dateTokenizer.countTokens() == 2 )
266             {
267                 dateTime += " UTC";
268             }
269             getCurrentChange().setDate( dateTime, userDatePattern );
270 
271             String authorPart = tokenizer.nextToken().trim();
272             String author = authorPart.substring( "author: ".length() );
273             getCurrentChange().setAuthor( author );
274             setStatus( GET_COMMENT );
275         }
276     }
277 
278     /**
279      * Process the current input line in the Get Comment state.
280      *
281      * @param line a line of text from the cvs log output
282      */
283     private void processGetComment( String line )
284     {
285         if ( line.startsWith( START_REVISION ) )
286         {
287             // add entry, and set state to get revision
288             addEntry( getCurrentChange(), getCurrentFile() );
289             // new change log entry
290             setCurrentChange( new ChangeSet() );
291             // same file name, but different rev
292             setCurrentFile( new ChangeFile( getCurrentFile().getName() ) );
293             setStatus( GET_REVISION );
294         }
295         else if ( line.startsWith( END_FILE ) )
296         {
297             addEntry( getCurrentChange(), getCurrentFile() );
298             setStatus( GET_FILE );
299         }
300         else
301         {
302             // keep gathering comments
303             getCurrentChange().setComment( getCurrentChange().getComment() + line + "\n" );
304         }
305     }
306 
307     /**
308      * Getter for property currentFile.
309      *
310      * @return Value of property currentFile.
311      */
312     private ChangeFile getCurrentFile()
313     {
314         return currentFile;
315     }
316 
317     /**
318      * Setter for property currentFile.
319      *
320      * @param currentFile New value of property currentFile.
321      */
322     private void setCurrentFile( ChangeFile currentFile )
323     {
324         this.currentFile = currentFile;
325     }
326 
327     /**
328      * Getter for property currentChange.
329      *
330      * @return Value of property currentChange.
331      */
332     private ChangeSet getCurrentChange()
333     {
334         return currentChange;
335     }
336 
337     /**
338      * Setter for property currentChange.
339      *
340      * @param currentChange New value of property currentChange.
341      */
342     private void setCurrentChange( ChangeSet currentChange )
343     {
344         this.currentChange = currentChange;
345     }
346 
347     /**
348      * Getter for property status.
349      *
350      * @return Value of property status.
351      */
352     private int getStatus()
353     {
354         return status;
355     }
356 
357     /**
358      * Setter for property status.
359      *
360      * @param status New value of property status.
361      */
362     private void setStatus( int status )
363     {
364         this.status = status;
365     }
366 }