001package org.apache.maven.scm.provider.bazaar.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.maven.scm.provider.bazaar.command.BazaarConsumer;
026
027import java.io.File;
028import java.util.ArrayList;
029import java.util.HashMap;
030import java.util.List;
031import java.util.Map;
032
033/**
034 * @author <a href="mailto:torbjorn@smorgrav.org">Torbj�rn Eikli Sm�rgrav</a>
035 *
036 */
037public class BazaarDiffConsumer
038    extends BazaarConsumer
039{
040
041    private static final String MODIFIED_FILE_TOKEN = "=== modified file ";
042
043    private static final String ADDED_FILE_TOKEN = "=== added file ";
044
045    private static final String DELETED_FILE_TOKEN = "=== deleted file ";
046
047    private static final String NO_NEWLINE_TOKEN = "\\ No newline at end of file";
048
049    private static final String FROM_FILE_TOKEN = "---";
050
051    private static final String TO_FILE_TOKEN = "+++";
052
053    private static final String ADDED_LINE_TOKEN = "+";
054
055    private static final String REMOVED_LINE_TOKEN = "-";
056
057    private static final String UNCHANGED_LINE_TOKEN = " ";
058
059    private static final String RANGE_TOKEN = "@@";
060
061    private ScmLogger logger;
062
063    private File workingDirectory;
064
065    private String currentFile;
066
067    private StringBuilder currentDifference;
068
069    private List<ScmFile> changedFiles = new ArrayList<ScmFile>();
070
071    private Map<String,CharSequence> differences = new HashMap<String,CharSequence>();
072
073    private StringBuilder patch = new StringBuilder();
074
075    public BazaarDiffConsumer( ScmLogger logger, File workingDirectory )
076    {
077        super( logger );
078        this.logger = logger;
079        this.workingDirectory = workingDirectory;
080    }
081
082    /** {@inheritDoc} */
083    public void doConsume( ScmFileStatus status, String line )
084    {
085        String tmpLine = new String( line );
086        patch.append( line ).append( "\n" );
087
088        // Parse line
089        if ( line.startsWith( MODIFIED_FILE_TOKEN ) )
090        {
091            tmpLine = line.substring( MODIFIED_FILE_TOKEN.length() );
092            tmpLine = tmpLine.trim();
093            status = ScmFileStatus.MODIFIED;
094            addChangedFile( status, line, tmpLine );
095        }
096        else if ( line.startsWith( ADDED_FILE_TOKEN ) )
097        {
098            tmpLine = line.substring( ADDED_FILE_TOKEN.length() );
099            tmpLine = tmpLine.trim();
100            status = ScmFileStatus.ADDED;
101            addChangedFile( status, line, tmpLine );
102        }
103        else if ( line.startsWith( DELETED_FILE_TOKEN ) )
104        {
105            tmpLine = line.substring( DELETED_FILE_TOKEN.length() );
106            tmpLine = tmpLine.trim();
107            status = ScmFileStatus.DELETED;
108            addChangedFile( status, line, tmpLine );
109        }
110        else if ( line.startsWith( TO_FILE_TOKEN ) || line.startsWith( FROM_FILE_TOKEN ) )
111        {
112            // ignore (to avoid conflicts with add and remove tokens)
113        }
114        else if ( line.startsWith( ADDED_LINE_TOKEN ) || line.startsWith( REMOVED_LINE_TOKEN )
115            || line.startsWith( UNCHANGED_LINE_TOKEN ) || line.startsWith( RANGE_TOKEN )
116            || line.startsWith( NO_NEWLINE_TOKEN ) )
117        {
118            currentDifference.append( line ).append( "\n" );
119        }
120    }
121
122    /**
123     * This method takes into account two types of diff output. <br>
124     * - Bazaar 0.7 format: dir/dir/myfile  <br>
125     * - Bazaar 0.8 format: a/dir/dir/myfile <br>
126     *
127     * @param status  Eg. modified or added
128     * @param line    The original bazaar output to process (for logging)
129     * @param tmpLine The bazaar output to process
130     */
131    private void addChangedFile( ScmFileStatus status, String line, String tmpLine )
132    {
133        tmpLine = tmpLine.substring( 1, tmpLine.length() - 1 );
134        boolean ok = addChangedFile( status, tmpLine );
135
136        if ( !ok )
137        {
138            int index = tmpLine.indexOf( '/' );
139            if ( index > -1 )
140            {
141                tmpLine = tmpLine.substring( index + 1 );
142                ok = addChangedFile( status, tmpLine );
143            }
144        }
145
146        if ( !ok )
147        {
148            if ( logger.isWarnEnabled() )
149            {
150                logger.warn( "Could not figure out of line: " + line );
151            }
152        }
153    }
154
155    /**
156     * @param status
157     * @param tmpLine
158     * @return True if tmpLine was a valid file and thus added to the changeset
159     */
160    private boolean addChangedFile( ScmFileStatus status, String tmpLine )
161    {
162        File tmpFile = new File( workingDirectory, tmpLine );
163        if ( status.equals( ScmFileStatus.DELETED ) )
164        {
165            return true;
166        }
167
168        if ( tmpFile.isFile() )
169        {
170            currentFile = tmpLine;
171            currentDifference = new StringBuilder();
172            differences.put( currentFile, currentDifference );
173            changedFiles.add( new ScmFile( tmpLine, status ) );
174            return true;
175        }
176
177        return false;
178    }
179
180    public List<ScmFile> getChangedFiles()
181    {
182        return changedFiles;
183    }
184
185    public Map<String,CharSequence> getDifferences()
186    {
187        return differences;
188    }
189
190    public String getPatch()
191    {
192        return patch.toString();
193    }
194}