Console.java revision 3dafbce3b8d025f465ebf0a4c6d018c7dc31654e
1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package vogar; 18 19import java.util.List; 20 21/** 22 * Controls, formats and emits output to the command line. 23 */ 24public class Console { 25 private static final Console INSTANCE = new Console(); 26 27 private boolean stream; 28 private boolean color; 29 private boolean verbose; 30 private String indent; 31 32 private String currentName; 33 private CurrentLine currentLine = CurrentLine.NEW; 34 private final StringBuilder bufferedOutput = new StringBuilder(); 35 36 private Console() {} 37 38 public static Console getInstance() { 39 return INSTANCE; 40 } 41 42 public void setStream(boolean stream) { 43 this.stream = stream; 44 } 45 46 public void setIndent(String indent) { 47 this.indent = indent; 48 } 49 50 public void setColor(boolean color) { 51 this.color = color; 52 } 53 54 public void setVerbose(boolean verbose) { 55 this.verbose = verbose; 56 } 57 58 public synchronized void verbose(String s) { 59 newLine(); 60 System.out.print(s); 61 System.out.flush(); 62 currentLine = CurrentLine.VERBOSE; 63 } 64 65 public synchronized void warn(String message) { 66 newLine(); 67 System.out.println(yellow("Warning: " + message)); 68 } 69 70 /** 71 * Warns, and also puts a list of strings afterwards. 72 */ 73 public synchronized void warn(String message, List<String> list) { 74 newLine(); 75 System.out.println(yellow("Warning: " + message)); 76 for (String item : list) { 77 System.out.println(yellow(indent + item)); 78 } 79 } 80 81 public synchronized void info(String s) { 82 newLine(); 83 System.out.println(s); 84 } 85 86 public synchronized void info(String message, Throwable throwable) { 87 newLine(); 88 System.out.println(message); 89 throwable.printStackTrace(System.out); 90 } 91 92 public synchronized void action(String name) { 93 newLine(); 94 System.out.print("Action " + name); 95 System.out.flush(); 96 currentName = name; 97 currentLine = CurrentLine.NAME; 98 } 99 100 /** 101 * Prints the beginning of the named outcome. 102 */ 103 public synchronized void outcome(String name) { 104 // if the outcome and action names are the same, omit the outcome name 105 if (name.equals(currentName)) { 106 return; 107 } 108 109 currentName = name; 110 newLine(); 111 System.out.print(indent + name); 112 System.out.flush(); 113 currentLine = CurrentLine.NAME; 114 } 115 116 /** 117 * Appends the action output immediately to the stream when streaming is on, 118 * or to a buffer when streaming is off. Buffered output will be held and 119 * printed only if the outcome is unsuccessful. 120 */ 121 public synchronized void streamOutput(String output) { 122 if (stream) { 123 printOutput(output); 124 } else { 125 bufferedOutput.append(output); 126 } 127 } 128 129 /** 130 * Writes the action's outcome. 131 */ 132 public synchronized void printResult(Result result, ResultValue resultValue) { 133 if (resultValue == ResultValue.OK) { 134 String prefix = (currentLine == CurrentLine.NAME) ? " " : "\n" + indent; 135 System.out.println(prefix + green("OK (" + result + ")")); 136 137 } else if (resultValue == ResultValue.FAIL) { 138 if (bufferedOutput.length() > 0) { 139 printOutput(bufferedOutput.toString()); 140 bufferedOutput.delete(0, bufferedOutput.length()); 141 } 142 143 newLine(); 144 System.out.println(indent + red("FAIL (" + result + ")")); 145 } else if (resultValue == ResultValue.IGNORE) { 146 if (bufferedOutput.length() > 0) { 147 printOutput(bufferedOutput.toString()); 148 bufferedOutput.delete(0, bufferedOutput.length()); 149 } 150 151 newLine(); 152 System.out.println(indent + yellow("SKIP (" + result + ")")); 153 } 154 155 currentName = null; 156 currentLine = CurrentLine.NEW; 157 } 158 159 public synchronized void summarizeFailures(List<String> failureNames) { 160 newLine(); 161 System.out.println("Failure summary:"); 162 for (String failureName : failureNames) { 163 System.out.println(red(failureName)); 164 } 165 } 166 167 public synchronized void summarizeSkips(List<String> skippedNames) { 168 newLine(); 169 System.out.println("Skip summary:"); 170 for (String skippedName : skippedNames) { 171 System.out.println(yellow(skippedName)); 172 } 173 } 174 175 /** 176 * Prints the action output with appropriate indentation. 177 */ 178 private void printOutput(String streamedOutput) { 179 if (streamedOutput.length() == 0) { 180 return; 181 } 182 183 String[] lines = messageToLines(streamedOutput); 184 185 if (currentLine != CurrentLine.STREAMED_OUTPUT) { 186 newLine(); 187 System.out.print(indent); 188 System.out.print(indent); 189 } 190 System.out.print(lines[0]); 191 currentLine = CurrentLine.STREAMED_OUTPUT; 192 193 for (int i = 1; i < lines.length; i++) { 194 newLine(); 195 196 if (lines[i].length() > 0) { 197 System.out.print(indent); 198 System.out.print(indent); 199 System.out.print(lines[i]); 200 currentLine = CurrentLine.STREAMED_OUTPUT; 201 } 202 } 203 } 204 205 /** 206 * Inserts a linebreak if necessary. 207 */ 208 private void newLine() { 209 if (currentLine == CurrentLine.NEW) { 210 return; 211 } else if (currentLine == CurrentLine.VERBOSE) { 212 // --verbose means "leave all the verbose output on the screen". 213 if (!verbose) { 214 // Otherwise we overwrite verbose output whenever something new arrives. 215 eraseCurrentLine(); 216 currentLine = CurrentLine.NEW; 217 return; 218 } 219 } 220 221 System.out.println(); 222 currentLine = CurrentLine.NEW; 223 } 224 225 /** 226 * Status of a currently-in-progress line of output. 227 */ 228 enum CurrentLine { 229 230 /** 231 * The line is blank. 232 */ 233 NEW, 234 235 /** 236 * The line contains streamed application output. Additional streamed 237 * output may be appended without additional line separators or 238 * indentation. 239 */ 240 STREAMED_OUTPUT, 241 242 /** 243 * The line contains the name of an action or outcome. The outcome's 244 * result (such as "OK") can be appended without additional line 245 * separators or indentation. 246 */ 247 NAME, 248 249 /** 250 * The line contains verbose output, and may be overwritten. 251 */ 252 VERBOSE, 253 } 254 255 /** 256 * Returns an array containing the lines of the given text. 257 */ 258 private String[] messageToLines(String message) { 259 // pass Integer.MAX_VALUE so split doesn't trim trailing empty strings. 260 return message.split("\r\n|\r|\n", Integer.MAX_VALUE); 261 } 262 263 private String green(String message) { 264 return color ? ("\u001b[32;1m" + message + "\u001b[0m") : message; 265 } 266 267 private String red(String message) { 268 return color ? ("\u001b[31;1m" + message + "\u001b[0m") : message; 269 } 270 271 private String yellow(String message) { 272 return color ? ("\u001b[33;1m" + message + "\u001b[0m") : message; 273 } 274 275 private void eraseCurrentLine() { 276 System.out.print(color ? "\u001b[2K\r" : "\n"); 277 System.out.flush(); 278 } 279} 280