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