001package org.apache.maven.scm.provider.git.command.diff;
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.ScmFile;
023import org.apache.maven.scm.ScmFileStatus;
024import org.apache.maven.scm.log.ScmLogger;
025import org.apache.regexp.RE;
026import org.apache.regexp.RESyntaxException;
027import org.codehaus.plexus.util.cli.StreamConsumer;
028
029import java.io.File;
030import java.util.ArrayList;
031import java.util.HashMap;
032import java.util.List;
033import java.util.Map;
034
035/**
036 * @author <a href="mailto:brett@apache.org">Brett Porter</a>
037 * @author <a href="mailto:struberg@yahoo.de">Mark Struberg</a>
038 * @author Olivier Lamy
039 *
040 */
041public class GitDiffConsumer
042    implements StreamConsumer
043{
044    // diff --git a/readme.txt b/readme.txt
045    // index fea1611..9e131cf 100644
046    // --- a/readme.txt
047    // +++ b/readme.txt
048    // @@ -1 +1 @@
049    // -/readme.txt
050    // \ No newline at end of file
051    // +new version of /readme.txt
052
053
054    /**
055     * patern matches the index line of the diff comparison
056     * paren.1 matches the first file
057     * paren.2 matches the 2nd file
058     */
059    private static final String DIFF_FILES_PATTERN = "^diff --git\\sa/(.*)\\sb/(.*)";
060
061    private static final String START_REVISION_TOKEN = "---";
062
063    private static final String END_REVISION_TOKEN = "+++";
064
065    private static final String ADDED_LINE_TOKEN = "+";
066
067    private static final String REMOVED_LINE_TOKEN = "-";
068
069    private static final String UNCHANGED_LINE_TOKEN = " ";
070
071    private static final String CHANGE_SEPARATOR_TOKEN = "@@";
072
073    private static final String NO_NEWLINE_TOKEN = "\\ No newline at end of file";
074
075    private static final String INDEX_LINE_TOKEN = "index ";
076
077    private static final String NEW_FILE_MODE_TOKEN = "new file mode ";
078
079    private static final String DELETED_FILE_MODE_TOKEN = "deleted file mode ";
080
081    private ScmLogger logger;
082
083    private String currentFile;
084
085    private StringBuilder currentDifference;
086
087    private List<ScmFile> changedFiles = new ArrayList<ScmFile>();
088
089    private Map<String,CharSequence> differences = new HashMap<String,CharSequence>();
090
091    private StringBuilder patch = new StringBuilder();
092
093    /**
094     * @see #DIFF_FILES_PATTERN
095     */
096    private RE filesRegexp;
097
098    // ----------------------------------------------------------------------
099    //
100    // ----------------------------------------------------------------------
101
102    public GitDiffConsumer( ScmLogger logger, File workingDirectory )
103    {
104        this.logger = logger;
105        try
106        {
107            filesRegexp = new RE( DIFF_FILES_PATTERN );
108        }
109        catch ( RESyntaxException ex )
110        {
111            throw new RuntimeException(
112                                        "INTERNAL ERROR: Could not create regexp to parse git log file. Something is probably wrong with the oro installation.",
113                                        ex );
114        }
115
116    }
117
118    // ----------------------------------------------------------------------
119    // StreamConsumer Implementation
120    // ----------------------------------------------------------------------
121
122    /** {@inheritDoc} */
123    public void consumeLine( String line )
124    {
125        if ( filesRegexp.match( line ) )
126        {
127            // start a new file
128            currentFile = filesRegexp.getParen( 1 );
129
130            changedFiles.add( new ScmFile( currentFile, ScmFileStatus.MODIFIED ) );
131
132            currentDifference = new StringBuilder();
133
134            differences.put( currentFile, currentDifference );
135
136            patch.append( line ).append( "\n" );
137
138            return;
139        }
140
141        if ( currentFile == null )
142        {
143            if ( logger.isWarnEnabled() )
144            {
145                logger.warn( "Unparseable line: '" + line + "'" );
146            }
147            patch.append( line ).append( "\n" );
148            return;
149        }
150        else if ( line.startsWith( INDEX_LINE_TOKEN ) )
151        {
152            // skip, though could parse to verify start revision and end revision
153            patch.append( line ).append( "\n" );
154        }
155        else if ( line.startsWith( NEW_FILE_MODE_TOKEN ) || line.startsWith( DELETED_FILE_MODE_TOKEN ) )
156        {
157            // skip, though could parse to verify file mode
158            patch.append( line ).append( "\n" );
159        }
160        else if ( line.startsWith( START_REVISION_TOKEN ) )
161        {
162            // skip, though could parse to verify filename, start revision
163            patch.append( line ).append( "\n" );
164        }
165        else if ( line.startsWith( END_REVISION_TOKEN ) )
166        {
167            // skip, though could parse to verify filename, end revision
168            patch.append( line ).append( "\n" );
169        }
170        else if ( line.startsWith( ADDED_LINE_TOKEN ) || line.startsWith( REMOVED_LINE_TOKEN )
171            || line.startsWith( UNCHANGED_LINE_TOKEN ) || line.startsWith( CHANGE_SEPARATOR_TOKEN )
172            || line.equals( NO_NEWLINE_TOKEN ) )
173        {
174            // add to buffer
175            currentDifference.append( line ).append( "\n" );
176            patch.append( line ).append( "\n" );
177        }
178        else
179        {
180            // TODO: handle property differences
181
182            if ( logger.isWarnEnabled() )
183            {
184                logger.warn( "Unparseable line: '" + line + "'" );
185            }
186            patch.append( line ).append( "\n" );
187            // skip to next file
188            currentFile = null;
189            currentDifference = null;
190        }
191    }
192
193    public List<ScmFile> getChangedFiles()
194    {
195        return changedFiles;
196    }
197
198    public Map<String,CharSequence> getDifferences()
199    {
200        return differences;
201    }
202
203    public String getPatch()
204    {
205        return patch.toString();
206    }
207
208}