001    package org.apache.maven.scm.provider.jazz.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 java.io.File;
023    import java.util.HashMap;
024    import java.util.List;
025    import java.util.Map;
026    
027    import org.apache.maven.scm.ScmException;
028    import org.apache.maven.scm.ScmFile;
029    import org.apache.maven.scm.ScmFileSet;
030    import org.apache.maven.scm.ScmFileStatus;
031    import org.apache.maven.scm.ScmVersion;
032    import org.apache.maven.scm.command.diff.AbstractDiffCommand;
033    import org.apache.maven.scm.command.diff.DiffScmResult;
034    import org.apache.maven.scm.command.status.StatusScmResult;
035    import org.apache.maven.scm.provider.ScmProviderRepository;
036    import org.apache.maven.scm.provider.jazz.command.JazzConstants;
037    import org.apache.maven.scm.provider.jazz.command.JazzScmCommand;
038    import org.apache.maven.scm.provider.jazz.command.consumer.DebugLoggerConsumer;
039    import org.apache.maven.scm.provider.jazz.command.consumer.ErrorConsumer;
040    import org.apache.maven.scm.provider.jazz.command.status.JazzStatusCommand;
041    
042    // The Maven SCM plugin "diff" goal may have different interpretations in RTC depending on how
043    // the user is using RTC. In one instance, the user may expect the diff to report back on the differences between
044    // the local 'sandbox' and their connected repository workspace (ie. What files are 'unresolved'). 
045    // Other users may want the diff the report back the differences between their connected repository workspace
046    // and the stream that it flows with (ie. What files are 'outgoing' / 'incoming').
047    // As a first step, we would have to figure out how to distinguish between these two use cases when using this goal.
048    
049    // Whilst, the above is true, based upon the SVN implementation, its diff does a difference
050    // between the local working copy (sandbox) vs's repository (workspace repository).
051    //
052    // So this implementation will compare the sandbox with the workspace repository (even if there is
053    // a valid flow target). As the "scm diff" command does not support this direct comparison (I have
054    // had an Enhancement Work Item opened to do so), we will call the "scm status" command to get all
055    // of the change files, and then iterate through all of them to get a diff of all of them. The combined
056    // output of all of the various diffs will then be returned as a single output operation.
057    // -Chris 24/02/12
058    
059    // The following RTC commands may be useful however it retrieving the required information. 
060    //
061    // 1. RTC "compare" command:  Compare two workspaces/streams/baselines/snapshots, showing differing baselines and change sets. 
062    // See the following links for additional information on the RTC "compare" command:
063    // RTC 2.0.0.2:
064    // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
065    // RTC 3.0:
066    // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
067    // RTC 3.0.1:
068    // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_compare.html
069    //
070    // 2. RTC "diff" command:  Compare two states of a file. 
071    // See the following links for additional information on the RTC "diff" command:
072    // RTC 2.0.0.2:
073    // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
074    // RTC 3.0:
075    // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
076    // RTC 3.0.1:
077    // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_diff.html
078    //
079    // 3. RTC "status" command:  Show modification status of items in a workspace. 
080    // See the following links for additional information on the RTC "status" command:
081    // RTC 2.0.0.2:
082    // http://publib.boulder.ibm.com/infocenter/rtc/v2r0m0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
083    // RTC 3.0:
084    // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
085    // RTC 3.0.1:
086    // http://publib.boulder.ibm.com/infocenter/clmhelp/v3r0m1/topic/com.ibm.team.scm.doc/topics/r_scm_cli_status.html
087    //
088    
089    /**
090     * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a>
091     */
092    public class JazzDiffCommand
093        extends AbstractDiffCommand
094    {
095        /**
096         * {@inheritDoc}
097         */
098        protected DiffScmResult executeDiffCommand( ScmProviderRepository repo, ScmFileSet fileSet,
099                                                    ScmVersion startRevision, ScmVersion endRevision )
100            throws ScmException
101        {
102            if ( getLogger().isDebugEnabled() )
103            {
104                getLogger().debug( "Executing diff command..." );
105            }
106    
107            File baseDir = fileSet.getBasedir();
108            File parentFolder = ( baseDir.getParentFile() != null ) ? baseDir.getParentFile() : baseDir;
109    
110            // First execute the status command to get the list of changed files.
111            JazzStatusCommand statusCmd = new JazzStatusCommand();
112            statusCmd.setLogger( getLogger() );
113            StatusScmResult statusCmdResult = statusCmd.executeStatusCommand( repo, fileSet );
114            List<ScmFile> statusScmFiles = statusCmdResult.getChangedFiles();
115    
116            // In this case, we also use it across multiple calls to "scm diff" so that we
117            // sum all output into on.
118            JazzScmCommand diffCmd = null;
119            StringBuilder patch = new StringBuilder();
120            Map<String, CharSequence> differences = new HashMap<String, CharSequence>();
121    
122            // Now lets iterate through them
123            for ( ScmFile file : statusScmFiles )
124            {
125                if ( file.getStatus() == ScmFileStatus.MODIFIED )
126                {
127                    // The "scm status" command returns files relative to the sandbox root.
128                    // Whereas the "scm diff" command needs them relative to the working directory.
129                    File fullPath = new File( parentFolder, file.getPath() );
130                    String relativePath = fullPath.toString().substring( baseDir.toString().length() );
131                    getLogger().debug( "Full Path     : '" + fullPath + "'" );
132                    getLogger().debug( "Relative Path : '" + relativePath + "'" );
133    
134                    // Now call "scm diff on it"
135                    // In this case, we use the DebugLoggerConsumer's ability to store captured output
136                    DebugLoggerConsumer diffConsumer = new DebugLoggerConsumer( getLogger() );
137                    ErrorConsumer errConsumer = new ErrorConsumer( getLogger() );
138                    diffCmd = createDiffCommand( repo, fileSet, relativePath );
139                    int status = diffCmd.execute( diffConsumer, errConsumer );
140                    if ( status != 0 || errConsumer.hasBeenFed() )
141                    {
142                        // Return a false result (not the usual SCMResult)
143                        return new DiffScmResult( diffCmd.toString(), "The scm diff command failed.",
144                                                  errConsumer.getOutput(), false );
145                    }
146                    // Append to patch (all combined)
147                    patch.append( diffConsumer.getOutput() );
148                    // Set the differences map <File, <CharSequence>
149                    differences.put( relativePath, diffConsumer.getOutput() );
150                }
151            }
152    
153            return new DiffScmResult( diffCmd.toString(), statusCmdResult.getChangedFiles(), differences,
154                                      patch.toString() );
155        }
156    
157        public JazzScmCommand createDiffCommand( ScmProviderRepository repo, ScmFileSet fileSet, String relativePath )
158        {
159            JazzScmCommand command = new JazzScmCommand( JazzConstants.CMD_DIFF, repo, fileSet, getLogger() );
160            command.addArgument( JazzConstants.ARG_FILE );
161            command.addArgument( relativePath );
162            return command;
163        }
164    }