1 /* 2 * Copyright (C) 2020 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.network.retriever 18 19 import android.util.JsonReader 20 import com.android.statementservice.retriever.AbstractAsset 21 import com.android.statementservice.retriever.AssetFactory 22 import com.android.statementservice.retriever.JsonParser 23 import com.android.statementservice.retriever.Relation 24 import com.android.statementservice.retriever.Statement 25 import com.android.statementservice.utils.Result 26 import com.android.statementservice.utils.StatementUtils 27 import java.io.StringReader 28 import java.util.ArrayList 29 import com.android.statementservice.retriever.WebAsset 30 import com.android.statementservice.retriever.AndroidAppAsset 31 32 /** 33 * Parses JSON from the Digital Asset Links specification. For examples, see [WebAsset], 34 * [AndroidAppAsset], and [Statement]. 35 */ 36 object StatementParser { 37 38 private const val FIELD_NOT_STRING_FORMAT_STRING = "Expected %s to be string." 39 private const val FIELD_NOT_ARRAY_FORMAT_STRING = "Expected %s to be array." 40 41 /** 42 * Parses a JSON array of statements. 43 */ 44 fun parseStatementList(statementList: String, source: AbstractAsset): Result<ParsedStatement> { 45 val statements: MutableList<Statement> = ArrayList() 46 val delegates: MutableList<String> = ArrayList() 47 StringReader(statementList).use { stringReader -> 48 JsonReader(stringReader).use { reader -> 49 reader.isLenient = false 50 reader.beginArray() 51 while (reader.hasNext()) { 52 val result = parseOneStatement(reader, source) 53 if (result is Result.Failure) { 54 continue 55 } 56 result as Result.Success 57 statements.addAll(result.value.statements) 58 delegates.addAll(result.value.delegates) 59 } 60 reader.endArray() 61 } 62 } 63 return Result.Success(ParsedStatement(statements, delegates)) 64 } 65 66 /** 67 * Parses a single JSON statement. 68 */ 69 fun parseStatement(statementString: String, source: AbstractAsset) = 70 StringReader(statementString).use { stringReader -> 71 JsonReader(stringReader).use { reader -> 72 reader.isLenient = false 73 parseOneStatement(reader, source) 74 } 75 } 76 77 /** 78 * Parses a single JSON statement. This method guarantees that exactly one JSON object 79 * will be consumed. 80 */ 81 private fun parseOneStatement( 82 reader: JsonReader, 83 source: AbstractAsset 84 ): Result<ParsedStatement> { 85 val statement = JsonParser.parse(reader) 86 val delegate = statement.optString(StatementUtils.DELEGATE_FIELD_DELEGATE) 87 if (!delegate.isNullOrEmpty()) { 88 return Result.Success(ParsedStatement(emptyList(), listOfNotNull(delegate))) 89 } 90 91 val targetObject = statement.optJSONObject(StatementUtils.ASSET_DESCRIPTOR_FIELD_TARGET) 92 ?: return Result.Failure( 93 FIELD_NOT_STRING_FORMAT_STRING.format(StatementUtils.ASSET_DESCRIPTOR_FIELD_TARGET) 94 ) 95 val relations = statement.optJSONArray(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION) 96 ?: return Result.Failure( 97 FIELD_NOT_ARRAY_FORMAT_STRING.format(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION) 98 ) 99 val target = AssetFactory.create(targetObject) 100 101 val statements = (0 until relations.length()) 102 .map { relations.getString(it) } 103 .map(Relation::create) 104 .map { Statement.create(source, target, it) } 105 return Result.Success(ParsedStatement(statements, listOfNotNull(delegate))) 106 } 107 108 data class ParsedStatement(val statements: List<Statement>, val delegates: List<String>) 109 } 110