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   */
39  public class HgConsumer extends AbstractConsumer {
40  
41      /**
42       * A list of known keywords from hg
43       */
44      private static final Map<String, ScmFileStatus> IDENTIFIERS = new HashMap<>();
45  
46      /**
47       * A list of known message prefixes from hg
48       */
49      private static final Map<String, String> MESSAGES = new HashMap<>();
50  
51      /**
52       * Number of lines to keep from Std.Err
53       * This size is set to ensure that we capture enough info
54       * but still keeps a low memory footprint.
55       */
56      private static final int MAX_STDERR_SIZE = 10;
57  
58      /**
59       * A list of the MAX_STDERR_SIZE last errors or warnings.
60       */
61      private final List<String> stderr = new ArrayList<>();
62  
63      static {
64          /* Statuses from hg add
65           */
66          IDENTIFIERS.put("adding", ScmFileStatus.ADDED);
67          IDENTIFIERS.put("unknown", ScmFileStatus.UNKNOWN);
68          IDENTIFIERS.put("modified", ScmFileStatus.MODIFIED);
69          IDENTIFIERS.put("removed", ScmFileStatus.DELETED);
70          IDENTIFIERS.put("renamed", ScmFileStatus.MODIFIED);
71  
72          /* Statuses from hg status;
73           */
74          IDENTIFIERS.put("A", ScmFileStatus.ADDED);
75          IDENTIFIERS.put("?", ScmFileStatus.UNKNOWN);
76          IDENTIFIERS.put("M", ScmFileStatus.MODIFIED);
77          IDENTIFIERS.put("R", ScmFileStatus.DELETED);
78          IDENTIFIERS.put("C", ScmFileStatus.CHECKED_IN);
79          IDENTIFIERS.put("!", ScmFileStatus.MISSING);
80          IDENTIFIERS.put("I", ScmFileStatus.UNKNOWN); // not precisely the same, but i think semantics work? - rwd
81  
82          MESSAGES.put("hg: WARNING:", "WARNING");
83          MESSAGES.put("hg: ERROR:", "ERROR");
84          MESSAGES.put("'hg' ", "ERROR"); // hg isn't found in windows path
85      }
86  
87      public void doConsume(ScmFileStatus status, String trimmedLine) {
88          // override this
89      }
90  
91      /** {@inheritDoc} */
92      public void consumeLine(String line) {
93          if (logger.isDebugEnabled()) {
94              logger.debug(line);
95          }
96          String trimmedLine = line.trim();
97  
98          String statusStr = processInputForKnownIdentifiers(trimmedLine);
99  
100         // If its not a status report - then maybe its a message?
101         if (statusStr == null) {
102             boolean isMessage = processInputForKnownMessages(trimmedLine);
103             // If it is then its already processed and we can ignore futher processing
104             if (isMessage) {
105                 return;
106             }
107         } else {
108             // Strip away identifier
109             trimmedLine = trimmedLine.substring(statusStr.length());
110             trimmedLine = trimmedLine.trim(); // one or more spaces
111         }
112 
113         ScmFileStatus status = statusStr != null ? (IDENTIFIERS.get(statusStr.intern())) : null;
114         doConsume(status, trimmedLine);
115     }
116 
117     /**
118      * Warnings and errors is usually printed out in Std.Err, thus for derived consumers
119      * operating on Std.Out this would typically return an empty string.
120      *
121      * @return Return the last lines interpreted as an warning or an error
122      */
123     public String getStdErr() {
124         StringBuilder str = new StringBuilder();
125         for (Iterator<String> it = stderr.iterator(); it.hasNext(); ) {
126             str.append(it.next());
127         }
128         return str.toString();
129     }
130 
131     private static String processInputForKnownIdentifiers(String line) {
132         for (Iterator<String> it = IDENTIFIERS.keySet().iterator(); it.hasNext(); ) {
133             String id = it.next();
134             if (line.startsWith(id)) {
135                 return id;
136             }
137         }
138         return null;
139     }
140 
141     private boolean processInputForKnownMessages(String line) {
142         for (Iterator<String> it = MESSAGES.keySet().iterator(); it.hasNext(); ) {
143             String prefix = it.next();
144             if (line.startsWith(prefix)) {
145                 stderr.add(line); // Add line
146                 if (stderr.size() > MAX_STDERR_SIZE) {
147                     stderr.remove(0); // Rotate list
148                 }
149                 String message = line.substring(prefix.length());
150                 if (MESSAGES.get(prefix).equals("WARNING")) {
151                     if (logger.isWarnEnabled()) {
152                         logger.warn(message);
153                     }
154                 } else {
155                     if (logger.isErrorEnabled()) {
156                         logger.error(message);
157                     }
158                 }
159                 return true;
160             }
161         }
162         return false;
163     }
164 }