001package org.apache.maven.scm.provider.jazz.command.status;
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.ScmProviderRepository;
026import org.apache.maven.scm.provider.jazz.command.consumer.AbstractRepositoryConsumer;
027import org.apache.maven.scm.provider.jazz.repository.JazzScmProviderRepository;
028
029import java.util.ArrayList;
030import java.util.List;
031import java.util.regex.Matcher;
032import java.util.regex.Pattern;
033
034/**
035 * Consume the output of the scm command for the "status" operation.
036 * <p/>
037 * It is normally just used to build up a list of ScmFile objects that have
038 * their ScmFileStatus set.
039 * This class has been expanded so that the Workspace, Component and Baseline
040 * are also collected and set back in the JazzScmProviderRepository.
041 * The Workspace and Component names are needed for some other commands (list,
042 * for example), so we can easily get this information here.
043 *
044 * @author <a href="mailto:ChrisGWarp@gmail.com">Chris Graham</a>
045 */
046public class JazzStatusConsumer
047    extends AbstractRepositoryConsumer
048{
049// We have have a workspace with no flow targets (it points to itself)
050//
051//  Workspace: (1000) "BogusRepositoryWorkspace" <-> (1000) "BogusRepositoryWorkspace"
052//    Component: (1001) "BogusComponent"
053//      Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
054//      Unresolved:
055//        d-- /BogusTest/pom.xml.releaseBackup
056//        d-- /BogusTest/release.properties
057//
058// Or, we have have one that does have a flow target (ie a stream or another workspace).
059//
060//  Workspace: (1156) "GPDBWorkspace" <-> (1157) "GPDBStream"
061//    Component: (1158) "GPDB" <-> (1157) "GPDBStream"
062//      Baseline: (1159) 1 "Initial Baseline"
063//
064// Note the (%d) numbers are aliases and are only valid for the machine/instance that made the
065// remote calls to the server. They are not to be shared across machines (ie don't make them global, public
066// or persistent).
067//
068
069    //  Workspace: (1000) "BogusRepositoryWorkspace" <-> (1000) "BogusRepositoryWorkspace"
070    //  Workspace: (1156) "GPDBWorkspace" <-> (1157) "GPDBStream"
071    private static final Pattern WORKSPACE_PATTERN = Pattern.compile( "\\((\\d+)\\) \"(.*)\" <-> \\((\\d+)\\) \"(.*)\"" );
072
073    //  Component: (1001) "BogusComponent"
074    private static final Pattern COMPONENT_PATTERN1 = Pattern.compile( "\\((\\d+)\\) \"(.*)\"" );
075
076    //  Component: (1158) "GPDB" <-> (1157) "GPDBStream"
077    //  Component: (1002) "FireDragon" <-> (1005) "MavenR3Stream Workspace" (outgoing addition)
078    private static final Pattern COMPONENT_PATTERN2 = Pattern.compile( "\\((\\d+)\\) \"(.*)\" <.*>" );
079
080    //  Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
081    private static final Pattern BASELINE_PATTERN = Pattern.compile( "\\((\\d+)\\) (\\d+) \"(.*)\"" );
082
083    // Additional data we collect. (eye catchers)
084
085    /**
086     * The "Status" command output line that contains the "Workspace" name.
087     */
088    public static final String STATUS_CMD_WORKSPACE = "Workspace:";
089
090    /**
091     * The "Status" command output line that contains the "Component" name.
092     */
093    public static final String STATUS_CMD_COMPONENT = "Component:";
094
095    /**
096     * The "Status" command output line that contains the "Workspace" name.
097     */
098    public static final String STATUS_CMD_BASELINE = "Baseline:";
099
100    // File Status Commands (eye catchers)
101
102    /**
103     * The "Status" command status flag for a resource that has been added.
104     */
105    public static final String STATUS_CMD_ADD_FLAG = "a-";
106
107    /**
108     * The "Status" command status flag for when the content or properties of
109     * a file have been modified, or the properties of a directory have changed.
110     */
111    public static final String STATUS_CMD_CHANGE_FLAG = "-c";
112
113    /**
114     * The "Status" command status flag for a resource that has been deleted.
115     */
116    public static final String STATUS_CMD_DELETE_FLAG = "d-";
117
118    /**
119     * The "Status" command status flag for a resource that has been renamed or moved.
120     */
121    public static final String STATUS_CMD_MOVED_FLAG = "m-";
122
123    /**
124     * A List of ScmFile objects that have their ScmFileStatus set.
125     */
126    private List<ScmFile> fChangedFiles = new ArrayList<ScmFile>();
127
128    /**
129     * Constructor for our "scm status" consumer.
130     *
131     * @param repo   The JazzScmProviderRepository being used.
132     * @param logger The ScmLogger to use.
133     */
134    public JazzStatusConsumer( ScmProviderRepository repo, ScmLogger logger )
135    {
136        super( repo, logger );
137    }
138
139    /**
140     * Process one line of output from the execution of the "scm status" command.
141     *
142     * @param line The line of output from the external command that has been pumped to us.
143     * @see org.codehaus.plexus.util.cli.StreamConsumer#consumeLine(java.lang.String)
144     */
145    public void consumeLine( String line )
146    {
147        super.consumeLine( line );
148        if ( containsWorkspace( line ) )
149        {
150            extractWorkspace( line );
151        }
152        if ( containsComponent( line ) )
153        {
154            extractComponent( line );
155        }
156        if ( containsBaseline( line ) )
157        {
158            extractBaseline( line );
159        }
160        if ( containsStatusFlag( line ) )
161        {
162            extractChangedFile( line );
163        }
164    }
165
166    private boolean containsWorkspace( String line )
167    {
168        return line.trim().startsWith( STATUS_CMD_WORKSPACE );
169    }
170
171    private void extractWorkspace( String line )
172    {
173        // With no stream (flow target):
174        //   Workspace: (1000) "BogusRepositoryWorkspace" <-> (1000) "BogusRepositoryWorkspace"
175        // With a stream:
176        //   Workspace: (1156) "GPDBWorkspace" <-> (1157) "GPDBStream"
177
178        Matcher matcher = WORKSPACE_PATTERN.matcher( line );
179        if ( matcher.find() )
180        {
181            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
182
183            int workspaceAlias = Integer.parseInt( matcher.group( 1 ) );
184            String workspace = matcher.group( 2 );
185            int streamAlias = Integer.parseInt( matcher.group( 3 ) );
186            String stream = matcher.group( 4 );
187            if ( getLogger().isDebugEnabled() )
188            {
189                getLogger().debug( "Successfully parsed \"Workspace:\" line:" );
190                getLogger().debug( "  workspaceAlias = " + workspaceAlias );
191                getLogger().debug( "  workspace      = " + workspace );
192                getLogger().debug( "  streamAlias    = " + streamAlias );
193                getLogger().debug( "  stream         = " + stream );
194            }
195            jazzRepository.setWorkspaceAlias( workspaceAlias );
196            jazzRepository.setWorkspace( workspace );
197            jazzRepository.setFlowTargetAlias( streamAlias );
198            jazzRepository.setFlowTarget( stream );
199        }
200    }
201
202    private boolean containsComponent( String line )
203    {
204        return line.trim().startsWith( STATUS_CMD_COMPONENT );
205    }
206
207    private void extractComponent( String line )
208    {
209        // With no stream (flow target):
210        //     Component: (1001) "BogusComponent"
211        // With a stream:
212        //     Component: (1158) "GPDB" <-> (1157) "GPDBStream"
213        // With some additional information:
214        //     Component: (1002) "FireDragon" <-> (1005) "MavenR3Stream Workspace" (outgoing addition)
215
216        Matcher matcher = COMPONENT_PATTERN1.matcher( line );
217        if ( matcher.find() )
218        {
219            //     Component: (1001) "BogusComponent"
220            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
221            int componentAlias = Integer.parseInt( matcher.group( 1 ) );
222            String component = matcher.group( 2 );
223            if ( getLogger().isDebugEnabled() )
224            {
225                getLogger().debug( "Successfully parsed \"Component:\" line:" );
226                getLogger().debug( "  componentAlias = " + componentAlias );
227                getLogger().debug( "  component      = " + component );
228            }
229            jazzRepository.setComponent( component );
230        }
231
232        matcher = COMPONENT_PATTERN2.matcher( line );
233        if ( matcher.find() )
234        {
235            //     Component: (1158) "GPDB" <-> (1157) "GPDBStream"
236            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
237            int componentAlias = Integer.parseInt( matcher.group( 1 ) );
238            String component = matcher.group( 2 );
239            if ( getLogger().isDebugEnabled() )
240            {
241                getLogger().debug( "Successfully parsed \"Component:\" line:" );
242                getLogger().debug( "  componentAlias = " + componentAlias );
243                getLogger().debug( "  component      = " + component );
244            }
245            jazzRepository.setComponent( component );
246        }
247    }
248
249    private boolean containsBaseline( String line )
250    {
251        return line.trim().startsWith( STATUS_CMD_BASELINE );
252    }
253
254    private void extractBaseline( String line )
255    {
256        // Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
257
258        Matcher matcher = BASELINE_PATTERN.matcher( line );
259        if ( matcher.find() )
260        {
261            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
262
263            int baselineAlias = Integer.parseInt( matcher.group( 1 ) );
264            int baselineId = Integer.parseInt( matcher.group( 2 ) );
265            String baseline = matcher.group( 3 );
266            if ( getLogger().isDebugEnabled() )
267            {
268                getLogger().debug( "Successfully parsed \"Baseline:\" line:" );
269                getLogger().debug( "  baselineAlias = " + baselineAlias );
270                getLogger().debug( "  baselineId    = " + baselineId );
271                getLogger().debug( "  baseline      = " + baseline );
272            }
273            jazzRepository.setBaseline( baseline );
274        }
275    }
276
277    private boolean containsStatusFlag( String line )
278    {
279        boolean containsStatusFlag = false;
280
281        if ( line.trim().length() > 2 )
282        {
283            String flag = line.trim().substring( 0, 2 );
284            if ( STATUS_CMD_ADD_FLAG.equals( flag ) ||
285                STATUS_CMD_CHANGE_FLAG.equals( flag ) ||
286                STATUS_CMD_DELETE_FLAG.equals( flag ) )
287            {
288                containsStatusFlag = true;
289            }
290        }
291        return containsStatusFlag;
292    }
293
294    private void extractChangedFile( String line )
295    {
296        String flag = line.trim().substring( 0, 2 );
297        String filePath = line.trim().substring( 3 ).trim();
298        ScmFileStatus status = ScmFileStatus.UNKNOWN;
299
300        if ( STATUS_CMD_ADD_FLAG.equals( flag ) )
301        {
302            status = ScmFileStatus.ADDED;
303        }
304
305        if ( STATUS_CMD_CHANGE_FLAG.equals( flag ) )
306        {
307            status = ScmFileStatus.MODIFIED;
308        }
309
310        if ( STATUS_CMD_DELETE_FLAG.equals( flag ) )
311        {
312            status = ScmFileStatus.DELETED;
313        }
314
315        if ( getLogger().isDebugEnabled() )
316        {
317            getLogger().debug( " Line               : '" + line + "'" );
318            getLogger().debug( " Extracted filePath : '" + filePath + "'" );
319            getLogger().debug( " Extracted     flag : '" + flag + "'" );
320            getLogger().debug( " Extracted   status : '" + status + "'" );
321        }
322
323        fChangedFiles.add( new ScmFile( filePath, status ) );
324    }
325
326    public List<ScmFile> getChangedFiles()
327    {
328        return fChangedFiles;
329    }
330}