Home | History | Annotate | Download | only in expr
      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 
     17 package android.databinding.tool.expr;
     18 
     19 import android.databinding.Bindable;
     20 import android.databinding.Observable;
     21 import android.databinding.tool.LayoutBinder;
     22 import android.databinding.tool.MockLayoutBinder;
     23 import android.databinding.tool.reflection.ModelAnalyzer;
     24 import android.databinding.tool.reflection.ModelClass;
     25 import android.databinding.tool.reflection.java.JavaAnalyzer;
     26 import android.databinding.tool.store.Location;
     27 import android.databinding.tool.util.L;
     28 import android.databinding.tool.writer.KCode;
     29 
     30 import org.junit.Before;
     31 import org.junit.Rule;
     32 import org.junit.Test;
     33 import org.junit.rules.TestWatcher;
     34 import org.junit.runner.Description;
     35 
     36 import java.util.ArrayList;
     37 import java.util.Arrays;
     38 import java.util.BitSet;
     39 import java.util.Collections;
     40 import java.util.List;
     41 
     42 import static org.junit.Assert.assertEquals;
     43 import static org.junit.Assert.assertFalse;
     44 import static org.junit.Assert.assertNotNull;
     45 import static org.junit.Assert.assertNull;
     46 import static org.junit.Assert.assertSame;
     47 import static org.junit.Assert.assertTrue;
     48 
     49 public class ExprModelTest {
     50 
     51     private static class DummyExpr extends Expr {
     52 
     53         String mKey;
     54 
     55         public DummyExpr(String key, DummyExpr... children) {
     56             super(children);
     57             mKey = key;
     58         }
     59 
     60         @Override
     61         protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
     62             return modelAnalyzer.findClass(Integer.class);
     63         }
     64 
     65         @Override
     66         protected List<Dependency> constructDependencies() {
     67             return constructDynamicChildrenDependencies();
     68         }
     69 
     70         @Override
     71         protected String computeUniqueKey() {
     72             return mKey + super.computeUniqueKey();
     73         }
     74 
     75         @Override
     76         protected KCode generateCode(boolean full) {
     77             return new KCode();
     78         }
     79 
     80         @Override
     81         protected String getInvertibleError() {
     82             return "DummyExpr cannot be 2-way.";
     83         }
     84     }
     85 
     86     ExprModel mExprModel;
     87 
     88     @Rule
     89     public TestWatcher mTestWatcher = new TestWatcher() {
     90         @Override
     91         protected void failed(Throwable e, Description description) {
     92             if (mExprModel != null && mExprModel.getFlagMapping() != null) {
     93                 final String[] mapping = mExprModel.getFlagMapping();
     94                 for (int i = 0; i < mapping.length; i++) {
     95                     L.d("flag %d: %s", i, mapping[i]);
     96                 }
     97             }
     98         }
     99     };
    100 
    101     @Before
    102     public void setUp() throws Exception {
    103         JavaAnalyzer.initForTests();
    104         mExprModel = new ExprModel();
    105     }
    106 
    107     @Test
    108     public void testAddNormal() {
    109         final DummyExpr d = new DummyExpr("a");
    110         assertSame(d, mExprModel.register(d));
    111         assertSame(d, mExprModel.register(d));
    112         assertEquals(1, mExprModel.mExprMap.size());
    113     }
    114 
    115     @Test
    116     public void testAddDupe1() {
    117         final DummyExpr d = new DummyExpr("a");
    118         assertSame(d, mExprModel.register(d));
    119         assertSame(d, mExprModel.register(new DummyExpr("a")));
    120         assertEquals(1, mExprModel.mExprMap.size());
    121     }
    122 
    123     @Test
    124     public void testAddMultiple() {
    125         mExprModel.register(new DummyExpr("a"));
    126         mExprModel.register(new DummyExpr("b"));
    127         assertEquals(2, mExprModel.mExprMap.size());
    128     }
    129 
    130 
    131     @Test
    132     public void testAddWithChildren() {
    133         DummyExpr a = new DummyExpr("a");
    134         DummyExpr b = new DummyExpr("b");
    135         DummyExpr c = new DummyExpr("c", a, b);
    136         mExprModel.register(c);
    137         DummyExpr a2 = new DummyExpr("a");
    138         DummyExpr b2 = new DummyExpr("b");
    139         DummyExpr c2 = new DummyExpr("c", a, b);
    140         assertEquals(c, mExprModel.register(c2));
    141     }
    142 
    143     @Test
    144     public void testShouldRead() {
    145         MockLayoutBinder lb = new MockLayoutBinder();
    146         mExprModel = lb.getModel();
    147         IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
    148         IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
    149         IdentifierExpr c = lb.addVariable("c", "java.lang.String", null);
    150         lb.parse("a == null ? b : c", false, null);
    151         mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class));
    152         lb.getModel().seal();
    153         List<Expr> shouldRead = getShouldRead();
    154         // a and a == null
    155         assertEquals(2, shouldRead.size());
    156         final List<Expr> readFirst = getReadFirst(shouldRead, null);
    157         assertEquals(1, readFirst.size());
    158         final Expr first = readFirst.get(0);
    159         assertSame(a, first);
    160         // now , assume we've read this
    161         final BitSet shouldReadFlags = first.getShouldReadFlags();
    162         assertNotNull(shouldReadFlags);
    163     }
    164 
    165     @Test
    166     public void testReadConstantTernary() {
    167         MockLayoutBinder lb = new MockLayoutBinder();
    168         mExprModel = lb.getModel();
    169         IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
    170         IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
    171         TernaryExpr ternaryExpr = parse(lb, "true ? a : b", TernaryExpr.class);
    172         mExprModel.seal();
    173         List<Expr> shouldRead = getShouldRead();
    174         assertExactMatch(shouldRead, ternaryExpr.getPred());
    175         List<Expr> first = getReadFirst(shouldRead);
    176         assertExactMatch(first, ternaryExpr.getPred());
    177         mExprModel.markBitsRead();
    178         shouldRead = getShouldRead();
    179         assertExactMatch(shouldRead, a, b, ternaryExpr);
    180         first = getReadFirst(shouldRead);
    181         assertExactMatch(first, a, b);
    182         List<Expr> justRead = new ArrayList<Expr>();
    183         justRead.add(a);
    184         justRead.add(b);
    185         first = filterOut(getReadFirst(shouldRead, justRead), justRead);
    186         assertExactMatch(first, ternaryExpr);
    187         assertFalse(mExprModel.markBitsRead());
    188     }
    189 
    190     @Test
    191     public void testTernaryWithPlus() {
    192         MockLayoutBinder lb = new MockLayoutBinder();
    193         mExprModel = lb.getModel();
    194         IdentifierExpr user = lb
    195                 .addVariable("user", "android.databinding.tool.expr.ExprModelTest.User",
    196                         null);
    197         MathExpr parsed = parse(lb, "user.name + \" \" + (user.lastName ?? \"\")", MathExpr.class);
    198         mExprModel.seal();
    199         List<Expr> toRead = getShouldRead();
    200         List<Expr> readNow = getReadFirst(toRead);
    201         assertEquals(1, readNow.size());
    202         assertSame(user, readNow.get(0));
    203         List<Expr> justRead = new ArrayList<Expr>();
    204         justRead.add(user);
    205         readNow = filterOut(getReadFirst(toRead, justRead), justRead);
    206         assertEquals(2, readNow.size()); //user.name && user.lastName
    207         justRead.addAll(readNow);
    208         // user.lastname (T, F), user.name + " "
    209         readNow = filterOut(getReadFirst(toRead, justRead), justRead);
    210         assertEquals(2, readNow.size()); //user.name && user.lastName
    211         justRead.addAll(readNow);
    212         readNow = filterOut(getReadFirst(toRead, justRead), justRead);
    213         assertEquals(0, readNow.size());
    214         mExprModel.markBitsRead();
    215 
    216         toRead = getShouldRead();
    217         assertEquals(2, toRead.size());
    218         justRead.clear();
    219         readNow = filterOut(getReadFirst(toRead, justRead), justRead);
    220         assertEquals(1, readNow.size());
    221         assertSame(parsed.getRight(), readNow.get(0));
    222         justRead.addAll(readNow);
    223 
    224         readNow = filterOut(getReadFirst(toRead, justRead), justRead);
    225         assertEquals(1, readNow.size());
    226         assertSame(parsed, readNow.get(0));
    227         justRead.addAll(readNow);
    228 
    229         readNow = filterOut(getReadFirst(toRead, justRead), justRead);
    230         assertEquals(0, readNow.size());
    231         mExprModel.markBitsRead();
    232         assertEquals(0, getShouldRead().size());
    233     }
    234 
    235     private List<Expr> filterOut(List<Expr> itr, final List<Expr> exclude) {
    236         List<Expr> result = new ArrayList<Expr>();
    237         for (Expr expr : itr) {
    238             if (!exclude.contains(expr)) {
    239                 result.add(expr);
    240             }
    241         }
    242         return result;
    243     }
    244 
    245     @Test
    246     public void testTernaryInsideTernary() {
    247         MockLayoutBinder lb = new MockLayoutBinder();
    248         mExprModel = lb.getModel();
    249         IdentifierExpr cond1 = lb.addVariable("cond1", "boolean", null);
    250         IdentifierExpr cond2 = lb.addVariable("cond2", "boolean", null);
    251 
    252         IdentifierExpr a = lb.addVariable("a", "boolean", null);
    253         IdentifierExpr b = lb.addVariable("b", "boolean", null);
    254         IdentifierExpr c = lb.addVariable("c", "boolean", null);
    255 
    256         final TernaryExpr ternaryExpr = parse(lb, "cond1 ? cond2 ? a : b : c", TernaryExpr.class);
    257         final TernaryExpr innerTernary = (TernaryExpr) ternaryExpr.getIfTrue();
    258         mExprModel.seal();
    259 
    260         List<Expr> toRead = getShouldRead();
    261         assertEquals(1, toRead.size());
    262         assertEquals(ternaryExpr.getPred(), toRead.get(0));
    263 
    264         List<Expr> readNow = getReadFirst(toRead);
    265         assertEquals(1, readNow.size());
    266         assertEquals(ternaryExpr.getPred(), readNow.get(0));
    267         int cond1True = ternaryExpr.getRequirementFlagIndex(true);
    268         int cond1False = ternaryExpr.getRequirementFlagIndex(false);
    269         // ok, it is read now.
    270         mExprModel.markBitsRead();
    271 
    272         // now it should read cond2 or c, depending on the flag from first
    273         toRead = getShouldRead();
    274         assertEquals(2, toRead.size());
    275         assertExactMatch(toRead, ternaryExpr.getIfFalse(), innerTernary.getPred());
    276         assertFlags(ternaryExpr.getIfFalse(), cond1False);
    277         assertFlags(ternaryExpr.getIfTrue(), cond1True);
    278 
    279         mExprModel.markBitsRead();
    280 
    281         // now it should read a or b, innerTernary, outerTernary
    282         toRead = getShouldRead();
    283         assertExactMatch(toRead, innerTernary.getIfTrue(), innerTernary.getIfFalse(), ternaryExpr,
    284                 innerTernary);
    285         assertFlags(innerTernary.getIfTrue(), innerTernary.getRequirementFlagIndex(true));
    286         assertFlags(innerTernary.getIfFalse(), innerTernary.getRequirementFlagIndex(false));
    287         assertFalse(mExprModel.markBitsRead());
    288     }
    289 
    290     @Test
    291     public void testRequirementFlags() {
    292         MockLayoutBinder lb = new MockLayoutBinder();
    293         mExprModel = lb.getModel();
    294         IdentifierExpr a = lb.addVariable("a", "java.lang.String", null);
    295         IdentifierExpr b = lb.addVariable("b", "java.lang.String", null);
    296         IdentifierExpr c = lb.addVariable("c", "java.lang.String", null);
    297         IdentifierExpr d = lb.addVariable("d", "java.lang.String", null);
    298         IdentifierExpr e = lb.addVariable("e", "java.lang.String", null);
    299         final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e", false, null);
    300         assertTrue(aTernary instanceof TernaryExpr);
    301         final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue();
    302         assertTrue(bTernary instanceof TernaryExpr);
    303         final Expr aIsNull = mExprModel
    304                 .comparison("==", a, mExprModel.symbol("null", Object.class));
    305         final Expr bIsNull = mExprModel
    306                 .comparison("==", b, mExprModel.symbol("null", Object.class));
    307         lb.getModel().seal();
    308         List<Expr> shouldRead = getShouldRead();
    309         // a and a == null
    310         assertEquals(2, shouldRead.size());
    311         assertFalse(a.getShouldReadFlags().isEmpty());
    312         assertTrue(a.getShouldReadFlags().get(a.getId()));
    313         assertTrue(b.getShouldReadFlags().isEmpty());
    314         assertTrue(c.getShouldReadFlags().isEmpty());
    315         assertTrue(d.getShouldReadFlags().isEmpty());
    316         assertTrue(e.getShouldReadFlags().isEmpty());
    317 
    318         List<Expr> readFirst = getReadFirst(shouldRead, null);
    319         assertEquals(1, readFirst.size());
    320         final Expr first = readFirst.get(0);
    321         assertSame(a, first);
    322         assertTrue(mExprModel.markBitsRead());
    323         for (Expr expr : mExprModel.getPendingExpressions()) {
    324             assertNull(expr.mShouldReadFlags);
    325         }
    326         shouldRead = getShouldRead();
    327         assertExactMatch(shouldRead, e, b, bIsNull);
    328 
    329         assertFlags(e, aTernary.getRequirementFlagIndex(false));
    330 
    331         assertFlags(b, aTernary.getRequirementFlagIndex(true));
    332         assertFlags(bIsNull, aTernary.getRequirementFlagIndex(true));
    333         assertTrue(mExprModel.markBitsRead());
    334         shouldRead = getShouldRead();
    335         assertEquals(4, shouldRead.size());
    336         assertTrue(shouldRead.contains(c));
    337         assertTrue(shouldRead.contains(d));
    338         assertTrue(shouldRead.contains(aTernary));
    339         assertTrue(shouldRead.contains(bTernary));
    340 
    341         assertTrue(c.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(true)));
    342         assertEquals(1, c.getShouldReadFlags().cardinality());
    343 
    344         assertTrue(d.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(false)));
    345         assertEquals(1, d.getShouldReadFlags().cardinality());
    346 
    347         assertTrue(bTernary.getShouldReadFlags().get(aTernary.getRequirementFlagIndex(true)));
    348         assertEquals(1, bTernary.getShouldReadFlags().cardinality());
    349         // +1 for invalidate all flag
    350         assertEquals(6, aTernary.getShouldReadFlags().cardinality());
    351         for (Expr expr : new Expr[]{a, b, c, d, e}) {
    352             assertTrue(aTernary.getShouldReadFlags().get(expr.getId()));
    353         }
    354 
    355         readFirst = getReadFirst(shouldRead);
    356         assertEquals(2, readFirst.size());
    357         assertTrue(readFirst.contains(c));
    358         assertTrue(readFirst.contains(d));
    359         assertFalse(mExprModel.markBitsRead());
    360     }
    361 
    362     @Test
    363     public void testPostConditionalDependencies() {
    364         MockLayoutBinder lb = new MockLayoutBinder();
    365         mExprModel = lb.getModel();
    366 
    367         IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName(), null);
    368         IdentifierExpr u2 = lb.addVariable("u2", User.class.getCanonicalName(), null);
    369         IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(), null);
    370         IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(), null);
    371         IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName(), null);
    372         IdentifierExpr d = lb.addVariable("d", int.class.getCanonicalName(), null);
    373         IdentifierExpr e = lb.addVariable("e", int.class.getCanonicalName(), null);
    374         TernaryExpr abTernary = parse(lb, "a > b ? u1.name : u2.name", TernaryExpr.class);
    375         TernaryExpr bcTernary = parse(lb, "b > c ? u1.getCond(d) ? u1.lastName : u2.lastName : `xx`"
    376                 + " + u2.getCond(e) ", TernaryExpr.class);
    377         Expr abCmp = abTernary.getPred();
    378         Expr bcCmp = bcTernary.getPred();
    379         Expr u1GetCondD = ((TernaryExpr) bcTernary.getIfTrue()).getPred();
    380         final MathExpr xxPlusU2getCondE = (MathExpr) bcTernary.getIfFalse();
    381         Expr u2GetCondE = xxPlusU2getCondE.getRight();
    382         Expr u1Name = abTernary.getIfTrue();
    383         Expr u2Name = abTernary.getIfFalse();
    384         Expr u1LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfTrue();
    385         Expr u2LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfFalse();
    386 
    387         mExprModel.seal();
    388         List<Expr> shouldRead = getShouldRead();
    389 
    390         assertExactMatch(shouldRead, a, b, c, abCmp, bcCmp);
    391 
    392         List<Expr> firstRead = getReadFirst(shouldRead);
    393 
    394         assertExactMatch(firstRead, a, b, c);
    395 
    396         assertFlags(a, a, b, u1, u2, u1Name, u2Name);
    397         assertFlags(b, a, b, u1, u2, u1Name, u2Name, c, d, u1LastName, u2LastName, e);
    398         assertFlags(c, b, c, u1, d, u1LastName, u2LastName, e);
    399         assertFlags(abCmp, a, b, u1, u2, u1Name, u2Name);
    400         assertFlags(bcCmp, b, c, u1, d, u1LastName, u2LastName, e);
    401 
    402         assertTrue(mExprModel.markBitsRead());
    403 
    404         shouldRead = getShouldRead();
    405         Expr[] batch = {d, e, u1, u2, u1GetCondD, u2GetCondE, xxPlusU2getCondE, abTernary,
    406                 abTernary.getIfTrue(), abTernary.getIfFalse()};
    407         assertExactMatch(shouldRead, batch);
    408         firstRead = getReadFirst(shouldRead);
    409         assertExactMatch(firstRead, d, e, u1, u2);
    410 
    411         assertFlags(d, bcTernary.getRequirementFlagIndex(true));
    412         assertFlags(e, bcTernary.getRequirementFlagIndex(false));
    413         assertFlags(u1, bcTernary.getRequirementFlagIndex(true),
    414                 abTernary.getRequirementFlagIndex(true));
    415         assertFlags(u2, bcTernary.getRequirementFlagIndex(false),
    416                 abTernary.getRequirementFlagIndex(false));
    417 
    418         assertFlags(u1GetCondD, bcTernary.getRequirementFlagIndex(true));
    419         assertFlags(u2GetCondE, bcTernary.getRequirementFlagIndex(false));
    420         assertFlags(xxPlusU2getCondE, bcTernary.getRequirementFlagIndex(false));
    421         assertFlags(abTernary, a, b, u1, u2, u1Name, u2Name);
    422         assertFlags(abTernary.getIfTrue(), abTernary.getRequirementFlagIndex(true));
    423         assertFlags(abTernary.getIfFalse(), abTernary.getRequirementFlagIndex(false));
    424 
    425         assertTrue(mExprModel.markBitsRead());
    426 
    427         shouldRead = getShouldRead();
    428         // FIXME: there is no real case to read u1 anymore because if b>c was not true,
    429         // u1.getCond(d) will never be set. Right now, we don't have mechanism to figure this out
    430         // and also it does not affect correctness (just an unnecessary if stmt)
    431         assertExactMatch(shouldRead, u1, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary);
    432         firstRead = getReadFirst(shouldRead);
    433         assertExactMatch(firstRead, u1, u2);
    434         assertFlags(u1, bcTernary.getIfTrue().getRequirementFlagIndex(true));
    435         assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false));
    436         assertFlags(u1LastName, bcTernary.getIfTrue().getRequirementFlagIndex(true));
    437         assertFlags(u2LastName, bcTernary.getIfTrue().getRequirementFlagIndex(false));
    438 
    439         assertFlags(bcTernary.getIfTrue(), bcTernary.getRequirementFlagIndex(true));
    440         assertFlags(bcTernary, b, c, u1, u2, d, u1LastName, u2LastName, e);
    441 
    442         assertFalse(mExprModel.markBitsRead());
    443     }
    444 
    445     @Test
    446     public void testCircularDependency() {
    447         MockLayoutBinder lb = new MockLayoutBinder();
    448         mExprModel = lb.getModel();
    449         IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(),
    450                 null);
    451         IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(),
    452                 null);
    453         final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class);
    454         mExprModel.seal();
    455         List<Expr> shouldRead = getShouldRead();
    456         assertExactMatch(shouldRead, a, abTernary.getPred());
    457         assertTrue(mExprModel.markBitsRead());
    458         shouldRead = getShouldRead();
    459         assertExactMatch(shouldRead, b, abTernary);
    460         assertFalse(mExprModel.markBitsRead());
    461     }
    462 
    463     @Test
    464     public void testNestedCircularDependency() {
    465         MockLayoutBinder lb = new MockLayoutBinder();
    466         mExprModel = lb.getModel();
    467         IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(),
    468                 null);
    469         IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(),
    470                 null);
    471         IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName(),
    472                 null);
    473         final TernaryExpr a3Ternary = parse(lb, "a > 3 ? c > 4 ? a : b : c", TernaryExpr.class);
    474         final TernaryExpr c4Ternary = (TernaryExpr) a3Ternary.getIfTrue();
    475         mExprModel.seal();
    476         List<Expr> shouldRead = getShouldRead();
    477         assertExactMatch(shouldRead, a, a3Ternary.getPred());
    478         assertTrue(mExprModel.markBitsRead());
    479         shouldRead = getShouldRead();
    480         assertExactMatch(shouldRead, c, c4Ternary.getPred());
    481         assertFlags(c, a3Ternary.getRequirementFlagIndex(true),
    482                 a3Ternary.getRequirementFlagIndex(false));
    483         assertFlags(c4Ternary.getPred(), a3Ternary.getRequirementFlagIndex(true));
    484     }
    485 
    486     @Test
    487     public void testInterExprDependency() {
    488         MockLayoutBinder lb = new MockLayoutBinder();
    489         mExprModel = lb.getModel();
    490         IdentifierExpr u = lb.addVariable("u", User.class.getCanonicalName(),
    491                 null);
    492         final Expr uComment = parse(lb, "u.comment", FieldAccessExpr.class);
    493         final TernaryExpr uTernary = parse(lb, "u.getUseComment ? u.comment : `xx`", TernaryExpr.class);
    494         mExprModel.seal();
    495         assertTrue(uTernary.getPred().canBeInvalidated());
    496         List<Expr> shouldRead = getShouldRead();
    497         assertExactMatch(shouldRead, u, uComment, uTernary.getPred());
    498         assertTrue(mExprModel.markBitsRead());
    499         shouldRead = getShouldRead();
    500         assertExactMatch(shouldRead, uComment, uTernary);
    501     }
    502 
    503     @Test
    504     public void testInterExprCircularDependency() {
    505         MockLayoutBinder lb = new MockLayoutBinder();
    506         mExprModel = lb.getModel();
    507         IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName(),
    508                 null);
    509         IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName(),
    510                 null);
    511         final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class);
    512         final TernaryExpr abTernary2 = parse(lb, "b > 3 ? b : a", TernaryExpr.class);
    513         mExprModel.seal();
    514         List<Expr> shouldRead = getShouldRead();
    515         assertExactMatch(shouldRead, a, b, abTernary.getPred(), abTernary2.getPred());
    516         assertTrue(mExprModel.markBitsRead());
    517         shouldRead = getShouldRead();
    518         assertExactMatch(shouldRead, abTernary, abTernary2);
    519     }
    520 
    521     @Test
    522     public void testInterExprCircularDependency2() {
    523         MockLayoutBinder lb = new MockLayoutBinder();
    524         mExprModel = lb.getModel();
    525         IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(),
    526                 null);
    527         IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(),
    528                 null);
    529         final TernaryExpr abTernary = parse(lb, "a ? b : true", TernaryExpr.class);
    530         final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class);
    531         mExprModel.seal();
    532         List<Expr> shouldRead = getShouldRead();
    533         assertExactMatch(shouldRead, a, b);
    534         assertFlags(a, a, b);
    535         assertFlags(b, a, b);
    536         List<Expr> readFirst = getReadFirst(shouldRead);
    537         assertExactMatch(readFirst, a, b);
    538         assertTrue(mExprModel.markBitsRead());
    539         shouldRead = getShouldRead();
    540         assertExactMatch(shouldRead, abTernary, baTernary);
    541         readFirst = getReadFirst(shouldRead);
    542         assertExactMatch(readFirst, abTernary, baTernary);
    543         assertFalse(mExprModel.markBitsRead());
    544         shouldRead = getShouldRead();
    545         assertEquals(0, shouldRead.size());
    546     }
    547 
    548     @Test
    549     public void testInterExprCircularDependency3() {
    550         MockLayoutBinder lb = new MockLayoutBinder();
    551         mExprModel = lb.getModel();
    552         IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(),
    553                 null);
    554         IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(),
    555                 null);
    556         IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(),
    557                 null);
    558         final TernaryExpr abTernary = parse(lb, "a ? b : c", TernaryExpr.class);
    559         final TernaryExpr abTernary2 = parse(lb, "b ? a : c", TernaryExpr.class);
    560         mExprModel.seal();
    561         List<Expr> shouldRead = getShouldRead();
    562         assertExactMatch(shouldRead, a, b);
    563         assertTrue(mExprModel.markBitsRead());
    564         shouldRead = getShouldRead();
    565         // read a and b again, this time for their dependencies and also the rest since everything
    566         // is ready to be read
    567         assertExactMatch(shouldRead, c, abTernary, abTernary2);
    568         mExprModel.markBitsRead();
    569         shouldRead = getShouldRead();
    570         assertEquals(0, shouldRead.size());
    571     }
    572 
    573     @Test
    574     public void testInterExprCircularDependency4() {
    575         MockLayoutBinder lb = new MockLayoutBinder();
    576         mExprModel = lb.getModel();
    577         IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(),
    578                 null);
    579         IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(),
    580                 null);
    581         IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(),
    582                 null);
    583         IdentifierExpr d = lb.addVariable("d", boolean.class.getCanonicalName(),
    584                 null);
    585         final TernaryExpr cTernary = parse(lb, "c ? (a ? d : false) : false", TernaryExpr.class);
    586         final TernaryExpr abTernary = parse(lb, "a ? b : true", TernaryExpr.class);
    587         final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class);
    588         mExprModel.seal();
    589         List<Expr> shouldRead = getShouldRead();
    590         // check if a,b or c should be read. these are easily calculated from binding expressions'
    591         // invalidation
    592         assertExactMatch(shouldRead, c, a, b);
    593 
    594         List<Expr> justRead = new ArrayList<Expr>();
    595         List<Expr> readFirst = getReadFirst(shouldRead);
    596         assertExactMatch(readFirst, c, a, b);
    597         Collections.addAll(justRead, a, b, c);
    598         assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size());
    599         assertTrue(mExprModel.markBitsRead());
    600         shouldRead = getShouldRead();
    601         // if a and b are not invalid, a won't be read in the first step. But if c's expression
    602         // is invalid and c == true, a must be read. Depending on a, d might be read as well.
    603         // don't need to read b anymore because `a ? b : true` and `b ? a : false` has the same
    604         // invalidation flags.
    605         assertExactMatch(shouldRead, a, abTernary, baTernary);
    606         justRead.clear();
    607 
    608         readFirst = getReadFirst(shouldRead);
    609         // first must read `a`.
    610         assertExactMatch(readFirst, a);
    611         Collections.addAll(justRead, a);
    612 
    613         readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
    614         assertExactMatch(readFirst, abTernary, baTernary);
    615         Collections.addAll(justRead, abTernary, baTernary);
    616 
    617         readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
    618         assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size());
    619         assertTrue(mExprModel.markBitsRead());
    620 
    621         shouldRead = getShouldRead();
    622         // now we can read adf ternary and c ternary
    623         justRead.clear();
    624         assertExactMatch(shouldRead, d, cTernary.getIfTrue(), cTernary);
    625         readFirst = getReadFirst(shouldRead);
    626         assertExactMatch(readFirst, d);
    627         Collections.addAll(justRead, d);
    628         readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
    629         assertExactMatch(readFirst, cTernary.getIfTrue());
    630         Collections.addAll(justRead, cTernary.getIfTrue());
    631 
    632         readFirst = filterOut(getReadFirst(shouldRead, justRead), justRead);
    633         assertExactMatch(readFirst, cTernary);
    634         Collections.addAll(justRead, cTernary);
    635 
    636         assertEquals(0, filterOut(getReadFirst(shouldRead, justRead), justRead).size());
    637 
    638         assertFalse(mExprModel.markBitsRead());
    639     }
    640 
    641     @Test
    642     public void testInterExprDeepDependency() {
    643         MockLayoutBinder lb = new MockLayoutBinder();
    644         mExprModel = lb.getModel();
    645         IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), null);
    646         IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), null);
    647         IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), null);
    648         final TernaryExpr t1 = parse(lb, "c ? (a ? b : true) : false", TernaryExpr.class);
    649         final TernaryExpr t2 = parse(lb, "c ? (b ? a : false) : true", TernaryExpr.class);
    650         final TernaryExpr abTernary = (TernaryExpr) t1.getIfTrue();
    651         final TernaryExpr baTernary = (TernaryExpr) t2.getIfTrue();
    652         mExprModel.seal();
    653         List<Expr> shouldRead = getShouldRead();
    654         assertExactMatch(shouldRead, c);
    655         assertTrue(mExprModel.markBitsRead());
    656         shouldRead = getShouldRead();
    657         assertExactMatch(shouldRead, a, b);
    658         assertTrue(mExprModel.markBitsRead());
    659         shouldRead = getShouldRead();
    660         assertExactMatch(shouldRead, a, b, t1.getIfTrue(), t2.getIfTrue(), t1, t2);
    661         assertFlags(b, abTernary.getRequirementFlagIndex(true));
    662         assertFlags(a, baTernary.getRequirementFlagIndex(true));
    663         assertFalse(mExprModel.markBitsRead());
    664     }
    665 
    666     @Test
    667     public void testInterExprDependencyNotReadyYet() {
    668         MockLayoutBinder lb = new MockLayoutBinder();
    669         mExprModel = lb.getModel();
    670         IdentifierExpr a = lb.addVariable("a", boolean.class.getCanonicalName(), null);
    671         IdentifierExpr b = lb.addVariable("b", boolean.class.getCanonicalName(), null);
    672         IdentifierExpr c = lb.addVariable("c", boolean.class.getCanonicalName(), null);
    673         IdentifierExpr d = lb.addVariable("d", boolean.class.getCanonicalName(), null);
    674         IdentifierExpr e = lb.addVariable("e", boolean.class.getCanonicalName(), null);
    675         final TernaryExpr cTernary = parse(lb, "c ? (a ? d : false) : false", TernaryExpr.class);
    676         final TernaryExpr baTernary = parse(lb, "b ? a : false", TernaryExpr.class);
    677         final TernaryExpr eaTernary = parse(lb, "e ? a : false", TernaryExpr.class);
    678         mExprModel.seal();
    679         List<Expr> shouldRead = getShouldRead();
    680         assertExactMatch(shouldRead, b, c, e);
    681         assertTrue(mExprModel.markBitsRead());
    682         shouldRead = getShouldRead();
    683         assertExactMatch(shouldRead, a, baTernary, eaTernary);
    684         assertTrue(mExprModel.markBitsRead());
    685         shouldRead = getShouldRead();
    686         assertExactMatch(shouldRead, d, cTernary.getIfTrue(), cTernary);
    687         assertFalse(mExprModel.markBitsRead());
    688     }
    689 
    690     @Test
    691     public void testNoFlagsForNonBindingStatic() {
    692         MockLayoutBinder lb = new MockLayoutBinder();
    693         mExprModel = lb.getModel();
    694         lb.addVariable("a", int.class.getCanonicalName(), null);
    695         final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
    696         mExprModel.seal();
    697         // +1 for invalidate all flag
    698         assertEquals(1, parsed.getRight().getInvalidFlags().cardinality());
    699         // +1 for invalidate all flag
    700         assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality());
    701         // +1 for invalidate all flag
    702         assertEquals(2, mExprModel.getInvalidateableFieldLimit());
    703     }
    704 
    705     @Test
    706     public void testFlagsForBindingStatic() {
    707         MockLayoutBinder lb = new MockLayoutBinder();
    708         mExprModel = lb.getModel();
    709         lb.addVariable("a", int.class.getCanonicalName(), null);
    710         final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class);
    711         final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
    712         mExprModel.seal();
    713         assertTrue(staticParsed.isBindingExpression());
    714         // +1 for invalidate all flag
    715         assertEquals(1, staticParsed.getInvalidFlags().cardinality());
    716         assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags());
    717         // +1 for invalidate all flag
    718         assertEquals(2, parsed.getLeft().getInvalidFlags().cardinality());
    719         // +1 for invalidate all flag
    720         assertEquals(2, mExprModel.getInvalidateableFieldLimit());
    721     }
    722 
    723     @Test
    724     public void testFinalFieldOfAVariable() {
    725         MockLayoutBinder lb = new MockLayoutBinder();
    726         mExprModel = lb.getModel();
    727         IdentifierExpr user = lb.addVariable("user", User.class.getCanonicalName(),
    728                 null);
    729         Expr fieldGet = parse(lb, "user.finalField", FieldAccessExpr.class);
    730         mExprModel.seal();
    731         assertTrue(fieldGet.isDynamic());
    732         // read user
    733         assertExactMatch(getShouldRead(), user, fieldGet);
    734         mExprModel.markBitsRead();
    735         // no need to read user.finalField
    736         assertEquals(0, getShouldRead().size());
    737     }
    738 
    739     @Test
    740     public void testFinalFieldOfAField() {
    741         MockLayoutBinder lb = new MockLayoutBinder();
    742         mExprModel = lb.getModel();
    743         lb.addVariable("user", User.class.getCanonicalName(), null);
    744         Expr finalFieldGet = parse(lb, "user.subObj.finalField", FieldAccessExpr.class);
    745         mExprModel.seal();
    746         assertTrue(finalFieldGet.isDynamic());
    747         Expr userSubObjGet = finalFieldGet.getChildren().get(0);
    748         // read user
    749         List<Expr> shouldRead = getShouldRead();
    750         assertEquals(3, shouldRead.size());
    751         assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet,
    752                 finalFieldGet);
    753         mExprModel.markBitsRead();
    754         // no need to read user.subObj.finalField because it is final
    755         assertEquals(0, getShouldRead().size());
    756     }
    757 
    758     @Test
    759     public void testFinalFieldOfAMethod() {
    760         MockLayoutBinder lb = new MockLayoutBinder();
    761         mExprModel = lb.getModel();
    762         lb.addVariable("user", User.class.getCanonicalName(), null);
    763         Expr finalFieldGet = parse(lb, "user.anotherSubObj.finalField", FieldAccessExpr.class);
    764         mExprModel.seal();
    765         assertTrue(finalFieldGet.isDynamic());
    766         Expr userSubObjGet = finalFieldGet.getChildren().get(0);
    767         // read user
    768         List<Expr> shouldRead = getShouldRead();
    769         assertEquals(3, shouldRead.size());
    770         assertExactMatch(shouldRead, userSubObjGet.getChildren().get(0), userSubObjGet,
    771                 finalFieldGet);
    772         mExprModel.markBitsRead();
    773         // no need to read user.subObj.finalField because it is final
    774         assertEquals(0, getShouldRead().size());
    775     }
    776 
    777     @Test
    778     public void testFinalOfAClass() {
    779         MockLayoutBinder lb = new MockLayoutBinder();
    780         mExprModel = lb.getModel();
    781         mExprModel.addImport("View", "android.view.View", null);
    782         FieldAccessExpr fieldAccess = parse(lb, "View.VISIBLE", FieldAccessExpr.class);
    783         assertFalse(fieldAccess.isDynamic());
    784         mExprModel.seal();
    785         assertEquals(0, getShouldRead().size());
    786     }
    787 
    788     @Test
    789     public void testStaticFieldOfInstance() {
    790         MockLayoutBinder lb = new MockLayoutBinder();
    791         mExprModel = lb.getModel();
    792         lb.addVariable("myView", "android.view.View", null);
    793         FieldAccessExpr fieldAccess = parse(lb, "myView.VISIBLE", FieldAccessExpr.class);
    794         assertFalse(fieldAccess.isDynamic());
    795         mExprModel.seal();
    796         assertEquals(0, getShouldRead().size());
    797         final Expr child = fieldAccess.getChild();
    798         assertTrue(child instanceof StaticIdentifierExpr);
    799         StaticIdentifierExpr id = (StaticIdentifierExpr) child;
    800         assertEquals(id.getResolvedType().getCanonicalName(), "android.view.View");
    801         // on demand import
    802         assertEquals("android.view.View", mExprModel.getImports().get("View"));
    803     }
    804 
    805     @Test
    806     public void testOnDemandImportConflict() {
    807         MockLayoutBinder lb = new MockLayoutBinder();
    808         mExprModel = lb.getModel();
    809         final IdentifierExpr myView = lb.addVariable("u", "android.view.View",
    810                 null);
    811         mExprModel.addImport("View", User.class.getCanonicalName(), null);
    812         final StaticIdentifierExpr id = mExprModel.staticIdentifierFor(myView.getResolvedType());
    813         mExprModel.seal();
    814         // on demand import with conflict
    815         assertEquals("android.view.View", mExprModel.getImports().get("View1"));
    816         assertEquals("View1", id.getName());
    817         assertEquals("android.view.View", id.getUserDefinedType());
    818     }
    819 
    820     @Test
    821     public void testOnDemandImportAlreadyImported() {
    822         MockLayoutBinder lb = new MockLayoutBinder();
    823         mExprModel = lb.getModel();
    824         final StaticIdentifierExpr ux = mExprModel.addImport("UX", User.class.getCanonicalName(),
    825                 null);
    826         final IdentifierExpr u = lb.addVariable("u", User.class.getCanonicalName(),
    827                 null);
    828         final StaticIdentifierExpr id = mExprModel.staticIdentifierFor(u.getResolvedType());
    829         mExprModel.seal();
    830         // on demand import with conflict
    831         assertSame(ux, id);
    832     }
    833 
    834     @Test
    835     public void testStaticMethodOfInstance() {
    836         MockLayoutBinder lb = new MockLayoutBinder();
    837         mExprModel = lb.getModel();
    838         lb.addVariable("user", User.class.getCanonicalName(), null);
    839         MethodCallExpr methodCall = parse(lb, "user.ourStaticMethod()", MethodCallExpr.class);
    840         assertTrue(methodCall.isDynamic());
    841         mExprModel.seal();
    842         final Expr child = methodCall.getTarget();
    843         assertTrue(child instanceof StaticIdentifierExpr);
    844         StaticIdentifierExpr id = (StaticIdentifierExpr) child;
    845         assertEquals(id.getResolvedType().getCanonicalName(), User.class.getCanonicalName());
    846     }
    847 
    848     @Test
    849     public void testFinalOfStaticField() {
    850         MockLayoutBinder lb = new MockLayoutBinder();
    851         mExprModel = lb.getModel();
    852         mExprModel.addImport("UX", User.class.getCanonicalName(), null);
    853         FieldAccessExpr fieldAccess = parse(lb, "UX.innerStaticInstance.finalStaticField",
    854                 FieldAccessExpr.class);
    855         assertFalse(fieldAccess.isDynamic());
    856         mExprModel.seal();
    857         // nothing to read since it is all final and static
    858         assertEquals(0, getShouldRead().size());
    859     }
    860 
    861     @Test
    862     public void testFinalOfFinalStaticField() {
    863         MockLayoutBinder lb = new MockLayoutBinder();
    864         mExprModel = lb.getModel();
    865         mExprModel.addImport("User", User.class.getCanonicalName(), null);
    866         FieldAccessExpr fieldAccess = parse(lb, "User.innerFinalStaticInstance.finalStaticField",
    867                 FieldAccessExpr.class);
    868         assertFalse(fieldAccess.isDynamic());
    869         mExprModel.seal();
    870         assertEquals(0, getShouldRead().size());
    871     }
    872 
    873     @Test
    874     public void testLocationTracking() {
    875         MockLayoutBinder lb = new MockLayoutBinder();
    876         mExprModel = lb.getModel();
    877         final String input = "a > 3 ? b : c";
    878         TernaryExpr ternaryExpr = parse(lb, input, TernaryExpr.class);
    879         final Location location = ternaryExpr.getLocations().get(0);
    880         assertNotNull(location);
    881         assertEquals(0, location.startLine);
    882         assertEquals(0, location.startOffset);
    883         assertEquals(0, location.endLine);
    884         assertEquals(input.length() - 1, location.endOffset);
    885 
    886         final ComparisonExpr comparison = (ComparisonExpr) ternaryExpr.getPred();
    887         final Location predLoc = comparison.getLocations().get(0);
    888         assertNotNull(predLoc);
    889         assertEquals(0, predLoc.startLine);
    890         assertEquals(0, predLoc.startOffset);
    891         assertEquals(0, predLoc.endLine);
    892         assertEquals(4, predLoc.endOffset);
    893 
    894         final Location aLoc = comparison.getLeft().getLocations().get(0);
    895         assertNotNull(aLoc);
    896         assertEquals(0, aLoc.startLine);
    897         assertEquals(0, aLoc.startOffset);
    898         assertEquals(0, aLoc.endLine);
    899         assertEquals(0, aLoc.endOffset);
    900 
    901         final Location tLoc = comparison.getRight().getLocations().get(0);
    902         assertNotNull(tLoc);
    903         assertEquals(0, tLoc.startLine);
    904         assertEquals(4, tLoc.startOffset);
    905         assertEquals(0, tLoc.endLine);
    906         assertEquals(4, tLoc.endOffset);
    907 
    908         final Location bLoc = ternaryExpr.getIfTrue().getLocations().get(0);
    909         assertNotNull(bLoc);
    910         assertEquals(0, bLoc.startLine);
    911         assertEquals(8, bLoc.startOffset);
    912         assertEquals(0, bLoc.endLine);
    913         assertEquals(8, bLoc.endOffset);
    914 
    915         final Location cLoc = ternaryExpr.getIfFalse().getLocations().get(0);
    916         assertNotNull(cLoc);
    917         assertEquals(0, cLoc.startLine);
    918         assertEquals(12, cLoc.startOffset);
    919         assertEquals(0, cLoc.endLine);
    920         assertEquals(12, cLoc.endOffset);
    921     }
    922 
    923 //    TODO uncomment when we have inner static access
    924 //    @Test
    925 //    public void testFinalOfInnerStaticClass() {
    926 //        MockLayoutBinder lb = new MockLayoutBinder();
    927 //        mExprModel = lb.getModel();
    928 //        mExprModel.addImport("User", User.class.getCanonicalName());
    929 //        FieldAccessExpr fieldAccess = parse(lb, "User.InnerStaticClass.finalStaticField", FieldAccessExpr.class);
    930 //        assertFalse(fieldAccess.isDynamic());
    931 //        mExprModel.seal();
    932 //        assertEquals(0, getShouldRead().size());
    933 //    }
    934 
    935     private void assertFlags(Expr a, int... flags) {
    936         BitSet bitset = new BitSet();
    937         for (int flag : flags) {
    938             bitset.set(flag);
    939         }
    940         assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags());
    941     }
    942 
    943     private void assertFlags(Expr a, Expr... exprs) {
    944         BitSet bitSet = a.getShouldReadFlags();
    945         for (Expr expr : exprs) {
    946             BitSet clone = (BitSet) bitSet.clone();
    947             clone.and(expr.getInvalidFlags());
    948             assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr
    949                     .getUniqueKey(), expr.getInvalidFlags(), clone);
    950         }
    951 
    952         BitSet composite = new BitSet();
    953         for (Expr expr : exprs) {
    954             composite.or(expr.getInvalidFlags());
    955         }
    956         assertEquals("composite flags should match", composite, bitSet);
    957     }
    958 
    959     private void assertExactMatch(List<Expr> iterable, Expr... exprs) {
    960         int i = 0;
    961         String listLog = Arrays.toString(iterable.toArray());
    962         String itemsLog = Arrays.toString(exprs);
    963         String log = "list: " + listLog + "\nitems: " + itemsLog;
    964         log("list", iterable);
    965         for (Expr expr : exprs) {
    966             assertTrue((i++) + ":must contain " + expr.getUniqueKey() + "\n" + log,
    967                     iterable.contains(expr));
    968         }
    969         i = 0;
    970         for (Expr expr : iterable) {
    971             assertTrue((i++) + ":must be expected " + expr.getUniqueKey() + "\n" + log,
    972                     Arrays.asList(exprs).contains(expr));
    973         }
    974     }
    975 
    976     private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) {
    977         final Expr parsed = binder.parse(input, false, null);
    978         assertTrue(klass.isAssignableFrom(parsed.getClass()));
    979         return (T) parsed;
    980     }
    981 
    982     private void log(String s, List<Expr> iterable) {
    983         L.d(s);
    984         for (Expr e : iterable) {
    985             L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(),
    986                     e.getShouldReadFlagsWithConditionals(), e.getReadSoFar());
    987         }
    988         L.d("end of %s", s);
    989     }
    990 
    991     private List<Expr> getReadFirst(List<Expr> shouldRead) {
    992         return getReadFirst(shouldRead, null);
    993     }
    994 
    995     private List<Expr> getReadFirst(List<Expr> shouldRead, final List<Expr> justRead) {
    996         List<Expr> result = new ArrayList<Expr>();
    997         for (Expr expr : shouldRead) {
    998             if (expr.shouldReadNow(justRead)) {
    999                 result.add(expr);
   1000             }
   1001         }
   1002         return result;
   1003     }
   1004 
   1005     private List<Expr> getShouldRead() {
   1006         return ExprModel.filterShouldRead(mExprModel.getPendingExpressions());
   1007     }
   1008 
   1009     public static class User implements Observable {
   1010 
   1011         String name;
   1012 
   1013         String lastName;
   1014 
   1015         public final int finalField = 5;
   1016 
   1017         public static InnerStaticClass innerStaticInstance = new InnerStaticClass();
   1018 
   1019         public static final InnerStaticClass innerFinalStaticInstance = new InnerStaticClass();
   1020 
   1021         public SubObj subObj = new SubObj();
   1022 
   1023         public String getName() {
   1024             return name;
   1025         }
   1026 
   1027         public String getLastName() {
   1028             return lastName;
   1029         }
   1030 
   1031         public boolean getCond(int i) {
   1032             return true;
   1033         }
   1034 
   1035         public SubObj getAnotherSubObj() {
   1036             return new SubObj();
   1037         }
   1038 
   1039         public static boolean ourStaticMethod() {
   1040             return true;
   1041         }
   1042 
   1043         public String comment;
   1044 
   1045         @Bindable
   1046         public boolean getUseComment() {
   1047             return true;
   1048         }
   1049 
   1050         @Override
   1051         public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
   1052 
   1053         }
   1054 
   1055         @Override
   1056         public void removeOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
   1057 
   1058         }
   1059 
   1060         public static class InnerStaticClass {
   1061 
   1062             public static final int finalField = 3;
   1063 
   1064             public static final int finalStaticField = 3;
   1065         }
   1066     }
   1067 
   1068     public static class SubObj {
   1069 
   1070         public final int finalField = 5;
   1071     }
   1072 
   1073 }
   1074