1f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/*
2f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Copyright (C) 2007 The Android Open Source Project
3f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
4f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Licensed under the Apache License, Version 2.0 (the "License");
5f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * you may not use this file except in compliance with the License.
6f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * You may obtain a copy of the License at
7f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
8f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *      http://www.apache.org/licenses/LICENSE-2.0
9f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project *
10f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Unless required by applicable law or agreed to in writing, software
11f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS,
12f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * See the License for the specific language governing permissions and
14f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * limitations under the License.
15f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
16f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
17f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectpackage com.android.dx.util;
18f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
19f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.FilterWriter;
20f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.IOException;
21f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectimport java.io.Writer;
22f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
23f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project/**
24f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * Writer that wraps another writer and passes width-limited and
25f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * optionally-prefixed output to its subordinate. When lines are
26f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * wrapped they are automatically indented based on the start of the
27f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project * line.
28f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project */
29f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Projectpublic final class IndentingWriter extends FilterWriter {
3099409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project    /** {@code null-ok;} optional prefix for every line */
31f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private final String prefix;
32f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
3399409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project    /** {@code > 0;} the maximum output width */
34f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private final int width;
35f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
3699409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project    /** {@code > 0;} the maximum indent */
37f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private final int maxIndent;
38f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
3999409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project    /** {@code >= 0;} current output column (zero-based) */
40f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private int column;
41f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
42f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** whether indent spaces are currently being collected */
43f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private boolean collectingIndent;
44f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
4599409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project    /** {@code >= 0;} current indent amount */
46f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private int indent;
47f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
48f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
49f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Constructs an instance.
50de75089fb7216d19e9c22cce4dc62a49513477d3Carl Shapiro     *
5199409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * @param out {@code non-null;} writer to send final output to
5299409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * @param width {@code >= 0;} the maximum output width (not including
5399409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * {@code prefix}), or {@code 0} for no maximum
5499409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * @param prefix {@code non-null;} the prefix for each line
55f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
56f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public IndentingWriter(Writer out, int width, String prefix) {
57f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        super(out);
58f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
59f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (out == null) {
60f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            throw new NullPointerException("out == null");
61f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
62f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
63f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (width < 0) {
64f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            throw new IllegalArgumentException("width < 0");
65f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
66f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
67f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        if (prefix == null) {
68f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            throw new NullPointerException("prefix == null");
69f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
70f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
71f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        this.width = (width != 0) ? width : Integer.MAX_VALUE;
72f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        this.maxIndent = width >> 1;
73f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        this.prefix = (prefix.length() == 0) ? null : prefix;
74f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
75f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        bol();
76f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
77f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
78f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
79f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Constructs a no-prefix instance.
80de75089fb7216d19e9c22cce4dc62a49513477d3Carl Shapiro     *
8199409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * @param out {@code non-null;} writer to send final output to
8299409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * @param width {@code >= 0;} the maximum output width (not including
8399409883d9c4c0ffb49b070ce307bb33a9dfe9f1The Android Open Source Project     * {@code prefix}), or {@code 0} for no maximum
84f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
85f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public IndentingWriter(Writer out, int width) {
86f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        this(out, width, "");
87f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
88f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
89f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
90f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
91f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public void write(int c) throws IOException {
92f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        synchronized (lock) {
93f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            if (collectingIndent) {
94f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                if (c == ' ') {
95f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    indent++;
96f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    if (indent >= maxIndent) {
97f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                        indent = maxIndent;
98f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                        collectingIndent = false;
99f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    }
100f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                } else {
101f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    collectingIndent = false;
102f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                }
103f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
104f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
105f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            if ((column == width) && (c != '\n')) {
106f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                out.write('\n');
107f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                column = 0;
108f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                /*
109f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                 * Note: No else, so this should fall through to the next
110f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                 * if statement.
111f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                 */
112f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
113f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
114f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            if (column == 0) {
115f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                if (prefix != null) {
116f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    out.write(prefix);
117f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                }
118f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
119f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                if (!collectingIndent) {
120f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    for (int i = 0; i < indent; i++) {
121f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                        out.write(' ');
122f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    }
123f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                    column = indent;
124f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                }
125f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
126f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
127f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            out.write(c);
128f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
129f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            if (c == '\n') {
130f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                bol();
131f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            } else {
132f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                column++;
133f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
134f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
135f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
136f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
137f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
138f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
139f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public void write(char[] cbuf, int off, int len) throws IOException {
140f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        synchronized (lock) {
141f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            while (len > 0) {
142f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                write(cbuf[off]);
143f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                off++;
144f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                len--;
145f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
146f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
147f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
148f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
149f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /** {@inheritDoc} */
150f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    @Override
151f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    public void write(String str, int off, int len) throws IOException {
152f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        synchronized (lock) {
153f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            while (len > 0) {
154f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                write(str.charAt(off));
155f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                off++;
156f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project                len--;
157f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project            }
158f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        }
159f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
160f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project
161f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    /**
162f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     * Indicates that output is at the beginning of a line.
163f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project     */
164f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    private void bol() {
165f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        column = 0;
166f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        collectingIndent = (maxIndent != 0);
167f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project        indent = 0;
168f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project    }
169f6c387128427e121477c1b32ad35cdcaa5101ba3The Android Open Source Project}
170