/* * Copyright (C) 2015 The Android Open Source Project * * 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 android.databinding; import android.databinding.parser.BindingExpressionLexer; import android.databinding.parser.BindingExpressionParser; import android.databinding.parser.BindingExpressionParser.AndOrOpContext; import android.databinding.parser.BindingExpressionParser.BinaryOpContext; import android.databinding.parser.BindingExpressionParser.BindingSyntaxContext; import android.databinding.parser.BindingExpressionParser.BitShiftOpContext; import android.databinding.parser.BindingExpressionParser.ComparisonOpContext; import android.databinding.parser.BindingExpressionParser.DefaultsContext; import android.databinding.parser.BindingExpressionParser.DotOpContext; import android.databinding.parser.BindingExpressionParser.ExpressionContext; import android.databinding.parser.BindingExpressionParser.GroupingContext; import android.databinding.parser.BindingExpressionParser.LiteralContext; import android.databinding.parser.BindingExpressionParser.MathOpContext; import android.databinding.parser.BindingExpressionParser.PrimaryContext; import android.databinding.parser.BindingExpressionParser.PrimitiveTypeContext; import android.databinding.parser.BindingExpressionParser.QuestionQuestionOpContext; import android.databinding.parser.BindingExpressionParser.ResourceContext; import android.databinding.parser.BindingExpressionParser.StringLiteralContext; import android.databinding.parser.BindingExpressionParser.TernaryOpContext; import android.databinding.parser.BindingExpressionParser.UnaryOpContext; import org.antlr.v4.runtime.ANTLRInputStream; import org.antlr.v4.runtime.CommonTokenStream; import org.antlr.v4.runtime.Token; import org.antlr.v4.runtime.tree.TerminalNode; import org.junit.Test; import java.io.StringReader; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNotNull; import static org.junit.Assert.assertNull; import static org.junit.Assert.assertTrue; public class BindingExpressionParserTest { @Test public void testSingleQuoteStringLiteral() throws Exception { String expr = "`test`"; LiteralContext literal = parseLiteral(expr); assertNotNull(literal); StringLiteralContext stringLiteral = literal.stringLiteral(); assertNotNull(stringLiteral); TerminalNode singleQuote = stringLiteral.SingleQuoteString(); Token token = singleQuote.getSymbol(); assertEquals("`test`", token.getText()); } @Test public void testDoubleQuoteStringLiteral() throws Exception { String expr = "\"test\""; LiteralContext literal = parseLiteral(expr); StringLiteralContext stringLiteral = literal.stringLiteral(); TerminalNode singleQuote = stringLiteral.DoubleQuoteString(); Token token = singleQuote.getSymbol(); assertEquals("\"test\"", token.getText()); } @Test public void testSingleQuoteEscapeStringLiteral() throws Exception { String expr = "`\"t\\`est\"`"; LiteralContext literal = parseLiteral(expr); StringLiteralContext stringLiteral = literal.stringLiteral(); TerminalNode singleQuote = stringLiteral.SingleQuoteString(); Token token = singleQuote.getSymbol(); assertEquals("`\"t\\`est\"`", token.getText()); } @Test public void testCharLiteral() throws Exception { LiteralContext literal = parseLiteral("'c'"); assertEquals("'c'", literal.getText()); literal = parseLiteral("'\\u0054'"); assertEquals("'\\u0054'", literal.getText()); literal = parseLiteral("'\\''"); assertEquals("'\\''", literal.getText()); } @Test public void testIntLiterals() throws Exception { compareIntLiteral("123"); compareIntLiteral("123l"); compareIntLiteral("1_2_3l"); compareIntLiteral("123L"); compareIntLiteral("0xdeadbeef"); compareIntLiteral("0xdeadbeefl"); compareIntLiteral("0Xdeadbeef"); compareIntLiteral("0xdead_beefl"); compareIntLiteral("0xdead_beefL"); compareIntLiteral("01234567"); compareIntLiteral("01234567L"); compareIntLiteral("01234567l"); compareIntLiteral("0123_45_67l"); compareIntLiteral("0b0101"); compareIntLiteral("0b0101_0101"); compareIntLiteral("0B0101_0101"); compareIntLiteral("0B0101_0101L"); compareIntLiteral("0B0101_0101l"); } @Test public void testFloatLiterals() throws Exception { compareFloatLiteral("0.12345"); compareFloatLiteral("0.12345f"); compareFloatLiteral("0.12345F"); compareFloatLiteral("132450.12345F"); compareFloatLiteral("132450.12345"); compareFloatLiteral("132450e123"); compareFloatLiteral("132450.4e123"); } @Test public void testBoolLiterals() throws Exception { compareBoolLiteral("true"); compareBoolLiteral("false"); } @Test public void testNullLiteral() throws Exception { LiteralContext literal = parseLiteral("null"); String token = literal.getText(); assertEquals("null", token); } @Test public void testVoidExtraction() throws Exception { PrimaryContext primary = parsePrimary("void.class"); assertNotNull(primary.classExtraction()); assertNull(primary.classExtraction().type()); assertEquals("void", primary.classExtraction().getChild(0).getText()); } @Test public void testPrimitiveClassExtraction() throws Exception { PrimaryContext primary = parsePrimary("int.class"); PrimitiveTypeContext type = primary.classExtraction().type().primitiveType(); assertEquals("int", type.getText()); } @Test public void testIdentifier() throws Exception { PrimaryContext primary = parsePrimary("abcdEfg"); assertEquals("abcdEfg", primary.identifier().getText()); } @Test public void testUnaryOperators() throws Exception { compareUnaryOperators("+"); compareUnaryOperators("-"); compareUnaryOperators("!"); compareUnaryOperators("~"); } @Test public void testMathOperators() throws Exception { compareMathOperators("+"); compareMathOperators("-"); compareMathOperators("*"); compareMathOperators("/"); compareMathOperators("%"); } @Test public void testBitShiftOperators() throws Exception { compareBitShiftOperators(">>>"); compareBitShiftOperators("<<"); compareBitShiftOperators(">>"); } @Test public void testComparisonShiftOperators() throws Exception { compareComparisonOperators("<"); compareComparisonOperators(">"); compareComparisonOperators("<="); compareComparisonOperators(">="); compareComparisonOperators("=="); compareComparisonOperators("!="); } @Test public void testAndOrOperators() throws Exception { compareAndOrOperators("&&"); compareAndOrOperators("||"); } @Test public void testBinaryOperators() throws Exception { compareBinaryOperators("&"); compareBinaryOperators("|"); compareBinaryOperators("^"); } @Test public void testTernaryOperator() throws Exception { TernaryOpContext expression = parseExpression("true ? 1 : 0"); assertEquals(5, expression.getChildCount()); assertEquals("true", ((PrimaryContext) expression.left).literal().javaLiteral().getText()); assertEquals("?", expression.op.getText()); assertEquals("1", ((PrimaryContext) expression.iftrue).literal().javaLiteral().getText()); assertEquals(":", expression.getChild(3).getText()); assertEquals("0", ((PrimaryContext) expression.iffalse).literal().javaLiteral().getText()); } @Test public void testDot() throws Exception { DotOpContext expression = parseExpression("one.two.three"); assertEquals(3, expression.getChildCount()); assertEquals("three", expression.Identifier().getText()); assertEquals(".", expression.getChild(1).getText()); DotOpContext left = (DotOpContext) expression.expression(); assertEquals("two", left.Identifier().getText()); assertEquals(".", left.getChild(1).getText()); assertEquals("one", ((PrimaryContext) left.expression()).identifier().getText()); } @Test public void testQuestionQuestion() throws Exception { QuestionQuestionOpContext expression = parseExpression("one ?? two"); assertEquals(3, expression.getChildCount()); assertEquals("one", ((PrimaryContext) expression.left).identifier().getText()); assertEquals("two", ((PrimaryContext) expression.right).identifier().getText()); assertEquals("??", expression.op.getText()); } @Test public void testResourceReference() throws Exception { compareResource("@id/foo_bar"); compareResource("@transition/foo_bar"); compareResource("@anim/foo_bar"); compareResource("@animator/foo_bar"); compareResource("@android:id/foo_bar"); compareResource("@app:id/foo_bar"); } @Test public void testDefaults() throws Exception { BindingSyntaxContext syntax = parseExpressionString("foo.bar, default = @id/foo_bar"); DefaultsContext defaults = syntax.defaults(); assertEquals("@id/foo_bar", defaults.constantValue().ResourceReference().getText()); } @Test public void testParentheses() throws Exception { GroupingContext grouping = parseExpression("(1234)"); assertEquals("1234", grouping.expression().getText()); } // ---------------------- Helpers -------------------- private void compareResource(String value) throws Exception { ResourceContext resourceContext = parseExpression(value); assertEquals(value, resourceContext.getText()); } private void compareUnaryOperators(String op) throws Exception { UnaryOpContext expression = parseExpression(op + " 2"); assertEquals(2, expression.getChildCount()); assertEquals(op, expression.op.getText()); assertEquals("2", ((PrimaryContext) expression.expression()).literal().javaLiteral() .getText()); } private void compareBinaryOperators(String op) throws Exception { BinaryOpContext expression = parseExpression("1 " + op + " 2"); assertEquals(3, expression.getChildCount()); assertTrue(expression.left instanceof ExpressionContext); String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); assertEquals("1", one); assertEquals(op, expression.op.getText()); assertTrue(expression.right instanceof ExpressionContext); String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); assertEquals("2", two); } private void compareMathOperators(String op) throws Exception { MathOpContext expression = parseExpression("1 " + op + " 2"); assertEquals(3, expression.getChildCount()); assertTrue(expression.left instanceof ExpressionContext); String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); assertEquals("1", one); assertEquals(op, expression.op.getText()); assertTrue(expression.right instanceof ExpressionContext); String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); assertEquals("2", two); } private void compareBitShiftOperators(String op) throws Exception { BitShiftOpContext expression = parseExpression("1 " + op + " 2"); assertEquals(3, expression.getChildCount()); assertTrue(expression.left instanceof ExpressionContext); String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); assertEquals("1", one); assertEquals(op, expression.op.getText()); assertTrue(expression.right instanceof ExpressionContext); String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); assertEquals("2", two); } private void compareComparisonOperators(String op) throws Exception { ComparisonOpContext expression = parseExpression("1 " + op + " 2"); assertEquals(3, expression.getChildCount()); assertTrue(expression.left instanceof ExpressionContext); String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); assertEquals("1", one); assertEquals(op, expression.op.getText()); assertTrue(expression.right instanceof ExpressionContext); String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); assertEquals("2", two); } private void compareAndOrOperators(String op) throws Exception { AndOrOpContext expression = parseExpression("1 " + op + " 2"); assertEquals(3, expression.getChildCount()); assertTrue(expression.left instanceof ExpressionContext); String one = ((PrimaryContext) expression.left).literal().javaLiteral().getText(); assertEquals("1", one); assertEquals(op, expression.op.getText()); assertTrue(expression.right instanceof ExpressionContext); String two = ((PrimaryContext) expression.right).literal().javaLiteral().getText(); assertEquals("2", two); } private void compareIntLiteral(String constant) throws Exception { LiteralContext literal = parseLiteral(constant); String token = literal.javaLiteral().getText(); assertEquals(constant, token); } private void compareFloatLiteral(String constant) throws Exception { LiteralContext literal = parseLiteral(constant); String token = literal.javaLiteral().getText(); assertEquals(constant, token); } private void compareBoolLiteral(String constant) throws Exception { LiteralContext literal = parseLiteral(constant); String token = literal.javaLiteral().getText(); assertEquals(constant, token); } private BindingSyntaxContext parse(String value) throws Exception { return parseExpressionString(value); } private <T extends ExpressionContext> T parseExpression(String value) throws Exception { ExpressionContext expressionContext = parse(value).expression(); return (T) expressionContext; } private PrimaryContext parsePrimary(String value) throws Exception { return parseExpression(value); } private LiteralContext parseLiteral(String value) throws Exception { return parsePrimary(value).literal(); } BindingExpressionParser.BindingSyntaxContext parseExpressionString(String s) throws Exception { ANTLRInputStream input = new ANTLRInputStream(new StringReader(s)); BindingExpressionLexer lexer = new BindingExpressionLexer(input); CommonTokenStream tokens = new CommonTokenStream(lexer); BindingExpressionParser parser = new BindingExpressionParser(tokens); return parser.bindingSyntax(); } }