• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2016 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 package android.databinding.tool.solver;
17 
18 import android.databinding.tool.expr.Expr;
19 import android.databinding.tool.util.Preconditions;
20 
21 import org.jetbrains.annotations.NotNull;
22 import org.jetbrains.annotations.Nullable;
23 
24 import java.util.ArrayList;
25 import java.util.HashMap;
26 import java.util.HashSet;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.Set;
30 
31 /**
32  * Represents all possible outcomes of an expressions with its branching.
33  */
34 public class ExecutionPath {
35     @Nullable //null for root and branches
36     private final Expr mExpr;
37     @NotNull
38     private List<ExecutionPath> mChildren = new ArrayList<ExecutionPath>();
39 
40     @Nullable
41     private ExecutionBranch mTrueBranch;
42 
43     @Nullable
44     private ExecutionBranch mFalseBranch;
45 
46     // values that we know due to branching
47     private Map<Expr, Boolean> mKnownValues = new HashMap<Expr, Boolean>();
48 
49     // expressions that are available right now
50     private Set<Expr> mScopeExpressions = new HashSet<Expr>();
51 
52     private final boolean mIsAlreadyEvaluated;
53 
createRoot()54     public static ExecutionPath createRoot() {
55         return new ExecutionPath(null, false);
56     }
57 
ExecutionPath(@ullable Expr expr, boolean isAlreadyEvaluated)58     private ExecutionPath(@Nullable Expr expr, boolean isAlreadyEvaluated) {
59         mExpr = expr;
60         mIsAlreadyEvaluated = isAlreadyEvaluated;
61     }
62 
63     @Nullable
addBranch(Expr pred, boolean expectedValue)64     public ExecutionPath addBranch(Expr pred, boolean expectedValue) {
65         // TODO special predicates like Symbol(true, false)
66         Preconditions.checkNull(expectedValue ? mTrueBranch : mFalseBranch,
67                 "Cannot add two " + expectedValue + "branches");
68         final Boolean knownValue = mKnownValues.get(pred);
69         if (knownValue != null) {
70             // we know the result. cut the branch
71             if (expectedValue == knownValue) {
72                 // just add as a path
73                 return addPath(null);
74             } else {
75                 // drop path. this cannot happen
76                 return null;
77             }
78         } else {
79             ExecutionPath path = createPath(null);
80             ExecutionBranch edge = new ExecutionBranch(path, pred, expectedValue);
81             path.mKnownValues.put(pred, expectedValue);
82             if (expectedValue) {
83                 if (mFalseBranch != null) {
84                     Preconditions.check(mFalseBranch.getConditional() == pred, "Cannot add"
85                             + " branches w/ different conditionals.");
86                 }
87                 mTrueBranch = edge;
88             } else {
89                 if (mTrueBranch != null) {
90                     Preconditions.check(mTrueBranch.getConditional() == pred, "Cannot add"
91                             + " branches w/ different conditionals.");
92                 }
93                 mFalseBranch = edge;
94             }
95             return path;
96         }
97     }
98 
createPath(@ullable Expr expr)99     private ExecutionPath createPath(@Nullable Expr expr) {
100         ExecutionPath path = new ExecutionPath(expr, expr == null ||
101                 mScopeExpressions.contains(expr));
102         // now pass down all values etc
103         path.mKnownValues.putAll(mKnownValues);
104         path.mScopeExpressions.addAll(mScopeExpressions);
105         return path;
106     }
107 
108     @NotNull
addPath(@ullable Expr expr)109     public ExecutionPath addPath(@Nullable Expr expr) {
110         Preconditions.checkNull(mFalseBranch, "Cannot add path after branches are set");
111         Preconditions.checkNull(mTrueBranch, "Cannot add path after branches are set");
112         final ExecutionPath path = createPath(expr);
113         if (expr != null) {
114             mScopeExpressions.add(expr);
115             path.mScopeExpressions.add(expr);
116         }
117         mChildren.add(path);
118         return path;
119     }
120 
debug(StringBuilder builder, int offset)121     public void debug(StringBuilder builder, int offset) {
122         offset(builder, offset);
123         if (mExpr != null || !mIsAlreadyEvaluated) {
124             builder.append("expr:").append(mExpr == null ? "root" : mExpr.getUniqueKey());
125             builder.append(" isRead:").append(mIsAlreadyEvaluated);
126         } else {
127             builder.append("branch");
128         }
129         if (!mKnownValues.isEmpty()) {
130             builder.append(" I know:");
131             for (Map.Entry<Expr, Boolean> entry : mKnownValues.entrySet()) {
132                 builder.append(" ");
133                 builder.append(entry.getKey().getUniqueKey());
134                 builder.append(" is ").append(entry.getValue());
135             }
136         }
137         for (ExecutionPath path : mChildren) {
138             builder.append("\n");
139             path.debug(builder, offset);
140         }
141         if (mTrueBranch != null) {
142             debug(builder, mTrueBranch, offset);
143         }
144         if (mFalseBranch != null) {
145             debug(builder, mFalseBranch, offset);
146         }
147     }
148 
149     @Nullable
getExpr()150     public Expr getExpr() {
151         return mExpr;
152     }
153 
154     @NotNull
getChildren()155     public List<ExecutionPath> getChildren() {
156         return mChildren;
157     }
158 
159     @Nullable
getTrueBranch()160     public ExecutionBranch getTrueBranch() {
161         return mTrueBranch;
162     }
163 
164     @Nullable
getFalseBranch()165     public ExecutionBranch getFalseBranch() {
166         return mFalseBranch;
167     }
168 
isAlreadyEvaluated()169     public boolean isAlreadyEvaluated() {
170         return mIsAlreadyEvaluated;
171     }
172 
debug(StringBuilder builder, ExecutionBranch branch, int offset)173     private void debug(StringBuilder builder, ExecutionBranch branch, int offset) {
174         builder.append("\n");
175         offset(builder, offset);
176         builder.append("if ")
177                 .append(branch.getConditional().getUniqueKey())
178                 .append(" is ").append(branch.getExpectedCondition()).append("\n");
179         branch.getPath().debug(builder, offset + 1);
180     }
181 
offset(StringBuilder builder, int offset)182     private void offset(StringBuilder builder, int offset) {
183         for (int i = 0; i < offset; i++) {
184             builder.append("  ");
185         }
186     }
187 
getKnownValues()188     public Map<Expr, Boolean> getKnownValues() {
189         return mKnownValues;
190     }
191 }
192