From 4a0141fb4be65312fa9629b51059cb7b3db0cfb9 Mon Sep 17 00:00:00 2001 From: Gunnar Morling Date: Sun, 18 Aug 2013 00:12:29 +0200 Subject: [PATCH] #64 Adding simple Writer which correctly indents generated code --- .../writer/IndentationCorrectingWriter.java | 137 ++++++++++++++++++ .../org/mapstruct/ap/writer/ModelWriter.java | 4 +- 2 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 processor/src/main/java/org/mapstruct/ap/writer/IndentationCorrectingWriter.java diff --git a/processor/src/main/java/org/mapstruct/ap/writer/IndentationCorrectingWriter.java b/processor/src/main/java/org/mapstruct/ap/writer/IndentationCorrectingWriter.java new file mode 100644 index 000000000..8d0c77449 --- /dev/null +++ b/processor/src/main/java/org/mapstruct/ap/writer/IndentationCorrectingWriter.java @@ -0,0 +1,137 @@ +/** + * Copyright 2012-2013 Gunnar Morling (http://www.gunnarmorling.de/) + * and/or other contributors as indicated by the @authors tag. See the + * copyright.txt file in the distribution for a full listing of all + * contributors. + * + * 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 org.mapstruct.ap.writer; + +import java.io.IOException; +import java.io.Writer; +import java.util.Arrays; + +/** + * EXPERIMENTAL: A writer used to write processed templates which corrects line indentation based on the nesting level + * as implied by (normal and curly) braces. + *

+ * This writer discards any leading whitespace characters following to a line break character. When the first + * non-whitespace character is written after a line break, the correct indentation characters are added, which is four + * whitespace characters per indentation level. + *

+ * This is a very basic implementation which does not take into account comments, escaping etc. + * + * @author Gunnar Morling + */ +class IndentationCorrectingWriter extends Writer { + + private enum State { + IN_TEXT, AFTER_LINE_BREAK; + + State handleCharacter(char c) { + if ( this == State.IN_TEXT ) { + if ( c == '\n' || c == '\r' ) { + return State.AFTER_LINE_BREAK; + } + else { + return State.IN_TEXT; + } + } + else if ( this == State.AFTER_LINE_BREAK ) { + if ( c == ' ' ) { + return State.AFTER_LINE_BREAK; + } + else if ( c == '\n' || c == '\r' ) { + return State.AFTER_LINE_BREAK; + } + else { + return State.IN_TEXT; + } + } + + throw new IllegalStateException( "Unexpected state or character." ); + } + } + + private final Writer delegate; + private State state = State.IN_TEXT; + + /** + * Keeps track of the current indentation level, as implied by brace characters. + */ + private int indentationLevel = 0; + + IndentationCorrectingWriter(Writer out) { + super( out ); + this.delegate = out; + } + + @Override + public void write(char[] cbuf, int off, int len) throws IOException { + int start = off; + int length = 0; + + for ( int i = off; i < len; i++ ) { + char c = cbuf[i]; + if ( c == '{' || c == '(' ) { + indentationLevel++; + } + else if ( c == '}' || c == ')' ) { + indentationLevel--; + } + + State newState = state.handleCharacter( c ); + length++; + + //write characters up to line-breaks + if ( state == State.IN_TEXT && newState == State.AFTER_LINE_BREAK ) { + delegate.write( cbuf, start, length ); + } + //first non-whitespace character after a line break; write out the correct indentation, discarding any + //original leading whitespace characters + else if ( state == State.AFTER_LINE_BREAK && newState == State.IN_TEXT ) { + char[] indentation = getIndentation( indentationLevel ); + delegate.write( indentation, 0, indentation.length ); + start = i; + length = 1; + } + //write out line-breaks following directly to other line breaks + else if ( state == State.AFTER_LINE_BREAK && ( c == '\n' || c == '\r' ) ) { + delegate.write( c ); + } + + state = newState; + } + + if ( state == State.IN_TEXT ) { + delegate.write( cbuf, start, length ); + } + } + + private static char[] getIndentation(int indentationLevel) { + char[] indentation = new char[indentationLevel * 4]; + Arrays.fill( indentation, ' ' ); + return indentation; + } + + @Override + public void flush() throws IOException { + delegate.flush(); + } + + @Override + public void close() throws IOException { + delegate.close(); + } +} diff --git a/processor/src/main/java/org/mapstruct/ap/writer/ModelWriter.java b/processor/src/main/java/org/mapstruct/ap/writer/ModelWriter.java index 6c59a9cf6..024d3a2e8 100644 --- a/processor/src/main/java/org/mapstruct/ap/writer/ModelWriter.java +++ b/processor/src/main/java/org/mapstruct/ap/writer/ModelWriter.java @@ -62,7 +62,7 @@ public class ModelWriter { public void writeModel(JavaFileObject sourceFile, ModelElement model) { try { - BufferedWriter writer = new BufferedWriter( sourceFile.openWriter() ); + BufferedWriter writer = new BufferedWriter( new IndentationCorrectingWriter( sourceFile.openWriter() ) ); Map, Object> values = new HashMap, Object>(); values.put( Configuration.class, CONFIGURATION ); @@ -87,7 +87,7 @@ public class ModelWriter { */ static class DefaultModelElementWriterContext implements Context { - private Map, Object> values; + private final Map, Object> values; DefaultModelElementWriterContext(Map, Object> values) { this.values = new HashMap, Object>( values );