View Javadoc

1   package org.apache.maven.svnlib;
2   
3   /* ====================================================================
4    *   Licensed to the Apache Software Foundation (ASF) under one or more
5    *   contributor license agreements.  See the NOTICE file distributed with
6    *   this work for additional information regarding copyright ownership.
7    *   The ASF licenses this file to You under the Apache License, Version 2.0
8    *   (the "License"); you may not use this file except in compliance with
9    *   the License.  You may obtain a copy of 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,
15   *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   *   See the License for the specific language governing permissions and
17   *   limitations under 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.ArrayList;
30  import java.util.Collection;
31  import java.util.Date;
32  
33  // commons imports
34  import org.apache.commons.logging.Log;
35  import org.apache.commons.logging.LogFactory;
36  
37  // maven
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  // regexp
44  import org.apache.regexp.RE;
45  import org.apache.regexp.RESyntaxException;
46  
47  
48  /**
49   * A class to parse the log output from Subversion.
50   *
51   * @author <a href="mailto:dion@multitask.com.au">dIon Gillard</a>
52   * @author <a href="mailto:pete-apache-dev@kazmier.com">Pete Kazmier</a>
53   * @author Daniel Rall
54   * @version $Id: SvnChangeLogParser.java 532339 2007-04-25 12:28:56Z ltheussl $
55   */
56  public class SvnChangeLogParser implements ChangeLogParser
57  {
58      /** Date formatter for svn timestamp (after a little massaging) */
59      private static final SimpleDateFormat SVN_TIMESTAMP =
60          new SimpleDateFormat( "yyyy-MM-dd HH:mm:ss zzzzzzzzz" );
61  
62      /** Log */
63      private static final Log LOG =
64          LogFactory.getLog( SvnChangeLogParser.class );
65  
66      /** State machine constant: expecting header */
67      private static final int GET_HEADER = 1;
68  
69      /** State machine constant: expecting file information */
70      private static final int GET_FILE = 2;
71  
72      /** State machine constant: expecting comments */
73      private static final int GET_COMMENT = 3;
74  
75      /** A file line begins with a space character */
76      private static final String FILE_BEGIN_TOKEN = " ";
77  
78      /** The file section ends with a blank line */
79      private static final String FILE_END_TOKEN = "";
80  
81      /** The filename starts after 5 characters */
82      private static final int FILE_START_INDEX = 5;
83  
84      /** The comment section ends with a dashed line */
85      private static final String COMMENT_END_TOKEN =
86          "------------------------------------"
87          + "------------------------------------";
88  
89      /** The pattern used to match svn header lines */
90      private static final String pattern =
91          "^r(\\d+)\\s+\\|\\s+" // revision number
92          + "([^|]+)\\|\\s+" // author username
93          + "(\\d+-\\d+-\\d+ " // date 2002-08-24
94          + "\\d+:\\d+:\\d+) " // time 16:01:00
95          + "([\\-+])(\\d\\d)(\\d\\d)"; // gmt offset -0400 
96  
97      /** Current status of the parser */
98      private int status = GET_HEADER;
99  
100     /** List of change log entries */
101     private Collection entries = new ArrayList();
102 
103     /** The current log entry being processed by the parser */
104     private ChangeLogEntry currentLogEntry;
105 
106     /** The current revision of the entry being processed by the parser */
107     private String currentRevision;
108 
109     /** The current comment of the entry being processed by the parser */
110     private StringBuffer currentComment;
111 
112     /** The regular expression used to match header lines */
113     private RE headerRegexp;
114 
115     /** The invoking changelog controller (useful to log messages) */
116     private ChangeLog changeLog;
117 
118     /**
119      * Default constructor.
120      */
121     public SvnChangeLogParser()
122     {
123         try
124         {
125             headerRegexp = new RE( pattern );
126         }
127         catch ( RESyntaxException ignored )
128         {
129             LOG.error( "Could not create regexp to parse svn log file", ignored );
130         }
131     }
132 
133     /**
134      * Initialize the parser from the change log.
135      *
136      * @param changeLog The controlling task
137      * @see ChangeLogParser#init(ChangeLog)
138      */
139     public void init( ChangeLog changeLog )
140     {
141         this.changeLog = changeLog;
142     }
143 
144     /**
145      * Clean up any parser resources.
146      *
147      * @see ChangeLogParser#cleanup()
148      */
149     public void cleanup()
150     {
151     }
152 
153     /**
154      * Parse the input stream into a collection.
155      *
156      * @param anInputStream An input stream containing svn log output
157      * @return A collection of ChangeLogEntry's
158      * @throws IOException When there are errors reading the provided stream
159      */
160     public Collection parse( InputStream anInputStream )
161         throws IOException
162     {
163         BufferedReader stream =
164             new BufferedReader( new InputStreamReader( anInputStream ) );
165 
166         // Current state transitions in the parser's state machine:
167         //      Get Header     -> Get File
168         //      Get File       -> Get Comment or Get (another) File
169         //      Get Comment    -> Get (another) Comment
170         String line = null;
171 
172         while ( ( line = stream.readLine() ) != null )
173         {
174             switch ( status )
175             {
176             case GET_HEADER :
177                 processGetHeader( line );
178 
179                 break;
180 
181             case GET_FILE :
182                 processGetFile( line );
183 
184                 break;
185 
186             case GET_COMMENT :
187                 processGetComment( line );
188 
189                 break;
190 
191             default :
192                 throw new IllegalStateException( "Unknown state: " + status );
193             }
194         }
195 
196         return entries;
197     }
198 
199     /**
200      * Process the current input line in the GET_HEADER state.  The
201      * author, date, and the revision of the entry are gathered.  Note,
202      * Subversion does not have per-file revisions, instead, the entire
203      * repository is given a single revision number, which is used for
204      * the revision number of each file.
205      *
206      * @param line A line of text from the svn log output
207      */
208     private void processGetHeader( String line )
209     {
210         if ( !headerRegexp.match( line ) )
211         {
212             return;
213         }
214 
215         currentRevision = headerRegexp.getParen( 1 );
216         currentLogEntry = new ChangeLogEntry();
217 
218         /* set author to be trimmed author field */
219         currentLogEntry.setAuthor( headerRegexp.getParen( 2 ).trim() );
220         currentLogEntry.setDate( parseDate() );
221 
222         status = GET_FILE;
223     }
224 
225     /**
226      * Process the current input line in the GET_FILE state.  This state
227      * adds each file entry line to the current change log entry.  Note,
228      * the revision number for the entire entry is used for the revision
229      * number of each file.
230      *
231      * @param line A line of text from the svn log output
232      */
233     private void processGetFile( String line )
234     {
235         if ( line.startsWith( FILE_BEGIN_TOKEN ) )
236         {
237             // Skip the status flags and just get the name of the file
238             String name = line.substring( FILE_START_INDEX );
239 
240             currentLogEntry.addFile( new ChangeLogFile( name, currentRevision ) );
241 
242             status = GET_FILE;
243         }
244         else if ( line.equals( FILE_END_TOKEN ) )
245         {
246             // Create a buffer for the collection of the comment now
247             // that we are leaving the GET_FILE state.
248             currentComment = new StringBuffer();
249 
250             status = GET_COMMENT;
251         }
252     }
253 
254     /**
255      * Process the current input line in the GET_COMMENT state.  This
256      * state gathers all of the comments that are part of a log entry.
257      *
258      * @param line a line of text from the svn log output
259      */
260     private void processGetComment( String line )
261     {
262         if ( line.equals( COMMENT_END_TOKEN ) )
263         {
264             currentLogEntry.setComment( currentComment.toString() );
265             entries.add( currentLogEntry );
266 
267             status = GET_HEADER;
268         }
269         else
270         {
271             currentComment.append( line ).append( '\n' );
272         }
273     }
274 
275     /**
276      * Converts the date timestamp from the svn output into a date
277      * object.
278      *
279      * @return A date representing the timestamp of the log entry.
280      */
281     private Date parseDate()
282     {
283         try
284         {
285             StringBuffer date =
286                 new StringBuffer().append( headerRegexp.getParen( 3 ) )
287                                     .append( " GMT" )
288                                     .append( headerRegexp.getParen( 4 ) )
289                                     .append( headerRegexp.getParen( 5 ) )
290                                     .append( ':' ).append( headerRegexp
291                     .getParen( 6 ) );
292 
293             return SVN_TIMESTAMP.parse( date.toString() );
294         }
295         catch ( ParseException e )
296         {
297             LOG.error( "ParseException Caught", e );
298 
299             return null;
300         }
301     }
302 
303     public void setDateFormatInFile( String dateFormat )
304     {
305     }
306 }