1 /*
2  * Copyright (C) 2015 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.statementservice.retriever;
18 
19 import android.util.JsonReader;
20 
21 import org.json.JSONArray;
22 import org.json.JSONException;
23 import org.json.JSONObject;
24 
25 import java.io.IOException;
26 import java.io.StringReader;
27 import java.util.ArrayList;
28 import java.util.List;
29 
30 /**
31  * Utility class that parses JSON-formatted statements.
32  */
33 /* package private */ final class StatementParser {
34 
35     private static final String FIELD_NOT_STRING_FORMAT_STRING = "Expected %s to be string.";
36     private static final String FIELD_NOT_ARRAY_FORMAT_STRING = "Expected %s to be array.";
37 
38     /**
39      * Parses a JSON array of statements.
40      */
parseStatementList(String statementList, AbstractAsset source)41     static ParsedStatement parseStatementList(String statementList, AbstractAsset source)
42             throws JSONException, IOException {
43         List<Statement> statements = new ArrayList<Statement>();
44         List<String> delegates = new ArrayList<String>();
45 
46         JsonReader reader = new JsonReader(new StringReader(statementList));
47         reader.setLenient(false);
48 
49         reader.beginArray();
50         while (reader.hasNext()) {
51             ParsedStatement result;
52             try {
53                 result = parseStatement(reader, source);
54             } catch (AssociationServiceException e) {
55                 // The element in the array is well formatted Json but not a well-formed Statement.
56                 continue;
57             }
58             statements.addAll(result.getStatements());
59             delegates.addAll(result.getDelegates());
60         }
61         reader.endArray();
62 
63         return new ParsedStatement(statements, delegates);
64     }
65 
66     /**
67      * Parses a single JSON statement.
68      */
parseStatement(String statementString, AbstractAsset source)69     static ParsedStatement parseStatement(String statementString, AbstractAsset source)
70             throws AssociationServiceException, IOException, JSONException {
71         JsonReader reader = new JsonReader(new StringReader(statementString));
72         reader.setLenient(false);
73         return parseStatement(reader, source);
74     }
75 
76     /**
77      * Parses a single JSON statement. This method guarantees that exactly one JSON object
78      * will be consumed.
79      */
parseStatement(JsonReader reader, AbstractAsset source)80     static ParsedStatement parseStatement(JsonReader reader, AbstractAsset source)
81             throws JSONException, AssociationServiceException, IOException {
82         List<Statement> statements = new ArrayList<Statement>();
83         List<String> delegates = new ArrayList<String>();
84 
85         JSONObject statement = JsonParser.parse(reader);
86 
87         if (statement.optString(Utils.DELEGATE_FIELD_DELEGATE, null) != null) {
88             delegates.add(statement.optString(Utils.DELEGATE_FIELD_DELEGATE));
89         } else {
90             JSONObject targetObject = statement.optJSONObject(Utils.ASSET_DESCRIPTOR_FIELD_TARGET);
91             if (targetObject == null) {
92                 throw new AssociationServiceException(String.format(
93                         FIELD_NOT_STRING_FORMAT_STRING, Utils.ASSET_DESCRIPTOR_FIELD_TARGET));
94             }
95 
96             JSONArray relations = statement.optJSONArray(Utils.ASSET_DESCRIPTOR_FIELD_RELATION);
97             if (relations == null) {
98                 throw new AssociationServiceException(String.format(
99                         FIELD_NOT_ARRAY_FORMAT_STRING, Utils.ASSET_DESCRIPTOR_FIELD_RELATION));
100             }
101 
102             AbstractAsset target = AssetFactory.create(targetObject);
103             for (int i = 0; i < relations.length(); i++) {
104                 statements.add(Statement
105                         .create(source, target, Relation.create(relations.getString(i))));
106             }
107         }
108 
109         return new ParsedStatement(statements, delegates);
110     }
111 }
112