001    package org.apache.maven.scm.provider.bazaar.command;
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.ScmFileStatus;
023    import org.apache.maven.scm.log.ScmLogger;
024    import org.apache.maven.scm.util.AbstractConsumer;
025    
026    import java.util.ArrayList;
027    import java.util.HashMap;
028    import java.util.Iterator;
029    import java.util.List;
030    import java.util.Map;
031    
032    /**
033     * Base consumer to do common parsing for all bazaar commands.
034     * <p/>
035     * More specific: log line each line if debug is enabled, get file status
036     * and detect warnings from bazaar
037     *
038     * @author <a href="mailto:torbjorn@smorgrav.org">Torbj�rn Eikli Sm�rgrav</a>
039     * @version $Id: BazaarConsumer.java 1212880 2011-12-10 21:22:16Z olamy $
040     */
041    public class BazaarConsumer
042        extends AbstractConsumer
043    {
044    
045        /**
046         * A list of known keywords from bazaar
047         */
048        private static final Map<String,ScmFileStatus> IDENTIFIERS = new HashMap<String,ScmFileStatus>();
049    
050        /**
051         * A list of known message prefixes from bazaar
052         */
053        private static final Map<String,String> MESSAGES = new HashMap<String,String>();
054    
055        /**
056         * Number of lines to keep from Std.Err
057         * This size is set to ensure that we capture enough info
058         * but still keeps a low memory footprint.
059         */
060        private static final int MAX_STDERR_SIZE = 10;
061    
062        /**
063         * A list of the MAX_STDERR_SIZE last errors or warnings.
064         */
065        private final List<String> stderr = new ArrayList<String>();
066    
067        static
068        {
069            IDENTIFIERS.put( "added", ScmFileStatus.ADDED );
070            IDENTIFIERS.put( "adding", ScmFileStatus.ADDED );
071            IDENTIFIERS.put( "unknown", ScmFileStatus.UNKNOWN );
072            IDENTIFIERS.put( "modified", ScmFileStatus.MODIFIED );
073            IDENTIFIERS.put( "removed", ScmFileStatus.DELETED );
074            IDENTIFIERS.put( "renamed", ScmFileStatus.RENAMED );
075            MESSAGES.put( "bzr: WARNING:", "WARNING" );
076            MESSAGES.put( "bzr: ERROR:", "ERROR" );
077            MESSAGES.put( "'bzr' ", "ERROR" ); // bzr isn't found in windows path
078        }
079    
080        public BazaarConsumer( ScmLogger logger )
081        {
082            super( logger );
083        }
084    
085        public void doConsume( ScmFileStatus status, String trimmedLine )
086        {
087            //override this
088        }
089    
090        /** {@inheritDoc} */
091        public void consumeLine( String line )
092        {
093            if ( getLogger().isDebugEnabled() )
094            {
095                getLogger().debug( line );
096            }
097            String trimmedLine = line.trim();
098    
099            String statusStr = processInputForKnownIdentifiers( trimmedLine );
100    
101            //If its not a status report - then maybe its a message?
102            if ( statusStr == null )
103            {
104                boolean isMessage = processInputForKnownMessages( trimmedLine );
105                //If it is then its already processed and we can ignore futher processing
106                if ( isMessage )
107                {
108                    return;
109                }
110            }
111            else
112            {
113                //Strip away identifier
114                trimmedLine = trimmedLine.substring( statusStr.length() );
115                trimmedLine = trimmedLine.trim(); //one or more spaces
116            }
117    
118            ScmFileStatus status = statusStr != null ? ( (ScmFileStatus) IDENTIFIERS.get( statusStr.intern() ) ) : null;
119            doConsume( status, trimmedLine );
120        }
121    
122        /**
123         * Warnings and errors is usually printed out in Std.Err, thus for derived consumers
124         * operating on Std.Out this would typically return an empty string.
125         *
126         * @return Return the last lines interpreted as an warning or an error
127         */
128        public String getStdErr()
129        {
130            StringBuilder str = new StringBuilder();
131            for ( Iterator<String> it = stderr.iterator(); it.hasNext(); )
132            {
133                str.append( it.next() );
134            }
135            return str.toString();
136        }
137    
138        private static String processInputForKnownIdentifiers( String line )
139        {
140            for ( Iterator<String> it = IDENTIFIERS.keySet().iterator(); it.hasNext(); )
141            {
142                String id = it.next();
143                if ( line.startsWith( id ) )
144                {
145                    return id;
146                }
147            }
148            return null;
149        }
150    
151        private boolean processInputForKnownMessages( String line )
152        {
153            for ( Iterator<String> it = MESSAGES.keySet().iterator(); it.hasNext(); )
154            {
155                String prefix = it.next();
156                if ( line.startsWith( prefix ) )
157                {
158                    stderr.add( line ); //Add line
159                    if ( stderr.size() > MAX_STDERR_SIZE )
160                    {
161                        stderr.remove( 0 ); //Rotate list
162                    }
163                    String message = line.substring( prefix.length() );
164                    if ( MESSAGES.get( prefix ).equals( "WARNING" ) )
165                    {
166                        if ( getLogger().isWarnEnabled() )
167                        {
168                            getLogger().warn( message );
169                        }
170                    }
171                    else
172                    {
173                        if ( getLogger().isErrorEnabled() )
174                        {
175                            getLogger().error( message );
176                        }
177                    }
178                    return true;
179                }
180            }
181            return false;
182        }
183    }