001    package 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    
022    import org.apache.maven.scm.ScmFile;
023    import org.apache.maven.scm.ScmFileStatus;
024    import org.apache.maven.scm.log.ScmLogger;
025    import org.apache.maven.scm.provider.bazaar.command.BazaarConsumer;
026    
027    import java.io.File;
028    import java.util.ArrayList;
029    import java.util.HashMap;
030    import java.util.List;
031    import java.util.Map;
032    
033    /**
034     * @author <a href="mailto:torbjorn@smorgrav.org">Torbj�rn Eikli Sm�rgrav</a>
035     * @version $Id: BazaarDiffConsumer.java 1306867 2012-03-29 13:45:10Z olamy $
036     */
037    public 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    }