/*
* Copyright (C) 2014 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#include "Rule.h"
#include <utils/String8.h>
using namespace android;
namespace split {
inline static void indentStr(String8& str, int indent) {
while (indent > 0) {
str.append(" ");
indent--;
}
}
Rule::Rule(const Rule& rhs)
: RefBase()
, op(rhs.op)
, key(rhs.key)
, negate(rhs.negate)
, stringArgs(rhs.stringArgs)
, longArgs(rhs.longArgs)
, subrules(rhs.subrules) {
}
String8 Rule::toJson(int indent) const {
String8 str;
indentStr(str, indent);
str.append("{\n");
indent++;
indentStr(str, indent);
str.append("\"op\": \"");
switch (op) {
case ALWAYS_TRUE:
str.append("ALWAYS_TRUE");
break;
case GREATER_THAN:
str.append("GREATER_THAN");
break;
case LESS_THAN:
str.append("LESS_THAN");
break;
case EQUALS:
str.append("EQUALS");
break;
case AND_SUBRULES:
str.append("AND_SUBRULES");
break;
case OR_SUBRULES:
str.append("OR_SUBRULES");
break;
case CONTAINS_ANY:
str.append("CONTAINS_ANY");
break;
default:
str.appendFormat("%d", op);
break;
}
str.append("\"");
if (negate) {
str.append(",\n");
indentStr(str, indent);
str.append("\"negate\": true");
}
bool includeKey = true;
switch (op) {
case AND_SUBRULES:
case OR_SUBRULES:
includeKey = false;
break;
default:
break;
}
if (includeKey) {
str.append(",\n");
indentStr(str, indent);
str.append("\"property\": \"");
switch (key) {
case NONE:
str.append("NONE");
break;
case SDK_VERSION:
str.append("SDK_VERSION");
break;
case SCREEN_DENSITY:
str.append("SCREEN_DENSITY");
break;
case NATIVE_PLATFORM:
str.append("NATIVE_PLATFORM");
break;
case LANGUAGE:
str.append("LANGUAGE");
break;
default:
str.appendFormat("%d", key);
break;
}
str.append("\"");
}
if (op == AND_SUBRULES || op == OR_SUBRULES) {
str.append(",\n");
indentStr(str, indent);
str.append("\"subrules\": [\n");
const size_t subruleCount = subrules.size();
for (size_t i = 0; i < subruleCount; i++) {
str.append(subrules[i]->toJson(indent + 1));
if (i != subruleCount - 1) {
str.append(",");
}
str.append("\n");
}
indentStr(str, indent);
str.append("]");
} else {
switch (key) {
case SDK_VERSION:
case SCREEN_DENSITY: {
str.append(",\n");
indentStr(str, indent);
str.append("\"args\": [");
const size_t argCount = longArgs.size();
for (size_t i = 0; i < argCount; i++) {
if (i != 0) {
str.append(", ");
}
str.appendFormat("%d", longArgs[i]);
}
str.append("]");
break;
}
case LANGUAGE:
case NATIVE_PLATFORM: {
str.append(",\n");
indentStr(str, indent);
str.append("\"args\": [");
const size_t argCount = stringArgs.size();
for (size_t i = 0; i < argCount; i++) {
if (i != 0) {
str.append(", ");
}
str.append(stringArgs[i]);
}
str.append("]");
break;
}
default:
break;
}
}
str.append("\n");
indent--;
indentStr(str, indent);
str.append("}");
return str;
}
sp<Rule> Rule::simplify(sp<Rule> rule) {
if (rule->op != AND_SUBRULES && rule->op != OR_SUBRULES) {
return rule;
}
Vector<sp<Rule> > newSubrules;
newSubrules.setCapacity(rule->subrules.size());
const size_t subruleCount = rule->subrules.size();
for (size_t i = 0; i < subruleCount; i++) {
sp<Rule> simplifiedRule = simplify(rule->subrules.editItemAt(i));
if (simplifiedRule != NULL) {
if (simplifiedRule->op == rule->op) {
newSubrules.appendVector(simplifiedRule->subrules);
} else {
newSubrules.add(simplifiedRule);
}
}
}
const size_t newSubruleCount = newSubrules.size();
if (newSubruleCount == 0) {
return NULL;
} else if (subruleCount == 1) {
return newSubrules.editTop();
}
rule->subrules = newSubrules;
return rule;
}
} // namespace split