1 /* 2 * Copyright (C) 2015 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 package android.databinding; 17 18 import android.databinding.parser.BindingExpressionBaseVisitor; 19 import android.databinding.parser.BindingExpressionLexer; 20 import android.databinding.parser.BindingExpressionParser; 21 import android.databinding.parser.BindingExpressionParser.AndOrOpContext; 22 import android.databinding.parser.BindingExpressionParser.BinaryOpContext; 23 import android.databinding.parser.BindingExpressionParser.BindingSyntaxContext; 24 import android.databinding.parser.BindingExpressionParser.BitShiftOpContext; 25 import android.databinding.parser.BindingExpressionParser.ComparisonOpContext; 26 import android.databinding.parser.BindingExpressionParser.DefaultsContext; 27 import android.databinding.parser.BindingExpressionParser.DotOpContext; 28 import android.databinding.parser.BindingExpressionParser.ExpressionContext; 29 import android.databinding.parser.BindingExpressionParser.GroupingContext; 30 import android.databinding.parser.BindingExpressionParser.LiteralContext; 31 import android.databinding.parser.BindingExpressionParser.MathOpContext; 32 import android.databinding.parser.BindingExpressionParser.PrimaryContext; 33 import android.databinding.parser.BindingExpressionParser.PrimitiveTypeContext; 34 import android.databinding.parser.BindingExpressionParser.QuestionQuestionOpContext; 35 import android.databinding.parser.BindingExpressionParser.ResourceContext; 36 import android.databinding.parser.BindingExpressionParser.StringLiteralContext; 37 import android.databinding.parser.BindingExpressionParser.TernaryOpContext; 38 import android.databinding.parser.BindingExpressionParser.UnaryOpContext; 39 40 import org.antlr.v4.runtime.ANTLRInputStream; 41 import org.antlr.v4.runtime.CommonTokenStream; 42 import org.antlr.v4.runtime.Token; 43 import org.antlr.v4.runtime.misc.NotNull; 44 import org.antlr.v4.runtime.tree.TerminalNode; 45 import org.junit.Test; 46 47 import java.io.StringReader; 48 49 import static org.junit.Assert.assertEquals; 50 import static org.junit.Assert.assertNotNull; 51 import static org.junit.Assert.assertNull; 52 import static org.junit.Assert.assertTrue; 53 54 public class BindingExpressionParserTest { 55 56 @Test testSingleQuoteStringLiteral()57 public void testSingleQuoteStringLiteral() throws Exception { 58 String expr = "`test`"; 59 LiteralContext literal = parseLiteral(expr); 60 assertNotNull(literal); 61 StringLiteralContext stringLiteral = literal.stringLiteral(); 62 assertNotNull(stringLiteral); 63 TerminalNode singleQuote = stringLiteral.SingleQuoteString(); 64 Token token = singleQuote.getSymbol(); 65 assertEquals("`test`", token.getText()); 66 } 67 68 @Test testDoubleQuoteStringLiteral()69 public void testDoubleQuoteStringLiteral() throws Exception { 70 String expr = "\"test\""; 71 72 LiteralContext literal = parseLiteral(expr); 73 StringLiteralContext stringLiteral = literal.stringLiteral(); 74 TerminalNode singleQuote = stringLiteral.DoubleQuoteString(); 75 Token token = singleQuote.getSymbol(); 76 assertEquals("\"test\"", token.getText()); 77 } 78 79 @Test testSingleQuoteEscapeStringLiteral()80 public void testSingleQuoteEscapeStringLiteral() throws Exception { 81 String expr = "`\"t\\`est\"`"; 82 LiteralContext literal = parseLiteral(expr); 83 StringLiteralContext stringLiteral = literal.stringLiteral(); 84 TerminalNode singleQuote = stringLiteral.SingleQuoteString(); 85 Token token = singleQuote.getSymbol(); 86 assertEquals("`\"t\\`est\"`", token.getText()); 87 } 88 89 @Test testCharLiteral()90 public void testCharLiteral() throws Exception { 91 LiteralContext literal = parseLiteral("'c'"); 92 assertEquals("'c'", literal.getText()); 93 literal = parseLiteral("'\\u0054'"); 94 assertEquals("'\\u0054'", literal.getText()); 95 literal = parseLiteral("'\\''"); 96 assertEquals("'\\''", literal.getText()); 97 } 98 99 @Test testIntLiterals()100 public void testIntLiterals() throws Exception { 101 compareIntLiteral("123"); 102 compareIntLiteral("123l"); 103 compareIntLiteral("1_2_3l"); 104 compareIntLiteral("123L"); 105 compareIntLiteral("0xdeadbeef"); 106 compareIntLiteral("0xdeadbeefl"); 107 compareIntLiteral("0Xdeadbeef"); 108 compareIntLiteral("0xdead_beefl"); 109 compareIntLiteral("0xdead_beefL"); 110 compareIntLiteral("01234567"); 111 compareIntLiteral("01234567L"); 112 compareIntLiteral("01234567l"); 113 compareIntLiteral("0123_45_67l"); 114 compareIntLiteral("0b0101"); 115 compareIntLiteral("0b0101_0101"); 116 compareIntLiteral("0B0101_0101"); 117 compareIntLiteral("0B0101_0101L"); 118 compareIntLiteral("0B0101_0101l"); 119 } 120 121 @Test testFloatLiterals()122 public void testFloatLiterals() throws Exception { 123 compareFloatLiteral("0.12345"); 124 compareFloatLiteral("0.12345f"); 125 compareFloatLiteral("0.12345F"); 126 compareFloatLiteral("132450.12345F"); 127 compareFloatLiteral("132450.12345"); 128 compareFloatLiteral("132450e123"); 129 compareFloatLiteral("132450.4e123"); 130 } 131 132 @Test testBoolLiterals()133 public void testBoolLiterals() throws Exception { 134 compareBoolLiteral("true"); 135 compareBoolLiteral("false"); 136 } 137 138 @Test testNullLiteral()139 public void testNullLiteral() throws Exception { 140 LiteralContext literal = parseLiteral("null"); 141 String token = literal.getText(); 142 assertEquals("null", token); 143 } 144 145 @Test testPrimitiveClassExtraction()146 public void testPrimitiveClassExtraction() throws Exception { 147 PrimaryContext primary = parsePrimary("int.class"); 148 PrimitiveTypeContext type = primary.classExtraction().type().primitiveType(); 149 assertEquals("int", type.getText()); 150 } 151 152 @Test testIdentifier()153 public void testIdentifier() throws Exception { 154 PrimaryContext primary = parsePrimary("abcdEfg"); 155 assertEquals("abcdEfg", primary.identifier().getText()); 156 } 157 158 @Test testUnaryOperators()159 public void testUnaryOperators() throws Exception { 160 compareUnaryOperators("+"); 161 compareUnaryOperators("-"); 162 compareUnaryOperators("!"); 163 compareUnaryOperators("~"); 164 } 165 166 @Test testMathOperators()167 public void testMathOperators() throws Exception { 168 compareMathOperators("+"); 169 compareMathOperators("-"); 170 compareMathOperators("*"); 171 compareMathOperators("/"); 172 compareMathOperators("%"); 173 } 174 175 @Test testBitShiftOperators()176 public void testBitShiftOperators() throws Exception { 177 compareBitShiftOperators(">>>"); 178 compareBitShiftOperators("<<"); 179 compareBitShiftOperators(">>"); 180 } 181 182 @Test testComparisonShiftOperators()183 public void testComparisonShiftOperators() throws Exception { 184 compareComparisonOperators("<"); 185 compareComparisonOperators(">"); 186 compareComparisonOperators("<="); 187 compareComparisonOperators(">="); 188 compareComparisonOperators("=="); 189 compareComparisonOperators("!="); 190 } 191 192 @Test testAndOrOperators()193 public void testAndOrOperators() throws Exception { 194 compareAndOrOperators("&&"); 195 compareAndOrOperators("||"); 196 } 197 198 @Test testBinaryOperators()199 public void testBinaryOperators() throws Exception { 200 compareBinaryOperators("&"); 201 compareBinaryOperators("|"); 202 compareBinaryOperators("^"); 203 } 204 205 @Test testTernaryOperator()206 public void testTernaryOperator() throws Exception { 207 TernaryOpContext expression = parseExpression("true ? 1 : 0"); 208 assertEquals(5, expression.getChildCount()); 209 assertEquals("true", 210 ((PrimaryContext) expression.left).literal().javaLiteral().getText()); 211 assertEquals("?", expression.op.getText()); 212 assertEquals("1", 213 ((PrimaryContext) expression.iftrue).literal().javaLiteral().getText()); 214 assertEquals(":", expression.getChild(3).getText()); 215 assertEquals("0", ((PrimaryContext) expression.iffalse).literal().javaLiteral().getText()); 216 } 217 218 @Test testDot()219 public void testDot() throws Exception { 220 DotOpContext expression = parseExpression("one.two.three"); 221 assertEquals(3, expression.getChildCount()); 222 assertEquals("three", expression.Identifier().getText()); 223 assertEquals(".", expression.getChild(1).getText()); 224 DotOpContext left = (DotOpContext) expression.expression(); 225 assertEquals("two", left.Identifier().getText()); 226 assertEquals(".", left.getChild(1).getText()); 227 assertEquals("one", ((PrimaryContext) left.expression()).identifier().getText()); 228 } 229 230 @Test testQuestionQuestion()231 public void testQuestionQuestion() throws Exception { 232 QuestionQuestionOpContext expression = parseExpression("one ?? two"); 233 assertEquals(3, expression.getChildCount()); 234 assertEquals("one", ((PrimaryContext) expression.left).identifier().getText()); 235 assertEquals("two", ((PrimaryContext) expression.right).identifier().getText()); 236 assertEquals("??", expression.op.getText()); 237 } 238 239 @Test testResourceReference()240 public void testResourceReference() throws Exception { 241 compareResource("@id/foo_bar"); 242 compareResource("@transition/foo_bar"); 243 compareResource("@anim/foo_bar"); 244 compareResource("@animator/foo_bar"); 245 compareResource("@android:id/foo_bar"); 246 compareResource("@app:id/foo_bar"); 247 } 248 249 @Test testDefaults()250 public void testDefaults() throws Exception { 251 BindingSyntaxContext syntax = parseExpressionString("foo.bar, default = @id/foo_bar"); 252 BindingExpressionParser.DefaultsContext defaults = syntax 253 .accept(new BindingExpressionBaseVisitor<DefaultsContext>() { 254 @Override 255 public BindingExpressionParser.DefaultsContext visitDefaults( 256 @NotNull BindingExpressionParser.DefaultsContext ctx) { 257 return ctx; 258 } 259 }); 260 assertEquals("@id/foo_bar", defaults.constantValue().ResourceReference().getText()); 261 } 262 263 @Test testParentheses()264 public void testParentheses() throws Exception { 265 GroupingContext grouping = parseExpression("(1234)"); 266 assertEquals("1234", grouping.expression().getText()); 267 } 268 269 // ---------------------- Helpers -------------------- 270 compareResource(String value)271 private void compareResource(String value) throws Exception { 272 ResourceContext resourceContext = parseExpression(value); 273 assertEquals(value, resourceContext.getText()); 274 } 275 compareUnaryOperators(String op)276 private void compareUnaryOperators(String op) throws Exception { 277 UnaryOpContext expression = parseExpression(op + " 2"); 278 assertEquals(2, expression.getChildCount()); 279 assertEquals(op, expression.op.getText()); 280 assertEquals("2", 281 ((PrimaryContext) expression.expression()).literal().javaLiteral() 282 .getText()); 283 } 284 compareBinaryOperators(String op)285 private void compareBinaryOperators(String op) throws Exception { 286 BinaryOpContext expression = parseExpression("1 " + op + " 2"); 287 assertEquals(3, expression.getChildCount()); 288 assertTrue(expression.left instanceof ExpressionContext); 289 String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); 290 assertEquals("1", one); 291 assertEquals(op, expression.op.getText()); 292 assertTrue(expression.right instanceof ExpressionContext); 293 String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); 294 assertEquals("2", two); 295 } 296 compareMathOperators(String op)297 private void compareMathOperators(String op) throws Exception { 298 MathOpContext expression = parseExpression("1 " + op + " 2"); 299 assertEquals(3, expression.getChildCount()); 300 assertTrue(expression.left instanceof ExpressionContext); 301 String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); 302 assertEquals("1", one); 303 assertEquals(op, expression.op.getText()); 304 assertTrue(expression.right instanceof ExpressionContext); 305 String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); 306 assertEquals("2", two); 307 } 308 compareBitShiftOperators(String op)309 private void compareBitShiftOperators(String op) throws Exception { 310 BitShiftOpContext expression = parseExpression("1 " + op + " 2"); 311 assertEquals(3, expression.getChildCount()); 312 assertTrue(expression.left instanceof ExpressionContext); 313 String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); 314 assertEquals("1", one); 315 assertEquals(op, expression.op.getText()); 316 assertTrue(expression.right instanceof ExpressionContext); 317 String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); 318 assertEquals("2", two); 319 } 320 compareComparisonOperators(String op)321 private void compareComparisonOperators(String op) throws Exception { 322 ComparisonOpContext expression = parseExpression("1 " + op + " 2"); 323 assertEquals(3, expression.getChildCount()); 324 assertTrue(expression.left instanceof ExpressionContext); 325 String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); 326 assertEquals("1", one); 327 assertEquals(op, expression.op.getText()); 328 assertTrue(expression.right instanceof ExpressionContext); 329 String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); 330 assertEquals("2", two); 331 } 332 compareAndOrOperators(String op)333 private void compareAndOrOperators(String op) throws Exception { 334 AndOrOpContext expression = parseExpression("1 " + op + " 2"); 335 assertEquals(3, expression.getChildCount()); 336 assertTrue(expression.left instanceof ExpressionContext); 337 String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); 338 assertEquals("1", one); 339 assertEquals(op, expression.op.getText()); 340 assertTrue(expression.right instanceof ExpressionContext); 341 String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); 342 assertEquals("2", two); 343 } 344 compareIntLiteral(String constant)345 private void compareIntLiteral(String constant) throws Exception { 346 LiteralContext literal = parseLiteral(constant); 347 String token = literal.javaLiteral().getText(); 348 assertEquals(constant, token); 349 } 350 compareFloatLiteral(String constant)351 private void compareFloatLiteral(String constant) throws Exception { 352 LiteralContext literal = parseLiteral(constant); 353 String token = literal.javaLiteral().getText(); 354 assertEquals(constant, token); 355 } 356 compareBoolLiteral(String constant)357 private void compareBoolLiteral(String constant) throws Exception { 358 LiteralContext literal = parseLiteral(constant); 359 String token = literal.javaLiteral().getText(); 360 assertEquals(constant, token); 361 } 362 parse(String value)363 private BindingSyntaxContext parse(String value) throws Exception { 364 return parseExpressionString(value); 365 } 366 parseExpression(String value)367 private <T extends ExpressionContext> T parseExpression(String value) throws Exception { 368 return (T) parse(value).accept(new BindingExpressionBaseVisitor<ExpressionContext>() { 369 @Override 370 public ExpressionContext visitRootExpr( 371 @NotNull BindingExpressionParser.RootExprContext ctx) { 372 return ctx.expression(); 373 } 374 }); 375 } 376 377 private PrimaryContext parsePrimary(String value) throws Exception { 378 return parseExpression(value); 379 } 380 381 private LiteralContext parseLiteral(String value) throws Exception { 382 return parsePrimary(value).literal(); 383 } 384 385 BindingExpressionParser.BindingSyntaxContext parseExpressionString(String s) throws Exception { 386 ANTLRInputStream input = new ANTLRInputStream(new StringReader(s)); 387 BindingExpressionLexer lexer = new BindingExpressionLexer(input); 388 CommonTokenStream tokens = new CommonTokenStream(lexer); 389 BindingExpressionParser parser = new BindingExpressionParser(tokens); 390 return parser.bindingSyntax(); 391 } 392 }