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