/* * Copyright (C) 2020 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. */ package com.android.statementservice.network.retriever import android.util.JsonReader import com.android.statementservice.retriever.AbstractAsset import com.android.statementservice.retriever.AssetFactory import com.android.statementservice.retriever.JsonParser import com.android.statementservice.retriever.Relation import com.android.statementservice.retriever.Statement import com.android.statementservice.utils.Result import com.android.statementservice.utils.StatementUtils import java.io.StringReader import java.util.ArrayList import com.android.statementservice.retriever.WebAsset import com.android.statementservice.retriever.AndroidAppAsset /** * Parses JSON from the Digital Asset Links specification. For examples, see [WebAsset], * [AndroidAppAsset], and [Statement]. */ object StatementParser { private const val FIELD_NOT_STRING_FORMAT_STRING = "Expected %s to be string." private const val FIELD_NOT_ARRAY_FORMAT_STRING = "Expected %s to be array." /** * Parses a JSON array of statements. */ fun parseStatementList(statementList: String, source: AbstractAsset): Result { val statements: MutableList = ArrayList() val delegates: MutableList = ArrayList() StringReader(statementList).use { stringReader -> JsonReader(stringReader).use { reader -> reader.isLenient = false reader.beginArray() while (reader.hasNext()) { val result = parseOneStatement(reader, source) if (result is Result.Failure) { continue } result as Result.Success statements.addAll(result.value.statements) delegates.addAll(result.value.delegates) } reader.endArray() } } return Result.Success(ParsedStatement(statements, delegates)) } /** * Parses a single JSON statement. */ fun parseStatement(statementString: String, source: AbstractAsset) = StringReader(statementString).use { stringReader -> JsonReader(stringReader).use { reader -> reader.isLenient = false parseOneStatement(reader, source) } } /** * Parses a single JSON statement. This method guarantees that exactly one JSON object * will be consumed. */ private fun parseOneStatement( reader: JsonReader, source: AbstractAsset ): Result { val statement = JsonParser.parse(reader) val delegate = statement.optString(StatementUtils.DELEGATE_FIELD_DELEGATE) if (!delegate.isNullOrEmpty()) { return Result.Success(ParsedStatement(emptyList(), listOfNotNull(delegate))) } val targetObject = statement.optJSONObject(StatementUtils.ASSET_DESCRIPTOR_FIELD_TARGET) ?: return Result.Failure( FIELD_NOT_STRING_FORMAT_STRING.format(StatementUtils.ASSET_DESCRIPTOR_FIELD_TARGET) ) val relations = statement.optJSONArray(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION) ?: return Result.Failure( FIELD_NOT_ARRAY_FORMAT_STRING.format(StatementUtils.ASSET_DESCRIPTOR_FIELD_RELATION) ) val target = AssetFactory.create(targetObject) val statements = (0 until relations.length()) .map { relations.getString(it) } .map(Relation::create) .map { Statement.create(source, target, it) } return Result.Success(ParsedStatement(statements, listOfNotNull(delegate))) } data class ParsedStatement(val statements: List, val delegates: List) }