Console.java revision ba1c2cee7506480a0a239e2d99a944d08fc47be5
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 info(String s) { 66 newLine(); 67 System.out.println(s); 68 } 69 70 public synchronized void info(String message, Throwable throwable) { 71 newLine(); 72 System.out.println(message); 73 throwable.printStackTrace(System.out); 74 } 75 76 public synchronized void action(String name) { 77 newLine(); 78 System.out.print("Action " + name); 79 System.out.flush(); 80 currentName = name; 81 currentLine = CurrentLine.NAME; 82 } 83 84 /** 85 * Prints the beginning of the named outcome. 86 */ 87 public synchronized void outcome(String name) { 88 // if the outcome and action names are the same, omit the outcome name 89 if (name.equals(currentName)) { 90 return; 91 } 92 93 currentName = name; 94 newLine(); 95 System.out.print(indent + name); 96 System.out.flush(); 97 currentLine = CurrentLine.NAME; 98 } 99 100 /** 101 * Appends the action output immediately to the stream when streaming is on, 102 * or to a buffer when streaming is off. Buffered output will be held and 103 * printed only if the outcome is unsuccessful. 104 */ 105 public synchronized void streamOutput(String output) { 106 if (stream) { 107 printOutput(output); 108 } else { 109 bufferedOutput.append(output); 110 } 111 } 112 113 /** 114 * Writes the action's outcome. 115 */ 116 public synchronized void printResult(Result result, ResultValue resultValue) { 117 if (resultValue == ResultValue.OK) { 118 String prefix = (currentLine == CurrentLine.NAME) ? " " : "\n" + indent; 119 System.out.println(prefix + green("OK (" + result + ")")); 120 121 } else if (resultValue == ResultValue.FAIL) { 122 if (bufferedOutput.length() > 0) { 123 printOutput(bufferedOutput.toString()); 124 bufferedOutput.delete(0, bufferedOutput.length()); 125 } 126 127 newLine(); 128 System.out.println(indent + red("FAIL (" + result + ")")); 129 } else if (resultValue == ResultValue.IGNORE) { 130 if (bufferedOutput.length() > 0) { 131 printOutput(bufferedOutput.toString()); 132 bufferedOutput.delete(0, bufferedOutput.length()); 133 } 134 135 newLine(); 136 System.out.println(indent + yellow("SKIP (" + result + ")")); 137 } 138 139 currentName = null; 140 currentLine = CurrentLine.NEW; 141 } 142 143 public synchronized void summarizeFailures(List<String> failureNames) { 144 System.out.println("Failure summary:"); 145 for (String failureName : failureNames) { 146 System.out.println(red(failureName)); 147 } 148 } 149 150 public synchronized void summarizeSkips(List<String> skippedNames) { 151 System.out.println("Skip summary:"); 152 for (String skippedName : skippedNames) { 153 System.out.println(yellow(skippedName)); 154 } 155 } 156 157 /** 158 * Prints the action output with appropriate indentation. 159 */ 160 private void printOutput(String streamedOutput) { 161 if (streamedOutput.length() == 0) { 162 return; 163 } 164 165 String[] lines = messageToLines(streamedOutput); 166 167 if (currentLine != CurrentLine.STREAMED_OUTPUT) { 168 newLine(); 169 System.out.print(indent); 170 System.out.print(indent); 171 } 172 System.out.print(lines[0]); 173 currentLine = CurrentLine.STREAMED_OUTPUT; 174 175 for (int i = 1; i < lines.length; i++) { 176 newLine(); 177 178 if (lines[i].length() > 0) { 179 System.out.print(indent); 180 System.out.print(indent); 181 System.out.print(lines[i]); 182 currentLine = CurrentLine.STREAMED_OUTPUT; 183 } 184 } 185 } 186 187 /** 188 * Inserts a linebreak if necessary. 189 */ 190 private void newLine() { 191 if (currentLine == CurrentLine.NEW) { 192 return; 193 } else if (currentLine == CurrentLine.VERBOSE) { 194 // --verbose means "leave all the verbose output on the screen". 195 if (!verbose) { 196 // Otherwise we overwrite verbose output whenever something new arrives. 197 eraseCurrentLine(); 198 currentLine = CurrentLine.NEW; 199 return; 200 } 201 } 202 203 System.out.println(); 204 currentLine = CurrentLine.NEW; 205 } 206 207 /** 208 * Status of a currently-in-progress line of output. 209 */ 210 enum CurrentLine { 211 212 /** 213 * The line is blank. 214 */ 215 NEW, 216 217 /** 218 * The line contains streamed application output. Additional streamed 219 * output may be appended without additional line separators or 220 * indentation. 221 */ 222 STREAMED_OUTPUT, 223 224 /** 225 * The line contains the name of an action or outcome. The outcome's 226 * result (such as "OK") can be appended without additional line 227 * separators or indentation. 228 */ 229 NAME, 230 231 /** 232 * The line contains verbose output, and may be overwritten. 233 */ 234 VERBOSE, 235 } 236 237 /** 238 * Returns an array containing the lines of the given text. 239 */ 240 private String[] messageToLines(String message) { 241 // pass Integer.MAX_VALUE so split doesn't trim trailing empty strings. 242 return message.split("\r\n|\r|\n", Integer.MAX_VALUE); 243 } 244 245 private String green(String message) { 246 return color ? ("\u001b[32;1m" + message + "\u001b[0m") : message; 247 } 248 249 private String red(String message) { 250 return color ? ("\u001b[31;1m" + message + "\u001b[0m") : message; 251 } 252 253 private String yellow(String message) { 254 return color ? ("\u001b[33;1m" + message + "\u001b[0m") : message; 255 } 256 257 private void eraseCurrentLine() { 258 System.out.print(color ? "\u001b[2K\r" : "\n"); 259 System.out.flush(); 260 } 261} 262