001    package 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    
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.ScmProviderRepository;
026    import org.apache.maven.scm.provider.jazz.command.consumer.AbstractRepositoryConsumer;
027    import org.apache.maven.scm.provider.jazz.repository.JazzScmProviderRepository;
028    import org.apache.regexp.RE;
029    import org.apache.regexp.RESyntaxException;
030    
031    import java.util.ArrayList;
032    import 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     */
046    public 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    }