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