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.annotation.NonNull; 20 21 import java.util.regex.Pattern; 22 23 /** 24 * An immutable value type representing a statement relation with "kind" and "detail". 25 * 26 * <p> The set of kinds is enumerated by the API: <ul> <li> <b>delegate_permission</b>: The detail 27 * field specifies which permission to delegate. A statement involving this relation does not 28 * constitute a requirement to do the delegation, just a permission to do so. </ul> 29 * 30 * <p> We may add other kinds in the future. 31 * 32 * <p> The detail field is a lowercase alphanumeric string with underscores and periods allowed 33 * (matching the regex [a-z0-9_.]+), but otherwise unstructured. 34 */ 35 public final class Relation { 36 37 private static final Pattern KIND_PATTERN = Pattern.compile("^[a-z0-9_.]+$"); 38 private static final Pattern DETAIL_PATTERN = Pattern.compile("^([a-z0-9_.]+)$"); 39 40 private final String mKind; 41 private final String mDetail; 42 Relation(String kind, String detail)43 private Relation(String kind, String detail) { 44 mKind = kind; 45 mDetail = detail; 46 } 47 48 /** 49 * Returns the relation's kind. 50 */ 51 @NonNull getKind()52 public String getKind() { 53 return mKind; 54 } 55 56 /** 57 * Returns the relation's detail. 58 */ 59 @NonNull getDetail()60 public String getDetail() { 61 return mDetail; 62 } 63 64 /** 65 * Creates a new Relation object for the specified {@code kind} and {@code detail}. 66 * 67 * @throws AssociationServiceException if {@code kind} or {@code detail} is not well formatted. 68 */ create(@onNull String kind, @NonNull String detail)69 public static Relation create(@NonNull String kind, @NonNull String detail) 70 throws AssociationServiceException { 71 if (!KIND_PATTERN.matcher(kind).matches() || !DETAIL_PATTERN.matcher(detail).matches()) { 72 throw new AssociationServiceException("Relation not well formatted."); 73 } 74 return new Relation(kind, detail); 75 } 76 77 /** 78 * Creates a new Relation object from its string representation. 79 * 80 * @throws AssociationServiceException if the relation is not well formatted. 81 */ create(@onNull String relation)82 public static Relation create(@NonNull String relation) throws AssociationServiceException { 83 String[] r = relation.split("/", 2); 84 if (r.length != 2) { 85 throw new AssociationServiceException("Relation not well formatted."); 86 } 87 return create(r[0], r[1]); 88 } 89 90 /** 91 * Returns true if {@code relation} has the same kind and detail. 92 */ matches(Relation relation)93 public boolean matches(Relation relation) { 94 return getKind().equals(relation.getKind()) && getDetail().equals(relation.getDetail()); 95 } 96 97 /** 98 * Returns a string representation of this relation. 99 */ 100 @Override toString()101 public String toString() { 102 StringBuilder relation = new StringBuilder(); 103 relation.append(getKind()); 104 relation.append("/"); 105 relation.append(getDetail()); 106 return relation.toString(); 107 } 108 109 // equals() and hashCode() are generated by Android Studio. 110 @Override equals(Object o)111 public boolean equals(Object o) { 112 if (this == o) { 113 return true; 114 } 115 if (o == null || getClass() != o.getClass()) { 116 return false; 117 } 118 119 Relation relation = (Relation) o; 120 121 if (mDetail != null ? !mDetail.equals(relation.mDetail) : relation.mDetail != null) { 122 return false; 123 } 124 if (mKind != null ? !mKind.equals(relation.mKind) : relation.mKind != null) { 125 return false; 126 } 127 128 return true; 129 } 130 131 @Override hashCode()132 public int hashCode() { 133 int result = mKind != null ? mKind.hashCode() : 0; 134 result = 31 * result + (mDetail != null ? mDetail.hashCode() : 0); 135 return result; 136 } 137 } 138