Relation.java revision 6a34bb2d6a6cbc7a70bdf0c53d238dc28e0b1d58
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 17package com.android.statementservice.retriever; 18 19import android.annotation.NonNull; 20 21import 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. It is also possible to specify '*' 34 * (the wildcard character) as the detail if the relation applies to any detail in the specified 35 * kind. 36 */ 37public final class Relation { 38 39 private static final Pattern KIND_PATTERN = Pattern.compile("^[a-z0-9_.]+$"); 40 private static final Pattern DETAIL_PATTERN = Pattern.compile("^([a-z0-9_.]+|[*])$"); 41 42 private static final String MATCH_ALL_DETAILS = "*"; 43 44 private final String mKind; 45 private final String mDetail; 46 47 private Relation(String kind, String detail) { 48 mKind = kind; 49 mDetail = detail; 50 } 51 52 /** 53 * Returns the relation's kind. 54 */ 55 @NonNull 56 public String getKind() { 57 return mKind; 58 } 59 60 /** 61 * Returns the relation's detail. 62 */ 63 @NonNull 64 public String getDetail() { 65 return mDetail; 66 } 67 68 /** 69 * Creates a new Relation object for the specified {@code kind} and {@code detail}. 70 * 71 * @throws AssociationServiceException if {@code kind} or {@code detail} is not well formatted. 72 */ 73 public static Relation create(@NonNull String kind, @NonNull String detail) 74 throws AssociationServiceException { 75 if (!KIND_PATTERN.matcher(kind).matches() || !DETAIL_PATTERN.matcher(detail).matches()) { 76 throw new AssociationServiceException("Relation not well formatted."); 77 } 78 return new Relation(kind, detail); 79 } 80 81 /** 82 * Creates a new Relation object from its string representation. 83 * 84 * @throws AssociationServiceException if the relation is not well formatted. 85 */ 86 public static Relation create(@NonNull String relation) throws AssociationServiceException { 87 String[] r = relation.split("/", 2); 88 if (r.length != 2) { 89 throw new AssociationServiceException("Relation not well formatted."); 90 } 91 return create(r[0], r[1]); 92 } 93 94 /** 95 * Returns true if {@code relation} has the same kind and detail. If {@code 96 * relation.getDetail()} is wildcard (*) then returns true if the kind is the same. 97 */ 98 public boolean matches(Relation relation) { 99 return getKind().equals(relation.getKind()) && (getDetail().equals(MATCH_ALL_DETAILS) 100 || getDetail().equals(relation.getDetail())); 101 } 102 103 /** 104 * Returns a string representation of this relation. 105 */ 106 @Override 107 public String toString() { 108 StringBuilder relation = new StringBuilder(); 109 relation.append(getKind()); 110 relation.append("/"); 111 relation.append(getDetail()); 112 return relation.toString(); 113 } 114 115 // equals() and hashCode() are generated by Android Studio. 116 @Override 117 public boolean equals(Object o) { 118 if (this == o) { 119 return true; 120 } 121 if (o == null || getClass() != o.getClass()) { 122 return false; 123 } 124 125 Relation relation = (Relation) o; 126 127 if (mDetail != null ? !mDetail.equals(relation.mDetail) : relation.mDetail != null) { 128 return false; 129 } 130 if (mKind != null ? !mKind.equals(relation.mKind) : relation.mKind != null) { 131 return false; 132 } 133 134 return true; 135 } 136 137 @Override 138 public int hashCode() { 139 int result = mKind != null ? mKind.hashCode() : 0; 140 result = 31 * result + (mDetail != null ? mDetail.hashCode() : 0); 141 return result; 142 } 143} 144