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
19731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyarimport android.databinding.tool.processing.Scope;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable;
21fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.Callable.Type;
22fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
23fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
24fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelMethod;
256047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.solver.ExecutionPath;
26fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.util.L;
27e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mountimport android.databinding.tool.writer.KCode;
28d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
29d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.ArrayList;
306047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.Arrays;
31d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarimport java.util.List;
32e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount
336047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport static android.databinding.tool.reflection.Callable.DYNAMIC;
346047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport static android.databinding.tool.reflection.Callable.STATIC;
356047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
36d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
37d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyarpublic class MethodCallExpr extends Expr {
38d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    final String mName;
39e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    Callable mGetter;
40bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    // Allow protected calls -- only used for ViewDataBinding methods.
41bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    private boolean mAllowProtected;
42d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
432611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    static List<Expr> concat(Expr e, List<Expr> list) {
449784c9aaedeb863018f5fcaa0a598e8e2f8ed2f3Yigit Boyar        List<Expr> merged = new ArrayList<Expr>();
452611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        merged.add(e);
462611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        merged.addAll(list);
472611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        return merged;
482611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar    }
492611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar
50d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    MethodCallExpr(Expr target, String name, List<Expr> args) {
512611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        super(concat(target, args));
52d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        mName = name;
53d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
54d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
556047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @SuppressWarnings("Duplicates")
56d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
5718243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    public void updateExpr(ModelAnalyzer modelAnalyzer) {
58731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        try {
59731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            Scope.enter(this);
60731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            resolveType(modelAnalyzer);
61731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            super.updateExpr(modelAnalyzer);
62731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        } finally {
63731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar            Scope.exit();
64731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        }
6518243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    }
6618243f6f1b7527272ef4feccdf4327d80d9f2241George Mount
6718243f6f1b7527272ef4feccdf4327d80d9f2241George Mount    @Override
68bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    protected KCode generateCode() {
69e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        KCode code = new KCode()
70bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                .app("", getTarget().toCode())
716047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                .app(".")
726047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                .app(getGetter().name)
736047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                .app("(");
74bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        appendArgs(code);
756047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        code.app(")");
766047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return code;
776047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
786047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
79bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    @Override
80bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public Expr cloneToModel(ExprModel model) {
81bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return model.methodCall(getTarget().cloneToModel(model), mName,
82bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                cloneToModel(model, getArgs()));
83bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    }
84bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
85bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    private void appendArgs(KCode code) {
86e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        boolean first = true;
87e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        for (Expr arg : getArgs()) {
88e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            if (first) {
89e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount                first = false;
90e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            } else {
91e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount                code.app(", ");
92e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            }
93bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            code.app("", arg.toCode());
94e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        }
956047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
966047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
976047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
986047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
996047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final List<ExecutionPath> targetPaths = getTarget().toExecutionPath(paths);
1006047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // after this, we need a null check.
1016047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        List<ExecutionPath> result = new ArrayList<ExecutionPath>();
1026047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        if (getTarget() instanceof StaticIdentifierExpr) {
1036047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            result.addAll(toExecutionPathInOrder(paths, getArgs()));
1046047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } else {
1056047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            for (ExecutionPath path : targetPaths) {
1066047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                Expr cmp = getModel()
1076047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                        .comparison("!=", getTarget(), getModel().symbol("null", Object.class));
1086047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                path.addPath(cmp);
1096047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                final ExecutionPath subPath = path.addBranch(cmp, true);
1106047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                if (subPath != null) {
1116047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    result.addAll(toExecutionPathInOrder(subPath, getArgs()));
1126047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                }
1136047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            }
1146047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
1156047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return result;
1166047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
1176047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
1186047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    private List<ExecutionPath> toExecutionPathInOrder(ExecutionPath path, List<Expr> args) {
1196047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return toExecutionPathInOrder(Arrays.asList(path), args);
120e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    }
121e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount
122e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    @Override
12379fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
124d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        if (mGetter == null) {
125895b618d9c6e3deb56465d0759cda57f50c46214Yigit Boyar            List<ModelClass> args = new ArrayList<ModelClass>();
1267920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount            for (Expr expr : getArgs()) {
127d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar                args.add(expr.getResolvedType());
128d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar            }
12918243f6f1b7527272ef4feccdf4327d80d9f2241George Mount
13018243f6f1b7527272ef4feccdf4327d80d9f2241George Mount            Expr target = getTarget();
131a70fed6415aa1e8bbbe929aee776402ac3b81c86George Mount            boolean isStatic = target instanceof StaticIdentifierExpr;
132bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            ModelMethod method = target.getResolvedType().getMethod(mName, args, isStatic,
133bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                    mAllowProtected);
134fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            if (method == null) {
135bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                StringBuilder argTypes = new StringBuilder();
136bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                for (ModelClass arg : args) {
137bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                    if (argTypes.length() != 0) {
138bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                        argTypes.append(", ");
139bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                    }
140bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                    argTypes.append(arg.toJavaCode());
141bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                }
142bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                String message = "cannot find method '" + mName + "(" + argTypes + ")' in class " +
143fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                        target.getResolvedType().toJavaCode();
144fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                IllegalArgumentException e = new IllegalArgumentException(message);
145bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                L.e(e, "cannot find method %s(%s) in class %s", mName, argTypes,
146fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                        target.getResolvedType().toJavaCode());
147fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount                throw e;
148fa9fe12980ef1103fabe33bf5ff0e2f53042a204George Mount            }
149ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            if (!isStatic && method.isStatic()) {
150ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                // found a static method on an instance. Use class instead
151ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                target.getParents().remove(this);
152ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                getChildren().remove(target);
153ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                StaticIdentifierExpr staticId = getModel()
154ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                        .staticIdentifierFor(target.getResolvedType());
155ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                getChildren().add(staticId);
156ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                staticId.getParents().add(this);
157ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                // make sure we update this in case we access it below
158ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar                target = getTarget();
159ec2f3896c21a504b464bf591cdb45b62692b6760Yigit Boyar            }
160019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            int flags = DYNAMIC;
161019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            if (method.isStatic()) {
162019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar                flags |= STATIC;
163019c36b97c7c172ac03997f6bf170e65b2ed7fe4Yigit Boyar            }
164d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount            mGetter = new Callable(Type.METHOD, method.getName(), null, method.getReturnType(args),
1656047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    method.getParameterTypes().length, flags, method);
166d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        }
167d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mGetter.resolvedType;
168d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
169d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
170d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
171d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    protected List<Dependency> constructDependencies() {
172dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
173dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        for (Dependency dependency : dependencies) {
1747920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount            if (dependency.getOther() == getTarget()) {
175dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar                dependency.setMandatory(true);
176dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar            }
177dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        }
178dc69f49d687ec036947f26a9bf9025a305de0721Yigit Boyar        return dependencies;
179d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
180d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
181d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    @Override
182d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    protected String computeUniqueKey() {
1832611838bffef5a009ca71e3e9e59a93f29b098edYigit Boyar        return join(getTarget().computeUniqueKey(), mName,
1847920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount                super.computeUniqueKey());
185d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
186d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
187d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public Expr getTarget() {
1887920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().get(0);
189d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
190d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
191d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public String getName() {
192d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mName;
193d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
194d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
195d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    public List<Expr> getArgs() {
1967920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().subList(1, getChildren().size());
197d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
198d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar
199e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount    public Callable getGetter() {
200d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar        return mGetter;
201d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar    }
202d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
203bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public void setAllowProtected() {
204bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        mAllowProtected = true;
205bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    }
206bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
207d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
208d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public String getInvertibleError() {
209d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return "Method calls may not be used in two-way expressions";
210d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
211bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
212bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    @Override
213bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public String toString() {
214bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        StringBuilder buf = new StringBuilder();
215bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        buf.append(getTarget())
216bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                .append('.')
217bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                .append(mName)
218bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                .append('(');
219bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        final List<Expr> args = getArgs();
220bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        for (int i = 0; i < args.size(); i++) {
221bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            Expr arg = args.get(i);
222bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            if (i != 0) {
223bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                buf.append(", ");
224bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            }
225bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            buf.append(arg);
226bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        }
227bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        buf.append(')');
228bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return buf.toString();
229bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    }
230d7af42b29ddf22f0068f7496c5ac6f4f34b543b6Yigit Boyar}
231