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;
028import org.apache.regexp.RE;
029import org.apache.regexp.RESyntaxException;
030
031import java.util.ArrayList;
032import java.util.List;
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 String WORKSPACE_PATTERN = "\\((\\d+)\\) \"(.*)\" <-> \\((\\d+)\\) \"(.*)\"";
072
073    /**
074     * @see #WORKSPACE_PATTERN
075     */
076    private RE workspaceRegExp;
077
078    //  Component: (1001) "BogusComponent"
079    private static final String COMPONENT_PATTERN1 = "\\((\\d+)\\) \"(.*)\"";
080
081    /**
082     * @see #COMPONENT_PATTERN1
083     */
084    private RE componentRegExp1;
085
086    //  Component: (1158) "GPDB" <-> (1157) "GPDBStream"
087    //  Component: (1002) "FireDragon" <-> (1005) "MavenR3Stream Workspace" (outgoing addition)
088    private static final String COMPONENT_PATTERN2 = "\\((\\d+)\\) \"(.*)\" <.*>";
089
090    /**
091     * @see #COMPONENT_PATTERN2
092     */
093    private RE componentRegExp2;
094
095    //  Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
096    private static final String BASELINE_PATTERN = "\\((\\d+)\\) (\\d+) \"(.*)\"";
097
098    /**
099     * @see #BASELINE_PATTERN
100     */
101    private RE baselineRegExp;
102
103    // Additional data we collect. (eye catchers)
104
105    /**
106     * The "Status" command output line that contains the "Workspace" name.
107     */
108    public static final String STATUS_CMD_WORKSPACE = "Workspace:";
109
110    /**
111     * The "Status" command output line that contains the "Component" name.
112     */
113    public static final String STATUS_CMD_COMPONENT = "Component:";
114
115    /**
116     * The "Status" command output line that contains the "Workspace" name.
117     */
118    public static final String STATUS_CMD_BASELINE = "Baseline:";
119
120    // File Status Commands (eye catchers)
121
122    /**
123     * The "Status" command status flag for a resource that has been added.
124     */
125    public static final String STATUS_CMD_ADD_FLAG = "a-";
126
127    /**
128     * The "Status" command status flag for when the content or properties of
129     * a file have been modified, or the properties of a directory have changed.
130     */
131    public static final String STATUS_CMD_CHANGE_FLAG = "-c";
132
133    /**
134     * The "Status" command status flag for a resource that has been deleted.
135     */
136    public static final String STATUS_CMD_DELETE_FLAG = "d-";
137
138    /**
139     * The "Status" command status flag for a resource that has been renamed or moved.
140     */
141    public static final String STATUS_CMD_MOVED_FLAG = "m-";
142
143    /**
144     * A List of ScmFile objects that have their ScmFileStatus set.
145     */
146    private List<ScmFile> fChangedFiles = new ArrayList<ScmFile>();
147
148    /**
149     * Constructor for our "scm status" consumer.
150     *
151     * @param repo   The JazzScmProviderRepository being used.
152     * @param logger The ScmLogger to use.
153     */
154    public JazzStatusConsumer( ScmProviderRepository repo, ScmLogger logger )
155    {
156        super( repo, logger );
157
158        try
159        {
160            workspaceRegExp = new RE( WORKSPACE_PATTERN );
161            componentRegExp1 = new RE( COMPONENT_PATTERN1 );
162            componentRegExp2 = new RE( COMPONENT_PATTERN2 );
163            baselineRegExp = new RE( BASELINE_PATTERN );
164        }
165        catch ( RESyntaxException ex )
166        {
167            throw new RuntimeException(
168                "INTERNAL ERROR: Could not create regexp to parse jazz scm status output. This shouldn't happen. Something is probably wrong with the oro installation.",
169                ex );
170        }
171    }
172
173    /**
174     * Process one line of output from the execution of the "scm status" command.
175     *
176     * @param line The line of output from the external command that has been pumped to us.
177     * @see org.codehaus.plexus.util.cli.StreamConsumer#consumeLine(java.lang.String)
178     */
179    public void consumeLine( String line )
180    {
181        super.consumeLine( line );
182        if ( containsWorkspace( line ) )
183        {
184            extractWorkspace( line );
185        }
186        if ( containsComponent( line ) )
187        {
188            extractComponent( line );
189        }
190        if ( containsBaseline( line ) )
191        {
192            extractBaseline( line );
193        }
194        if ( containsStatusFlag( line ) )
195        {
196            extractChangedFile( line );
197        }
198    }
199
200    private boolean containsWorkspace( String line )
201    {
202        return line.trim().startsWith( STATUS_CMD_WORKSPACE );
203    }
204
205    private void extractWorkspace( String line )
206    {
207        // With no stream (flow target):
208        //   Workspace: (1000) "BogusRepositoryWorkspace" <-> (1000) "BogusRepositoryWorkspace"
209        // With a stream:
210        //   Workspace: (1156) "GPDBWorkspace" <-> (1157) "GPDBStream"
211
212        if ( workspaceRegExp.match( line ) )
213        {
214            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
215
216            int workspaceAlias = Integer.parseInt( workspaceRegExp.getParen( 1 ) );
217            String workspace = workspaceRegExp.getParen( 2 );
218            int streamAlias = Integer.parseInt( workspaceRegExp.getParen( 3 ) );
219            String stream = workspaceRegExp.getParen( 4 );
220            if ( getLogger().isDebugEnabled() )
221            {
222                getLogger().debug( "Successfully parsed \"Workspace:\" line:" );
223                getLogger().debug( "  workspaceAlias = " + workspaceAlias );
224                getLogger().debug( "  workspace      = " + workspace );
225                getLogger().debug( "  streamAlias    = " + streamAlias );
226                getLogger().debug( "  stream         = " + stream );
227            }
228            jazzRepository.setWorkspaceAlias( workspaceAlias );
229            jazzRepository.setWorkspace( workspace );
230            jazzRepository.setFlowTargetAlias( streamAlias );
231            jazzRepository.setFlowTarget( stream );
232        }
233    }
234
235    private boolean containsComponent( String line )
236    {
237        return line.trim().startsWith( STATUS_CMD_COMPONENT );
238    }
239
240    private void extractComponent( String line )
241    {
242        // With no stream (flow target):
243        //     Component: (1001) "BogusComponent"
244        // With a stream:
245        //     Component: (1158) "GPDB" <-> (1157) "GPDBStream"
246        // With some additional information:
247        //     Component: (1002) "FireDragon" <-> (1005) "MavenR3Stream Workspace" (outgoing addition)
248
249        if ( componentRegExp1.match( line ) )
250        {
251            //     Component: (1001) "BogusComponent"
252            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
253            int componentAlias = Integer.parseInt( componentRegExp1.getParen( 1 ) );
254            String component = componentRegExp1.getParen( 2 );
255            if ( getLogger().isDebugEnabled() )
256            {
257                getLogger().debug( "Successfully parsed \"Component:\" line:" );
258                getLogger().debug( "  componentAlias = " + componentAlias );
259                getLogger().debug( "  component      = " + component );
260            }
261            jazzRepository.setComponent( component );
262        }
263
264        if ( componentRegExp2.match( line ) )
265        {
266            //     Component: (1158) "GPDB" <-> (1157) "GPDBStream"
267            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
268            int componentAlias = Integer.parseInt( componentRegExp2.getParen( 1 ) );
269            String component = componentRegExp2.getParen( 2 );
270            if ( getLogger().isDebugEnabled() )
271            {
272                getLogger().debug( "Successfully parsed \"Component:\" line:" );
273                getLogger().debug( "  componentAlias = " + componentAlias );
274                getLogger().debug( "  component      = " + component );
275            }
276            jazzRepository.setComponent( component );
277        }
278    }
279
280    private boolean containsBaseline( String line )
281    {
282        return line.trim().startsWith( STATUS_CMD_BASELINE );
283    }
284
285    private void extractBaseline( String line )
286    {
287        // Baseline: (1128) 27 "BogusTestJazz-3.0.0.40"
288
289        if ( baselineRegExp.match( line ) )
290        {
291            JazzScmProviderRepository jazzRepository = (JazzScmProviderRepository) getRepository();
292
293            int baselineAlias = Integer.parseInt( baselineRegExp.getParen( 1 ) );
294            int baselineId = Integer.parseInt( baselineRegExp.getParen( 2 ) );
295            String baseline = baselineRegExp.getParen( 3 );
296            if ( getLogger().isDebugEnabled() )
297            {
298                getLogger().debug( "Successfully parsed \"Baseline:\" line:" );
299                getLogger().debug( "  baselineAlias = " + baselineAlias );
300                getLogger().debug( "  baselineId    = " + baselineId );
301                getLogger().debug( "  baseline      = " + baseline );
302            }
303            jazzRepository.setBaseline( baseline );
304        }
305    }
306
307    private boolean containsStatusFlag( String line )
308    {
309        boolean containsStatusFlag = false;
310
311        if ( line.trim().length() > 2 )
312        {
313            String flag = line.trim().substring( 0, 2 );
314            if ( STATUS_CMD_ADD_FLAG.equals( flag ) ||
315                STATUS_CMD_CHANGE_FLAG.equals( flag ) ||
316                STATUS_CMD_DELETE_FLAG.equals( flag ) )
317            {
318                containsStatusFlag = true;
319            }
320        }
321        return containsStatusFlag;
322    }
323
324    private void extractChangedFile( String line )
325    {
326        String flag = line.trim().substring( 0, 2 );
327        String filePath = line.trim().substring( 3 ).trim();
328        ScmFileStatus status = ScmFileStatus.UNKNOWN;
329
330        if ( STATUS_CMD_ADD_FLAG.equals( flag ) )
331        {
332            status = ScmFileStatus.ADDED;
333        }
334
335        if ( STATUS_CMD_CHANGE_FLAG.equals( flag ) )
336        {
337            status = ScmFileStatus.MODIFIED;
338        }
339
340        if ( STATUS_CMD_DELETE_FLAG.equals( flag ) )
341        {
342            status = ScmFileStatus.DELETED;
343        }
344
345        if ( getLogger().isDebugEnabled() )
346        {
347            getLogger().debug( " Line               : '" + line + "'" );
348            getLogger().debug( " Extracted filePath : '" + filePath + "'" );
349            getLogger().debug( " Extracted     flag : '" + flag + "'" );
350            getLogger().debug( " Extracted   status : '" + status + "'" );
351        }
352
353        fChangedFiles.add( new ScmFile( filePath, status ) );
354    }
355
356    public List<ScmFile> getChangedFiles()
357    {
358        return fChangedFiles;
359    }
360}