1/*
2 * Copyright (C) 2016 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.voicemail.impl.utils;
18
19import java.io.PrintWriter;
20import java.io.Writer;
21import java.util.Arrays;
22
23/**
24 * Lightweight wrapper around {@link PrintWriter} that automatically indents newlines based on
25 * internal state. It also automatically wraps long lines based on given line length.
26 *
27 * <p>Delays writing indent until first actual write on a newline, enabling indent modification
28 * after newline.
29 */
30public class IndentingPrintWriter extends PrintWriter {
31
32  private final String singleIndent;
33  private final int wrapLength;
34
35  /** Mutable version of current indent */
36  private StringBuilder indentBuilder = new StringBuilder();
37  /** Cache of current {@link #indentBuilder} value */
38  private char[] currentIndent;
39  /** Length of current line being built, excluding any indent */
40  private int currentLength;
41
42  /**
43   * Flag indicating if we're currently sitting on an empty line, and that next write should be
44   * prefixed with the current indent.
45   */
46  private boolean emptyLine = true;
47
48  private char[] singleChar = new char[1];
49
50  public IndentingPrintWriter(Writer writer, String singleIndent) {
51    this(writer, singleIndent, -1);
52  }
53
54  public IndentingPrintWriter(Writer writer, String singleIndent, int wrapLength) {
55    super(writer);
56    this.singleIndent = singleIndent;
57    this.wrapLength = wrapLength;
58  }
59
60  public void increaseIndent() {
61    indentBuilder.append(singleIndent);
62    currentIndent = null;
63  }
64
65  public void decreaseIndent() {
66    indentBuilder.delete(0, singleIndent.length());
67    currentIndent = null;
68  }
69
70  public void printPair(String key, Object value) {
71    print(key + "=" + String.valueOf(value) + " ");
72  }
73
74  public void printPair(String key, Object[] value) {
75    print(key + "=" + Arrays.toString(value) + " ");
76  }
77
78  public void printHexPair(String key, int value) {
79    print(key + "=0x" + Integer.toHexString(value) + " ");
80  }
81
82  @Override
83  public void println() {
84    write('\n');
85  }
86
87  @Override
88  public void write(int c) {
89    singleChar[0] = (char) c;
90    write(singleChar, 0, 1);
91  }
92
93  @Override
94  public void write(String s, int off, int len) {
95    final char[] buf = new char[len];
96    s.getChars(off, len - off, buf, 0);
97    write(buf, 0, len);
98  }
99
100  @Override
101  public void write(char[] buf, int offset, int count) {
102    final int indentLength = indentBuilder.length();
103    final int bufferEnd = offset + count;
104    int lineStart = offset;
105    int lineEnd = offset;
106
107    // March through incoming buffer looking for newlines
108    while (lineEnd < bufferEnd) {
109      char ch = buf[lineEnd++];
110      currentLength++;
111      if (ch == '\n') {
112        maybeWriteIndent();
113        super.write(buf, lineStart, lineEnd - lineStart);
114        lineStart = lineEnd;
115        emptyLine = true;
116        currentLength = 0;
117      }
118
119      // Wrap if we've pushed beyond line length
120      if (wrapLength > 0 && currentLength >= wrapLength - indentLength) {
121        if (!emptyLine) {
122          // Give ourselves a fresh line to work with
123          super.write('\n');
124          emptyLine = true;
125          currentLength = lineEnd - lineStart;
126        } else {
127          // We need more than a dedicated line, slice it hard
128          maybeWriteIndent();
129          super.write(buf, lineStart, lineEnd - lineStart);
130          super.write('\n');
131          emptyLine = true;
132          lineStart = lineEnd;
133          currentLength = 0;
134        }
135      }
136    }
137
138    if (lineStart != lineEnd) {
139      maybeWriteIndent();
140      super.write(buf, lineStart, lineEnd - lineStart);
141    }
142  }
143
144  private void maybeWriteIndent() {
145    if (emptyLine) {
146      emptyLine = false;
147      if (indentBuilder.length() != 0) {
148        if (currentIndent == null) {
149          currentIndent = indentBuilder.toString().toCharArray();
150        }
151        super.write(currentIndent, 0, currentIndent.length);
152      }
153    }
154  }
155}
156