diff --git a/agrest-engine/src/main/java/io/agrest/exp/parser/AgExpressionParser.java b/agrest-engine/src/main/java/io/agrest/exp/parser/AgExpressionParser.java index 732133362..47cc5aa83 100644 --- a/agrest-engine/src/main/java/io/agrest/exp/parser/AgExpressionParser.java +++ b/agrest-engine/src/main/java/io/agrest/exp/parser/AgExpressionParser.java @@ -983,32 +983,34 @@ final public void stringLiteral() throws ParseException { case SINGLE_QUOTED_STRING:{ jj_consume_token(SINGLE_QUOTED_STRING); ExpScalar jjtn001 = new ExpScalar(JJTSCALAR); - boolean jjtc001 = true; - jjtree.openNodeScope(jjtn001); + boolean jjtc001 = true; + jjtree.openNodeScope(jjtn001); try { jjtree.closeNodeScope(jjtn001, 0); - jjtc001 = false; + jjtc001 = false; jjtn001.jjtSetValue(token_source.literalValue); + jjtn001.syncScalarImage("'" + token_source.literalValue + "'"); } finally { if (jjtc001) { - jjtree.closeNodeScope(jjtn001, 0); - } + jjtree.closeNodeScope(jjtn001, 0); + } } break; } case DOUBLE_QUOTED_STRING:{ jj_consume_token(DOUBLE_QUOTED_STRING); ExpScalar jjtn002 = new ExpScalar(JJTSCALAR); - boolean jjtc002 = true; - jjtree.openNodeScope(jjtn002); + boolean jjtc002 = true; + jjtree.openNodeScope(jjtn002); try { jjtree.closeNodeScope(jjtn002, 0); - jjtc002 = false; + jjtc002 = false; jjtn002.jjtSetValue(token_source.literalValue); + jjtn002.syncScalarImage("\"" + token_source.literalValue + "\""); } finally { if (jjtc002) { - jjtree.closeNodeScope(jjtn002, 0); - } + jjtree.closeNodeScope(jjtn002, 0); + } } break; } diff --git a/agrest-engine/src/main/java/io/agrest/exp/parser/ExpScalar.java b/agrest-engine/src/main/java/io/agrest/exp/parser/ExpScalar.java index 9db0c6b35..7fa00fa63 100644 --- a/agrest-engine/src/main/java/io/agrest/exp/parser/ExpScalar.java +++ b/agrest-engine/src/main/java/io/agrest/exp/parser/ExpScalar.java @@ -2,9 +2,14 @@ /* JavaCCOptions:MULTI=true,NODE_USES_PARSER=false,VISITOR=true,TRACK_TOKENS=false,NODE_PREFIX=Exp,NODE_EXTENDS=,NODE_FACTORY=,SUPPORT_CLASS_VISIBILITY_PUBLIC=true */ package io.agrest.exp.parser; +import java.math.BigDecimal; +import java.math.BigInteger; import java.util.function.Function; public class ExpScalar extends ExpBaseScalar { + + protected String scalarImage = String.valueOf(value); + public ExpScalar(int id) { super(id); } @@ -27,6 +32,39 @@ protected Object transformExpression(Function transformer) { return transformer.apply(new ExpScalar(getValue())); } + protected void syncScalarImage(CharSequence value) { + if (value.length() < 2) { + scalarImage = "'" + value + "'"; + return; + } + String stringValue = value.toString(); + char firstChar = stringValue.charAt(0); + if (firstChar != '\'' && firstChar != '"') { + scalarImage = "'" + value + "'"; + return; + } + String escapedContent = stringValue + .substring(1, stringValue.length() - 1) + .replaceAll(String.valueOf(firstChar), "\\\\" + firstChar); + scalarImage = firstChar + escapedContent + firstChar; + } + + protected void syncScalarImage(Long value) { + scalarImage = value + "L"; + } + + protected void syncScalarImage(BigInteger value) { + scalarImage = value + "H"; + } + + protected void syncScalarImage(BigDecimal value) { + scalarImage = value + "B"; + } + + protected void syncScalarImage(Object value) { + scalarImage = String.valueOf(value); + } + /** * Accept the visitor. **/ @@ -35,6 +73,32 @@ public T jjtAccept(AgExpressionParserVisitor visitor, T data) { return visitor.visit(this, data); } + @Override + public void jjtSetValue(Object value) { + super.jjtSetValue(value); + if (value == null) { + scalarImage = "null"; + return; + } + if (value instanceof CharSequence) { + syncScalarImage(((CharSequence) value)); + return; + } + if (value.getClass() == Long.class) { + syncScalarImage(((Long) value)); + return; + } + if (value.getClass() == BigInteger.class) { + syncScalarImage(((BigInteger) value)); + return; + } + if (value.getClass() == BigDecimal.class) { + syncScalarImage(((BigDecimal) value)); + return; + } + syncScalarImage(value); + } + @Override protected ExpScalar shallowCopy() { return new ExpScalar(getValue()); diff --git a/agrest-engine/src/main/java/io/agrest/exp/parser/ExpStringConverter.java b/agrest-engine/src/main/java/io/agrest/exp/parser/ExpStringConverter.java index f637e93b8..a7d9ca64b 100644 --- a/agrest-engine/src/main/java/io/agrest/exp/parser/ExpStringConverter.java +++ b/agrest-engine/src/main/java/io/agrest/exp/parser/ExpStringConverter.java @@ -3,8 +3,6 @@ import io.agrest.protocol.Exp; import java.util.Arrays; -import java.util.HashSet; -import java.util.List; import java.util.Set; import java.util.stream.Collectors; @@ -260,9 +258,7 @@ public static String convert(ExpPath exp) { } public static String convert(ExpScalar exp) { - return exp.value instanceof CharSequence - ? "'" + exp.value + "'" - : String.valueOf(exp.value); + return exp.scalarImage; } public static String convert(ExpScalarList exp) { diff --git a/agrest-engine/src/main/java/io/agrest/protocol/Exp.java b/agrest-engine/src/main/java/io/agrest/protocol/Exp.java index 944908ff2..eada017a6 100644 --- a/agrest-engine/src/main/java/io/agrest/protocol/Exp.java +++ b/agrest-engine/src/main/java/io/agrest/protocol/Exp.java @@ -64,26 +64,27 @@ static Exp path(String path) { */ static Exp scalar(Object value) { if (value == null) { - return new ExpScalar(AgExpressionParserTreeConstants.JJTSCALAR); + return new ExpScalar(); } ExpBaseScalar scalar; if (value instanceof Collection) { - scalar = new ExpScalarList(AgExpressionParserTreeConstants.JJTSCALARLIST); - } else if (value.getClass().isArray()) { + scalar = new ExpScalarList((Collection) value); + return scalar; + } + if (value.getClass().isArray()) { Class componentType = value.getClass().getComponentType(); if (componentType.isPrimitive()) { value = ExpUtils.wrapPrimitiveArray(value); } else { value = Arrays.asList((Object[]) value); } - scalar = new ExpScalarList(AgExpressionParserTreeConstants.JJTSCALARLIST); - } else { - scalar = new ExpScalar(AgExpressionParserTreeConstants.JJTSCALAR); + scalar = new ExpScalarList(); + scalar.jjtSetValue(value); + return scalar; } - scalar.jjtSetValue(value); - return scalar; + return new ExpScalar(value); } /** diff --git a/agrest-engine/src/main/jjtree/io/agrest/exp/parser/AgExpressionParser.jjt b/agrest-engine/src/main/jjtree/io/agrest/exp/parser/AgExpressionParser.jjt index 6a7685858..d7e4937bf 100644 --- a/agrest-engine/src/main/jjtree/io/agrest/exp/parser/AgExpressionParser.jjt +++ b/agrest-engine/src/main/jjtree/io/agrest/exp/parser/AgExpressionParser.jjt @@ -178,9 +178,17 @@ void stringParameter() : {} void stringLiteral() : {} { - { jjtThis.jjtSetValue(token_source.literalValue); } #Scalar(0) + + { + jjtThis.jjtSetValue(token_source.literalValue); + jjtThis.syncScalarImage("'" + token_source.literalValue + "'"); + } #Scalar(0) | - { jjtThis.jjtSetValue(token_source.literalValue); } #Scalar(0) + + { + jjtThis.jjtSetValue(token_source.literalValue); + jjtThis.syncScalarImage("\"" + token_source.literalValue + "\""); + } #Scalar(0) } void stringExpression() : {} diff --git a/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarFloatTest.java b/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarFloatTest.java index 977d11f44..5ac1d3aa0 100644 --- a/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarFloatTest.java +++ b/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarFloatTest.java @@ -49,15 +49,13 @@ void parse(String expString) { "1e1|10.0", "1E1|10.0", "1.1e1|11.0", - "3.4028235e+38f|3.4028235E38", - "1.7976931348623157e+308|1.7976931348623157E308", - "1.7976931348623157e+308d|1.7976931348623157E308", "1_2.1|12.1", "2.2__34|2.234", ".3e7_6|3.0E75", - - // TODO: this is wrong, B suffix expected - "1.7976931348623157e+309b|1.7976931348623157E+309" + "3.4028235e+38f|3.4028235E38", + "1.7976931348623157e+308|1.7976931348623157E308", + "1.7976931348623157e+308d|1.7976931348623157E308", + "1.7976931348623157e+309b|1.7976931348623157E+309B" }) public void parsedToString(String expString, String expected) { assertEquals(expected, Exp.parse(expString).toString()); diff --git a/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarIntTest.java b/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarIntTest.java index 60bb0b6df..76af9adb4 100644 --- a/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarIntTest.java +++ b/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarIntTest.java @@ -50,10 +50,8 @@ void parse(String expString) { "01_2|10", "0x1_A|26", "123___456|123456", - - // TODO: this is wrong, L suffix expected in the output - "2147483648L|2147483648", - "9223372036854775808H|9223372036854775808", + "2147483648L|2147483648L", + "9223372036854775808H|9223372036854775808H", "01234567|342391", "0x12345678|305419896", "0x09abcdef|162254319" diff --git a/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarStringTest.java b/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarStringTest.java index a2e8b6283..5e0431be2 100644 --- a/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarStringTest.java +++ b/agrest-engine/src/test/java/io/agrest/exp/parser/ExpScalarStringTest.java @@ -28,13 +28,12 @@ void parse(String expString) { @CsvSource(delimiter = '|', quoteCharacter = 'X', value = { "'example'|'example'", " 'example' |'example'", - "\"example\"|'example'", + "\"example\"|\"example\"", "''|''", "' '|' '", - "\"\\\"example\\\"\"|'\"example\"'", - // TODO: this is wrong, single quote must be escaped or double quotes used in output - "\"a'b\"|'a'b'", - "'a\"b'|'a\"b'" + "\"\\\"example\\\"\"|\"\\\"example\\\"\"", + "\"a\\\"'b\"|\"a\\\"'b\"", + "'a\"\\'b'|'a\"\\'b'" }) public void parsedToString(String expString, String expected) { assertEquals(expected, Exp.parse(expString).toString());