001package 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
022import org.apache.maven.scm.ScmFileStatus;
023import org.apache.maven.scm.log.ScmLogger;
024import org.apache.maven.scm.util.AbstractConsumer;
025
026import java.util.ArrayList;
027import java.util.HashMap;
028import java.util.Iterator;
029import java.util.List;
030import 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 *
040 */
041public 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}