1373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver/*
2373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Copyright 2013, Google Inc.
3373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * All rights reserved.
4373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver *
5373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Redistribution and use in source and binary forms, with or without
6373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * modification, are permitted provided that the following conditions are
7373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * met:
8373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver *
9373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver *     * Redistributions of source code must retain the above copyright
10373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * notice, this list of conditions and the following disclaimer.
11373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver *     * Redistributions in binary form must reproduce the above
12373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * copyright notice, this list of conditions and the following disclaimer
13373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * in the documentation and/or other materials provided with the
14373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * distribution.
15373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver *     * Neither the name of Google Inc. nor the names of its
16373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * contributors may be used to endorse or promote products derived from
17373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * this software without specific prior written permission.
18373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver *
19373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
20373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
21373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
22373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
23373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
24373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
25373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
26373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
27373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
28373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
29373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
30373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */
31373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
32373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverpackage org.jf.util;
33373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
34373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport java.io.FilterWriter;
35373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport java.io.IOException;
36373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverimport java.io.Writer;
37373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
38373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver/**
39373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * Writer that wraps another writer and passes width-limited and
40373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * optionally-prefixed output to its subordinate. When lines are
41373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * wrapped they are automatically indented based on the start of the
42373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver * line.
43373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver */
44373ff22ec69bb6e93646994347b6d80502be1588Ben Gruverpublic final class WrappedIndentingWriter extends FilterWriter {
45373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** null-ok; optional prefix for every line */
46373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private final String prefix;
47373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
48373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** > 0; the maximum output width */
49373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private final int width;
50373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
51373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** > 0; the maximum indent */
52373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private final int maxIndent;
53373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
54373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** >= 0; current output column (zero-based) */
55373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private int column;
56373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
57373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** whether indent spaces are currently being collected */
58373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private boolean collectingIndent;
59373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
60373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** >= 0; current indent amount */
61373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private int indent;
62373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
63373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /**
64373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * Constructs an instance.
65373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     *
66373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * @param out non-null; writer to send final output to
67373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * @param width >= 0; the maximum output width (not including
68373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * <code>prefix</code>), or <code>0</code> for no maximum
69373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * @param prefix non-null; the prefix for each line
70373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     */
71373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    public WrappedIndentingWriter(Writer out, int width, String prefix) {
72373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        super(out);
73373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
74373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        if (out == null) {
75373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            throw new NullPointerException("out == null");
76373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        }
77373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
78373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        if (width < 0) {
79373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            throw new IllegalArgumentException("width < 0");
80373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        }
81373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
82373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        if (prefix == null) {
83373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            throw new NullPointerException("prefix == null");
84373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        }
85373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
86373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        this.width = (width != 0) ? width : Integer.MAX_VALUE;
87373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        this.maxIndent = width >> 1;
88373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        this.prefix = (prefix.length() == 0) ? null : prefix;
89373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
90373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        bol();
91373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    }
92373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
93373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /**
94373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * Constructs a no-prefix instance.
95373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     *
96373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * @param out non-null; writer to send final output to
97373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * @param width &gt;= 0; the maximum output width (not including
98373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * <code>prefix</code>), or <code>0</code> for no maximum
99373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     */
100373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    public WrappedIndentingWriter(Writer out, int width) {
101373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        this(out, width, "");
102373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    }
103373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
104373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** {@inheritDoc} */
105373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    @Override
106373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    public void write(int c) throws IOException {
107373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        synchronized (lock) {
108373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            if (collectingIndent) {
109373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                if (c == ' ') {
110373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    indent++;
111373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    if (indent >= maxIndent) {
112373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                        indent = maxIndent;
113373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                        collectingIndent = false;
114373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    }
115373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                } else {
116373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    collectingIndent = false;
117373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                }
118373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            }
119373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
120373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            if ((column == width) && (c != '\n')) {
121373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                out.write('\n');
122373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                column = 0;
123373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                /*
124373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                 * Note: No else, so this should fall through to the next
125373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                 * if statement.
126373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                 */
127373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            }
128373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
129373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            if (column == 0) {
130373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                if (prefix != null) {
131373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    out.write(prefix);
132373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                }
133373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
134373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                if (!collectingIndent) {
135373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    for (int i = 0; i < indent; i++) {
136373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                        out.write(' ');
137373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    }
138373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                    column = indent;
139373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                }
140373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            }
141373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
142373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            out.write(c);
143373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
144373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            if (c == '\n') {
145373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                bol();
146373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            } else {
147373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                column++;
148373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            }
149373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        }
150373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    }
151373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
152373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** {@inheritDoc} */
153373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    @Override
154373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    public void write(char[] cbuf, int off, int len) throws IOException {
155373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        synchronized (lock) {
156373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            while (len > 0) {
157373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                write(cbuf[off]);
158373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                off++;
159373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                len--;
160373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            }
161373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        }
162373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    }
163373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
164373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /** {@inheritDoc} */
165373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    @Override
166373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    public void write(String str, int off, int len) throws IOException {
167373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        synchronized (lock) {
168373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            while (len > 0) {
169373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                write(str.charAt(off));
170373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                off++;
171373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver                len--;
172373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver            }
173373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        }
174373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    }
175373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver
176373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    /**
177373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     * Indicates that output is at the beginning of a line.
178373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver     */
179373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    private void bol() {
180373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        column = 0;
181373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        collectingIndent = (maxIndent != 0);
182373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver        indent = 0;
183373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver    }
184373ff22ec69bb6e93646994347b6d80502be1588Ben Gruver}
185