/** * Copyright 2011-2017 Asakusa Framework Team. * * 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.asakusafw.utils.java.internal.model.util; import java.io.PrintWriter; import java.util.Iterator; import java.util.List; import java.util.Map.Entry; import java.util.SortedMap; import java.util.TreeMap; /** * An implementation of {@link EmitContext} for writing into {@link PrintWriter}. */ public class PrintEmitContext implements EmitContext { private static final String INDENT = " "; //$NON-NLS-1$ private final PrintWriter writer; private State state; private int indentation; private boolean inDocComment; private boolean inComment; private int column; private int bodyColumn; private final SortedMap<Integer, String> commentPool; /** * Creates a new instance. * @param writer the target writer * @throws IllegalArgumentException if the parameter is {@code null} */ public PrintEmitContext(PrintWriter writer) { if (writer == null) { throw new IllegalArgumentException("writer must not be null"); //$NON-NLS-1$ } this.writer = writer; this.state = State.INIT; this.indentation = 0; this.inDocComment = false; this.column = 0; this.bodyColumn = 0; this.commentPool = new TreeMap<>(); } @Override public void flushComments() { flushComments(commentPool); } @Override public void flushComments(int location) { SortedMap<Integer, String> head = commentPool.headMap(location); flushComments(head); } private void flushComments(SortedMap<Integer, String> comments) { assert comments != null; if (comments.isEmpty()) { return; } inComment = true; Iterator<Entry<Integer, String>> iter = comments.entrySet().iterator(); while (iter.hasNext()) { Entry<Integer, String> next = iter.next(); before(State.BLOCK_COMMENT); putToken(next.getValue()); iter.remove(); } inComment = false; } @Override public void keyword(String keyword) { if (keyword == null) { throw new IllegalArgumentException("keyword must not be null"); //$NON-NLS-1$ } before(State.KEYWORD); putToken(keyword); } @Override public void symbol(String symbol) { if (symbol == null) { throw new IllegalArgumentException("symbol must not be null"); //$NON-NLS-1$ } before(State.SYMBOL); putToken(symbol); } @Override public void immediate(String immediate) { if (immediate == null) { throw new IllegalArgumentException("immediate must not be null"); //$NON-NLS-1$ } before(State.IMMEDIATE); putToken(immediate); } @Override public void operator(String symbol) { if (symbol == null) { throw new IllegalArgumentException("symbol must not be null"); //$NON-NLS-1$ } before(State.OPERATOR); putToken(symbol); } @Override public void separator(String symbol) { if (symbol == null) { throw new IllegalArgumentException("symbol must not be null"); //$NON-NLS-1$ } before(State.SEPARATOR); putToken(symbol); } @Override public void padding() { before(State.PADDING); } @Override public void blockPadding() { before(State.LINE_END); } @Override public void comment(int location, String content) { if (content == null) { throw new IllegalArgumentException("content must not be null"); //$NON-NLS-1$ } commentPool.put(location, content); } @Override public void classBlock(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } genericBlock(direction); } @Override public void arrayInitializerBlock(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } if (direction == EmitDirection.BEGIN) { before(State.SYMBOL); putToken("{"); //$NON-NLS-1$ push(); } else { pop(); before(State.SEPARATOR); putToken("}"); //$NON-NLS-1$ } } @Override public void statementBlock(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } genericBlock(direction); } @Override public void switchLabel(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } if (direction == EmitDirection.BEGIN) { push(); } else { pop(); } } @Override public void statement(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } if (direction == EmitDirection.END) { before(State.LINE_END); } } @Override public void declaration(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } if (direction == EmitDirection.END) { before(State.LINE_END); } } @Override public void docComment(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } if (direction == EmitDirection.BEGIN) { before(State.LINE_END); before(State.BLOCK_START); putToken("/**"); //$NON-NLS-1$ this.inDocComment = true; } else { this.inDocComment = false; before(State.BLOCK_END); putToken(" */"); //$NON-NLS-1$ } } @Override public void docBlock(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } statement(direction); } @Override public void docInlineBlock(EmitDirection direction) { if (direction == null) { throw new IllegalArgumentException("direction must not be null"); //$NON-NLS-1$ } if (direction == EmitDirection.BEGIN) { before(State.SYMBOL); putToken("{"); //$NON-NLS-1$ } else { before(State.SYMBOL); putToken("}"); //$NON-NLS-1$ } } @Override public void putBlockComment(List<String> contents) { if (contents == null) { throw new IllegalArgumentException("contents must not be null"); //$NON-NLS-1$ } before(State.BLOCK_COMMENT); inComment = true; putToken("/*"); //$NON-NLS-1$ before(State.LINE_END); for (String line : contents) { before(State.BLOCK_COMMENT); putToken(" * "); //$NON-NLS-1$ putToken(line); before(State.LINE_END); } before(State.BLOCK_COMMENT); putToken(" */"); //$NON-NLS-1$ inComment = false; before(State.LINE_END); } @Override public void putLineComment(String content) { if (content == null) { throw new IllegalArgumentException("content must not be null"); //$NON-NLS-1$ } before(State.BLOCK_COMMENT); inComment = true; putToken("// "); //$NON-NLS-1$ putToken(content); inComment = false; before(State.LINE_END); } @Override public void putInlineComment(String content) { if (content == null) { throw new IllegalArgumentException("content must not be null"); //$NON-NLS-1$ } before(State.INLINE_COMMENT); inComment = true; putToken("/* "); //$NON-NLS-1$ putToken(content); putToken(" */"); //$NON-NLS-1$ inComment = false; before(State.SEPARATOR); } private void genericBlock(EmitDirection direction) { assert direction != null; if (direction == EmitDirection.BEGIN) { before(State.BLOCK_START); putToken("{"); //$NON-NLS-1$ push(); } else { pop(); before(State.BLOCK_END); putToken("}"); //$NON-NLS-1$ } } private void before(State next) { assert next != null; State prev = state; state = next; switch (prev) { case INIT: { if (next == State.LINE_END) { state = State.INIT; } break; } case IMMEDIATE: case KEYWORD: { if (next != State.PADDING && next != State.SYMBOL && next != State.SEPARATOR && next != State.LINE_END) { putPadding(); } break; } case OPERATOR: { if (next != State.PADDING && next != State.SEPARATOR && next != State.LINE_END) { putPadding(); } break; } case SEPARATOR: { if (next != State.PADDING && next != State.SYMBOL && next != State.SEPARATOR && next != State.LINE_END) { putPadding(); } break; } case SYMBOL: { // do nothing break; } case PADDING: { if (next != State.PADDING) { putPadding(); } break; } case BLOCK_START: { if (next != State.LINE_END) { putLineBreak(); } break; } case BLOCK_END: { if (next == State.SEPARATOR) { state = State.LINE_END; } else if (next == State.BLOCK_START) { state = State.LINE_END; } else if (next != State.LINE_END) { putLineBreak(); } break; } case BLOCK_COMMENT: { if (next != State.LINE_END) { putLineBreak(); } break; } case INLINE_COMMENT: { if (next != State.LINE_END) { putPadding(); } break; } case LINE_END: { if (next != State.LINE_END) { putLineBreak(); } break; } default: throw new AssertionError(prev); } } private void push() { indentation++; } private void pop() { indentation--; } private void putToken(String token) { assert token != null; int length = token.length(); if (inComment == false && column + length > 120 && bodyColumn + length > 80) { putLineBreak(true); } writer.print(token); column += length; bodyColumn += length; } private void putPadding() { writer.print(" "); //$NON-NLS-1$ column += 1; bodyColumn += 1; } private void putLineBreak() { putLineBreak(false); } private void putLineBreak(boolean wrap) { writer.println(); column = 0; for (int i = 0; i < indentation; i++) { writer.print(INDENT); column += INDENT.length(); } if (wrap) { writer.print(INDENT); writer.print(INDENT); column += INDENT.length() * 2; } if (inDocComment) { writer.print(" * "); //$NON-NLS-1$ column += 3; bodyColumn += 3; } } private enum State { /** * The initial state. */ INIT, /** * After emit symbols. */ SYMBOL, /** * After emit immediate. */ IMMEDIATE, /** * After emit keywords. */ KEYWORD, /** * After emit operators. */ OPERATOR, /** * After emit separators. */ SEPARATOR, /** * After emit paddings. */ PADDING, /** * After emit block begin symbols. */ BLOCK_START, /** * After emit block end symbols. */ BLOCK_END, /** * After emit block comments. */ BLOCK_COMMENT, /** * After emit inline comments. */ INLINE_COMMENT, /** * End of line (before line break). */ LINE_END, } }