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
21using namespace android;
22
23namespace split {
24
25inline static void indentStr(String8& str, int indent) {
26    while (indent > 0) {
27        str.append("  ");
28        indent--;
29    }
30}
31
32Rule::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
42String8 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
177sp<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