1 /*
2  * Copyright (C) 2014 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 
17 #include "Rule.h"
18 
19 #include <utils/String8.h>
20 
21 using namespace android;
22 
23 namespace split {
24 
indentStr(String8 & str,int indent)25 inline static void indentStr(String8& str, int indent) {
26     while (indent > 0) {
27         str.append("  ");
28         indent--;
29     }
30 }
31 
Rule(const Rule & rhs)32 Rule::Rule(const Rule& rhs)
33     : RefBase()
34     , op(rhs.op)
35     , key(rhs.key)
36     , negate(rhs.negate)
37     , stringArgs(rhs.stringArgs)
38     , longArgs(rhs.longArgs)
39     , subrules(rhs.subrules) {
40 }
41 
toJson(int indent) const42 String8 Rule::toJson(int indent) const {
43     String8 str;
44     indentStr(str, indent);
45     str.append("{\n");
46     indent++;
47     indentStr(str, indent);
48     str.append("\"op\": \"");
49     switch (op) {
50         case ALWAYS_TRUE:
51             str.append("ALWAYS_TRUE");
52             break;
53         case GREATER_THAN:
54             str.append("GREATER_THAN");
55             break;
56         case LESS_THAN:
57             str.append("LESS_THAN");
58             break;
59         case EQUALS:
60             str.append("EQUALS");
61             break;
62         case AND_SUBRULES:
63             str.append("AND_SUBRULES");
64             break;
65         case OR_SUBRULES:
66             str.append("OR_SUBRULES");
67             break;
68         case CONTAINS_ANY:
69             str.append("CONTAINS_ANY");
70             break;
71         default:
72             str.appendFormat("%d", op);
73             break;
74     }
75     str.append("\"");
76 
77     if (negate) {
78         str.append(",\n");
79         indentStr(str, indent);
80         str.append("\"negate\": true");
81     }
82 
83     bool includeKey = true;
84     switch (op) {
85         case AND_SUBRULES:
86         case OR_SUBRULES:
87             includeKey = false;
88             break;
89         default:
90             break;
91     }
92 
93     if (includeKey) {
94         str.append(",\n");
95         indentStr(str, indent);
96         str.append("\"property\": \"");
97         switch (key) {
98             case NONE:
99                 str.append("NONE");
100                 break;
101             case SDK_VERSION:
102                 str.append("SDK_VERSION");
103                 break;
104             case SCREEN_DENSITY:
105                 str.append("SCREEN_DENSITY");
106                 break;
107             case NATIVE_PLATFORM:
108                 str.append("NATIVE_PLATFORM");
109                 break;
110             case LANGUAGE:
111                 str.append("LANGUAGE");
112                 break;
113             default:
114                 str.appendFormat("%d", key);
115                 break;
116         }
117         str.append("\"");
118     }
119 
120     if (op == AND_SUBRULES || op == OR_SUBRULES) {
121         str.append(",\n");
122         indentStr(str, indent);
123         str.append("\"subrules\": [\n");
124         const size_t subruleCount = subrules.size();
125         for (size_t i = 0; i < subruleCount; i++) {
126             str.append(subrules[i]->toJson(indent + 1));
127             if (i != subruleCount - 1) {
128                 str.append(",");
129             }
130             str.append("\n");
131         }
132         indentStr(str, indent);
133         str.append("]");
134     } else {
135         switch (key) {
136             case SDK_VERSION:
137             case SCREEN_DENSITY: {
138                 str.append(",\n");
139                 indentStr(str, indent);
140                 str.append("\"args\": [");
141                 const size_t argCount = longArgs.size();
142                 for (size_t i = 0; i < argCount; i++) {
143                     if (i != 0) {
144                         str.append(", ");
145                     }
146                     str.appendFormat("%d", longArgs[i]);
147                 }
148                 str.append("]");
149                 break;
150             }
151             case LANGUAGE:
152             case NATIVE_PLATFORM: {
153                 str.append(",\n");
154                 indentStr(str, indent);
155                 str.append("\"args\": [");
156                 const size_t argCount = stringArgs.size();
157                 for (size_t i = 0; i < argCount; i++) {
158                     if (i != 0) {
159                         str.append(", ");
160                     }
161                     str.append(stringArgs[i]);
162                 }
163                 str.append("]");
164                 break;
165             }
166             default:
167                 break;
168         }
169     }
170     str.append("\n");
171     indent--;
172     indentStr(str, indent);
173     str.append("}");
174     return str;
175 }
176 
simplify(sp<Rule> rule)177 sp<Rule> Rule::simplify(sp<Rule> rule) {
178     if (rule->op != AND_SUBRULES && rule->op != OR_SUBRULES) {
179         return rule;
180     }
181 
182     Vector<sp<Rule> > newSubrules;
183     newSubrules.setCapacity(rule->subrules.size());
184     const size_t subruleCount = rule->subrules.size();
185     for (size_t i = 0; i < subruleCount; i++) {
186         sp<Rule> simplifiedRule = simplify(rule->subrules.editItemAt(i));
187         if (simplifiedRule != NULL) {
188             if (simplifiedRule->op == rule->op) {
189                 newSubrules.appendVector(simplifiedRule->subrules);
190             } else {
191                 newSubrules.add(simplifiedRule);
192             }
193         }
194     }
195 
196     const size_t newSubruleCount = newSubrules.size();
197     if (newSubruleCount == 0) {
198         return NULL;
199     } else if (subruleCount == 1) {
200         return newSubrules.editTop();
201     }
202     rule->subrules = newSubrules;
203     return rule;
204 }
205 
206 } // namespace split
207