001 package org.apache.maven.scm.provider.vss.commands.changelog;
002
003 /*
004 * Licensed to the Apache Software Foundation (ASF) under one
005 * or more contributor license agreements. See the NOTICE file
006 * distributed with this work for additional information
007 * regarding copyright ownership. The ASF licenses this file
008 * to you under the Apache License, Version 2.0 (the
009 * "License"); you may not use this file except in compliance
010 * with the License. You may obtain a copy of the License at
011 *
012 * http://www.apache.org/licenses/LICENSE-2.0
013 *
014 * Unless required by applicable law or agreed to in writing,
015 * software distributed under the License is distributed on an
016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
017 * KIND, either express or implied. See the License for the
018 * specific language governing permissions and limitations
019 * under the License.
020 */
021
022 import org.apache.maven.scm.ChangeFile;
023 import org.apache.maven.scm.ChangeSet;
024 import org.apache.maven.scm.log.ScmLogger;
025 import org.apache.maven.scm.provider.vss.repository.VssScmProviderRepository;
026 import org.apache.maven.scm.util.AbstractConsumer;
027
028 import java.text.SimpleDateFormat;
029 import java.util.ArrayList;
030 import java.util.Collections;
031 import java.util.List;
032 import java.util.Map;
033 import java.util.TreeMap;
034 import java.util.Vector;
035
036 /**
037 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a>
038 * @author Olivier Lamy
039 *
040 */
041 public class VssChangeLogConsumer
042 extends AbstractConsumer
043 {
044 /**
045 * Custom date/time formatter. Rounds ChangeLogEntry times to the nearest
046 * minute.
047 */
048 private static final SimpleDateFormat ENTRY_KEY_TIMESTAMP_FORMAT = new SimpleDateFormat( "yyyyMMddHHmm" );
049
050 // state machine constants for reading Starteam output
051
052 /**
053 * expecting file information
054 */
055 private static final int GET_FILE = 1;
056
057 /**
058 * expecting file path information
059 */
060 private static final int GET_FILE_PATH = 2;
061
062 /**
063 * expecting date
064 */
065 private static final int GET_AUTHOR = 3;
066
067 /**
068 * expecting comments
069 */
070 private static final int GET_COMMENT = 4;
071
072 /**
073 * expecting revision
074 */
075 private static final int GET_REVISION = 5;
076
077 /**
078 * unknown vss history line status
079 */
080 private static final int GET_UNKNOWN = 6;
081
082 /**
083 * Marks start of file data
084 */
085 private static final String START_FILE = "***** ";
086
087 /**
088 * Marks start of file data
089 */
090 private static final String START_FILE_PATH = "$/";
091
092 /**
093 * Marks start of revision
094 */
095 private static final String START_REVISION = "Version";
096
097 /**
098 * Marks author data
099 */
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 }