• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
DummyExpr(String key, DummyExpr... children)55         public DummyExpr(String key, DummyExpr... children) {
56             super(children);
57             mKey = key;
58         }
59 
60         @Override
resolveType(ModelAnalyzer modelAnalyzer)61         protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
62             return modelAnalyzer.findClass(Integer.class);
63         }
64 
65         @Override
constructDependencies()66         protected List<Dependency> constructDependencies() {
67             return constructDynamicChildrenDependencies();
68         }
69 
70         @Override
computeUniqueKey()71         protected String computeUniqueKey() {
72             return mKey + super.computeUniqueKey();
73         }
74 
75         @Override
generateCode(boolean full)76         protected KCode generateCode(boolean full) {
77             return new KCode();
78         }
79 
80         @Override
getInvertibleError()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
setUp()102     public void setUp() throws Exception {
103         JavaAnalyzer.initForTests();
104         mExprModel = new ExprModel();
105     }
106 
107     @Test
testAddNormal()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
testAddDupe1()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
testAddMultiple()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
testAddWithChildren()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
testShouldRead()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
testReadConstantTernary()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
testTernaryWithPlus()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 
filterOut(List<Expr> itr, final List<Expr> exclude)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
testTernaryInsideTernary()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
testRequirementFlags()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
testPostConditionalDependencies()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
testCircularDependency()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
testNestedCircularDependency()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
testInterExprDependency()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
testInterExprCircularDependency()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
testInterExprCircularDependency2()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
testInterExprCircularDependency3()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
testInterExprCircularDependency4()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
testInterExprDeepDependency()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
testInterExprDependencyNotReadyYet()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
testNoFlagsForNonBindingStatic()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
testFlagsForBindingStatic()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
testFinalFieldOfAVariable()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
testFinalFieldOfAField()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
testFinalFieldOfAMethod()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
testFinalOfAClass()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
testStaticFieldOfInstance()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
testOnDemandImportConflict()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
testOnDemandImportAlreadyImported()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
testStaticMethodOfInstance()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
testFinalOfStaticField()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
testFinalOfFinalStaticField()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
testLocationTracking()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 
assertFlags(Expr a, int... flags)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 
assertFlags(Expr a, Expr... exprs)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 
assertExactMatch(List<Expr> iterable, Expr... exprs)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 
parse(LayoutBinder binder, String input, Class<T> klass)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 
log(String s, List<Expr> iterable)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 
getReadFirst(List<Expr> shouldRead)991     private List<Expr> getReadFirst(List<Expr> shouldRead) {
992         return getReadFirst(shouldRead, null);
993     }
994 
getReadFirst(List<Expr> shouldRead, final List<Expr> justRead)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 
getShouldRead()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 
getName()1023         public String getName() {
1024             return name;
1025         }
1026 
getLastName()1027         public String getLastName() {
1028             return lastName;
1029         }
1030 
getCond(int i)1031         public boolean getCond(int i) {
1032             return true;
1033         }
1034 
getAnotherSubObj()1035         public SubObj getAnotherSubObj() {
1036             return new SubObj();
1037         }
1038 
ourStaticMethod()1039         public static boolean ourStaticMethod() {
1040             return true;
1041         }
1042 
1043         public String comment;
1044 
1045         @Bindable
getUseComment()1046         public boolean getUseComment() {
1047             return true;
1048         }
1049 
1050         @Override
addOnPropertyChangedCallback(OnPropertyChangedCallback callback)1051         public void addOnPropertyChangedCallback(OnPropertyChangedCallback callback) {
1052 
1053         }
1054 
1055         @Override
removeOnPropertyChangedCallback(OnPropertyChangedCallback callback)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