Home | History | Annotate | Download | only in util
      1 /*
      2  * Copyright (C) 2017 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 package com.android.compatibility.common.util;
     18 
     19 import org.json.JSONArray;
     20 import org.json.JSONException;
     21 import org.json.JSONObject;
     22 
     23 import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRule;
     24 import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleAction;
     25 import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRuleCondition;
     26 import com.android.compatibility.common.util.BusinessLogic.BusinessLogicRulesList;
     27 
     28 import java.io.File;
     29 import java.io.IOException;
     30 import java.text.ParseException;
     31 import java.text.SimpleDateFormat;
     32 import java.util.ArrayList;
     33 import java.util.Date;
     34 import java.util.HashMap;
     35 import java.util.List;
     36 import java.util.Map;
     37 import java.util.Scanner;
     38 import java.util.TimeZone;
     39 
     40 /**
     41  * Factory for creating a {@link BusinessLogic}
     42  */
     43 public class BusinessLogicFactory {
     44 
     45     // Name of list object storing test-rules pairs
     46     private static final String BUSINESS_LOGIC_RULES_LISTS = "businessLogicRulesLists";
     47     // Name of test name string
     48     private static final String TEST_NAME = "testName";
     49     // Name of rules object (one 'rules' object to a single test)
     50     private static final String BUSINESS_LOGIC_RULES = "businessLogicRules";
     51     // Name of rule conditions array
     52     private static final String RULE_CONDITIONS = "ruleConditions";
     53     // Name of rule actions array
     54     private static final String RULE_ACTIONS = "ruleActions";
     55     // Description of a rule list object
     56     private static final String RULES_LIST_DESCRIPTION = "description";
     57     // Name of method name string
     58     private static final String METHOD_NAME = "methodName";
     59     // Name of method args array of strings
     60     private static final String METHOD_ARGS = "methodArgs";
     61     // Name of the field in the response object that stores that the auth status of the request.
     62     private static final String AUTHENTICATION_STATUS = "authenticationStatus";
     63     public static final String CONDITIONAL_TESTS_ENABLED = "conditionalTestsEnabled";
     64     // Name of the timestamp field
     65     private static final String TIMESTAMP = "timestamp";
     66     // Date and time pattern for raw timestamp string
     67     private static final String TIMESTAMP_PATTERN = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";
     68 
     69     /**
     70      * Create a BusinessLogic instance from a file of business logic data, formatted in JSON.
     71      * This format is identical to that which is received from the Android Partner business logic
     72      * service.
     73      */
     74     public static BusinessLogic createFromFile(File f) {
     75         // Populate the map from testname to business rules for this new BusinessLogic instance
     76         Map<String, List<BusinessLogicRulesList>> rulesMap = new HashMap<>();
     77         BusinessLogic bl = new BusinessLogic();
     78         try {
     79             String businessLogicString = readFile(f);
     80             JSONObject root = new JSONObject(businessLogicString);
     81             JSONArray jsonRulesLists = null;
     82             if (root.has(AUTHENTICATION_STATUS)){
     83                 String authStatus = root.getString(AUTHENTICATION_STATUS);
     84                 bl.setAuthenticationStatus(authStatus);
     85             }
     86             if (root.has(CONDITIONAL_TESTS_ENABLED)){
     87                 boolean enabled = root.getBoolean(CONDITIONAL_TESTS_ENABLED);
     88                 bl.mConditionalTestsEnabled = enabled;
     89             }
     90             if (root.has(TIMESTAMP)) {
     91                 bl.mTimestamp = parseTimestamp(root.getString(TIMESTAMP));
     92             }
     93             try {
     94                 jsonRulesLists = root.getJSONArray(BUSINESS_LOGIC_RULES_LISTS);
     95             } catch (JSONException e) {
     96                 bl.mRules = rulesMap;
     97                 return bl; // no rules defined for this suite, leave internal map empty
     98             }
     99             for (int i = 0; i < jsonRulesLists.length(); i++) {
    100                 JSONObject jsonRulesList = jsonRulesLists.getJSONObject(i);
    101                 String testName = jsonRulesList.getString(TEST_NAME);
    102                 List<BusinessLogicRulesList> testRulesLists = rulesMap.get(testName);
    103                 if (testRulesLists == null) {
    104                     testRulesLists = new ArrayList<>();
    105                 }
    106                 testRulesLists.add(extractRulesList(jsonRulesList));
    107                 rulesMap.put(testName, testRulesLists);
    108             }
    109         } catch (IOException | JSONException e) {
    110             throw new RuntimeException("Business Logic failed", e);
    111         }
    112         // Return business logic
    113         bl.mRules = rulesMap;
    114         return bl;
    115     }
    116 
    117     /* Extract a BusinessLogicRulesList from the representative JSON object */
    118     private static BusinessLogicRulesList extractRulesList(JSONObject rulesListJSONObject)
    119             throws JSONException {
    120         // First, parse the description for this rule list object, if one exists
    121         String description = null;
    122         try {
    123             description = rulesListJSONObject.getString(RULES_LIST_DESCRIPTION);
    124         } catch (JSONException e) { /* no description set, leave null */}
    125 
    126         // Next, get the list of rules
    127         List<BusinessLogicRule> rules = new ArrayList<>();
    128         JSONArray rulesJSONArray = null;
    129         try {
    130             rulesJSONArray = rulesListJSONObject.getJSONArray(BUSINESS_LOGIC_RULES);
    131         } catch (JSONException e) {
    132             // no rules defined for this test case, return new, rule-less BusinessLogicRulesList
    133             return new BusinessLogicRulesList(rules, description);
    134         }
    135         for (int j = 0; j < rulesJSONArray.length(); j++) {
    136             JSONObject ruleJSONObject = rulesJSONArray.getJSONObject(j);
    137             // Build conditions list
    138             List<BusinessLogicRuleCondition> ruleConditions =
    139                     extractRuleConditionList(ruleJSONObject);
    140             // Build actions list
    141             List<BusinessLogicRuleAction> ruleActions =
    142                     extractRuleActionList(ruleJSONObject);
    143             rules.add(new BusinessLogicRule(ruleConditions, ruleActions));
    144         }
    145         return new BusinessLogicRulesList(rules, description);
    146     }
    147 
    148     /* Extract all BusinessLogicRuleConditions from a JSON business logic rule */
    149     private static List<BusinessLogicRuleCondition> extractRuleConditionList(
    150             JSONObject ruleJSONObject) throws JSONException {
    151         List<BusinessLogicRuleCondition> ruleConditions = new ArrayList<>();
    152         // Rules do not require a condition, return empty list if no condition is found
    153         JSONArray ruleConditionsJSONArray = null;
    154         try {
    155             ruleConditionsJSONArray = ruleJSONObject.getJSONArray(RULE_CONDITIONS);
    156         } catch (JSONException e) {
    157             return ruleConditions; // no conditions for this rule, apply in all cases
    158         }
    159         for (int i = 0; i < ruleConditionsJSONArray.length(); i++) {
    160             JSONObject ruleConditionJSONObject = ruleConditionsJSONArray.getJSONObject(i);
    161             String methodName = ruleConditionJSONObject.getString(METHOD_NAME);
    162             boolean negated = false;
    163             if (methodName.startsWith("!")) {
    164                 methodName = methodName.substring(1); // remove negation from method name string
    165                 negated = true; // change "negated" property to true
    166             }
    167             List<String> methodArgs = new ArrayList<>();
    168             JSONArray methodArgsJSONArray = null;
    169             try {
    170                 methodArgsJSONArray = ruleConditionJSONObject.getJSONArray(METHOD_ARGS);
    171             } catch (JSONException e) {
    172                 // No method args for this rule condition, add rule condition with empty args list
    173                 ruleConditions.add(new BusinessLogicRuleCondition(methodName, methodArgs, negated));
    174                 continue;
    175             }
    176             for (int j = 0; j < methodArgsJSONArray.length(); j++) {
    177                 methodArgs.add(methodArgsJSONArray.getString(j));
    178             }
    179             ruleConditions.add(new BusinessLogicRuleCondition(methodName, methodArgs, negated));
    180         }
    181         return ruleConditions;
    182     }
    183 
    184     /* Extract all BusinessLogicRuleActions from a JSON business logic rule */
    185     private static List<BusinessLogicRuleAction> extractRuleActionList(JSONObject ruleJSONObject)
    186             throws JSONException {
    187         List<BusinessLogicRuleAction> ruleActions = new ArrayList<>();
    188         // All rules require at least one action, line below throws JSONException if not
    189         JSONArray ruleActionsJSONArray = ruleJSONObject.getJSONArray(RULE_ACTIONS);
    190         for (int i = 0; i < ruleActionsJSONArray.length(); i++) {
    191             JSONObject ruleActionJSONObject = ruleActionsJSONArray.getJSONObject(i);
    192             String methodName = ruleActionJSONObject.getString(METHOD_NAME);
    193             List<String> methodArgs = new ArrayList<>();
    194             JSONArray methodArgsJSONArray = null;
    195             try {
    196                 methodArgsJSONArray = ruleActionJSONObject.getJSONArray(METHOD_ARGS);
    197             } catch (JSONException e) {
    198                 // No method args for this rule action, add rule action with empty args list
    199                 ruleActions.add(new BusinessLogicRuleAction(methodName, methodArgs));
    200                 continue;
    201             }
    202             for (int j = 0; j < methodArgsJSONArray.length(); j++) {
    203                 methodArgs.add(methodArgsJSONArray.getString(j));
    204             }
    205             ruleActions.add(new BusinessLogicRuleAction(methodName, methodArgs));
    206         }
    207         return ruleActions;
    208     }
    209 
    210     /* Pare a timestamp string with format TIMESTAMP_PATTERN to a date object */
    211     private static Date parseTimestamp(String timestamp) {
    212         SimpleDateFormat format = new SimpleDateFormat(TIMESTAMP_PATTERN);
    213         format.setTimeZone(TimeZone.getTimeZone("UTC"));
    214         try {
    215             return format.parse(timestamp);
    216         } catch (ParseException e) {
    217             return null;
    218         }
    219     }
    220 
    221     /* Extract string from file */
    222     private static String readFile(File f) throws IOException {
    223         StringBuilder sb = new StringBuilder((int) f.length());
    224         String lineSeparator = System.getProperty("line.separator");
    225         try (Scanner scanner = new Scanner(f)) {
    226             while(scanner.hasNextLine()) {
    227                 sb.append(scanner.nextLine() + lineSeparator);
    228             }
    229             return sb.toString();
    230         }
    231     }
    232 }
    233