View Javadoc
1   package org.apache.maven.scm.provider.vss.commands.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.provider.vss.repository.VssScmProviderRepository;
26  import org.apache.maven.scm.util.AbstractConsumer;
27  
28  import java.text.SimpleDateFormat;
29  import java.util.ArrayList;
30  import java.util.Collections;
31  import java.util.List;
32  import java.util.Map;
33  import java.util.TreeMap;
34  import java.util.Vector;
35  
36  /**
37   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
38   * @author Olivier Lamy
39   *
40   */
41  public class VssChangeLogConsumer
42      extends AbstractConsumer
43  {
44      /**
45       * Custom date/time formatter. Rounds ChangeLogEntry times to the nearest
46       * minute.
47       */
48      private static final SimpleDateFormat ENTRY_KEY_TIMESTAMP_FORMAT = new SimpleDateFormat( "yyyyMMddHHmm" );
49  
50      // state machine constants for reading Starteam output
51  
52      /**
53       * expecting file information
54       */
55      private static final int GET_FILE = 1;
56  
57      /**
58       * expecting file path information
59       */
60      private static final int GET_FILE_PATH = 2;
61  
62      /**
63       * expecting date
64       */
65      private static final int GET_AUTHOR = 3;
66  
67      /**
68       * expecting comments
69       */
70      private static final int GET_COMMENT = 4;
71  
72      /**
73       * expecting revision
74       */
75      private static final int GET_REVISION = 5;
76  
77      /**
78       * unknown vss history line status
79       */
80      private static final int GET_UNKNOWN = 6;
81  
82      /**
83       * Marks start of file data
84       */
85      private static final String START_FILE = "*****  ";
86  
87      /**
88       * Marks start of file data
89       */
90      private static final String START_FILE_PATH = "$/";
91  
92      /**
93       * Marks start of revision
94       */
95      private static final String START_REVISION = "Version";
96  
97      /**
98       * Marks author data
99       */
100     private static final String START_AUTHOR = "User: ";
101 
102     /**
103      * Marks comment data
104      */
105     private static final String START_COMMENT = "Comment: ";
106 
107     /**
108      * rcs entries, in reverse (date, time, author, comment) order
109      */
110     private Map<String, ChangeSet> entries = new TreeMap<String, ChangeSet>( Collections.reverseOrder() );
111 
112     private ChangeFile currentFile;
113 
114     private ChangeSet currentChangeSet;
115 
116     /**
117      * last status of the parser
118      */
119     private int lastStatus = GET_FILE;
120 
121     private VssScmProviderRepository repo;
122 
123     private String userDatePattern;
124 
125     public VssChangeLogConsumer( VssScmProviderRepository repo, String userDatePattern, ScmLogger logger )
126     {
127         super( logger );
128         this.userDatePattern = userDatePattern;
129         this.repo = repo;
130     }
131 
132     public List<ChangeSet> getModifications()
133     {
134         return new ArrayList<ChangeSet>( entries.values() );
135     }
136 
137     /** {@inheritDoc} */
138     public void consumeLine( String line )
139     {
140         switch ( getLineStatus( line ) )
141         {
142             case GET_FILE:
143                 processGetFile( line );
144                 break;
145             case GET_REVISION:
146                 processGetRevision( line );
147                 break;
148             case GET_AUTHOR:
149                 processGetAuthor( line );
150                 break;
151             case GET_FILE_PATH:
152                 processGetFilePath( line );
153                 break;
154             case GET_COMMENT:
155                 processGetComment( line );
156                 break;
157             default:
158                 break;
159         }
160     }
161 
162     /**
163      * Process the current input line in the Get Comment state.
164      *
165      * @param line a line of text from the VSS log output
166      */
167     private void processGetComment( String line )
168     {
169         String[] commentLine = line.split( ":" );
170         if ( commentLine.length == 2 )
171         {
172             currentChangeSet.setComment( commentLine[1] );
173         }
174         //Comment suite on a new line
175         else
176         {
177             String comment = currentChangeSet.getComment();
178             comment = comment + " " + line;
179             currentChangeSet.setComment( comment );
180         }
181     }
182 
183     /**
184      * Process the current input line in the Get Author state.
185      *
186      * @param line a line of text from the VSS log output
187      */
188     private void processGetAuthor( String line )
189     {
190         String[] result = line.split( "\\s" );
191         Vector<String> vector = new Vector<String>();
192         for ( int i = 0; i < result.length; i++ )
193         {
194             if ( !result[i].equals( "" ) )
195             {
196                 vector.add( result[i] );
197             }
198         }
199         currentChangeSet.setAuthor( vector.get( 1 ) );
200         currentChangeSet.setDate(
201             parseDate( vector.get( 3 ) + " " + vector.get( 5 ), userDatePattern, "dd.MM.yy HH:mm" ) );
202     }
203 
204     /**
205      * Process the current input line in the Get File state.
206      *
207      * @param line a line of text from the VSS log output
208      */
209     private void processGetFile( String line )
210     {
211         currentChangeSet = ( new ChangeSet() );
212         String[] fileLine = line.split( " " );
213         currentFile = new ChangeFile( fileLine[2] );
214     }
215 
216     /**
217      * Process the current input line in the Get File Path state.
218      *
219      * @param line a line of text from the VSS log output
220      */
221     private void processGetFilePath( String line )
222     {
223         if ( currentFile != null )
224         {
225             String fileName = currentFile.getName();
226 
227             String path = line.substring( line.indexOf( '$' ), line.length() );
228             String longPath = path.substring( repo.getProject()
229                 .length() + 1, path.length() ) + "/" + fileName;
230             currentFile.setName( longPath );
231             addEntry( currentChangeSet, currentFile );
232         }
233     }
234 
235     /**
236      * Process the current input line in the Get Revision state.
237      *
238      * @param line a line of text from the VSS log output
239      */
240     private void processGetRevision( String line )
241     {
242         String[] revisionLine = line.split( " " );
243         currentFile.setRevision( revisionLine[1] );
244     }
245 
246     /**
247      * Identify the status of a vss history line
248      *
249      * @param line The line to process
250      * @return status
251      */
252     private int getLineStatus( String line )
253     {
254         int argument = GET_UNKNOWN;
255         if ( line.startsWith( START_FILE ) )
256         {
257             argument = GET_FILE;
258         }
259         else if ( line.startsWith( START_REVISION ) )
260         {
261             argument = GET_REVISION;
262         }
263         else if ( line.startsWith( START_AUTHOR ) )
264         {
265             argument = GET_AUTHOR;
266         }
267         else if ( line.indexOf( START_FILE_PATH ) != -1 )
268         {
269             argument = GET_FILE_PATH;
270         }
271         else if ( line.startsWith( START_COMMENT ) )
272         {
273             argument = GET_COMMENT;
274         }
275         else if ( lastStatus == GET_COMMENT )
276         {
277             //Comment suite on a new line
278             argument = lastStatus;
279         }
280         lastStatus = argument;
281 
282         return argument;
283     }
284 
285     /**
286      * Add a change log entry to the list (if it's not already there) with the
287      * given file.
288      *
289      * @param entry a {@link ChangeSet}to be added to the list if another
290      *              with the same key doesn't exist already. If the entry's author
291      *              is null, the entry wont be added
292      * @param file  a {@link ChangeFile}to be added to the entry
293      */
294     private void addEntry( ChangeSet entry, ChangeFile file )
295     {
296         // do not add if entry is not populated
297         if ( entry.getAuthor() == null )
298         {
299             return;
300         }
301 
302         String key = ENTRY_KEY_TIMESTAMP_FORMAT.format( entry.getDate() ) + entry.getAuthor() + entry.getComment();
303 
304         if ( !entries.containsKey( key ) )
305         {
306             entry.addFile( file );
307             entries.put( key, entry );
308         }
309         else
310         {
311             ChangeSet existingEntry = (ChangeSet) entries.get( key );
312             existingEntry.addFile( file );
313         }
314     }
315 }