ExprModelTest.java revision ee7586713d68806b556a425cbebf007a56261ff3
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
17package android.databinding.tool.expr;
18
19import com.google.common.base.Predicate;
20import com.google.common.collect.Iterables;
21
22import org.apache.commons.lang3.ArrayUtils;
23import org.apache.commons.lang3.NotImplementedException;
24import org.junit.Before;
25import org.junit.Rule;
26import org.junit.Test;
27import org.junit.rules.TestWatcher;
28import org.junit.runner.Description;
29
30import android.databinding.tool.LayoutBinder;
31import android.databinding.tool.MockLayoutBinder;
32import android.databinding.tool.reflection.ModelAnalyzer;
33import android.databinding.tool.reflection.ModelClass;
34import android.databinding.tool.reflection.java.JavaAnalyzer;
35import android.databinding.tool.util.L;
36
37import java.util.ArrayList;
38import java.util.Arrays;
39import java.util.BitSet;
40import java.util.List;
41
42import static org.junit.Assert.assertEquals;
43import static org.junit.Assert.assertFalse;
44import static org.junit.Assert.assertNotNull;
45import static org.junit.Assert.assertNull;
46import static org.junit.Assert.assertSame;
47import static org.junit.Assert.assertTrue;
48
49public 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
76    ExprModel mExprModel;
77
78    @Rule
79    public TestWatcher mTestWatcher = new TestWatcher() {
80        @Override
81        protected void failed(Throwable e, Description description) {
82            if (mExprModel != null && mExprModel.getFlagMapping() != null) {
83                final String[] mapping = mExprModel.getFlagMapping();
84                for (int i = 0; i < mapping.length; i++) {
85                    L.d("flag %d: %s", i, mapping[i]);
86                }
87            }
88        }
89    };
90
91    @Before
92    public void setUp() throws Exception {
93        JavaAnalyzer.initForTests();
94        mExprModel = new ExprModel();
95    }
96
97    @Test
98    public void testAddNormal() {
99        final DummyExpr d = new DummyExpr("a");
100        assertSame(d, mExprModel.register(d));
101        assertSame(d, mExprModel.register(d));
102        assertEquals(1, mExprModel.mExprMap.size());
103    }
104
105    @Test
106    public void testAddDupe1() {
107        final DummyExpr d = new DummyExpr("a");
108        assertSame(d, mExprModel.register(d));
109        assertSame(d, mExprModel.register(new DummyExpr("a")));
110        assertEquals(1, mExprModel.mExprMap.size());
111    }
112
113    @Test
114    public void testAddMultiple() {
115        mExprModel.register(new DummyExpr("a"));
116        mExprModel.register(new DummyExpr("b"));
117        assertEquals(2, mExprModel.mExprMap.size());
118    }
119
120
121    @Test
122    public void testAddWithChildren() {
123        DummyExpr a = new DummyExpr("a");
124        DummyExpr b = new DummyExpr("b");
125        DummyExpr c = new DummyExpr("c", a, b);
126        mExprModel.register(c);
127        DummyExpr a2 = new DummyExpr("a");
128        DummyExpr b2 = new DummyExpr("b");
129        DummyExpr c2 = new DummyExpr("c", a, b);
130        assertEquals(c, mExprModel.register(c2));
131    }
132
133    @Test
134    public void testShouldRead() {
135        LayoutBinder lb = new MockLayoutBinder();
136        mExprModel = lb.getModel();
137        IdentifierExpr a = lb.addVariable("a", "java.lang.String");
138        IdentifierExpr b = lb.addVariable("b", "java.lang.String");
139        IdentifierExpr c = lb.addVariable("c", "java.lang.String");
140        lb.parse("a == null ? b : c");
141        mExprModel.comparison("==", a, mExprModel.symbol("null", Object.class));
142        lb.getModel().seal();
143        Iterable<Expr> shouldRead = getShouldRead();
144        // a and a == null
145        assertEquals(2, Iterables.size(shouldRead));
146        final Iterable<Expr> readFirst = getReadFirst(shouldRead, null);
147        assertEquals(1, Iterables.size(readFirst));
148        final Expr first = Iterables.getFirst(readFirst, null);
149        assertSame(a, first);
150        // now , assume we've read this
151        final BitSet shouldReadFlags = first.getShouldReadFlags();
152        assertNotNull(shouldReadFlags);
153    }
154
155    @Test
156    public void testTernaryWithPlus() {
157        LayoutBinder lb = new MockLayoutBinder();
158        mExprModel = lb.getModel();
159        IdentifierExpr user = lb
160                .addVariable("user", "android.databinding.tool.expr.ExprModelTest.User");
161        MathExpr parsed = parse(lb, "user.name + \" \" + (user.lastName ?? \"\")", MathExpr.class);
162        mExprModel.seal();
163        Iterable<Expr> toRead = getShouldRead();
164        Iterable<Expr> readNow = getReadFirst(toRead);
165        assertEquals(1, Iterables.size(readNow));
166        assertSame(user, Iterables.getFirst(readNow, null));
167        List<Expr> justRead = new ArrayList<Expr>();
168        justRead.add(user);
169        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
170        assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName
171        Iterables.addAll(justRead, readNow);
172        // user.lastname (T, F), user.name + " "
173        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
174        assertEquals(2, Iterables.size(readNow)); //user.name && user.lastName
175        Iterables.addAll(justRead, readNow);
176        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
177        assertEquals(0, Iterables.size(readNow));
178        mExprModel.markBitsRead();
179
180        toRead = getShouldRead();
181        assertEquals(2, Iterables.size(toRead));
182        justRead.clear();
183        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
184        assertEquals(1, Iterables.size(readNow));
185        assertSame(parsed.getRight(), Iterables.getFirst(readNow, null));
186        Iterables.addAll(justRead, readNow);
187
188        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
189        assertEquals(1, Iterables.size(readNow));
190        assertSame(parsed, Iterables.getFirst(readNow, null));
191        Iterables.addAll(justRead, readNow);
192
193        readNow = filterOut(getReadFirst(toRead, justRead), justRead);
194        assertEquals(0, Iterables.size(readNow));
195        mExprModel.markBitsRead();
196        assertEquals(0, Iterables.size(getShouldRead()));
197    }
198
199    private List<Expr> filterOut(Iterable itr, final Iterable exclude) {
200        return Arrays.asList(Iterables.toArray(Iterables.filter(itr, new Predicate() {
201            @Override
202            public boolean apply(Object input) {
203                return !Iterables.contains(exclude, input);
204            }
205        }), Expr.class));
206    }
207
208    @Test
209    public void testTernaryInsideTernary() {
210        LayoutBinder lb = new MockLayoutBinder();
211        mExprModel = lb.getModel();
212        IdentifierExpr cond1 = lb.addVariable("cond1", "boolean");
213        IdentifierExpr cond2 = lb.addVariable("cond2", "boolean");
214
215        IdentifierExpr a = lb.addVariable("a", "boolean");
216        IdentifierExpr b = lb.addVariable("b", "boolean");
217        IdentifierExpr c = lb.addVariable("c", "boolean");
218
219        final TernaryExpr ternaryExpr = parse(lb, "cond1 ? cond2 ? a : b : c", TernaryExpr.class);
220        final TernaryExpr innerTernary = (TernaryExpr) ternaryExpr.getIfTrue();
221        mExprModel.seal();
222
223        Iterable<Expr> toRead = getShouldRead();
224        assertEquals(1, Iterables.size(toRead));
225        assertEquals(ternaryExpr.getPred(), Iterables.getFirst(toRead, null));
226
227        Iterable<Expr> readNow = getReadFirst(toRead);
228        assertEquals(1, Iterables.size(readNow));
229        assertEquals(ternaryExpr.getPred(), Iterables.getFirst(readNow, null));
230        int cond1True = ternaryExpr.getRequirementFlagIndex(true);
231        int cond1False = ternaryExpr.getRequirementFlagIndex(false);
232        // ok, it is read now.
233        mExprModel.markBitsRead();
234
235        // now it should read cond2 or c, depending on the flag from first
236        toRead = getShouldRead();
237        assertEquals(2, Iterables.size(toRead));
238        assertExactMatch(toRead, ternaryExpr.getIfFalse(), innerTernary.getPred());
239        assertFlags(ternaryExpr.getIfFalse(), cond1False);
240        assertFlags(ternaryExpr.getIfTrue(), cond1True);
241
242        mExprModel.markBitsRead();
243
244        // now it should read a or b, innerTernary, outerTernary
245        toRead = getShouldRead();
246        assertExactMatch(toRead, innerTernary.getIfTrue(), innerTernary.getIfFalse(), ternaryExpr,
247                innerTernary);
248        assertFlags(innerTernary.getIfTrue(), innerTernary.getRequirementFlagIndex(true));
249        assertFlags(innerTernary.getIfFalse(), innerTernary.getRequirementFlagIndex(false));
250        assertFalse(mExprModel.markBitsRead());
251    }
252
253    @Test
254    public void testRequirementFlags() {
255        LayoutBinder lb = new MockLayoutBinder();
256        mExprModel = lb.getModel();
257        IdentifierExpr a = lb.addVariable("a", "java.lang.String");
258        IdentifierExpr b = lb.addVariable("b", "java.lang.String");
259        IdentifierExpr c = lb.addVariable("c", "java.lang.String");
260        IdentifierExpr d = lb.addVariable("d", "java.lang.String");
261        IdentifierExpr e = lb.addVariable("e", "java.lang.String");
262        final Expr aTernary = lb.parse("a == null ? b == null ? c : d : e");
263        assertTrue(aTernary instanceof TernaryExpr);
264        final Expr bTernary = ((TernaryExpr) aTernary).getIfTrue();
265        assertTrue(bTernary instanceof TernaryExpr);
266        final Expr aIsNull = mExprModel
267                .comparison("==", a, mExprModel.symbol("null", Object.class));
268        final Expr bIsNull = mExprModel
269                .comparison("==", b, mExprModel.symbol("null", Object.class));
270        lb.getModel().seal();
271        Iterable<Expr> shouldRead = getShouldRead();
272        // a and a == null
273        assertEquals(2, Iterables.size(shouldRead));
274        assertFalse(a.getShouldReadFlags().isEmpty());
275        assertTrue(a.getShouldReadFlags().get(a.getId()));
276        assertTrue(b.getShouldReadFlags().isEmpty());
277        assertTrue(c.getShouldReadFlags().isEmpty());
278        assertTrue(d.getShouldReadFlags().isEmpty());
279        assertTrue(e.getShouldReadFlags().isEmpty());
280
281        Iterable<Expr> readFirst = getReadFirst(shouldRead, null);
282        assertEquals(1, Iterables.size(readFirst));
283        final Expr first = Iterables.getFirst(readFirst, null);
284        assertSame(a, first);
285        assertTrue(mExprModel.markBitsRead());
286        for (Expr expr : mExprModel.getPendingExpressions()) {
287            assertNull(expr.mShouldReadFlags);
288        }
289        shouldRead = getShouldRead();
290        assertExactMatch(shouldRead, e, b, bIsNull);
291
292        assertFlags(e, aTernary.getRequirementFlagIndex(false));
293
294        assertFlags(b, aTernary.getRequirementFlagIndex(true));
295        assertFlags(bIsNull, aTernary.getRequirementFlagIndex(true));
296        assertTrue(mExprModel.markBitsRead());
297        shouldRead = getShouldRead();
298        assertEquals(4, Iterables.size(shouldRead));
299        assertTrue(Iterables.contains(shouldRead, c));
300        assertTrue(Iterables.contains(shouldRead, d));
301        assertTrue(Iterables.contains(shouldRead, aTernary));
302        assertTrue(Iterables.contains(shouldRead, bTernary));
303
304        assertTrue(c.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(true)));
305        assertEquals(1, c.getShouldReadFlags().cardinality());
306
307        assertTrue(d.getShouldReadFlags().get(bTernary.getRequirementFlagIndex(false)));
308        assertEquals(1, d.getShouldReadFlags().cardinality());
309
310        assertTrue(bTernary.getShouldReadFlags().get(aTernary.getRequirementFlagIndex(true)));
311        assertEquals(1, bTernary.getShouldReadFlags().cardinality());
312
313        assertEquals(5, aTernary.getShouldReadFlags().cardinality());
314        for (Expr expr : new Expr[]{a, b, c, d, e}) {
315            assertTrue(aTernary.getShouldReadFlags().get(expr.getId()));
316        }
317
318        readFirst = getReadFirst(shouldRead);
319        assertEquals(2, Iterables.size(readFirst));
320        assertTrue(Iterables.contains(readFirst, c));
321        assertTrue(Iterables.contains(readFirst, d));
322        assertFalse(mExprModel.markBitsRead());
323    }
324
325    @Test
326    public void testPostConditionalDependencies() {
327        LayoutBinder lb = new MockLayoutBinder();
328        mExprModel = lb.getModel();
329
330        IdentifierExpr u1 = lb.addVariable("u1", User.class.getCanonicalName());
331        IdentifierExpr u2 = lb.addVariable("u2", User.class.getCanonicalName());
332        IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName());
333        IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName());
334        IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName());
335        IdentifierExpr d = lb.addVariable("d", int.class.getCanonicalName());
336        IdentifierExpr e = lb.addVariable("e", int.class.getCanonicalName());
337        TernaryExpr abTernary = parse(lb, "a > b ? u1.name : u2.name", TernaryExpr.class);
338        TernaryExpr bcTernary = parse(lb, "b > c ? u1.getCond(d) ? u1.lastName : u2.lastName : `xx`"
339                + " + u2.getCond(e) ", TernaryExpr.class);
340        Expr abCmp = abTernary.getPred();
341        Expr bcCmp = bcTernary.getPred();
342        Expr u1GetCondD = ((TernaryExpr) bcTernary.getIfTrue()).getPred();
343        final MathExpr xxPlusU2getCondE = (MathExpr) bcTernary.getIfFalse();
344        Expr u2GetCondE = xxPlusU2getCondE.getRight();
345        Expr u1Name = abTernary.getIfTrue();
346        Expr u2Name = abTernary.getIfFalse();
347        Expr u1LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfTrue();
348        Expr u2LastName = ((TernaryExpr) bcTernary.getIfTrue()).getIfFalse();
349
350        mExprModel.seal();
351        Iterable<Expr> shouldRead = getShouldRead();
352
353        assertExactMatch(shouldRead, a, b, c, abCmp, bcCmp);
354
355        Iterable<Expr> firstRead = getReadFirst(shouldRead);
356
357        assertExactMatch(firstRead, a, b, c);
358
359        assertFlags(a, a, b, u1, u2, u1Name, u2Name);
360        assertFlags(b, a, b, u1, u2, u1Name, u2Name, c, d, u1LastName, u2LastName, e);
361        assertFlags(c, b, c, u1, d, u1LastName, u2LastName, e);
362        assertFlags(abCmp, a, b, u1, u2, u1Name, u2Name);
363        assertFlags(bcCmp, b, c, u1, d, u1LastName, u2LastName, e);
364
365        assertTrue(mExprModel.markBitsRead());
366
367        shouldRead = getShouldRead();
368        Expr[] batch = {d, e, u1, u2, u1GetCondD, u2GetCondE, xxPlusU2getCondE, abTernary,
369                abTernary.getIfTrue(), abTernary.getIfFalse()};
370        assertExactMatch(shouldRead, batch);
371        firstRead = getReadFirst(shouldRead);
372        assertExactMatch(firstRead, d, e, u1, u2);
373
374        assertFlags(d, bcTernary.getRequirementFlagIndex(true));
375        assertFlags(e, bcTernary.getRequirementFlagIndex(false));
376        assertFlags(u1, bcTernary.getRequirementFlagIndex(true),
377                abTernary.getRequirementFlagIndex(true));
378        assertFlags(u2, bcTernary.getRequirementFlagIndex(false),
379                abTernary.getRequirementFlagIndex(false));
380
381        assertFlags(u1GetCondD, bcTernary.getRequirementFlagIndex(true));
382        assertFlags(u2GetCondE, bcTernary.getRequirementFlagIndex(false));
383        assertFlags(xxPlusU2getCondE, bcTernary.getRequirementFlagIndex(false));
384        assertFlags(abTernary, a, b, u1, u2, u1Name, u2Name);
385        assertFlags(abTernary.getIfTrue(), abTernary.getRequirementFlagIndex(true));
386        assertFlags(abTernary.getIfFalse(), abTernary.getRequirementFlagIndex(false));
387
388        assertTrue(mExprModel.markBitsRead());
389
390        shouldRead = getShouldRead();
391        // actually, there is no real case to read u1 anymore because if b>c was not true,
392        // u1.getCond(d) will never be set. Right now, we don't have mechanism to figure this out
393        // and also it does not affect correctness (just an unnecessary if stmt)
394        assertExactMatch(shouldRead, u2, u1LastName, u2LastName, bcTernary.getIfTrue(), bcTernary);
395        firstRead = getReadFirst(shouldRead);
396        assertExactMatch(firstRead, u1LastName, u2);
397
398        assertFlags(u1LastName, bcTernary.getIfTrue().getRequirementFlagIndex(true));
399        assertFlags(u2LastName, bcTernary.getIfTrue().getRequirementFlagIndex(false));
400        assertFlags(u2, bcTernary.getIfTrue().getRequirementFlagIndex(false));
401
402        assertFlags(bcTernary.getIfTrue(), bcTernary.getRequirementFlagIndex(true));
403        assertFlags(bcTernary, b, c, u1, u2, d, u1LastName, u2LastName, e);
404    }
405
406    @Test
407    public void testCircularDependency() {
408        LayoutBinder lb = new MockLayoutBinder();
409        mExprModel = lb.getModel();
410        IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName());
411        IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName());
412        final TernaryExpr abTernary = parse(lb, "a > 3 ? a : b", TernaryExpr.class);
413        mExprModel.seal();
414        Iterable<Expr> shouldRead = getShouldRead();
415        assertExactMatch(shouldRead, a, abTernary.getPred());
416        assertTrue(mExprModel.markBitsRead());
417        shouldRead = getShouldRead();
418        assertExactMatch(shouldRead, b, abTernary);
419        assertFalse(mExprModel.markBitsRead());
420    }
421
422    @Test
423    public void testNestedCircularDependency() {
424        LayoutBinder lb = new MockLayoutBinder();
425        mExprModel = lb.getModel();
426        IdentifierExpr a = lb.addVariable("a", int.class.getCanonicalName());
427        IdentifierExpr b = lb.addVariable("b", int.class.getCanonicalName());
428        IdentifierExpr c = lb.addVariable("c", int.class.getCanonicalName());
429        final TernaryExpr a3Ternary = parse(lb, "a > 3 ? c > 4 ? a : b : c", TernaryExpr.class);
430        final TernaryExpr c4Ternary = (TernaryExpr) a3Ternary.getIfTrue();
431        mExprModel.seal();
432        Iterable<Expr> shouldRead = getShouldRead();
433        assertExactMatch(shouldRead, a, a3Ternary.getPred());
434        assertTrue(mExprModel.markBitsRead());
435        shouldRead = getShouldRead();
436        assertExactMatch(shouldRead, c, c4Ternary.getPred());
437        assertFlags(c, a3Ternary.getRequirementFlagIndex(true),
438                a3Ternary.getRequirementFlagIndex(false));
439        assertFlags(c4Ternary.getPred(), a3Ternary.getRequirementFlagIndex(true));
440    }
441
442    @Test
443    public void testNoFlagsForNonBindingStatic() {
444        LayoutBinder lb = new MockLayoutBinder();
445        mExprModel = lb.getModel();
446        lb.addVariable("a", int.class.getCanonicalName());
447        final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
448        mExprModel.seal();
449        assertTrue(parsed.getRight().getInvalidFlags().isEmpty());
450        assertEquals(1, parsed.getLeft().getInvalidFlags().cardinality());
451        assertEquals(1, mExprModel.getInvalidateableFieldLimit());
452    }
453
454    @Test
455    public void testFlagsForBindingStatic() {
456        LayoutBinder lb = new MockLayoutBinder();
457        mExprModel = lb.getModel();
458        lb.addVariable("a", int.class.getCanonicalName());
459        final Expr staticParsed = parse(lb, "3 + 2", MathExpr.class);
460        final MathExpr parsed = parse(lb, "a * (3 + 2)", MathExpr.class);
461        mExprModel.seal();
462        assertTrue(staticParsed.isBindingExpression());
463        assertEquals(1, staticParsed.getInvalidFlags().cardinality());
464        assertEquals(parsed.getRight().getInvalidFlags(), staticParsed.getInvalidFlags());
465        assertEquals(1, parsed.getLeft().getInvalidFlags().cardinality());
466        assertEquals(2, mExprModel.getInvalidateableFieldLimit());
467    }
468
469    private void assertFlags(Expr a, int... flags) {
470        BitSet bitset = new BitSet();
471        for (int flag : flags) {
472            bitset.set(flag);
473        }
474        assertEquals("flag test for " + a.getUniqueKey(), bitset, a.getShouldReadFlags());
475    }
476
477    private void assertFlags(Expr a, Expr... exprs) {
478        BitSet bitSet = a.getShouldReadFlags();
479        for (Expr expr : exprs) {
480            BitSet clone = (BitSet) bitSet.clone();
481            clone.and(expr.getInvalidFlags());
482            assertEquals("should read flags of " + a.getUniqueKey() + " should include " + expr
483                    .getUniqueKey(), expr.getInvalidFlags(), clone);
484        }
485
486        BitSet composite = new BitSet();
487        for (Expr expr : exprs) {
488            composite.or(expr.getInvalidFlags());
489        }
490        assertEquals("composite flags should match", composite, bitSet);
491    }
492
493    private void assertExactMatch(Iterable<Expr> iterable, Expr... exprs) {
494        int i = 0;
495        log("list", iterable);
496        for (Expr expr : exprs) {
497            assertTrue((i++) + ":must contain " + expr.getUniqueKey(),
498                    Iterables.contains(iterable, expr));
499        }
500        i = 0;
501        for (Expr expr : iterable) {
502            assertTrue((i++) + ":must be expected " + expr.getUniqueKey(),
503                    ArrayUtils.contains(exprs, expr));
504        }
505    }
506
507    private <T extends Expr> T parse(LayoutBinder binder, String input, Class<T> klass) {
508        final Expr parsed = binder.parse(input);
509        assertTrue(klass.isAssignableFrom(parsed.getClass()));
510        return (T) parsed;
511    }
512
513    private void log(String s, Iterable<Expr> iterable) {
514        L.d(s);
515        for (Expr e : iterable) {
516            L.d(": %s : %s allFlags: %s readSoFar: %s", e.getUniqueKey(), e.getShouldReadFlags(),
517                    e.getShouldReadFlagsWithConditionals(), e.getReadSoFar());
518        }
519        L.d("end of %s", s);
520    }
521
522    private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead) {
523        return getReadFirst(shouldRead, null);
524    }
525
526    private Iterable<Expr> getReadFirst(Iterable<Expr> shouldRead, final Iterable<Expr> justRead) {
527        return Iterables.filter(shouldRead, new Predicate<Expr>() {
528            @Override
529            public boolean apply(Expr input) {
530                return input.shouldReadNow(justRead);
531            }
532        });
533    }
534
535    private Iterable<Expr> getShouldRead() {
536        return mExprModel.filterShouldRead(mExprModel.getPendingExpressions());
537    }
538
539    public static class User {
540
541        String name;
542
543        String lastName;
544
545        public String getName() {
546            return name;
547        }
548
549        public String getLastName() {
550            return lastName;
551        }
552
553        public boolean getCond(int i) {
554            return true;
555        }
556    }
557}
558