Author: henrib Date: Sat Aug 29 08:39:04 2009 New Revision: 809099 URL: http://svn.apache.org/viewvc?rev=809099&view=rev Log: added Unicode escape sequence to String literal handling; fixed non-escapable character handling; added $ as escapable character in UnifiedJEXL
Modified: commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/parser/StringParser.java commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/JexlTest.java commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/UnifiedJEXLTest.java Modified: commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java?rev=809099&r1=809098&r2=809099&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java (original) +++ commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/UnifiedJEXL.java Sat Aug 29 08:39:04 2009 @@ -869,11 +869,11 @@ IMMEDIATE0, /** Parsing after # .*/ DEFERRED0, - /** Parsing afer ${ . */ + /** Parsing afer ${ .*/ IMMEDIATE1, - /** Parsing afer #{ . */ + /** Parsing afer #{ .*/ DEFERRED1, - /** Parsing afer \ . */ + /** Parsing afer \ .*/ ESCAPE } @@ -944,7 +944,6 @@ case IMMEDIATE1: // ${... if (c == '}') { // materialize the immediate expr - //Expression iexpr = createExpression(ExpressionType.IMMEDIATE, strb, null); Expression iexpr = new ImmediateExpression(strb.toString(), toNode(strb), null); builder.add(iexpr); strb.delete(0, Integer.MAX_VALUE); @@ -996,6 +995,8 @@ case ESCAPE: if (c == '#') { strb.append('#'); + } else if (c == '$') { + strb.append('$'); } else { strb.append('\\'); strb.append(c); Modified: commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/parser/StringParser.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/parser/StringParser.java?rev=809099&r1=809098&r2=809099&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/parser/StringParser.java (original) +++ commons/proper/jexl/branches/2.0/src/main/java/org/apache/commons/jexl/parser/StringParser.java Sat Aug 29 08:39:04 2009 @@ -18,6 +18,20 @@ /** * Common constant strings utilities. + * <p> + * This package methods read JEXL string literals and handle escaping through the + * 'backslash' (ie: \) character. Escaping is used to neutralize string delimiters (the single + * and double quotes) and read Unicode hexadecimal encoded characters. + * </p> + * <p> + * The only escapable characters are the single and double quotes - ''' and '"' -, + * a Unicode sequence starting with 'u' followed by 4 hexadecimals and + * the backslash character - '\' - itself. + * </p> + * <p> + * A sequence where '\' occurs before any non-escapable character or sequence has no effect, the + * sequence output being the same as the input. + * </p> */ public class StringParser { /** @@ -47,7 +61,7 @@ public static int readString(StringBuilder strb, CharSequence str, int index, char sep) { return read(strb, str, index, str.length(), sep); } - + /** * Read the remainder of a string till a given separator, * handles escaping through '\' syntax. @@ -64,9 +78,17 @@ for (; index < end; ++index) { char c = str.charAt(index); if (escape) { - strb.append(c); - if (c == '\\') - strb.append('\\'); + if (c == 'u' && (index + 4) < end && readUnicodeChar(strb, str, index + 1) > 0) { + index += 4; + } + else { + // if c is not an escapable character, re-emmit the backslash before it + boolean notSeparator = sep == 0? c != '\'' && c != '"' : c != sep; + if (notSeparator && c != '\\' ) { + strb.append('\\'); + } + strb.append(c); + } escape = false; continue; } @@ -81,4 +103,36 @@ } return index; } + + /** + * Reads a Unicode escape character. + * @param strb the builder to write the character to + * @param str the sequence + * @param begin the begin offset in sequence (after the '\\u') + * @return 0 if char could not be read, 4 otherwise + */ + private static final int readUnicodeChar(StringBuilder strb, CharSequence str, int begin) { + char xc = 0; + int bits = 12; + int value = 0; + for(int offset = 0; offset < 4; ++offset) { + char c = str.charAt(begin + offset); + if (c >= '0' && c <= '9') { + value = (c - '0'); + } + else if (c >= 'a' && c <= 'h') { + value = (c - 'a' + 10); + } + else if (c >= 'A' && c <= 'H') { + value = (c - 'A' + 10); + } + else { + return 0; + } + xc |= value << bits; + bits -= 4; + } + strb.append(xc); + return 4; + } } \ No newline at end of file Modified: commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/JexlTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/JexlTest.java?rev=809099&r1=809098&r2=809099&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/JexlTest.java (original) +++ commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/JexlTest.java Sat Aug 29 08:39:04 2009 @@ -739,10 +739,15 @@ // jc.getVars().put("commons-logging", version); // assertExpression(jc, "commons-logging", version); } - + public void testUnicodeSupport() throws Exception { - assertExpression(JexlHelper.createContext(), "myvar == 'Użytkownik'", Boolean.FALSE); + JexlContext jc = JexlHelper.createContext(); + assertExpression(jc, "myvar == 'Użytkownik'", Boolean.FALSE); + assertExpression(jc, "'c:\\some\\windows\\path'", "c:\\some\\windows\\path"); + assertExpression(jc, "'foo\\u0020bar'", "foo\u0020bar"); + assertExpression(jc, "'foo\\u0020\\u0020bar'", "foo\u0020\u0020bar"); + assertExpression(jc, "'\\u0020foobar\\u0020'", "\u0020foobar\u0020"); } public static final class Duck { Modified: commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/UnifiedJEXLTest.java URL: http://svn.apache.org/viewvc/commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/UnifiedJEXLTest.java?rev=809099&r1=809098&r2=809099&view=diff ============================================================================== --- commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/UnifiedJEXLTest.java (original) +++ commons/proper/jexl/branches/2.0/src/test/java/org/apache/commons/jexl/UnifiedJEXLTest.java Sat Aug 29 08:39:04 2009 @@ -157,10 +157,16 @@ } public void testEscape() throws Exception { - UnifiedJEXL.Expression expr = EL.parse("#\\{'world'\\}"); JexlContext none = null; - Object o = expr.evaluate(none); + UnifiedJEXL.Expression expr; + Object o; + // $ and # are escapable in UnifiedJEXL + expr = EL.parse("\\#{'world'}"); + o = expr.evaluate(none); assertEquals("#{'world'}", o); + expr = EL.parse("\\${'world'}"); + o = expr.evaluate(none); + assertEquals("${'world'}", o); } public void testEscapeString() throws Exception { @@ -170,6 +176,13 @@ assertEquals("\"world's finest\"", o); } + public void testNonEscapeString() throws Exception { + UnifiedJEXL.Expression expr = EL.parse("c:\\some\\windows\\path"); + JexlContext none = null; + Object o = expr.evaluate(none); + assertEquals("c:\\some\\windows\\path", o); + } + public void testMalformed() throws Exception { try { UnifiedJEXL.Expression expr = EL.parse("${'world'"); @@ -237,4 +250,4 @@ test.setUp(); test.testBadContextNested(); } -} \ No newline at end of file +}