View Javadoc
1   package org.apache.maven.scm.provider.hg.command;
2   
3   /*
4    * Licensed to the Apache Software Foundation (ASF) under one
5    * or more contributor license agreements.  See the NOTICE file
6    * distributed with this work for additional information
7    * regarding copyright ownership.  The ASF licenses this file
8    * to you under the Apache License, Version 2.0 (the
9    * "License"); you may not use this file except in compliance
10   * with the License.  You may obtain a copy of the License at
11   *
12   * http://www.apache.org/licenses/LICENSE-2.0
13   *
14   * Unless required by applicable law or agreed to in writing,
15   * software distributed under the License is distributed on an
16   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
17   * KIND, either express or implied.  See the License for the
18   * specific language governing permissions and limitations
19   * under the License.
20   */
21  
22  import org.apache.maven.scm.ScmFileStatus;
23  import org.apache.maven.scm.log.ScmLogger;
24  import org.apache.maven.scm.util.AbstractConsumer;
25  
26  import java.util.ArrayList;
27  import java.util.HashMap;
28  import java.util.Iterator;
29  import java.util.List;
30  import java.util.Map;
31  
32  /**
33   * Base consumer to do common parsing for all hg commands.
34   * <p/>
35   * More specific: log line each line if debug is enabled, get file status
36   * and detect warnings from hg
37   *
38   * @author <a href="mailto:thurner.rupert@ymono.net">thurner rupert</a>
39   *
40   */
41  public class HgConsumer
42      extends AbstractConsumer
43  {
44  
45      /**
46       * A list of known keywords from hg
47       */
48      private static final Map<String, ScmFileStatus> IDENTIFIERS = new HashMap<String, ScmFileStatus>();
49  
50      /**
51       * A list of known message prefixes from hg
52       */
53      private static final Map<String, String> MESSAGES = new HashMap<String, String>();
54  
55      /**
56       * Number of lines to keep from Std.Err
57       * This size is set to ensure that we capture enough info
58       * but still keeps a low memory footprint.
59       */
60      private static final int MAX_STDERR_SIZE = 10;
61  
62      /**
63       * A list of the MAX_STDERR_SIZE last errors or warnings.
64       */
65      private final List<String> stderr = new ArrayList<String>();
66  
67      static
68      {
69          /** Statuses from hg add
70           */
71          IDENTIFIERS.put( "adding", ScmFileStatus.ADDED );
72          IDENTIFIERS.put( "unknown", ScmFileStatus.UNKNOWN );
73          IDENTIFIERS.put( "modified", ScmFileStatus.MODIFIED );
74          IDENTIFIERS.put( "removed", ScmFileStatus.DELETED );
75          IDENTIFIERS.put( "renamed", ScmFileStatus.MODIFIED );
76  
77          /** Statuses from hg status;
78           */
79          IDENTIFIERS.put( "A", ScmFileStatus.ADDED );
80          IDENTIFIERS.put( "?", ScmFileStatus.UNKNOWN );
81          IDENTIFIERS.put( "M", ScmFileStatus.MODIFIED );
82          IDENTIFIERS.put( "R", ScmFileStatus.DELETED );
83          IDENTIFIERS.put( "C", ScmFileStatus.CHECKED_IN );
84          IDENTIFIERS.put( "!", ScmFileStatus.MISSING );
85          IDENTIFIERS.put( "I", ScmFileStatus.UNKNOWN ); // not precisely the same, but i think semantics work? - rwd
86  
87          MESSAGES.put( "hg: WARNING:", "WARNING" );
88          MESSAGES.put( "hg: ERROR:", "ERROR" );
89          MESSAGES.put( "'hg' ", "ERROR" ); // hg isn't found in windows path
90      }
91  
92      public HgConsumer( ScmLogger logger )
93      {
94          super( logger );
95      }
96  
97      public void doConsume( ScmFileStatus status, String trimmedLine )
98      {
99          //override this
100     }
101 
102     /** {@inheritDoc} */
103     public void consumeLine( String line )
104     {
105         if ( getLogger().isDebugEnabled() )
106         {
107             getLogger().debug( line );
108         }
109         String trimmedLine = line.trim();
110 
111         String statusStr = processInputForKnownIdentifiers( trimmedLine );
112 
113         //If its not a status report - then maybe its a message?
114         if ( statusStr == null )
115         {
116             boolean isMessage = processInputForKnownMessages( trimmedLine );
117             //If it is then its already processed and we can ignore futher processing
118             if ( isMessage )
119             {
120                 return;
121             }
122         }
123         else
124         {
125             //Strip away identifier
126             trimmedLine = trimmedLine.substring( statusStr.length() );
127             trimmedLine = trimmedLine.trim(); //one or more spaces
128         }
129 
130         ScmFileStatus status = statusStr != null ? ( (ScmFileStatus) IDENTIFIERS.get( statusStr.intern() ) ) : null;
131         doConsume( status, trimmedLine );
132     }
133 
134     /**
135      * Warnings and errors is usually printed out in Std.Err, thus for derived consumers
136      * operating on Std.Out this would typically return an empty string.
137      *
138      * @return Return the last lines interpreted as an warning or an error
139      */
140     public String getStdErr()
141     {
142         StringBuilder str = new StringBuilder();
143         for ( Iterator<String> it = stderr.iterator(); it.hasNext(); )
144         {
145             str.append( it.next() );
146         }
147         return str.toString();
148     }
149 
150     private static String processInputForKnownIdentifiers( String line )
151     {
152         for ( Iterator<String> it = IDENTIFIERS.keySet().iterator(); it.hasNext(); )
153         {
154             String id = it.next();
155             if ( line.startsWith( id ) )
156             {
157                 return id;
158             }
159         }
160         return null;
161     }
162 
163     private boolean processInputForKnownMessages( String line )
164     {
165         for ( Iterator<String> it = MESSAGES.keySet().iterator(); it.hasNext(); )
166         {
167             String prefix = it.next();
168             if ( line.startsWith( prefix ) )
169             {
170                 stderr.add( line ); //Add line
171                 if ( stderr.size() > MAX_STDERR_SIZE )
172                 {
173                     stderr.remove( 0 ); //Rotate list
174                 }
175                 String message = line.substring( prefix.length() );
176                 if ( MESSAGES.get( prefix ).equals( "WARNING" ) )
177                 {
178                     if ( getLogger().isWarnEnabled() )
179                     {
180                         getLogger().warn( message );
181                     }
182                 }
183                 else
184                 {
185                     if ( getLogger().isErrorEnabled() )
186                     {
187                         getLogger().error( message );
188                     }
189                 }
190                 return true;
191             }
192         }
193         return false;
194     }
195 }