From fa00a330ac055265fea8a42668ce642879ee55b5 Mon Sep 17 00:00:00 2001 From: Sebastian Zarnekow Date: Wed, 31 Oct 2018 18:55:46 +0100 Subject: [PATCH] Base the STRINGValueConverter on the reusable new JavaStringConverter --- .../xtext/util/JavaStringConverter.java | 58 ++++ .../conversion/impl/STRINGValueConverter.java | 273 +++++++----------- 2 files changed, 162 insertions(+), 169 deletions(-) diff --git a/org.eclipse.xtext.util/src/org/eclipse/xtext/util/JavaStringConverter.java b/org.eclipse.xtext.util/src/org/eclipse/xtext/util/JavaStringConverter.java index 7b990bbce..4942d1077 100644 --- a/org.eclipse.xtext.util/src/org/eclipse/xtext/util/JavaStringConverter.java +++ b/org.eclipse.xtext.util/src/org/eclipse/xtext/util/JavaStringConverter.java @@ -193,4 +193,62 @@ public class JavaStringConverter { public char toHex(int i) { return "0123456789ABCDEF".charAt(i & 0xF); } + + protected boolean isHexSequence(String in, int off, int chars) { + return doIsHexSequence(in, off, chars); + } + + public static boolean doIsHexSequence(String in, int off, int chars) { + for (int i = off; i < in.length() && i < off + chars; i++) { + char c = in.charAt(i); + if (!isHex(c)) { + return false; + } + } + return true; + } + + protected boolean isHexSequence(char[] in, int off, int chars) { + return doIsHexSequence(in, off, chars); + } + + public static boolean doIsHexSequence(char[] in, int off, int chars) { + for (int i = off; i < in.length && i < off + chars; i++) { + char c = in[i]; + if (!isHex(c)) { + return false; + } + } + return true; + } + + protected static boolean isHex(char c) { + switch (c) { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case 'a': + case 'b': + case 'c': + case 'd': + case 'e': + case 'f': + case 'A': + case 'B': + case 'C': + case 'D': + case 'E': + case 'F': + return true; + default: + return false; + } + } } \ No newline at end of file diff --git a/org.eclipse.xtext/src/org/eclipse/xtext/conversion/impl/STRINGValueConverter.java b/org.eclipse.xtext/src/org/eclipse/xtext/conversion/impl/STRINGValueConverter.java index 7d5bfb039..ef2ae1673 100644 --- a/org.eclipse.xtext/src/org/eclipse/xtext/conversion/impl/STRINGValueConverter.java +++ b/org.eclipse.xtext/src/org/eclipse/xtext/conversion/impl/STRINGValueConverter.java @@ -10,6 +10,7 @@ package org.eclipse.xtext.conversion.impl; import org.eclipse.xtext.conversion.ValueConverterException; import org.eclipse.xtext.conversion.ValueConverterWithValueException; import org.eclipse.xtext.nodemodel.INode; +import org.eclipse.xtext.util.JavaStringConverter; import org.eclipse.xtext.util.Strings; /** @@ -46,43 +47,116 @@ public class STRINGValueConverter extends AbstractLexerBasedConverter { * @see Strings#convertFromJavaString(String, boolean) */ protected String convertFromString(String literal, INode node) throws ValueConverterWithValueException { - int length = literal.length(); - StringBuilder result = new StringBuilder(length); - ErrorInfo errorInfo = new ErrorInfo(); + Implementation converter = createConverter(); + String result = converter.convertFromJavaString(literal); + if (converter.errorMessage != null) { + throw new ValueConverterWithValueException(converter.errorMessage, node, result.toString(), converter.errorIndex, + converter.errorLength, null); + } + return result; + } + + /** + * @since 2.16 + */ + protected Implementation createConverter() { + return new Implementation(); + } + + /** + * @since 2.16 + */ + protected class Implementation extends JavaStringConverter { + String errorMessage = null; + int errorIndex = -1; + int errorLength = -1; int nextIndex = 1; - while(nextIndex < length - 1) { - nextIndex = unescapeCharAndAppendTo(literal, nextIndex, result, errorInfo); + + public String convertFromJavaString(String literal) { + int idx = literal.indexOf('\\'); + if (idx < 0 && literal.length() > 1 && literal.charAt(0) == literal.charAt(literal.length() - 1)) { + return literal.substring(1, literal.length() - 1); + } + return convertFromJavaString(literal, true, 1, new StringBuilder(literal.length())); } - if (nextIndex < length) { - if (nextIndex != length - 1) { - throw new IllegalStateException(); + @Override + protected String convertFromJavaString(String string, boolean useUnicode, int index, StringBuilder result) { + int length = string.length(); + while(index < length - 1) { + nextIndex = index = unescapeCharAndAppendTo(string, useUnicode, index, result); } - char next = literal.charAt(nextIndex); - if (literal.charAt(0) != next) { - result.append(next); - if (errorInfo.errorMessage == null) { - if (next == '\\') { - errorInfo.errorMessage = getInvalidEscapeSequenceMessage(); - errorInfo.errorIndex = nextIndex; - errorInfo.errorLength = 1; - } else { - errorInfo.errorMessage = getStringNotClosedMessage(); - } - } else { - errorInfo.errorMessage = getStringNotClosedMessage(); - errorInfo.errorIndex = -1; - errorInfo.errorLength = -1; + if (nextIndex < length) { + if (nextIndex != length - 1) { + throw new IllegalStateException(); } + char next = string.charAt(nextIndex); + if (string.charAt(0) != next) { + result.append(next); + if (errorMessage == null) { + if (next == '\\') { + errorMessage = getInvalidEscapeSequenceMessage(); + errorIndex = nextIndex; + errorLength = 1; + } else { + errorMessage = getStringNotClosedMessage(); + } + } else { + errorMessage = getStringNotClosedMessage(); + errorIndex = -1; + errorLength = -1; + } + } + } else if (nextIndex == length) { + errorMessage = getStringNotClosedMessage(); } - } else if (nextIndex == length) { - errorInfo.errorMessage = getStringNotClosedMessage(); + return result.toString(); } - if (errorInfo.errorMessage != null) { - throw new ValueConverterWithValueException(errorInfo.errorMessage, node, result.toString(), errorInfo.errorIndex, - errorInfo.errorLength, null); + + @Override + protected boolean isHexSequence(char[] in, int off, int chars) { + // keep chance to use overridden methods by funneling it through STRINGValueConverter + return STRINGValueConverter.this.isHexSequence(in, off, chars); } - return result.toString(); + + @Override + protected boolean isInvalidUnicodeEscapeSequence(String string, int index) { + return super.isInvalidUnicodeEscapeSequence(string, index) || !isHexSequence(string, index, 4); + } + + @Override + protected int handleInvalidUnicodeEscapeSequnce(String string, int index, StringBuilder result) { + result.append('u'); + errorMessage = "Invalid unicode"; + errorIndex = index - 2; + errorLength = 2; + return index; + } + + @Override + protected int doUnescapeCharAndAppendTo(String string, boolean useUnicode, int index, StringBuilder result) { + if (string.length() == index) { + if (errorMessage == null) { + errorMessage = getInvalidEscapeSequenceMessage(); + errorIndex = index - 1; + errorLength = 1; + } + return index; + } + return super.doUnescapeCharAndAppendTo(string, useUnicode, index, result); + } + + @Override + protected int handleUnknownEscapeSequence(String string, char c, boolean useUnicode, int index, StringBuilder result) { + if (errorMessage == null) { + errorMessage = getInvalidEscapeSequenceMessage(); + errorIndex = index - 2; + errorLength = 2; + } + result.append(c); + return index; + } + } /** @@ -99,150 +173,11 @@ public class STRINGValueConverter extends AbstractLexerBasedConverter { return "String literal is not properly closed"; } - private static class ErrorInfo { - String errorMessage = null; - int errorIndex = -1; - int errorLength = -1; - } - - private int unescapeCharAndAppendTo(String string, int index, StringBuilder result, ErrorInfo errorInfo) { - char c = string.charAt(index++); - if (c == '\\') { - index = doUnescapeCharAndAppendTo(string, index, result, errorInfo); - } else { - result.append(c); - } - return index; - } - - private int doUnescapeCharAndAppendTo(String string, int index, StringBuilder result, ErrorInfo errorInfo) { - if (string.length() == index) { - if (errorInfo.errorMessage == null) { - errorInfo.errorMessage = getInvalidEscapeSequenceMessage(); - errorInfo.errorIndex = index - 1; - errorInfo.errorLength = 1; - } - return index; - } - char c = string.charAt(index++); - switch(c) { - case 'b': - c = '\b'; - break; - case 't': - c = '\t'; - break; - case 'n': - c = '\n'; - break; - case 'f': - c = '\f'; - break; - case 'r': - c = '\r'; - break; - case '"': - case '\'': - case '\\': - break; - case 'u': - return unescapeUnicodeSequence(string, index, result, errorInfo); - default: - if (errorInfo.errorMessage == null) { - errorInfo.errorMessage = getInvalidEscapeSequenceMessage(); - errorInfo.errorIndex = index - 2; - errorInfo.errorLength = 2; - } - } - result.append(c); - return index; - } - - private int unescapeUnicodeSequence(String string, int index, StringBuilder result, ErrorInfo errorInfo) { - try { - if (index+4 > string.length() || !isHexSequence(string, index, 4)) { - result.append('u'); - errorInfo.errorMessage = "Invalid unicode"; - errorInfo.errorIndex = index - 2; - errorInfo.errorLength = 2; - return index; - } - result.append((char) Integer.parseInt(string.substring(index, index + 4), 16)); - return index + 4; - } catch (NumberFormatException e) { - throw new IllegalArgumentException("Illegal \\uxxxx encoding in " + string); - } - } - - private boolean isHexSequence(String in, int off, int chars) { - for (int i = off; i < in.length() && i < off + chars; i++) { - char c = in.charAt(i); - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - continue; - default: - return false; - } - } - return true; - } - /** * @since 2.7 */ protected boolean isHexSequence(char[] in, int off, int chars) { - for (int i = off; i < in.length && i < off + chars; i++) { - char c = in[i]; - switch (c) { - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - case 'a': - case 'b': - case 'c': - case 'd': - case 'e': - case 'f': - case 'A': - case 'B': - case 'C': - case 'D': - case 'E': - case 'F': - continue; - default: - return false; - } - } - return true; + return Implementation.doIsHexSequence(in, off, chars); } } \ No newline at end of file