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     * @version $Id: VssChangeLogConsumer.java 1134851 2011-06-12 01:24:05Z godin $
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    }