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