• 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.tool.BindingTarget;
20 import android.databinding.tool.CallbackWrapper;
21 import android.databinding.tool.InverseBinding;
22 import android.databinding.tool.reflection.ModelAnalyzer;
23 import android.databinding.tool.reflection.ModelClass;
24 import android.databinding.tool.reflection.ModelMethod;
25 import android.databinding.tool.store.Location;
26 import android.databinding.tool.util.L;
27 import android.databinding.tool.util.Preconditions;
28 import android.databinding.tool.writer.ExprModelExt;
29 import android.databinding.tool.writer.FlagSet;
30 
31 import org.antlr.v4.runtime.ParserRuleContext;
32 import org.jetbrains.annotations.Nullable;
33 
34 import java.util.ArrayList;
35 import java.util.Arrays;
36 import java.util.BitSet;
37 import java.util.HashMap;
38 import java.util.List;
39 import java.util.Map;
40 import java.util.concurrent.atomic.AtomicInteger;
41 
42 public class ExprModel {
43 
44     Map<String, Expr> mExprMap = new HashMap<String, Expr>();
45 
46     List<Expr> mBindingExpressions = new ArrayList<Expr>();
47 
48     private int mInvalidateableFieldLimit = 0;
49 
50     private int mRequirementIdCount = 0;
51 
52     // each arg list receives a unique id even if it is the same arguments and method.
53     private int mArgListIdCounter = 0;
54 
55     private static final String TRUE_KEY_SUFFIX = "== true";
56     private static final String FALSE_KEY_SUFFIX = "== false";
57 
58     /**
59      * Any expression can be invalidated by invalidating this flag.
60      */
61     private BitSet mInvalidateAnyFlags;
62     private int mInvalidateAnyFlagIndex;
63 
64     /**
65      * Used by code generation. Keeps the list of expressions that are waiting to be evaluated.
66      */
67     private List<Expr> mPendingExpressions;
68 
69     /**
70      * Used for converting flags into identifiers while debugging.
71      */
72     private String[] mFlagMapping;
73 
74     private int mFlagBucketCount;// how many buckets we use to identify flags
75 
76     private List<Expr> mObservables;
77 
78     private boolean mSealed = false;
79 
80     private Map<String, String> mImports = new HashMap<String, String>();
81 
82     private ParserRuleContext mCurrentParserContext;
83     private Location mCurrentLocationInFile;
84 
85     private Map<String, CallbackWrapper> mCallbackWrappers = new HashMap<String, CallbackWrapper>();
86 
87     private AtomicInteger mCallbackIdCounter = new AtomicInteger();
88 
89     private ExprModelExt mExt = new ExprModelExt();
90 
91     /**
92      * Adds the expression to the list of expressions and returns it.
93      * If it already exists, returns existing one.
94      *
95      * @param expr The new parsed expression
96      * @return The expression itself or another one if the same thing was parsed before
97      */
register(T expr)98     public <T extends Expr> T register(T expr) {
99         Preconditions.check(!mSealed, "Cannot add expressions to a model after it is sealed");
100         Location location = null;
101         if (mCurrentParserContext != null) {
102             location = new Location(mCurrentParserContext);
103             location.setParentLocation(mCurrentLocationInFile);
104         }
105         //noinspection unchecked
106         T existing = (T) mExprMap.get(expr.getUniqueKey());
107         if (existing != null) {
108             Preconditions.check(expr.getParents().isEmpty(),
109                     "If an expression already exists, it should've never been added to a parent,"
110                             + "if thats the case, somewhere we are creating an expression w/o"
111                             + "calling expression model");
112             // tell the expr that it is being swapped so that if it was added to some other expr
113             // as a parent, those can swap their references
114             expr.onSwappedWith(existing);
115             if (location != null) {
116                 existing.addLocation(location);
117             }
118             return existing;
119         }
120         mExprMap.put(expr.getUniqueKey(), expr);
121         expr.setModel(this);
122         if (location != null) {
123             expr.addLocation(location);
124         }
125         return expr;
126     }
127 
markSealed()128     protected void markSealed() {
129         mSealed = true;
130     }
131 
getExt()132     public ExprModelExt getExt() {
133         return mExt;
134     }
135 
obtainCallbackId()136     public int obtainCallbackId() {
137         return mCallbackIdCounter.incrementAndGet();
138     }
139 
setCurrentParserContext(ParserRuleContext currentParserContext)140     public void setCurrentParserContext(ParserRuleContext currentParserContext) {
141         mCurrentParserContext = currentParserContext;
142     }
143 
getCurrentParserContext()144     public ParserRuleContext getCurrentParserContext() {
145         return mCurrentParserContext;
146     }
147 
getCurrentLocationInFile()148     public Location getCurrentLocationInFile() {
149         return mCurrentLocationInFile;
150     }
151 
getExprMap()152     public Map<String, Expr> getExprMap() {
153         return mExprMap;
154     }
155 
size()156     public int size() {
157         return mExprMap.size();
158     }
159 
comparison(String op, Expr left, Expr right)160     public ComparisonExpr comparison(String op, Expr left, Expr right) {
161         return register(new ComparisonExpr(op, left, right));
162     }
163 
instanceOfOp(Expr expr, String type)164     public InstanceOfExpr instanceOfOp(Expr expr, String type) {
165         return register(new InstanceOfExpr(expr, type));
166     }
167 
field(Expr parent, String name)168     public FieldAccessExpr field(Expr parent, String name) {
169         return register(new FieldAccessExpr(parent, name));
170     }
171 
observableField(Expr parent, String name)172     public FieldAccessExpr observableField(Expr parent, String name) {
173         return register(new ObservableFieldExpr(parent, name));
174     }
175 
methodReference(Expr parent, String name)176     public MethodReferenceExpr methodReference(Expr parent, String name) {
177         return register(new MethodReferenceExpr(parent, name));
178     }
179 
symbol(String text, Class type)180     public SymbolExpr symbol(String text, Class type) {
181         return register(new SymbolExpr(text, type));
182     }
183 
ternary(Expr pred, Expr ifTrue, Expr ifFalse)184     public TernaryExpr ternary(Expr pred, Expr ifTrue, Expr ifFalse) {
185         return register(new TernaryExpr(pred, ifTrue, ifFalse));
186     }
187 
identifier(String name)188     public IdentifierExpr identifier(String name) {
189         return register(new IdentifierExpr(name));
190     }
191 
staticIdentifier(String name)192     public StaticIdentifierExpr staticIdentifier(String name) {
193         return register(new StaticIdentifierExpr(name));
194     }
195 
builtInVariable(String name, String type, String accessCode)196     public BuiltInVariableExpr builtInVariable(String name, String type, String accessCode) {
197         return register(new BuiltInVariableExpr(name, type, accessCode));
198     }
199 
viewFieldExpr(BindingTarget bindingTarget)200     public ViewFieldExpr viewFieldExpr(BindingTarget bindingTarget) {
201         return register(new ViewFieldExpr(bindingTarget));
202     }
203 
204     /**
205      * Creates a static identifier for the given class or returns the existing one.
206      */
staticIdentifierFor(final ModelClass modelClass)207     public StaticIdentifierExpr staticIdentifierFor(final ModelClass modelClass) {
208         final String type = modelClass.getCanonicalName();
209         // check for existing
210         StaticIdentifierExpr id = findStaticIdentifierExpr(type);
211         if (id != null) {
212             return id;
213         }
214 
215         // does not exist. Find a name for it.
216         int cnt = 0;
217         int dotIndex = type.lastIndexOf(".");
218         String baseName;
219         Preconditions.check(dotIndex < type.length() - 1, "Invalid type %s", type);
220         if (dotIndex == -1) {
221             baseName = type;
222         } else {
223             baseName = type.substring(dotIndex + 1);
224         }
225         while (true) {
226             String candidate = cnt == 0 ? baseName : baseName + cnt;
227             if (!mImports.containsKey(candidate)) {
228                 return addImport(candidate, type, null);
229             }
230             cnt ++;
231             Preconditions.check(cnt < 100, "Failed to create an import for " + type);
232         }
233     }
234 
235     @Nullable
236     private StaticIdentifierExpr findStaticIdentifierExpr(String type) {
237         for (Expr expr : mExprMap.values()) {
238             if (expr instanceof StaticIdentifierExpr) {
239                 StaticIdentifierExpr id = (StaticIdentifierExpr) expr;
240                 if (id.getUserDefinedType().equals(type)) {
241                     return id;
242                 }
243             }
244         }
245         return null;
246     }
247 
248     public MethodCallExpr methodCall(Expr target, String name, List<Expr> args) {
249         return register(new MethodCallExpr(target, name, args));
250     }
251 
252     public MathExpr math(Expr left, String op, Expr right) {
253         return register(new MathExpr(left, op, right));
254     }
255 
256     public TernaryExpr logical(Expr left, String op, Expr right) {
257         if ("&&".equals(op)) {
258             // left && right
259             // left ? right : false
260             return register(new TernaryExpr(left, right, symbol("false", boolean.class)));
261         } else {
262             // left || right
263             // left ? true : right
264             return register(new TernaryExpr(left, symbol("true", boolean.class), right));
265         }
266     }
267 
268     public BitShiftExpr bitshift(Expr left, String op, Expr right) {
269         return register(new BitShiftExpr(left, op, right));
270     }
271 
272     public UnaryExpr unary(String op, Expr expr) {
273         return register(new UnaryExpr(op, expr));
274     }
275 
276     public Expr resourceExpr(BindingTarget target, String packageName, String resourceType,
277             String resourceName, List<Expr> args) {
278         return register(new ResourceExpr(target, packageName, resourceType, resourceName, args));
279     }
280 
281     public Expr bracketExpr(Expr variableExpr, Expr argExpr) {
282         return register(new BracketExpr(variableExpr, argExpr));
283     }
284 
285     public Expr castExpr(String type, Expr expr) {
286         return register(new CastExpr(type, expr));
287     }
288 
289     public TwoWayListenerExpr twoWayListenerExpr(InverseBinding inverseBinding) {
290         return register(new TwoWayListenerExpr(inverseBinding));
291     }
292     public List<Expr> getBindingExpressions() {
293         return mBindingExpressions;
294     }
295 
296     public StaticIdentifierExpr addImport(String alias, String type, Location location) {
297         String existing = mImports.get(alias);
298         if (existing != null) {
299             if (existing.equals(type)) {
300                 final StaticIdentifierExpr id = findStaticIdentifierExpr(type);
301                 Preconditions.checkNotNull(id, "Missing import expression although it is"
302                         + " registered");
303                 return id;
304             } else {
305                 L.e("%s has already been defined as %s but trying to re-define as %s", alias,
306                         existing, type);
307             }
308         }
309 
310         final StaticIdentifierExpr id = staticIdentifier(alias);
311         L.d("adding import %s as %s klass: %s", type, alias, id.getClass().getSimpleName());
312         id.setUserDefinedType(type);
313         if (location != null) {
314             id.addLocation(location);
315         }
316         mImports.put(alias, type);
317         return id;
318     }
319 
320     public Map<String, String> getImports() {
321         return mImports;
322     }
323 
324     /**
325      * The actual thingy that is set on the binding target.
326      *
327      * Input must be already registered
328      */
329     public Expr bindingExpr(Expr bindingExpr) {
330         Preconditions.check(mExprMap.containsKey(bindingExpr.getUniqueKey()),
331                 "Main expression should already be registered");
332         if (!mBindingExpressions.contains(bindingExpr)) {
333             mBindingExpressions.add(bindingExpr);
334         }
335         return bindingExpr;
336     }
337 
338     public void removeExpr(Expr expr) {
339         Preconditions.check(!mSealed, "Can't modify the expression list after sealing the model.");
340         mBindingExpressions.remove(expr);
341         mExprMap.remove(expr.getUniqueKey());
342     }
343 
344     public List<Expr> getObservables() {
345         return mObservables;
346     }
347 
348     /**
349      * Give id to each expression. Will be useful if we serialize.
350      */
351     public void seal() {
352         L.d("sealing model");
353         List<Expr> notifiableExpressions = new ArrayList<Expr>();
354         //ensure class analyzer. We need to know observables at this point
355         final ModelAnalyzer modelAnalyzer = ModelAnalyzer.getInstance();
356         updateExpressions(modelAnalyzer);
357 
358         int counter = 0;
359         final Iterable<Expr> observables = filterObservables(modelAnalyzer);
360         List<String> flagMapping = new ArrayList<String>();
361         mObservables = new ArrayList<Expr>();
362         for (Expr expr : observables) {
363             // observables gets initial ids
364             flagMapping.add(expr.getUniqueKey());
365             expr.setId(counter++);
366             mObservables.add(expr);
367             notifiableExpressions.add(expr);
368             L.d("observable %s", expr.getUniqueKey());
369         }
370 
371         // non-observable identifiers gets next ids
372         final Iterable<Expr> nonObservableIds = filterNonObservableIds(modelAnalyzer);
373         for (Expr expr : nonObservableIds) {
374             flagMapping.add(expr.getUniqueKey());
375             expr.setId(counter++);
376             notifiableExpressions.add(expr);
377             L.d("non-observable %s", expr.getUniqueKey());
378         }
379 
380         // descendants of observables gets following ids
381         for (Expr expr : observables) {
382             for (Expr parent : expr.getParents()) {
383                 if (parent.hasId()) {
384                     continue;// already has some id, means observable
385                 }
386                 // only fields earn an id
387                 if (parent instanceof FieldAccessExpr) {
388                     FieldAccessExpr fae = (FieldAccessExpr) parent;
389                     L.d("checking field access expr %s. getter: %s", fae,fae.getGetter());
390                     // FAE#getter might be null if it is used only in a callback.
391                     if (fae.getGetter() != null && fae.isDynamic()
392                             && fae.getGetter().canBeInvalidated()) {
393                         flagMapping.add(parent.getUniqueKey());
394                         parent.setId(counter++);
395                         notifiableExpressions.add(parent);
396                         L.d("notifiable field %s : %s for %s : %s", parent.getUniqueKey(),
397                                 Integer.toHexString(System.identityHashCode(parent)),
398                                 expr.getUniqueKey(),
399                                 Integer.toHexString(System.identityHashCode(expr)));
400                     }
401                 }
402             }
403         }
404 
405         // now all 2-way bound view fields
406         for (Expr expr : mExprMap.values()) {
407             if (expr instanceof FieldAccessExpr) {
408                 FieldAccessExpr fieldAccessExpr = (FieldAccessExpr) expr;
409                 if (fieldAccessExpr.getTarget() instanceof ViewFieldExpr) {
410                     flagMapping.add(fieldAccessExpr.getUniqueKey());
411                     fieldAccessExpr.setId(counter++);
412                 }
413             }
414         }
415 
416         // non-dynamic binding expressions receive some ids so that they can be invalidated
417         L.d("list of binding expressions");
418         for (int i = 0; i < mBindingExpressions.size(); i++) {
419             L.d("[%d] %s", i, mBindingExpressions.get(i));
420         }
421         // we don't assign ids to constant binding expressions because now invalidateAll has its own
422         // flag.
423 
424         for (Expr expr : notifiableExpressions) {
425             expr.enableDirectInvalidation();
426         }
427 
428         // make sure all dependencies are resolved to avoid future race conditions
429         for (Expr expr : mExprMap.values()) {
430             expr.getDependencies();
431         }
432         mInvalidateAnyFlagIndex = counter ++;
433         flagMapping.add("INVALIDATE ANY");
434         mInvalidateableFieldLimit = counter;
435         BitSet invalidateableFlags = new BitSet();
436         for (int i = 0; i < mInvalidateableFieldLimit; i++) {
437             invalidateableFlags.set(i, true);
438         }
439 
440         // make sure all dependencies are resolved to avoid future race conditions
441         for (Expr expr : mExprMap.values()) {
442             if (expr.isConditional()) {
443                 L.d("requirement id for %s is %d", expr, counter);
444                 expr.setRequirementId(counter);
445                 flagMapping.add(expr.getUniqueKey() + FALSE_KEY_SUFFIX);
446                 flagMapping.add(expr.getUniqueKey() + TRUE_KEY_SUFFIX);
447                 counter += 2;
448             }
449         }
450         BitSet conditionalFlags = new BitSet();
451         for (int i = mInvalidateableFieldLimit; i < counter; i++) {
452             conditionalFlags.set(i, true);
453         }
454         mRequirementIdCount = (counter - mInvalidateableFieldLimit) / 2;
455 
456         // everybody gets an id
457         for (Map.Entry<String, Expr> entry : mExprMap.entrySet()) {
458             final Expr value = entry.getValue();
459             if (!value.hasId()) {
460                 value.setId(counter++);
461             }
462         }
463 
464         mFlagMapping = new String[flagMapping.size()];
465         flagMapping.toArray(mFlagMapping);
466 
467         mFlagBucketCount = 1 + (getTotalFlagCount() / FlagSet.sBucketSize);
468         mInvalidateAnyFlags = new BitSet();
469         mInvalidateAnyFlags.set(mInvalidateAnyFlagIndex, true);
470 
471         for (Expr expr : mExprMap.values()) {
472             expr.getShouldReadFlagsWithConditionals();
473         }
474 
475         for (Expr expr : mExprMap.values()) {
476             // ensure all types are calculated
477             expr.getResolvedType();
478         }
479 
480         mSealed = true;
481     }
482 
483     /**
484      * Run updateExpr on each binding expression until no new expressions are added.
485      * <p>
486      * Some expressions (e.g. field access) may replace themselves and add/remove new dependencies
487      * so we need to make sure each expression's update is called at least once.
488      */
489     private void updateExpressions(ModelAnalyzer modelAnalyzer) {
490         int startSize = -1;
491         while (startSize != mExprMap.size()) {
492             startSize = mExprMap.size();
493             ArrayList<Expr> exprs = new ArrayList<Expr>(mBindingExpressions);
494             for (Expr expr : exprs) {
495                 expr.updateExpr(modelAnalyzer);
496             }
497         }
498     }
499 
500     public int getFlagBucketCount() {
501         return mFlagBucketCount;
502     }
503 
504     public int getTotalFlagCount() {
505         return mRequirementIdCount * 2 + mInvalidateableFieldLimit;
506     }
507 
508     public int getInvalidateableFieldLimit() {
509         return mInvalidateableFieldLimit;
510     }
511 
512     public String[] getFlagMapping() {
513         return mFlagMapping;
514     }
515 
516     public String getFlag(int id) {
517         return mFlagMapping[id];
518     }
519 
520     private List<Expr> filterNonObservableIds(final ModelAnalyzer modelAnalyzer) {
521         List<Expr> result = new ArrayList<Expr>();
522         for (Expr input : mExprMap.values()) {
523             if (input instanceof IdentifierExpr
524                     && !input.hasId()
525                     && !input.isObservable()
526                     && input.isDynamic()) {
527                 result.add(input);
528             }
529         }
530         return result;
531     }
532 
533     private Iterable<Expr> filterObservables(final ModelAnalyzer modelAnalyzer) {
534         List<Expr> result = new ArrayList<Expr>();
535         for (Expr input : mExprMap.values()) {
536             if (input.isObservable()) {
537                 result.add(input);
538             }
539         }
540         return result;
541     }
542 
543     public List<Expr> getPendingExpressions() {
544         if (mPendingExpressions == null) {
545             mPendingExpressions = new ArrayList<Expr>();
546             for (Expr expr : mExprMap.values()) {
547                 // if an expression is NOT dynanic but has conditional dependants, still return it
548                 // so that conditional flags can be set
549                 if (!expr.isRead() && (expr.isDynamic() || expr.hasConditionalDependant())) {
550                     mPendingExpressions.add(expr);
551                 }
552             }
553         }
554         return mPendingExpressions;
555     }
556 
557     public boolean markBitsRead() {
558         // each has should read flags, we set them back on them
559         List<Expr> markedSomeFlagsRead = new ArrayList<Expr>();
560         for (Expr expr : filterShouldRead(getPendingExpressions())) {
561             expr.markFlagsAsRead(expr.getShouldReadFlags());
562             markedSomeFlagsRead.add(expr);
563         }
564         return pruneDone(markedSomeFlagsRead);
565     }
566 
567     private boolean pruneDone(List<Expr> markedSomeFlagsAsRead) {
568         boolean marked = true;
569         List<Expr> markedAsReadList = new ArrayList<Expr>();
570         while (marked) {
571             marked = false;
572             for (Expr expr : mExprMap.values()) {
573                 if (expr.isRead()) {
574                     continue;
575                 }
576                 if (expr.markAsReadIfDone()) {
577                     L.d("marked %s as read ", expr.getUniqueKey());
578                     marked = true;
579                     markedAsReadList.add(expr);
580                     markedSomeFlagsAsRead.remove(expr);
581                 }
582             }
583         }
584         boolean elevated = false;
585         for (Expr markedAsRead : markedAsReadList) {
586             for (Dependency dependency : markedAsRead.getDependants()) {
587                 if (dependency.getDependant().considerElevatingConditionals(markedAsRead)) {
588                     elevated = true;
589                 }
590             }
591         }
592         if (!elevated) {
593             for (Expr partialRead : markedSomeFlagsAsRead) {
594                 // even if all paths are not satisfied, we can elevate certain conditional
595                 // dependencies if all of their paths are satisfied.
596                 for (Dependency dependency : partialRead.getDependants()) {
597                     Expr dependant = dependency.getDependant();
598                     if (dependant.isConditional() && dependant.getAllCalculationPaths()
599                             .areAllPathsSatisfied(partialRead.mReadSoFar)) {
600                         if (dependant.considerElevatingConditionals(partialRead)) {
601                             elevated = true;
602                         }
603                     }
604                 }
605             }
606         }
607         if (elevated) {
608             // some conditionals are elevated. We should re-calculate flags
609             for (Expr expr : getPendingExpressions()) {
610                 if (!expr.isRead()) {
611                     expr.invalidateReadFlags();
612                 }
613             }
614             mPendingExpressions = null;
615         }
616         return elevated;
617     }
618 
619     private static boolean hasConditionalOrNestedCannotReadDependency(Expr expr) {
620         for (Dependency dependency : expr.getDependencies()) {
621             if (dependency.isConditional() || dependency.getOther().hasNestedCannotRead()) {
622                 return true;
623             }
624         }
625         return false;
626     }
627 
628     public static ArrayList<Expr> filterShouldRead(Iterable<Expr> exprs) {
629         ArrayList<Expr> result = new ArrayList<Expr>();
630         for (Expr expr : exprs) {
631             if (!expr.getShouldReadFlags().isEmpty() &&
632                     !hasConditionalOrNestedCannotReadDependency(expr)) {
633                 result.add(expr);
634             }
635         }
636         return result;
637     }
638 
639     /**
640      * May return null if flag is equal to invalidate any flag.
641      */
642     public Expr findFlagExpression(int flag) {
643         if (mInvalidateAnyFlags.get(flag)) {
644             return null;
645         }
646         final String key = mFlagMapping[flag];
647         if (mExprMap.containsKey(key)) {
648             return mExprMap.get(key);
649         }
650         int falseIndex = key.indexOf(FALSE_KEY_SUFFIX);
651         if (falseIndex > -1) {
652             final String trimmed = key.substring(0, falseIndex);
653             return mExprMap.get(trimmed);
654         }
655         int trueIndex = key.indexOf(TRUE_KEY_SUFFIX);
656         if (trueIndex > -1) {
657             final String trimmed = key.substring(0, trueIndex);
658             return mExprMap.get(trimmed);
659         }
660         // log everything we call
661         StringBuilder error = new StringBuilder();
662         error.append("cannot find flag:").append(flag).append("\n");
663         error.append("invalidate any flag:").append(mInvalidateAnyFlags).append("\n");
664         error.append("key:").append(key).append("\n");
665         error.append("flag mapping:").append(Arrays.toString(mFlagMapping));
666         L.e(error.toString());
667         return null;
668     }
669 
670     public BitSet getInvalidateAnyBitSet() {
671         return mInvalidateAnyFlags;
672     }
673 
674     public int getInvalidateAnyFlagIndex() {
675         return mInvalidateAnyFlagIndex;
676     }
677 
678     public Expr argListExpr(Iterable<Expr> expressions) {
679         return register(new ArgListExpr(mArgListIdCounter ++, expressions));
680     }
681 
682     public void setCurrentLocationInFile(Location location) {
683         mCurrentLocationInFile = location;
684     }
685 
686     public Expr listenerExpr(Expr expression, String name, ModelClass listenerType,
687             ModelMethod listenerMethod) {
688         return register(new ListenerExpr(expression, name, listenerType, listenerMethod));
689     }
690 
691     public FieldAssignmentExpr assignment(Expr target, String name, Expr value) {
692         return register(new FieldAssignmentExpr(target, name, value));
693     }
694 
695     public Map<String, CallbackWrapper> getCallbackWrappers() {
696         return mCallbackWrappers;
697     }
698 
699     public CallbackWrapper callbackWrapper(ModelClass klass, ModelMethod method) {
700         final String key = CallbackWrapper.uniqueKey(klass, method);
701         CallbackWrapper wrapper = mCallbackWrappers.get(key);
702         if (wrapper == null) {
703             wrapper = new CallbackWrapper(klass, method);
704             mCallbackWrappers.put(key, wrapper);
705         }
706         return wrapper;
707     }
708 
709     public LambdaExpr lambdaExpr(Expr expr, CallbackExprModel callbackExprModel) {
710         return register(new LambdaExpr(expr, callbackExprModel));
711     }
712 
713     public IdentifierExpr findIdentifier(String name) {
714         for (Expr expr : mExprMap.values()) {
715             if (expr instanceof IdentifierExpr && name.equals(((IdentifierExpr) expr).getName())) {
716                 return (IdentifierExpr) expr;
717             }
718         }
719         return null;
720     }
721 }
722