1d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar/*
2d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Copyright (C) 2015 The Android Open Source Project
3d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
4d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Licensed under the Apache License, Version 2.0 (the "License");
5d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * you may not use this file except in compliance with the License.
6d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * You may obtain a copy of the License at
7d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
8d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *      http://www.apache.org/licenses/LICENSE-2.0
9d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar *
10d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * Unless required by applicable law or agreed to in writing, software
11d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * distributed under the License is distributed on an "AS IS" BASIS,
12d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * See the License for the specific language governing permissions and
14d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar * limitations under the License.
15d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar */
16d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.expr;
18d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
19d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport static android.databinding.tool.reflection.Callable.DYNAMIC;
20d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mountimport static android.databinding.tool.reflection.Callable.STATIC;
21d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
22731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.processing.Scope;
23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable;
24fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable.Type;
25fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
26fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
27fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelMethod;
28fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
29e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mountimport android.databinding.tool.writer.KCode;
30d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
31d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.ArrayList;
32d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.List;
33e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount
34d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
35d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarpublic class MethodCallExpr extends Expr {
36d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    final String mName;
37d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
38e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    Callable mGetter;
39d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
402611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    static List<Expr> concat(Expr e, List<Expr> list) {
419784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<Expr> merged = new ArrayList<Expr>();
422611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        merged.add(e);
432611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        merged.addAll(list);
442611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        return merged;
452611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    }
462611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar
47d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    MethodCallExpr(Expr target, String name, List<Expr> args) {
482611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        super(concat(target, args));
49d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        mName = name;
50d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
51d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
52d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
5318243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    public void updateExpr(ModelAnalyzer modelAnalyzer) {
54731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        try {
55731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            Scope.enter(this);
56731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            resolveType(modelAnalyzer);
57731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            super.updateExpr(modelAnalyzer);
58731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        } finally {
59731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            Scope.exit();
60731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        }
6118243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    }
6218243f6f1b7527272ef4feccdf4327d80d9f2241George Mount
6318243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    @Override
64d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    protected KCode generateCode(boolean expand) {
65e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        KCode code = new KCode()
66d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        .app("", getTarget().toCode(expand))
67e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        .app(".")
68e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        .app(getGetter().name)
69e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        .app("(");
70e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        boolean first = true;
71e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        for (Expr arg : getArgs()) {
72e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            if (first) {
73e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount                first = false;
74e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            } else {
75e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount                code.app(", ");
76e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            }
77d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            code.app("", arg.toCode(expand));
78e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        }
79e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        code.app(")");
80e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        return code;
81e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    }
82e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount
83e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    @Override
8479fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
85d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        if (mGetter == null) {
86895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar            List<ModelClass> args = new ArrayList<ModelClass>();
877920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount            for (Expr expr : getArgs()) {
88d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar                args.add(expr.getResolvedType());
89d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar            }
9018243f6f1b7527272ef4feccdf4327d80d9f2241George Mount
9118243f6f1b7527272ef4feccdf4327d80d9f2241George Mount            Expr target = getTarget();
92a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount            boolean isStatic = target instanceof StaticIdentifierExpr;
93fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic);
94fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (method == null) {
95fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                String message = "cannot find method '" + mName + "' in class " +
96fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                        target.getResolvedType().toJavaCode();
97fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                IllegalArgumentException e = new IllegalArgumentException(message);
98fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                L.e(e, "cannot find method %s in class %s", mName,
99fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                        target.getResolvedType().toJavaCode());
100fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                throw e;
101fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
102ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            if (!isStatic && method.isStatic()) {
103ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                // found a static method on an instance. Use class instead
104ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                target.getParents().remove(this);
105ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                getChildren().remove(target);
106ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                StaticIdentifierExpr staticId = getModel()
107ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        .staticIdentifierFor(target.getResolvedType());
108ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                getChildren().add(staticId);
109ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                staticId.getParents().add(this);
110ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                // make sure we update this in case we access it below
111ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                target = getTarget();
112ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            }
113019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            int flags = DYNAMIC;
114019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            if (method.isStatic()) {
115019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                flags |= STATIC;
116019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            }
117d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mGetter = new Callable(Type.METHOD, method.getName(), null, method.getReturnType(args),
11888ce44ccc65e74a8553244ca246cc9f4c48483e0Yigit Boyar                    method.getParameterTypes().length, flags);
119d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
120d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mGetter.resolvedType;
121d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
122d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
123d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
124d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    protected List<Dependency> constructDependencies() {
125dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
126dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        for (Dependency dependency : dependencies) {
1277920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount            if (dependency.getOther() == getTarget()) {
128dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar                dependency.setMandatory(true);
129dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar            }
130dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        }
131dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        return dependencies;
132d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
133d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
134d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
135d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    protected String computeUniqueKey() {
1362611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        return join(getTarget().computeUniqueKey(), mName,
1377920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                super.computeUniqueKey());
138d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
139d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
140d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public Expr getTarget() {
1417920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().get(0);
142d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
143d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
144d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public String getName() {
145d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mName;
146d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
147d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
148d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public List<Expr> getArgs() {
1497920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().subList(1, getChildren().size());
150d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
151d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
152e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    public Callable getGetter() {
153d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mGetter;
154d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
155d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
156d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
157d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public String getInvertibleError() {
158d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return "Method calls may not be used in two-way expressions";
159d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
160d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar}
161