16a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen/* 26a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Copyright (C) 2015 The Android Open Source Project 36a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 46a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Licensed under the Apache License, Version 2.0 (the "License"); 56a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * you may not use this file except in compliance with the License. 66a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * You may obtain a copy of the License at 76a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 86a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * http://www.apache.org/licenses/LICENSE-2.0 96a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 106a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Unless required by applicable law or agreed to in writing, software 116a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * distributed under the License is distributed on an "AS IS" BASIS, 126a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 136a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * See the License for the specific language governing permissions and 146a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * limitations under the License. 156a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 166a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 176a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenpackage com.android.statementservice.retriever; 186a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 196a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport android.annotation.NonNull; 206a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 216a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenimport java.util.regex.Pattern; 226a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 236a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen/** 246a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * An immutable value type representing a statement relation with "kind" and "detail". 256a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 266a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * <p> The set of kinds is enumerated by the API: <ul> <li> <b>delegate_permission</b>: The detail 276a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * field specifies which permission to delegate. A statement involving this relation does not 286a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * constitute a requirement to do the delegation, just a permission to do so. </ul> 296a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 306a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * <p> We may add other kinds in the future. 316a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 326a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * <p> The detail field is a lowercase alphanumeric string with underscores and periods allowed 33b409110cc114c30ddd6a3a64e6c2395caf413decJoseph Wen * (matching the regex [a-z0-9_.]+), but otherwise unstructured. 346a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 356a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wenpublic final class Relation { 366a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 376a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen private static final Pattern KIND_PATTERN = Pattern.compile("^[a-z0-9_.]+$"); 38b409110cc114c30ddd6a3a64e6c2395caf413decJoseph Wen private static final Pattern DETAIL_PATTERN = Pattern.compile("^([a-z0-9_.]+)$"); 396a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 406a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen private final String mKind; 416a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen private final String mDetail; 426a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 436a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen private Relation(String kind, String detail) { 446a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen mKind = kind; 456a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen mDetail = detail; 466a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 476a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 486a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen /** 496a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Returns the relation's kind. 506a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 516a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen @NonNull 526a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public String getKind() { 536a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return mKind; 546a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 556a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 566a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen /** 576a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Returns the relation's detail. 586a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 596a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen @NonNull 606a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public String getDetail() { 616a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return mDetail; 626a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 636a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 646a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen /** 656a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Creates a new Relation object for the specified {@code kind} and {@code detail}. 666a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 676a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * @throws AssociationServiceException if {@code kind} or {@code detail} is not well formatted. 686a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 696a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public static Relation create(@NonNull String kind, @NonNull String detail) 706a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen throws AssociationServiceException { 716a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen if (!KIND_PATTERN.matcher(kind).matches() || !DETAIL_PATTERN.matcher(detail).matches()) { 726a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen throw new AssociationServiceException("Relation not well formatted."); 736a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 746a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return new Relation(kind, detail); 756a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 766a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 776a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen /** 786a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Creates a new Relation object from its string representation. 796a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * 806a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * @throws AssociationServiceException if the relation is not well formatted. 816a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 826a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public static Relation create(@NonNull String relation) throws AssociationServiceException { 836a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen String[] r = relation.split("/", 2); 846a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen if (r.length != 2) { 856a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen throw new AssociationServiceException("Relation not well formatted."); 866a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 876a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return create(r[0], r[1]); 886a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 896a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 906a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen /** 91b409110cc114c30ddd6a3a64e6c2395caf413decJoseph Wen * Returns true if {@code relation} has the same kind and detail. 926a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 936a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public boolean matches(Relation relation) { 94b409110cc114c30ddd6a3a64e6c2395caf413decJoseph Wen return getKind().equals(relation.getKind()) && getDetail().equals(relation.getDetail()); 956a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 966a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 976a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen /** 986a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen * Returns a string representation of this relation. 996a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen */ 1006a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen @Override 1016a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public String toString() { 1026a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen StringBuilder relation = new StringBuilder(); 1036a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen relation.append(getKind()); 1046a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen relation.append("/"); 1056a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen relation.append(getDetail()); 1066a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return relation.toString(); 1076a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1086a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 1096a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen // equals() and hashCode() are generated by Android Studio. 1106a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen @Override 1116a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public boolean equals(Object o) { 1126a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen if (this == o) { 1136a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return true; 1146a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1156a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen if (o == null || getClass() != o.getClass()) { 1166a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return false; 1176a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1186a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 1196a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen Relation relation = (Relation) o; 1206a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 1216a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen if (mDetail != null ? !mDetail.equals(relation.mDetail) : relation.mDetail != null) { 1226a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return false; 1236a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1246a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen if (mKind != null ? !mKind.equals(relation.mKind) : relation.mKind != null) { 1256a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return false; 1266a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1276a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 1286a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return true; 1296a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1306a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen 1316a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen @Override 1326a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen public int hashCode() { 1336a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen int result = mKind != null ? mKind.hashCode() : 0; 1346a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen result = 31 * result + (mDetail != null ? mDetail.hashCode() : 0); 1356a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen return result; 1366a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen } 1376a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58Joseph Wen} 138