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 public void printHexPair(String key, int value) { 72 print(key + "=0x" + Integer.toHexString(value) + " "); 73 } 74 75 @Override 76 public void write(char[] buf, int offset, int count) { 77 final int indentLength = mIndentBuilder.length(); 78 final int bufferEnd = offset + count; 79 int lineStart = offset; 80 int lineEnd = offset; 81 82 // March through incoming buffer looking for newlines 83 while (lineEnd < bufferEnd) { 84 char ch = buf[lineEnd++]; 85 mCurrentLength++; 86 if (ch == '\n') { 87 maybeWriteIndent(); 88 super.write(buf, lineStart, lineEnd - lineStart); 89 lineStart = lineEnd; 90 mEmptyLine = true; 91 mCurrentLength = 0; 92 } 93 94 // Wrap if we've pushed beyond line length 95 if (mWrapLength > 0 && mCurrentLength >= mWrapLength - indentLength) { 96 if (!mEmptyLine) { 97 // Give ourselves a fresh line to work with 98 super.write('\n'); 99 mEmptyLine = true; 100 mCurrentLength = lineEnd - lineStart; 101 } else { 102 // We need more than a dedicated line, slice it hard 103 maybeWriteIndent(); 104 super.write(buf, lineStart, lineEnd - lineStart); 105 super.write('\n'); 106 mEmptyLine = true; 107 lineStart = lineEnd; 108 mCurrentLength = 0; 109 } 110 } 111 } 112 113 if (lineStart != lineEnd) { 114 maybeWriteIndent(); 115 super.write(buf, lineStart, lineEnd - lineStart); 116 } 117 } 118 119 private void maybeWriteIndent() { 120 if (mEmptyLine) { 121 mEmptyLine = false; 122 if (mIndentBuilder.length() != 0) { 123 if (mCurrentIndent == null) { 124 mCurrentIndent = mIndentBuilder.toString().toCharArray(); 125 } 126 super.write(mCurrentIndent, 0, mCurrentIndent.length); 127 } 128 } 129 } 130 } 131