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