1 /*
2  * Copyright (C) 2007 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 
17 package com.android.dexgen.util;
18 
19 import java.io.FilterWriter;
20 import java.io.IOException;
21 import java.io.Writer;
22 
23 /**
24  * Writer that wraps another writer and passes width-limited and
25  * optionally-prefixed output to its subordinate. When lines are
26  * wrapped they are automatically indented based on the start of the
27  * line.
28  */
29 public final class IndentingWriter extends FilterWriter {
30     /** {@code null-ok;} optional prefix for every line */
31     private final String prefix;
32 
33     /** {@code > 0;} the maximum output width */
34     private final int width;
35 
36     /** {@code > 0;} the maximum indent */
37     private final int maxIndent;
38 
39     /** {@code >= 0;} current output column (zero-based) */
40     private int column;
41 
42     /** whether indent spaces are currently being collected */
43     private boolean collectingIndent;
44 
45     /** {@code >= 0;} current indent amount */
46     private int indent;
47 
48     /**
49      * Constructs an instance.
50      *
51      * @param out {@code non-null;} writer to send final output to
52      * @param width {@code >= 0;} the maximum output width (not including
53      * {@code prefix}), or {@code 0} for no maximum
54      * @param prefix {@code non-null;} the prefix for each line
55      */
IndentingWriter(Writer out, int width, String prefix)56     public IndentingWriter(Writer out, int width, String prefix) {
57         super(out);
58 
59         if (out == null) {
60             throw new NullPointerException("out == null");
61         }
62 
63         if (width < 0) {
64             throw new IllegalArgumentException("width < 0");
65         }
66 
67         if (prefix == null) {
68             throw new NullPointerException("prefix == null");
69         }
70 
71         this.width = (width != 0) ? width : Integer.MAX_VALUE;
72         this.maxIndent = width >> 1;
73         this.prefix = (prefix.length() == 0) ? null : prefix;
74 
75         bol();
76     }
77 
78     /**
79      * Constructs a no-prefix instance.
80      *
81      * @param out {@code non-null;} writer to send final output to
82      * @param width {@code >= 0;} the maximum output width (not including
83      * {@code prefix}), or {@code 0} for no maximum
84      */
IndentingWriter(Writer out, int width)85     public IndentingWriter(Writer out, int width) {
86         this(out, width, "");
87     }
88 
89     /** {@inheritDoc} */
90     @Override
write(int c)91     public void write(int c) throws IOException {
92         synchronized (lock) {
93             if (collectingIndent) {
94                 if (c == ' ') {
95                     indent++;
96                     if (indent >= maxIndent) {
97                         indent = maxIndent;
98                         collectingIndent = false;
99                     }
100                 } else {
101                     collectingIndent = false;
102                 }
103             }
104 
105             if ((column == width) && (c != '\n')) {
106                 out.write('\n');
107                 column = 0;
108                 /*
109                  * Note: No else, so this should fall through to the next
110                  * if statement.
111                  */
112             }
113 
114             if (column == 0) {
115                 if (prefix != null) {
116                     out.write(prefix);
117                 }
118 
119                 if (!collectingIndent) {
120                     for (int i = 0; i < indent; i++) {
121                         out.write(' ');
122                     }
123                     column = indent;
124                 }
125             }
126 
127             out.write(c);
128 
129             if (c == '\n') {
130                 bol();
131             } else {
132                 column++;
133             }
134         }
135     }
136 
137     /** {@inheritDoc} */
138     @Override
write(char[] cbuf, int off, int len)139     public void write(char[] cbuf, int off, int len) throws IOException {
140         synchronized (lock) {
141             while (len > 0) {
142                 write(cbuf[off]);
143                 off++;
144                 len--;
145             }
146         }
147     }
148 
149     /** {@inheritDoc} */
150     @Override
write(String str, int off, int len)151     public void write(String str, int off, int len) throws IOException {
152         synchronized (lock) {
153             while (len > 0) {
154                 write(str.charAt(off));
155                 off++;
156                 len--;
157             }
158         }
159     }
160 
161     /**
162      * Indicates that output is at the beginning of a line.
163      */
bol()164     private void bol() {
165         column = 0;
166         collectingIndent = (maxIndent != 0);
167         indent = 0;
168     }
169 }
170