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 =
072        Pattern.compile( "\\((\\d+)\\) \"(.*)\" <-> \\((\\d+)\\) \"(.*)\"" );
073
074    //  Component: (1001) "BogusComponent"
075    private static final Pattern COMPONENT_PATTERN1 = Pattern.compile( "\\((\\d+)\\) \"(.*)\"" );
076
077    //  Component: (1158) "GPDB" <-> (1157) "GPDBStream"
078    //  Component: (1002) "FireDragon" <-> (1005) "MavenR3Stream Workspace" (outgoing addition)
079    private static final Pattern COMPONENT_PATTERN2 = Pattern.compile( "\\((\\d+)\\) \"(.*)\" <.*>" );
080
081    //  Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
082    private static final Pattern BASELINE_PATTERN = Pattern.compile( "\\((\\d+)\\) (\\d+) \"(.*)\"" );
083
084    // Additional data we collect. (eye catchers)
085
086    /**
087     * The "Status" command output line that contains the "Workspace" name.
088     */
089    public static final String STATUS_CMD_WORKSPACE = "Workspace:";
090
091    /**
092     * The "Status" command output line that contains the "Component" name.
093     */
094    public static final String STATUS_CMD_COMPONENT = "Component:";
095
096    /**
097     * The "Status" command output line that contains the "Workspace" name.
098     */
099    public static final String STATUS_CMD_BASELINE = "Baseline:";
100
101    // File Status Commands (eye catchers)
102
103    /**
104     * The "Status" command status flag for a resource that has been added.
105     */
106    public static final String STATUS_CMD_ADD_FLAG = "a-";
107
108    /**
109     * The "Status" command status flag for when the content or properties of
110     * a file have been modified, or the properties of a directory have changed.
111     */
112    public static final String STATUS_CMD_CHANGE_FLAG = "-c";
113
114    /**
115     * The "Status" command status flag for a resource that has been deleted.
116     */
117    public static final String STATUS_CMD_DELETE_FLAG = "d-";
118
119    /**
120     * The "Status" command status flag for a resource that has been renamed or moved.
121     */
122    public static final String STATUS_CMD_MOVED_FLAG = "m-";
123
124    /**
125     * A List of ScmFile objects that have their ScmFileStatus set.
126     */
127    private List<ScmFile> fChangedFiles = new ArrayList<ScmFile>();
128
129    /**
130     * Constructor for our "scm status" consumer.
131     *
132     * @param repo   The JazzScmProviderRepository being used.
133     * @param logger The ScmLogger to use.
134     */
135    public JazzStatusConsumer( ScmProviderRepository repo, ScmLogger logger )
136    {
137        super( repo, logger );
138    }
139
140    /**
141     * Process one line of output from the execution of the "scm status" command.
142     *
143     * @param line The line of output from the external command that has been pumped to us.
144     * @see org.codehaus.plexus.util.cli.StreamConsumer#consumeLine(java.lang.String)
145     */
146    public void consumeLine( String line )
147    {
148        super.consumeLine( line );
149        if ( containsWorkspace( line ) )
150        {
151            extractWorkspace( line );
152        }
153        if ( containsComponent( line ) )
154        {
155            extractComponent( line );
156        }
157        if ( containsBaseline( line ) )
158        {
159            extractBaseline( line );
160        }
161        if ( containsStatusFlag( line ) )
162        {
163            extractChangedFile( line );
164        }
165    }
166
167    private boolean containsWorkspace( String line )
168    {
169        return line.trim().startsWith( STATUS_CMD_WORKSPACE );
170    }
171
172    private void extractWorkspace( String line )
173    {
174        // With no stream (flow target):
175        //   Workspace: (1000) "BogusRepositoryWorkspace" <-> (1000) "BogusRepositoryWorkspace"
176        // With a stream:
177        //   Workspace: (1156) "GPDBWorkspace" <-> (1157) "GPDBStream"
178
179        Matcher matcher = WORKSPACE_PATTERN.matcher( line );
180        if ( matcher.find() )
181        {
182            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
183
184            int workspaceAlias = Integer.parseInt( matcher.group( 1 ) );
185            String workspace = matcher.group( 2 );
186            int streamAlias = Integer.parseInt( matcher.group( 3 ) );
187            String stream = matcher.group( 4 );
188            if ( getLogger().isDebugEnabled() )
189            {
190                getLogger().debug( "Successfully parsed \"Workspace:\" line:" );
191                getLogger().debug( "  workspaceAlias = " + workspaceAlias );
192                getLogger().debug( "  workspace      = " + workspace );
193                getLogger().debug( "  streamAlias    = " + streamAlias );
194                getLogger().debug( "  stream         = " + stream );
195            }
196            jazzRepository.setWorkspaceAlias( workspaceAlias );
197            jazzRepository.setWorkspace( workspace );
198            jazzRepository.setFlowTargetAlias( streamAlias );
199            jazzRepository.setFlowTarget( stream );
200        }
201    }
202
203    private boolean containsComponent( String line )
204    {
205        return line.trim().startsWith( STATUS_CMD_COMPONENT );
206    }
207
208    private void extractComponent( String line )
209    {
210        // With no stream (flow target):
211        //     Component: (1001) "BogusComponent"
212        // With a stream:
213        //     Component: (1158) "GPDB" <-> (1157) "GPDBStream"
214        // With some additional information:
215        //     Component: (1002) "FireDragon" <-> (1005) "MavenR3Stream Workspace" (outgoing addition)
216
217        Matcher matcher = COMPONENT_PATTERN1.matcher( line );
218        if ( matcher.find() )
219        {
220            //     Component: (1001) "BogusComponent"
221            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
222            int componentAlias = Integer.parseInt( matcher.group( 1 ) );
223            String component = matcher.group( 2 );
224            if ( getLogger().isDebugEnabled() )
225            {
226                getLogger().debug( "Successfully parsed \"Component:\" line:" );
227                getLogger().debug( "  componentAlias = " + componentAlias );
228                getLogger().debug( "  component      = " + component );
229            }
230            jazzRepository.setComponent( component );
231        }
232
233        matcher = COMPONENT_PATTERN2.matcher( line );
234        if ( matcher.find() )
235        {
236            //     Component: (1158) "GPDB" <-> (1157) "GPDBStream"
237            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
238            int componentAlias = Integer.parseInt( matcher.group( 1 ) );
239            String component = matcher.group( 2 );
240            if ( getLogger().isDebugEnabled() )
241            {
242                getLogger().debug( "Successfully parsed \"Component:\" line:" );
243                getLogger().debug( "  componentAlias = " + componentAlias );
244                getLogger().debug( "  component      = " + component );
245            }
246            jazzRepository.setComponent( component );
247        }
248    }
249
250    private boolean containsBaseline( String line )
251    {
252        return line.trim().startsWith( STATUS_CMD_BASELINE );
253    }
254
255    private void extractBaseline( String line )
256    {
257        // Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
258
259        Matcher matcher = BASELINE_PATTERN.matcher( line );
260        if ( matcher.find() )
261        {
262            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
263
264            int baselineAlias = Integer.parseInt( matcher.group( 1 ) );
265            int baselineId = Integer.parseInt( matcher.group( 2 ) );
266            String baseline = matcher.group( 3 );
267            if ( getLogger().isDebugEnabled() )
268            {
269                getLogger().debug( "Successfully parsed \"Baseline:\" line:" );
270                getLogger().debug( "  baselineAlias = " + baselineAlias );
271                getLogger().debug( "  baselineId    = " + baselineId );
272                getLogger().debug( "  baseline      = " + baseline );
273            }
274            jazzRepository.setBaseline( baseline );
275        }
276    }
277
278    private boolean containsStatusFlag( String line )
279    {
280        boolean containsStatusFlag = false;
281
282        if ( line.trim().length() > 2 )
283        {
284            String flag = line.trim().substring( 0, 2 );
285            if ( STATUS_CMD_ADD_FLAG.equals( flag ) || 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}