IndentingPrintWriter.java revision 328ebf222167ee1d25a54fd34c8293e183303752
1a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley/*
2a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * Copyright (C) 2012 The Android Open Source Project
3a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley *
4a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * Licensed under the Apache License, Version 2.0 (the "License");
5a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * you may not use this file except in compliance with the License.
6a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * You may obtain a copy of the License at
7a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley *
8a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley *      http://www.apache.org/licenses/LICENSE-2.0
9a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley *
10a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * Unless required by applicable law or agreed to in writing, software
11a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * distributed under the License is distributed on an "AS IS" BASIS,
12a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * See the License for the specific language governing permissions and
14a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * limitations under the License.
15a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley */
16a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
17a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wileypackage com.android.internal.util;
18a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
19a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wileyimport java.io.PrintWriter;
20a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wileyimport java.io.Writer;
21a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
22a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley/**
2386105fb24919eca17e71a5ec820d75ac57bbdc65Christopher Wiley * Lightweight wrapper around {@link PrintWriter} that automatically indents
24a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * newlines based on internal state. It also automatically wraps long lines
25a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * based on given line length.
26a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley * <p>
27217f61b2e8d2beed2fe7fa1124e1d4f52a042ab8Alex Vakulenko * Delays writing indent until first actual write on a newline, enabling indent
28217f61b2e8d2beed2fe7fa1124e1d4f52a042ab8Alex Vakulenko * modification after newline.
29a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley */
30a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wileypublic class IndentingPrintWriter extends PrintWriter {
31af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    private final String mSingleIndent;
32a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    private final int mWrapLength;
33a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
34af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    /** Mutable version of current indent */
35a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    private StringBuilder mIndentBuilder = new StringBuilder();
36a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    /** Cache of current {@link #mIndentBuilder} value */
37af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    private char[] mCurrentIndent;
38a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    /** Length of current line being built, excluding any indent */
39a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    private int mCurrentLength;
40a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
41af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    /**
42a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley     * Flag indicating if we're currently sitting on an empty line, and that
43a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley     * next write should be prefixed with the current indent.
44af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley     */
45a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    private boolean mEmptyLine = true;
46af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
47af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    public IndentingPrintWriter(Writer writer, String singleIndent) {
48af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        this(writer, singleIndent, -1);
49af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
50af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
51a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    public IndentingPrintWriter(Writer writer, String singleIndent, int wrapLength) {
52a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley        super(writer);
53af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        mSingleIndent = singleIndent;
54af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        mWrapLength = wrapLength;
55af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
56af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
57a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    public void increaseIndent() {
58af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        mIndentBuilder.append(mSingleIndent);
59af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        mCurrentIndent = null;
60a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    }
61af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
62a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    public void decreaseIndent() {
63af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        mIndentBuilder.delete(0, mSingleIndent.length());
64a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley        mCurrentIndent = null;
65af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
66a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
67a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley    public void printPair(String key, Object value) {
68af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        print(key + "=" + String.valueOf(value) + " ");
69af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
70af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
71af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    public void printHexPair(String key, int value) {
72af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        print(key + "=0x" + Integer.toHexString(value) + " ");
73af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
74af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
75af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    @Override
76af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    public void write(char[] buf, int offset, int count) {
77af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        final int indentLength = mIndentBuilder.length();
78af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        final int bufferEnd = offset + count;
79af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        int lineStart = offset;
80af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        int lineEnd = offset;
8186105fb24919eca17e71a5ec820d75ac57bbdc65Christopher Wiley
82af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        // March through incoming buffer looking for newlines
83a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley        while (lineEnd < bufferEnd) {
84a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley            char ch = buf[lineEnd++];
85a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley            mCurrentLength++;
86af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            if (ch == '\n') {
87af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                maybeWriteIndent();
88af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                super.write(buf, lineStart, lineEnd - lineStart);
89af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                lineStart = lineEnd;
90af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                mEmptyLine = true;
91af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                mCurrentLength = 0;
92af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            }
93af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley
9486105fb24919eca17e71a5ec820d75ac57bbdc65Christopher Wiley            // Wrap if we've pushed beyond line length
95af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            if (mWrapLength > 0 && mCurrentLength >= mWrapLength - indentLength) {
96af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                if (!mEmptyLine) {
97af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    // Give ourselves a fresh line to work with
98af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    super.write('\n');
9986105fb24919eca17e71a5ec820d75ac57bbdc65Christopher Wiley                    mEmptyLine = true;
100af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    mCurrentLength = lineEnd - lineStart;
101af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                } else {
102af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    // We need more than a dedicated line, slice it hard
103af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    maybeWriteIndent();
104a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley                    super.write(buf, lineStart, lineEnd - lineStart);
105a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley                    super.write('\n');
106a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley                    mEmptyLine = true;
107a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley                    lineStart = lineEnd;
108af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    mCurrentLength = 0;
109a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley                }
110af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            }
111af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        }
112a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
113a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley        if (lineStart != lineEnd) {
114af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            maybeWriteIndent();
115af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            super.write(buf, lineStart, lineEnd - lineStart);
116af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        }
117af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
118a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley
119af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    private void maybeWriteIndent() {
120af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley        if (mEmptyLine) {
121af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            mEmptyLine = false;
122af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley            if (mIndentBuilder.length() != 0) {
123af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                if (mCurrentIndent == null) {
124af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                    mCurrentIndent = mIndentBuilder.toString().toCharArray();
125af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley                }
12686105fb24919eca17e71a5ec820d75ac57bbdc65Christopher Wiley                super.write(mCurrentIndent, 0, mCurrentIndent.length);
127a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley            }
128a591506fbf1445877fc2f97bca1e00b51ccc3a85Christopher Wiley        }
129af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley    }
130af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley}
131af52902e4746aab2bd667ac9bdeb1bbb3e46b539Christopher Wiley