BracketExpr.java revision bb4a033fcd5cd20e5be46ef8ead442dc7db2454d
15cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount/*
25cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * Copyright (C) 2015 The Android Open Source Project
35cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount *
45cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * Licensed under the Apache License, Version 2.0 (the "License");
55cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * you may not use this file except in compliance with the License.
65cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * You may obtain a copy of the License at
75cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount *
85cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount *      http://www.apache.org/licenses/LICENSE-2.0
95cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount *
105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * Unless required by applicable law or agreed to in writing, software
115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * distributed under the License is distributed on an "AS IS" BASIS,
125cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
135cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * See the License for the specific language governing permissions and
145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount * limitations under the License.
155cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount */
165cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
17fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountpackage android.databinding.tool.expr;
185cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
19fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelAnalyzer;
20fead9ca09b117136b35bc5bf137340a754f9edddGeorge Mountimport android.databinding.tool.reflection.ModelClass;
216047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport android.databinding.tool.solver.ExecutionPath;
22e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mountimport android.databinding.tool.writer.KCode;
235cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
24bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mountimport com.google.common.collect.Lists;
25bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
266047998943beebd81e0ae1068df39c0cbee38628Yigit Boyarimport java.util.ArrayList;
275cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mountimport java.util.List;
285cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
295cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mountpublic class BracketExpr extends Expr {
305cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
31af146d6a8c0efcf5682d14047c06866a5548f78fYigit Boyar    public enum BracketAccessor {
325cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        ARRAY,
335cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        LIST,
345cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        MAP,
355cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
365cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
375cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    private BracketAccessor mAccessor;
385cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
395cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    BracketExpr(Expr target, Expr arg) {
405cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        super(target, arg);
415cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
425cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
435cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    @Override
4479fc7f3727815ab35bb1bb2e060bfb7db3176eedGeorge Mount    protected ModelClass resolveType(ModelAnalyzer modelAnalyzer) {
45731b74f7f44e67312a1fc4161c4e0aae221b2417Yigit Boyar        ModelClass targetType = getTarget().getResolvedType();
465cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        if (targetType.isArray()) {
475cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mAccessor = BracketAccessor.ARRAY;
48e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount        } else if (targetType.isList()) {
495cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mAccessor = BracketAccessor.LIST;
50e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount        } else if (targetType.isMap()) {
515cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            mAccessor = BracketAccessor.MAP;
525cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        } else {
535cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount            throw new IllegalArgumentException("Cannot determine variable type used in [] " +
548e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                    "expression. Cast the value to List, Map, " +
558e5d3b4aa4e47fc0150b4a26b58ec6e5c17b9d16George Mount                    "or array. Type detected: " + targetType.toJavaCode());
565cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        }
57e6c6d3bf4fac3fa11c5780cfd3bc14cdb0caaea1George Mount        return targetType.getComponentType();
585cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
595cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
605cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    @Override
616047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    public List<ExecutionPath> toExecutionPath(List<ExecutionPath> paths) {
626047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        final List<ExecutionPath> targetPaths = getTarget().toExecutionPath(paths);
636047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        // after this, we need a null check.
646047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        List<ExecutionPath> result = new ArrayList<ExecutionPath>();
656047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        if (getTarget() instanceof StaticIdentifierExpr) {
666047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            result.addAll(toExecutionPathInOrder(paths, getTarget()));
676047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        } else {
686047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            for (ExecutionPath path : targetPaths) {
696047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                Expr cmp = getModel().comparison("!=", getTarget(),
706047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                        getModel().symbol("null", Object.class));
716047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                path.addPath(cmp);
726047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                final ExecutionPath subPath = path.addBranch(cmp, true);
736047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                if (subPath != null) {
746047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    final List<ExecutionPath> argPath = getArg().toExecutionPath(subPath);
756047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                    result.addAll(addJustMeToExecutionPath(argPath));
766047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar                }
776047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar            }
786047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        }
796047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar        return result;
806047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    }
816047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar
826047998943beebd81e0ae1068df39c0cbee38628Yigit Boyar    @Override
835cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected List<Dependency> constructDependencies() {
84e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount        final List<Dependency> dependencies = constructDynamicChildrenDependencies();
85e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount        for (Dependency dependency : dependencies) {
86e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount            if (dependency.getOther() == getTarget()) {
87e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount                dependency.setMandatory(true);
88e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount            }
89e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount        }
90e0d5ed7613cb72192430cd2ed8e4159618ca308eGeorge Mount        return dependencies;
915cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
925cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
935cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    protected String computeUniqueKey() {
94d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        final String targetKey = getTarget().computeUniqueKey();
95bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return join(targetKey, "$", getArg().computeUniqueKey(), "$");
96d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
97d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
98d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
99d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    public String getInvertibleError() {
100d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return null;
1015cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
1025cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
1035cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public Expr getTarget() {
1047920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().get(0);
1055cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
1065cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
1075cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public Expr getArg() {
1087920e17f7b501d5792e7e3250e9dbb69eca86adeGeorge Mount        return getChildren().get(1);
1095cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
1105cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
1115cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public BracketAccessor getAccessor() {
1125cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount        return mAccessor;
1135cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
1145cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount
1155cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    public boolean argCastsInteger() {
116d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount        return mAccessor != BracketAccessor.MAP && getArg().getResolvedType().isObject();
1175cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount    }
118e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount
119e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    @Override
120bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    protected KCode generateCode() {
12176b791f78542a2feb191482a2204de95eaf8ee72George Mount        String cast = argCastsInteger() ? "(Integer) " : "";
122e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        switch (getAccessor()) {
12376b791f78542a2feb191482a2204de95eaf8ee72George Mount            case ARRAY: {
12476b791f78542a2feb191482a2204de95eaf8ee72George Mount                return new KCode().
12576b791f78542a2feb191482a2204de95eaf8ee72George Mount                        app("getFromArray(", getTarget().toCode()).
12676b791f78542a2feb191482a2204de95eaf8ee72George Mount                        app(", ").
12776b791f78542a2feb191482a2204de95eaf8ee72George Mount                        app(cast, getArg().toCode()).app(")");
12876b791f78542a2feb191482a2204de95eaf8ee72George Mount            }
12976b791f78542a2feb191482a2204de95eaf8ee72George Mount            case LIST: {
13076b791f78542a2feb191482a2204de95eaf8ee72George Mount                ModelClass listType = ModelAnalyzer.getInstance().findClass(java.util.List.class).
13176b791f78542a2feb191482a2204de95eaf8ee72George Mount                        erasure();
13276b791f78542a2feb191482a2204de95eaf8ee72George Mount                ModelClass targetType = getTarget().getResolvedType().erasure();
13376b791f78542a2feb191482a2204de95eaf8ee72George Mount                if (listType.isAssignableFrom(targetType)) {
13476b791f78542a2feb191482a2204de95eaf8ee72George Mount                    return new KCode().
13576b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app("getFromList(", getTarget().toCode()).
13676b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app(", ").
13776b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app(cast, getArg().toCode()).
13876b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app(")");
13976b791f78542a2feb191482a2204de95eaf8ee72George Mount                } else {
14076b791f78542a2feb191482a2204de95eaf8ee72George Mount                    return new KCode().
14176b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app("", getTarget().toCode()).
14276b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app(".get(").
14376b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app(cast, getArg().toCode()).
14476b791f78542a2feb191482a2204de95eaf8ee72George Mount                            app(")");
145e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount                }
14676b791f78542a2feb191482a2204de95eaf8ee72George Mount            }
147e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount            case MAP:
14876b791f78542a2feb191482a2204de95eaf8ee72George Mount                return new KCode().
14976b791f78542a2feb191482a2204de95eaf8ee72George Mount                        app("", getTarget().toCode()).
15076b791f78542a2feb191482a2204de95eaf8ee72George Mount                        app(".get(", getArg().toCode()).
15176b791f78542a2feb191482a2204de95eaf8ee72George Mount                        app(")");
152e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        }
153e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount        throw new IllegalStateException("Invalid BracketAccessor type");
154e52882df6130221462bf07f5f2b52de5c4b0f8deGeorge Mount    }
155d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount
156d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    @Override
157bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public Expr generateInverse(ExprModel model, Expr value, String bindingClassName) {
158bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        Expr arg = getArg().cloneToModel(model);
159bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        arg = argCastsInteger()
160bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                ? model.castExpr("int", model.castExpr("Integer", arg))
161bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                : arg;
162bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        StaticIdentifierExpr viewDataBinding =
163bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                model.staticIdentifier(ModelAnalyzer.VIEW_DATA_BINDING);
164bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        viewDataBinding.setUserDefinedType(ModelAnalyzer.VIEW_DATA_BINDING);
165bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        ModelClass targetType = getTarget().getResolvedType();
166bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        if ((targetType.isList() || targetType.isMap()) &&
167bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount                value.getResolvedType().isPrimitive()) {
168bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            ModelClass boxed = value.getResolvedType().box();
169bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount            value = model.castExpr(boxed.toJavaCode(), value);
170bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        }
171bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        List<Expr> args = Lists.newArrayList(getTarget().cloneToModel(model), arg, value);
172bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        MethodCallExpr setter = model.methodCall(viewDataBinding, "setTo", args);
173bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        setter.setAllowProtected();
174bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return setter;
175bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    }
176bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
177bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    @Override
178bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public Expr cloneToModel(ExprModel model) {
179bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return model.bracketExpr(getTarget().cloneToModel(model), getArg().cloneToModel(model));
180bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    }
181bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount
182bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    @Override
183bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount    public String toString() {
184bb4a033fcd5cd20e5be46ef8ead442dc7db2454dGeorge Mount        return getTarget().toString() + '[' + getArg() + ']';
185d3f2b9229472c9dae9bf4ae8b3e2d653b5653b01George Mount    }
1865cd681c345db8f606d7d5a8662e20e059f21a86cGeorge Mount}
187