16047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar/*
26047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Copyright (C) 2016 The Android Open Source Project
36047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *
46047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
56047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * you may not use this file except in compliance with the License.
66047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * You may obtain a copy of the License at
76047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *
86047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
96047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar *
106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Unless required by applicable law or agreed to in writing, software
116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * See the License for the specific language governing permissions and
146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * limitations under the License.
156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar */
166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarpackage android.databinding.tool.expr;
186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.processing.ErrorMessages;
206047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.processing.Scope;
216047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.store.Location;
226047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.util.L;
236047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.util.Preconditions;
246047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.ArrayList;
266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.List;
276047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.Map;
286047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
296047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar/**
306047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * Callbacks are evaluated when event happens, not when execute pending is run. To separate their
316047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * expressions, we provide a separate model for them that extends the main model. This allows them
326047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar * to introduce their own variables etc. without mixing them with other expressions.
336047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar */
346047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarpublic class CallbackExprModel extends ExprModel {
356047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    // used for imports and other stuff.
366047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    final ExprModel mOriginal;
376047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    final List<CallbackArgExpr> mArguments = new ArrayList<CallbackArgExpr>();
386047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public CallbackExprModel(ExprModel original) {
396047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mOriginal = original;
406047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
416047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
426047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
436047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public Map<String, String> getImports() {
446047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mOriginal.getImports();
456047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
466047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
476047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
486047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public StaticIdentifierExpr addImport(String alias, String type, Location location) {
496047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mOriginal.addImport(alias, type, location);
506047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
516047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
526047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
536047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public <T extends Expr> T register(T expr) {
546047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // locations are only synced to main model so we need to sync overselves here.
556047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        setCurrentLocationInFile(mOriginal.getCurrentLocationInFile());
566047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        setCurrentParserContext(mOriginal.getCurrentParserContext());
576047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return super.register(expr);
586047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
596047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
606047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
616047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public void seal() {
626047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // ensure all types are calculated
636047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        for (Expr expr : mExprMap.values()) {
646047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            expr.getResolvedType();
656047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            expr.markAsUsedInCallback();
666047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
676047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        markSealed();
686047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // we do not resolve dependencies for these expression because they are resolved via
696047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // ExecutionPath and should not interfere with the main expr model's dependency graph.
706047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
716047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
726047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
736047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public IdentifierExpr identifier(String name) {
746047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        CallbackArgExpr arg = findArgByName(name);
756047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        if (arg != null) {
766047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            return arg;
776047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
786047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        IdentifierExpr id = new IdentifierExpr(name);
796047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final Expr existing = mExprMap.get(id.getUniqueKey());
806047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        if (existing == null) {
816047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar             // this is not a method variable reference. register it in the main model
826047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            final IdentifierExpr identifier = mOriginal.identifier(name);
836047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            mExprMap.put(identifier.getUniqueKey(), identifier);
846047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            identifier.markAsUsedInCallback();
856047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            return identifier;
866047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
876047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return (IdentifierExpr) existing;
886047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
896047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
906047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private CallbackArgExpr findArgByName(String name) {
916047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        for (CallbackArgExpr arg : mArguments) {
926047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            if (name.equals(arg.getName())) {
936047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                return arg;
946047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            }
956047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
966047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return null;
976047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
986047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
996047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public CallbackArgExpr callbackArg(String name) {
1006047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        Preconditions.checkNull(findArgByName(name),
1016047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                ErrorMessages.DUPLICATE_CALLBACK_ARGUMENT, name);
1026047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final CallbackArgExpr id = new CallbackArgExpr(mArguments.size(), name);
1036047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final CallbackArgExpr added = register(id);
1046047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        mArguments.add(added);
1056047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1066047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        try {
1076047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            Scope.enter(added);
1086047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            IdentifierExpr identifierWithSameName = mOriginal.findIdentifier(name);
1096047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            if (identifierWithSameName != null) {
1106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                L.w(ErrorMessages.CALLBACK_VARIABLE_NAME_CLASH, name, name,
1116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                        identifierWithSameName.getUserDefinedType());
1126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            }
1136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } finally {
1146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            Scope.exit();
1156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
1166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return added;
1176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public int getArgCount() {
1206047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mArguments.size();
1216047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1226047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1236047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public List<CallbackArgExpr> getArguments() {
1246047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return mArguments;
1256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar}
127