#515, introduction of string definition handling in IndentationCorrectingWriter

This commit is contained in:
sjaakd 2015-05-11 20:57:15 +02:00
parent 2ece25a086
commit 124c2a4001
5 changed files with 292 additions and 28 deletions

View File

@ -29,6 +29,16 @@ import java.util.Arrays;
* This writer discards any leading whitespace characters following to a line break character. When the first * 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 * non-whitespace character is written after a line break, the correct indentation characters are added, which is four
* whitespace characters per indentation level. * whitespace characters per indentation level.
*
* <p>
* The state pattern is line oriented. It starts by writing text. Indentation is increased if a brace '('or
* brace '{' is encountered in the code to be generated and written out in state: IN_TEXT_START_OF_LINE. Whenever
* a line end occurs (PC or Linux style) the amount of enters is checked and at max set to 2.
*
* Whenever a string definition is encountered in the code that should be generated, increasing the indentation is
* stopped (so `{` and '(' are ignored) until the end of the string is encountered ('"'). To avoid writing a new
* indentation, the state then returns to IN_TEXT.
*
* <p> * <p>
* This is a very basic implementation which does not take into account comments, escaping etc. * This is a very basic implementation which does not take into account comments, escaping etc.
* *
@ -43,7 +53,7 @@ class IndentationCorrectingWriter extends Writer {
private static final String LINE_SEPARATOR = System.getProperty( "line.separator" ); private static final String LINE_SEPARATOR = System.getProperty( "line.separator" );
private static final boolean IS_WINDOWS = System.getProperty( "os.name" ).startsWith( "Windows" ); private static final boolean IS_WINDOWS = System.getProperty( "os.name" ).startsWith( "Windows" );
private State currentState = State.IN_TEXT; private State currentState = State.START_OF_LINE;
private final StateContext context; private final StateContext context;
IndentationCorrectingWriter(Writer out) { IndentationCorrectingWriter(Writer out) {
@ -99,12 +109,22 @@ class IndentationCorrectingWriter extends Writer {
private enum State { private enum State {
/** /**
* Within any text. * Within any text, before encountering a String definition.
*/ */
IN_TEXT { START_OF_LINE {
@Override @Override
State doHandleCharacter(char c, StateContext context) { State doHandleCharacter(char c, StateContext context) {
switch ( c ) { switch ( c ) {
case '{':
case '(':
context.indentationLevel++;
return IN_TEXT;
case '}':
case ')':
context.indentationLevel--;
return IN_TEXT;
case '\"':
return IN_STRING;
case '\r': case '\r':
return isWindows() ? IN_LINE_BREAK : AFTER_LINE_BREAK; return isWindows() ? IN_LINE_BREAK : AFTER_LINE_BREAK;
case '\n': case '\n':
@ -142,27 +162,115 @@ class IndentationCorrectingWriter extends Writer {
flush( context ); flush( context );
} }
private void flush(StateContext context) throws IOException { },
if ( null != context.characters && context.currentIndex - context.lastStateChange > 0 ) {
context.writer.write(
context.characters,
context.lastStateChange,
context.currentIndex - context.lastStateChange
);
if ( DEBUG ) { /**
System.out.print( * Within any text, but after a String (" ").
new String( */
java.util.Arrays.copyOfRange( IN_TEXT {
context.characters, @Override
context.lastStateChange, State doHandleCharacter(char c, StateContext context) {
context.currentIndex switch ( c ) {
) case '{':
) case '(':
); context.indentationLevel++;
return IN_TEXT;
case '}':
case ')':
context.indentationLevel--;
return IN_TEXT;
case '\"':
return IN_STRING;
case '\r':
return isWindows() ? IN_LINE_BREAK : AFTER_LINE_BREAK;
case '\n':
return AFTER_LINE_BREAK;
default:
return IN_TEXT;
} }
} }
/**
* Writes out the current text.
*/
@Override
void onExit(StateContext context) throws IOException {
flush( context );
} }
/**
* Writes out the current text.
*/
@Override
void onBufferFinished(StateContext context) throws IOException {
flush( context );
}
},
/**
* In a String definition, Between un-escaped quotes " "
*/
IN_STRING {
@Override
State doHandleCharacter(char c, StateContext context) {
switch ( c ) {
case '\"':
return IN_TEXT;
case '\\':
return IN_STRING_ESCAPED_CHAR;
default:
return IN_STRING;
}
}
/**
* Writes out the current text.
*/
@Override
void onExit(StateContext context) throws IOException {
flush( context );
}
/**
* Writes out the current text.
*/
@Override
void onBufferFinished(StateContext context) throws IOException {
flush( context );
}
},
/**
* In a String, character following an escape character '\', should be ignored, can also be '"' that
* should be ignored.
*/
IN_STRING_ESCAPED_CHAR {
@Override
State doHandleCharacter(char c, StateContext context) {
// ignore escaped character
return IN_STRING;
}
/**
* Writes out the current text.
*/
@Override
void onExit(StateContext context) throws IOException {
flush( context );
}
/**
* Writes out the current text.
*/
@Override
void onBufferFinished(StateContext context) throws IOException {
flush( context );
}
}, },
/** /**
@ -186,7 +294,16 @@ class IndentationCorrectingWriter extends Writer {
AFTER_LINE_BREAK { AFTER_LINE_BREAK {
@Override @Override
State doHandleCharacter(char c, StateContext context) { State doHandleCharacter(char c, StateContext context) {
switch ( c ) { switch ( c ) {
case '{':
case '(':
context.indentationLevel++;
return START_OF_LINE;
case '}':
case ')':
context.indentationLevel--;
return START_OF_LINE;
case '\r': case '\r':
return isWindows() ? IN_LINE_BREAK : AFTER_LINE_BREAK; return isWindows() ? IN_LINE_BREAK : AFTER_LINE_BREAK;
case ' ': case ' ':
@ -195,7 +312,7 @@ class IndentationCorrectingWriter extends Writer {
context.consecutiveLineBreaks++; context.consecutiveLineBreaks++;
return AFTER_LINE_BREAK; return AFTER_LINE_BREAK;
default: default:
return IN_TEXT; return START_OF_LINE;
} }
} }
@ -220,12 +337,7 @@ class IndentationCorrectingWriter extends Writer {
}; };
final State handleCharacter(char c, StateContext context) throws IOException { final State handleCharacter(char c, StateContext context) throws IOException {
if ( c == '{' || c == '(' ) {
context.indentationLevel++;
}
else if ( c == '}' || c == ')' ) {
context.indentationLevel--;
}
return doHandleCharacter( c, context ); return doHandleCharacter( c, context );
} }
@ -245,6 +357,28 @@ class IndentationCorrectingWriter extends Writer {
void onBufferFinished(StateContext context) throws IOException { void onBufferFinished(StateContext context) throws IOException {
} }
protected void flush(StateContext context) throws IOException {
if ( null != context.characters && context.currentIndex - context.lastStateChange > 0 ) {
context.writer.write(
context.characters,
context.lastStateChange,
context.currentIndex - context.lastStateChange
);
if ( DEBUG ) {
System.out.print(
new String(
java.util.Arrays.copyOfRange(
context.characters,
context.lastStateChange,
context.currentIndex
)
)
);
}
}
}
} }
/** /**

View File

@ -0,0 +1,33 @@
/**
* Copyright 2012-2015 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.test.bugs._515;
import org.mapstruct.Mapper;
import org.mapstruct.Mapping;
import org.mapstruct.factory.Mappers;
@Mapper
public abstract class Issue515Mapper {
public static final Issue515Mapper INSTANCE = Mappers.getMapper( Issue515Mapper.class );
@Mapping( target = "id", expression = "java(\"blah)\\\"\")" )
public abstract Target map(Source source);
}

View File

@ -0,0 +1,40 @@
/**
* Copyright 2012-2015 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.test.bugs._515;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mapstruct.ap.testutil.IssueKey;
import org.mapstruct.ap.testutil.WithClasses;
import org.mapstruct.ap.testutil.runner.AnnotationProcessorTestRunner;
/**
* Reproducer for https://github.com/mapstruct/mapstruct/issues/515.
*
* @author Sjaak Derksen
*/
@IssueKey( "515" )
@RunWith(AnnotationProcessorTestRunner.class)
public class Issue515Test {
@Test
@WithClasses( { Issue515Mapper.class, Source.class, Target.class } )
public void shouldIgnoreParanthesesOpenInStringDefinition() {
}
}

View File

@ -0,0 +1,23 @@
/**
* Copyright 2012-2015 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.test.bugs._515;
public class Source {
}

View File

@ -0,0 +1,34 @@
/**
* Copyright 2012-2015 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.test.bugs._515;
public class Target {
private String id;
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
}