/* * Copyright (c) 2002-2012 Alibaba Group Holding Limited. * All rights reserved. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package com.alibaba.citrus.util.internal; import static com.alibaba.citrus.util.Assert.*; import static com.alibaba.citrus.util.BasicConstant.*; import static com.alibaba.citrus.util.ObjectUtil.*; import static com.alibaba.citrus.util.StringUtil.*; import java.util.ArrayList; /** * 支持分级缩进的string builder。 * * @author Michael Zhou */ public class IndentableStringBuilder extends NormalizableStringBuilder<IndentableStringBuilder> { private final IndentStack indents = new IndentStack(); private final int defaultIndent; private int indentLevel; private int quoteLevel; private boolean lazyAppendNewLine; // 推迟输出换行,推迟到下一个字符被输出前 private boolean lazyStartHangingIndent; // 推迟启动缩进,推迟到下一个换行后或下一个start()。其效果为悬挂缩进 private int hangingIndent; public IndentableStringBuilder() { this(-1); } public IndentableStringBuilder(int indent) { this.defaultIndent = indent <= 0 ? 2 : indent; } @Override public void clear() { super.clear(); indents.clear(); indentLevel = 0; quoteLevel = 0; lazyAppendNewLine = false; lazyStartHangingIndent = false; hangingIndent = 0; } /** 此处收到的字符中,所有 CR/LF/CRLF 均已被规格化成统一的LF了。 */ @Override protected void visit(char c) { boolean newLine = endsWithNewLine(); if (c == LF && lazyStartHangingIndent) { appendInternalNewLine(); doStartHanglingIndentIfRequired(); return; } // 在end quote后追加换行 if (!newLine && lazyAppendNewLine) { appendInternalNewLine(); newLine = true; } // 输出begin quotes for (; quoteLevel < indentLevel; quoteLevel++) { String beginQuote = indents.getBeginQuote(quoteLevel); if (isEmpty(beginQuote)) { if (!newLine && indents.independent(quoteLevel)) { appendInternalNewLine(); newLine = true; } } else { if (newLine) { appendIndent(quoteLevel); } else { if (!endsWith(" ")) { appendInternal(" "); // begin quote前空一格 } } appendInternal(beginQuote); appendInternalNewLine(); newLine = true; } } lazyAppendNewLine = false; // 输出字符 if (c == LF) { appendInternalNewLine(); } else { if (newLine) { appendIndent(indentLevel); } appendInternal(c); } } /** 创建一级缩进。 */ public IndentableStringBuilder start() { return start(null, null, -1); } /** 创建一级缩进。 */ public IndentableStringBuilder start(int indent) { return start(null, null, indent); } /** 创建一级缩进,使用指定的前后括弧。 */ public IndentableStringBuilder start(String beginQuote, String endQuote) { return start(beginQuote, endQuote, -1); } /** 创建一级缩进,使用指定的前后括弧。 */ public IndentableStringBuilder start(String beginQuote, String endQuote, int indent) { doStartHanglingIndentIfRequired(); indents.pushIndent(beginQuote, endQuote, indent); indentLevel++; return this; } /** 从下一个换行或start()开始悬挂缩进。 */ public IndentableStringBuilder startHangingIndent() { return startHangingIndent(0); } /** 从下一个换行或start()开始悬挂缩进。 */ public IndentableStringBuilder startHangingIndent(int indentOffset) { doStartHanglingIndentIfRequired(); lazyStartHangingIndent = true; if (!lazyAppendNewLine && lineLength() - currentIndent() > 0 && quoteLevel >= indentLevel) { hangingIndent = defaultIndent(lineLength() - currentIndent() + indentOffset); } else { hangingIndent = defaultIndent(indentOffset); } return this; } /** 确保悬挂缩进(如果有的话)已经启动。 */ private void doStartHanglingIndentIfRequired() { if (lazyStartHangingIndent) { lazyStartHangingIndent = false; start(EMPTY_STRING, EMPTY_STRING, hangingIndent); } } /** 结束一级缩进。注意,输出结果之前,须至少调用一次end(),以确保最后的换行可以被输出。 */ public IndentableStringBuilder end() { flush(); // 结束未发生的悬挂缩进 if (lazyStartHangingIndent) { if (!endsWithNewLine()) { lazyAppendNewLine = true; } lazyStartHangingIndent = false; return this; } // 对于刚开始就结束的,不输出end quote if (indentLevel > quoteLevel) { indentLevel--; } else { assertTrue(indentLevel == quoteLevel, "indentLevel != quoteLevel"); if (indentLevel > 0) { indentLevel--; quoteLevel--; String endQuote = indents.getEndQuote(indentLevel); if (!isEmpty(endQuote)) { // 确保end quote之前换行 if (!endsWithNewLine()) { appendInternalNewLine(); } // 输出end quote appendIndent(indentLevel); appendInternal(endQuote); } lazyAppendNewLine = true; } } indents.popIndent(); return this; } /** 取得当前缩进的数量。 */ public int currentIndent() { return indents.getCurrentIndent(); } /** 如果indent未指定,则取得默认indent。 */ private int defaultIndent(int indent) { return indent <= 0 ? defaultIndent : indent; } private void appendIndent(int indentLevel) { int indent = indents.getIndent(indentLevel - 1); for (int j = 0; j < indent; j++) { appendInternal(' '); } } /** 存放缩进信息的栈。 */ private class IndentStack extends ArrayList<Object> { private static final long serialVersionUID = -876139304840511103L; private static final int entrySize = 4; public String getBeginQuote(int indentLevel) { if (indentLevel < 0 || indentLevel >= depth()) { return EMPTY_STRING; } return (String) super.get(indentLevel * entrySize); } public String getEndQuote(int indentLevel) { if (indentLevel < 0 || indentLevel >= depth()) { return EMPTY_STRING; } return (String) super.get(indentLevel * entrySize + 1); } public int getIndent(int indentLevel) { if (indentLevel < 0 || indentLevel >= depth()) { return 0; } return (Integer) super.get(indentLevel * entrySize + 2); } /** 如果当前level依附于后一个level,则返回false。 */ public boolean independent(int indentLevel) { if (indentLevel < 0 || indentLevel >= depth() - 1) { return true; } int i1 = (Integer) super.get(indentLevel * entrySize + 3); int i2 = (Integer) super.get((indentLevel + 1) * entrySize + 3); return i1 != i2; } public int getCurrentIndent() { int depth = depth(); if (depth > 0) { return getIndent(depth - 1); } else { return 0; } } public int depth() { return super.size() / entrySize; } public void pushIndent(String beginQuote, String endQuote, int indent) { super.add(defaultIfNull(beginQuote, "{")); super.add(defaultIfNull(endQuote, "}")); super.add(defaultIndent(indent) + getCurrentIndent()); super.add(length()); } public void popIndent() { int length = super.size(); if (length > 0) { for (int i = 0; i < entrySize; i++) { super.remove(--length); } } } } } /** * 将CR/LF/CRLF统一成LF的string builder。 * * @author Michael Zhou */ abstract class NormalizableStringBuilder<B extends NormalizableStringBuilder<B>> implements Appendable { protected final static char CR = '\r'; protected final static char LF = '\n'; private final static char NONE = '\0'; private final StringBuilder out = new StringBuilder(); private final String newLine; private int newLineStartIndex = 0; private char readAheadBuffer = '\0'; public NormalizableStringBuilder() { this(null); } public NormalizableStringBuilder(String newLine) { this.newLine = defaultIfNull(newLine, String.valueOf(LF)); } /** 清除所有内容。 */ public void clear() { out.setLength(0); newLineStartIndex = 0; readAheadBuffer = '\0'; } /** 取得buffer中内容的长度。 */ public final int length() { return out.length(); } /** 取得当前行的长度。 */ public final int lineLength() { return out.length() - newLineStartIndex; } /** <code>Appendable</code>接口方法。 */ public final B append(CharSequence csq) { return append(csq, 0, csq.length()); } /** <code>Appendable</code>接口方法。 */ public final B append(CharSequence csq, int start, int end) { for (int i = start; i < end; i++) { append(csq.charAt(i)); } return thisObject(); } /** <code>Appendable</code>接口方法。 */ public final B append(char c) { // 将 CR|LF|CRLF 转化成统一的 LF switch (readAheadBuffer) { case NONE: switch (c) { case CR: // \r readAheadBuffer = CR; break; case LF: // \n readAheadBuffer = NONE; visit(LF); break; default: readAheadBuffer = NONE; visit(c); break; } break; case CR: switch (c) { case CR: // \r\r readAheadBuffer = CR; visit(LF); break; case LF: // \r\n readAheadBuffer = NONE; visit(LF); break; default: readAheadBuffer = NONE; visit(LF); visit(c); break; } break; default: unreachableCode(); break; } return thisObject(); } /** 子类覆盖此方法,以便接收所有字符。其中,所有 CR/LF/CRLF 均已被规格化成统一的LF了。 */ protected abstract void visit(char c); /** 子类通过此方法向内部buffer中添加内容。 */ protected final void appendInternal(String s) { out.append(s); } /** 子类通过此方法向内部buffer中添加内容。 */ protected final void appendInternal(char c) { out.append(c); } /** * 子类通过此方法向内部buffer中添加换行。 * <p> * 子类必须通过此方法来换行,否则<code>newLineStartIndex</code>会不正确。 * </p> */ protected final void appendInternalNewLine() { out.append(newLine); newLineStartIndex = out.length(); } /** 判断buf是否以指定字符串结尾。 */ public final boolean endsWith(String testStr) { if (testStr == null) { return false; } int testStrLength = testStr.length(); int bufferLength = out.length(); if (bufferLength < testStrLength) { return false; } int baseIndex = bufferLength - testStrLength; for (int i = 0; i < testStrLength; i++) { if (out.charAt(baseIndex + i) != testStr.charAt(i)) { return false; } } return true; } /** 判断out是否以换行结尾,或者是空buffer。 */ public final boolean endsWithNewLine() { return out.length() == 0 || endsWith(newLine); } private B thisObject() { @SuppressWarnings("unchecked") B buf = (B) this; return buf; } /** 确保最后一个换行被输出。 */ public final void flush() { if (readAheadBuffer == CR) { readAheadBuffer = NONE; visit(LF); } } @Override public final String toString() { return out.toString(); } }