View Javadoc
1   package org.apache.maven.scm.provider.starteam.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.provider.starteam.command.StarteamCommandLineUtils;
26  import org.apache.maven.scm.util.AbstractConsumer;
27  
28  import java.io.File;
29  import java.text.SimpleDateFormat;
30  import java.util.ArrayList;
31  import java.util.Date;
32  import java.util.List;
33  import java.util.Locale;
34  
35  /**
36   * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
37   * @author Olivier Lamy
38   *
39   */
40  public class StarteamChangeLogConsumer
41      extends AbstractConsumer
42  {
43      private SimpleDateFormat localFormat = new SimpleDateFormat( "", Locale.getDefault() );
44  
45      private List<ChangeSet> entries = new ArrayList<ChangeSet>();
46  
47      private String workingDirectory;
48  
49      private String currentDir = "";
50  
51      // state machine constants for reading Starteam output
52  
53      /**
54       * expecting file information
55       */
56      private static final int GET_FILE = 1;
57  
58      /**
59       * expecting date
60       */
61      private static final int GET_AUTHOR = 2;
62  
63      /**
64       * expecting comments
65       */
66      private static final int GET_COMMENT = 3;
67  
68      /**
69       * expecting revision
70       */
71      private static final int GET_REVISION = 4;
72  
73  
74      /**
75       * Marks current directory data
76       */
77      private static final String DIR_MARKER = "(working dir: ";
78  
79      /**
80       * Marks start of file data
81       */
82      private static final String START_FILE = "History for: ";
83  
84  
85      /**
86       * Marks end of file
87       */
88      private static final String END_FILE =
89          "===================================" + "==========================================";
90  
91      /**
92       * Marks start of revision
93       */
94      private static final String START_REVISION = "----------------------------";
95  
96      /**
97       * Marks revision data
98       */
99      private static final String REVISION_TAG = "Branch Revision: ";
100 
101     /**
102      * Marks author data
103      */
104     private static final String AUTHOR_TAG = "Author: ";
105 
106     /**
107      * Marks date data
108      */
109     private static final String DATE_TAG = " Date: ";
110 
111     /**
112      * current status of the parser
113      */
114     private int status = GET_FILE;
115 
116     /**
117      * the current log entry being processed by the parser
118      */
119     private ChangeSet currentChange = null;
120 
121     /**
122      * the current file being processed by the parser
123      */
124     private ChangeFile currentFile = null;
125 
126     /**
127      * the before date
128      */
129     private Date startDate;
130 
131     /**
132      * the to date
133      */
134     private Date endDate;
135 
136     private String userDateFormat;
137 
138     // ----------------------------------------------------------------------
139     //
140     // ----------------------------------------------------------------------
141 
142     public StarteamChangeLogConsumer( File workingDirectory, ScmLogger logger, Date startDate, Date endDate,
143                                       String userDateFormat )
144     {
145         super( logger );
146 
147         this.workingDirectory = workingDirectory.getPath().replace( '\\', '/' );
148 
149         this.startDate = startDate;
150 
151         this.endDate = endDate;
152 
153         this.userDateFormat = userDateFormat;
154 
155         //work around for all en_US compatible locales, where Starteam
156         // stcmd hist output uses a different format, ugly eh?
157         // makesure to change the test file as well if this ever got fixed
158 
159         if ( "M/d/yy h:mm a".equals( localFormat.toLocalizedPattern() ) )
160         {
161             this.localFormat = new SimpleDateFormat( "M/d/yy h:mm:ss a z" );
162         }
163     }
164 
165     // ----------------------------------------------------------------------
166     //
167     // ----------------------------------------------------------------------
168 
169     public List<ChangeSet> getModifications()
170     {
171         return entries;
172     }
173 
174     // ----------------------------------------------------------------------
175     // StreamConsumer Implementation
176     // ----------------------------------------------------------------------
177 
178     /** {@inheritDoc} */
179     public void consumeLine( String line )
180     {
181         if ( getLogger().isDebugEnabled() )
182         {
183             getLogger().debug( line );
184         }
185 
186         int pos = 0;
187 
188         if ( ( pos = line.indexOf( DIR_MARKER ) ) != -1 )
189         {
190             processDirectory( line, pos );
191             return;
192         }
193 
194         // current state transitions in the state machine - starts with Get File
195         //      Get File                -> Get Revision
196         //      Get Revision            -> Get Date or Get File
197         //      Get Date                -> Get Comment
198         //      Get Comment             -> Get Comment or Get Revision
199         switch ( getStatus() )
200         {
201             case GET_FILE:
202                 processGetFile( line );
203                 break;
204             case GET_REVISION:
205                 processGetRevision( line );
206                 break;
207             case GET_AUTHOR:
208                 processGetAuthor( line );
209                 break;
210             case GET_COMMENT:
211                 processGetComment( line );
212                 break;
213             default:
214                 throw new IllegalStateException( "Unknown state: " + status );
215         }
216     }
217 
218     // ----------------------------------------------------------------------
219     //
220     // ----------------------------------------------------------------------
221 
222     /**
223      * Add a change log entry to the list (if it's not already there)
224      * with the given file.
225      *
226      * @param entry a {@link ChangeSet} to be added to the list if another
227      *              with the same key doesn't exist already. If the entry's author
228      *              is null, the entry wont be added
229      * @param file  a {@link ChangeFile} to be added to the entry
230      */
231     private void addEntry( ChangeSet entry, ChangeFile file )
232     {
233         // do not add if entry is not populated
234         if ( entry.getAuthor() == null )
235         {
236             return;
237         }
238 
239         // do not add if entry is out of date range
240         if ( startDate != null && entry.getDate().before( startDate ) )
241         {
242             return;
243         }
244 
245         if ( endDate != null && entry.getDate().after( endDate ) )
246         {
247             return;
248         }
249 
250         entry.addFile( file );
251 
252         entries.add( entry );
253     }
254 
255     private void processDirectory( String line, int pos )
256     {
257         String dirPath = line.substring( pos + DIR_MARKER.length(), line.length() - 1 ).replace( '\\', '/' );
258         try
259         {
260             this.currentDir = StarteamCommandLineUtils.getRelativeChildDirectory( this.workingDirectory, dirPath );
261         }
262         catch ( IllegalStateException e )
263         {
264             String error = "Working and checkout directories are not on the same tree";
265 
266             if ( getLogger().isErrorEnabled() )
267             {
268                 getLogger().error( error );
269 
270                 getLogger().error( "Working directory: " + workingDirectory );
271 
272                 getLogger().error( "Checked out directory: " + dirPath );
273             }
274 
275             throw new IllegalStateException( error );
276         }
277     }
278 
279     /**
280      * Process the current input line in the Get File state.
281      *
282      * @param line a line of text from the Starteam log output
283      */
284     private void processGetFile( String line )
285     {
286         if ( line.startsWith( START_FILE ) )
287         {
288             setCurrentChange( new ChangeSet() );
289 
290             setCurrentFile(
291                 new ChangeFile( this.currentDir + "/" + line.substring( START_FILE.length(), line.length() ) ) );
292 
293             setStatus( GET_REVISION );
294         }
295     }
296 
297     /**
298      * Process the current input line in the Get Revision state.
299      *
300      * @param line a line of text from the Starteam log output
301      */
302     private void processGetRevision( String line )
303     {
304         int pos;
305 
306         if ( ( pos = line.indexOf( REVISION_TAG ) ) != -1 )
307         {
308             getCurrentFile().setRevision( line.substring( pos + REVISION_TAG.length() ) );
309 
310             setStatus( GET_AUTHOR );
311         }
312         else if ( line.startsWith( END_FILE ) )
313         {
314             // If we encounter an end of file line, it means there
315             // are no more revisions for the current file.
316             // there could also be a file still being processed.
317             setStatus( GET_FILE );
318 
319             addEntry( getCurrentChange(), getCurrentFile() );
320         }
321     }
322 
323     /**
324      * Process the current input line in the Get Author/Date state.
325      *
326      * @param line a line of text from the Starteam log output
327      */
328     private void processGetAuthor( String line )
329     {
330         if ( line.startsWith( AUTHOR_TAG ) )
331         {
332             int posDateTag = line.indexOf( DATE_TAG );
333 
334             String author = line.substring( AUTHOR_TAG.length(), posDateTag );
335 
336             getCurrentChange().setAuthor( author );
337 
338             String date = line.substring( posDateTag + DATE_TAG.length() );
339 
340             Date dateObj = parseDate( date, userDateFormat, localFormat.toPattern() );
341 
342             if ( dateObj != null )
343             {
344                 getCurrentChange().setDate( dateObj );
345             }
346             else
347             {
348                 getCurrentChange().setDate( date, userDateFormat );
349             }
350 
351             setStatus( GET_COMMENT );
352         }
353     }
354 
355     /**
356      * Process the current input line in the Get Comment state.
357      *
358      * @param line a line of text from the Starteam log output
359      */
360     private void processGetComment( String line )
361     {
362         if ( line.startsWith( START_REVISION ) )
363         {
364             // add entry, and set state to get revision
365             addEntry( getCurrentChange(), getCurrentFile() );
366 
367             // new change log entry
368             setCurrentChange( new ChangeSet() );
369 
370             // same file name, but different rev
371             setCurrentFile( new ChangeFile( getCurrentFile().getName() ) );
372 
373             setStatus( GET_REVISION );
374         }
375         else if ( line.startsWith( END_FILE ) )
376         {
377             addEntry( getCurrentChange(), getCurrentFile() );
378 
379             setStatus( GET_FILE );
380         }
381         else
382         {
383             // keep gathering comments
384             getCurrentChange().setComment( getCurrentChange().getComment() + line + "\n" );
385         }
386     }
387 
388     /**
389      * Getter for property currentFile.
390      *
391      * @return Value of property currentFile.
392      */
393     private ChangeFile getCurrentFile()
394     {
395         return currentFile;
396     }
397 
398     /**
399      * Setter for property currentFile.
400      *
401      * @param currentFile New value of property currentFile.
402      */
403     private void setCurrentFile( ChangeFile currentFile )
404     {
405         this.currentFile = currentFile;
406     }
407 
408     /**
409      * Getter for property currentChange.
410      *
411      * @return Value of property currentChange.
412      */
413     private ChangeSet getCurrentChange()
414     {
415         return currentChange;
416     }
417 
418     /**
419      * Setter for property currentChange.
420      *
421      * @param currentChange New value of property currentChange.
422      */
423     private void setCurrentChange( ChangeSet currentChange )
424     {
425         this.currentChange = currentChange;
426     }
427 
428     /**
429      * Getter for property status.
430      *
431      * @return Value of property status.
432      */
433     private int getStatus()
434     {
435         return status;
436     }
437 
438     /**
439      * Setter for property status.
440      *
441      * @param status New value of property status.
442      */
443     private void setStatus( int status )
444     {
445         this.status = status;
446     }
447 }