001package 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 022import org.apache.maven.scm.ChangeFile; 023import org.apache.maven.scm.ChangeSet; 024import org.apache.maven.scm.log.ScmLogger; 025import org.apache.maven.scm.provider.vss.repository.VssScmProviderRepository; 026import org.apache.maven.scm.util.AbstractConsumer; 027 028import java.text.SimpleDateFormat; 029import java.util.ArrayList; 030import java.util.Collections; 031import java.util.List; 032import java.util.Map; 033import java.util.TreeMap; 034import java.util.Vector; 035 036/** 037 * @author <a href="mailto:evenisse@apache.org">Emmanuel Venisse</a> 038 * @author Olivier Lamy 039 * 040 */ 041public 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}