1/*
2 * Copyright (C) 2011 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 com.android.tools.lint;
18
19import com.android.tools.lint.detector.api.Location;
20import com.android.tools.lint.detector.api.Position;
21import com.android.tools.lint.detector.api.Severity;
22import com.google.common.annotations.Beta;
23
24import java.io.IOException;
25import java.io.Writer;
26import java.util.List;
27
28/**
29 * A reporter which emits lint warnings as plain text strings
30 * <p>
31 * <b>NOTE: This is not a public or final API; if you rely on this be prepared
32 * to adjust your code for the next tools release.</b>
33 */
34@Beta
35public class TextReporter extends Reporter {
36    private final Writer mWriter;
37    private final boolean mClose;
38
39    /**
40     * Constructs a new {@link TextReporter}
41     *
42     * @param client the client
43     * @param writer the writer to write into
44     * @param close whether the writer should be closed when done
45     */
46    public TextReporter(Main client, Writer writer, boolean close) {
47        super(client, null);
48        mWriter = writer;
49        mClose = close;
50    }
51
52    @Override
53    public void write(int errorCount, int warningCount, List<Warning> issues) throws IOException {
54        boolean abbreviate = mClient.getDriver().isAbbreviating();
55
56        StringBuilder output = new StringBuilder(issues.size() * 200);
57        if (issues.size() == 0) {
58            mWriter.write('\n');
59            mWriter.write("No issues found.");
60            mWriter.write('\n');
61            mWriter.flush();
62        } else {
63            for (Warning warning : issues) {
64                int startLength = output.length();
65
66                if (warning.path != null) {
67                    output.append(warning.path);
68                    output.append(':');
69
70                    if (warning.line >= 0) {
71                        output.append(Integer.toString(warning.line + 1));
72                        output.append(':');
73                    }
74                    if (startLength < output.length()) {
75                        output.append(' ');
76                    }
77                }
78
79                Severity severity = warning.severity;
80                if (severity == Severity.FATAL) {
81                    // Treat the fatal error as an error such that we don't display
82                    // both "Fatal:" and "Error:" etc in the error output.
83                    severity = Severity.ERROR;
84                }
85                output.append(severity.getDescription());
86                output.append(':');
87                output.append(' ');
88
89                output.append(warning.message);
90                if (warning.issue != null) {
91                    output.append(' ').append('[');
92                    output.append(warning.issue.getId());
93                    output.append(']');
94                }
95
96                output.append('\n');
97
98                if (warning.errorLine != null && warning.errorLine.length() > 0) {
99                    output.append(warning.errorLine);
100                }
101
102                if (warning.location != null && warning.location.getSecondary() != null) {
103                    Location location = warning.location.getSecondary();
104                    while (location != null) {
105                        if (location.getMessage() != null
106                                && location.getMessage().length() > 0) {
107                            output.append("    "); //$NON-NLS-1$
108                            String path = mClient.getDisplayPath(warning.project,
109                                    location.getFile());
110                            output.append(path);
111
112                            Position start = location.getStart();
113                            if (start != null) {
114                                int line = start.getLine();
115                                if (line >= 0) {
116                                    output.append(':');
117                                    output.append(Integer.toString(line + 1));
118                                }
119                            }
120
121                            if (location.getMessage() != null
122                                    && location.getMessage().length() > 0) {
123                                output.append(':');
124                                output.append(' ');
125                                output.append(location.getMessage());
126                            }
127
128                            output.append('\n');
129                        }
130
131                        location = location.getSecondary();
132                    }
133
134                    if (!abbreviate) {
135                        location = warning.location.getSecondary();
136                        StringBuilder sb = new StringBuilder();
137                        sb.append("Also affects: ");
138                        int begin = sb.length();
139                        while (location != null) {
140                            if (location.getMessage() == null
141                                    || location.getMessage().length() > 0) {
142                                if (sb.length() > begin) {
143                                    sb.append(", ");
144                                }
145
146                                String path = mClient.getDisplayPath(warning.project,
147                                        location.getFile());
148                                sb.append(path);
149
150                                Position start = location.getStart();
151                                if (start != null) {
152                                    int line = start.getLine();
153                                    if (line >= 0) {
154                                        sb.append(':');
155                                        sb.append(Integer.toString(line + 1));
156                                    }
157                                }
158                            }
159
160                            location = location.getSecondary();
161                        }
162                        String wrapped = Main.wrap(sb.toString(), Main.MAX_LINE_WIDTH, "     "); //$NON-NLS-1$
163                        output.append(wrapped);
164                    }
165                }
166            }
167
168            mWriter.write(output.toString());
169
170            mWriter.write(String.format("%1$d errors, %2$d warnings",
171                    errorCount, warningCount));
172            mWriter.write('\n');
173            mWriter.flush();
174            if (mClose) {
175                mWriter.close();
176            }
177        }
178    }
179}