1 /*
2  * Copyright (C) 2023 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 android.healthconnect.cts.database;
18 
19 import androidx.annotation.IntDef;
20 import androidx.annotation.NonNull;
21 import androidx.annotation.Nullable;
22 
23 import java.lang.annotation.Retention;
24 import java.lang.annotation.RetentionPolicy;
25 import java.util.ArrayList;
26 import java.util.Collections;
27 import java.util.List;
28 
29 /** ForeignKeyInfo contains information about all the attributes that a Foreign Key can hold. */
30 public class ForeignKeyInfo {
31     private final String mForeignKeyName;
32     private final String mForeignKeyTableName;
33     private final String mForeignKeyReferredColumn;
34     private final List<Integer> mForeignKeyFlags;
35 
36     /**
37      * ForeignKeyFlags contains all types of flags that can be used while creating a foreign key for
38      * a table.
39      */
40     @IntDef({
41             ON_DELETE_CASCADE,
42             ON_DELETE_SET_NULL,
43             ON_DELETE_SET_DEFAULT,
44             ON_DELETE_RESTRICT,
45             ON_UPDATE_CASCADE,
46             ON_UPDATE_SET_NULL,
47             ON_UPDATE_SET_DEFAULT,
48             ON_UPDATE_RESTRICT,
49             DEFERRABLE_FLAG,
50             INITIALLY_DEFERRED
51     })
52     @Retention(RetentionPolicy.SOURCE)
53     public @interface ForeignKeyFlags {
54     }
55 
56     public static final int ON_DELETE_CASCADE = 0;
57     public static final int ON_DELETE_SET_NULL = 1;
58     public static final int ON_DELETE_SET_DEFAULT = 2;
59     public static final int ON_DELETE_RESTRICT = 3;
60     public static final int ON_UPDATE_CASCADE = 4;
61     public static final int ON_UPDATE_SET_NULL = 5;
62     public static final int ON_UPDATE_SET_DEFAULT = 6;
63     public static final int ON_UPDATE_RESTRICT = 7;
64     public static final int DEFERRABLE_FLAG = 8;
65     public static final int INITIALLY_DEFERRED = 9;
66 
67     /** Creates an instance for ForeignKeyInfo. */
ForeignKeyInfo(Builder builder)68     ForeignKeyInfo(Builder builder) {
69         mForeignKeyName = builder.mForeignKeyName;
70         mForeignKeyTableName = builder.mForeignKeyTableName;
71         mForeignKeyReferredColumn = builder.mForeignKeyReferredColumn;
72         mForeignKeyFlags = builder.mForeignKeyFlags;
73     }
74 
75     /** Builder pattern for ForeignKeyInfo. */
76     public static class Builder {
77         private final String mForeignKeyName;
78         private final String mForeignKeyTableName;
79         private final String mForeignKeyReferredColumn;
80         private final List<Integer> mForeignKeyFlags;
81 
Builder(String foreignKey, String referencedTable, String referencedColumn)82         Builder(String foreignKey, String referencedTable, String referencedColumn) {
83             mForeignKeyName = foreignKey;
84             mForeignKeyTableName = referencedTable;
85             mForeignKeyReferredColumn = referencedColumn;
86             mForeignKeyFlags = new ArrayList<>();
87         }
88 
89         /** Appends flag to the existing list of flags. */
addFlag(@oreignKeyFlags int foreignKeyFlag)90         public ForeignKeyInfo.Builder addFlag(@ForeignKeyFlags int foreignKeyFlag) {
91             mForeignKeyFlags.add(foreignKeyFlag);
92             return this;
93         }
94 
95         /** Builds the ForeignKeyInfo object. */
build()96         public ForeignKeyInfo build() {
97             return new ForeignKeyInfo(this);
98         }
99     }
100 
101     /**
102      * @return name of the foreignKey.
103      */
104     @NonNull
getForeignKeyName()105     public String getForeignKeyName() {
106         return mForeignKeyName;
107     }
108 
109     /**
110      * @return referenced table of the foreignKey.
111      */
112     @NonNull
getForeignKeyTableName()113     public String getForeignKeyTableName() {
114         return mForeignKeyTableName;
115     }
116 
117     /**
118      * @return primary key of referenced table of the foreign key.
119      */
120     @NonNull
getForeignKeyReferredColumnName()121     public String getForeignKeyReferredColumnName() {
122         return mForeignKeyReferredColumn;
123     }
124 
125     /**
126      * @return list of all the flags of the foreignkey.
127      */
128     @Nullable
getForeignKeyFlags()129     public List<Integer> getForeignKeyFlags() {
130         return mForeignKeyFlags;
131     }
132 
133     /**
134      * @return true if the objects of the two ForeignkeyInfo are same otherwise false.
135      */
136     @NonNull
isEqual(ForeignKeyInfo expectedForeignKey)137     public Boolean isEqual(ForeignKeyInfo expectedForeignKey) {
138         if (mForeignKeyName.equals(expectedForeignKey.mForeignKeyName)
139                 && mForeignKeyTableName.equals(expectedForeignKey.mForeignKeyTableName)
140                 && mForeignKeyReferredColumn.equals(expectedForeignKey.mForeignKeyReferredColumn)) {
141 
142             List<Integer> flagList = mForeignKeyFlags;
143             List<Integer> expectedFlags = expectedForeignKey.mForeignKeyFlags;
144             Collections.sort(flagList);
145             Collections.sort(expectedFlags);
146             return flagList.equals(expectedFlags);
147         }
148         return false;
149     }
150 
151     /**
152      * Compares two ForeignKeyInfo and stores any backward incompatible change to the corresponding
153      * ErrorInfo of foreignKey.
154      */
checkForeignKeyDiff( ForeignKeyInfo expectedForeignkeyInfo, List<String> modificationOfForeignKey, String tableName)155     public void checkForeignKeyDiff(
156             ForeignKeyInfo expectedForeignkeyInfo,
157             List<String> modificationOfForeignKey,
158             String tableName) {
159 
160         if (!mForeignKeyTableName.equals(expectedForeignkeyInfo.mForeignKeyTableName)) {
161             modificationOfForeignKey.add(
162                     "Referenced table has been changed for Foreign key: "
163                             + mForeignKeyName
164                             + " of table: "
165                             + tableName);
166         }
167         if (!mForeignKeyReferredColumn.equals(expectedForeignkeyInfo.mForeignKeyReferredColumn)) {
168             modificationOfForeignKey.add(
169                     "Primary Key of Referenced table has been changed for Foreign key: "
170                             + mForeignKeyName
171                             + " of table: "
172                             + tableName);
173         }
174 
175         List<Integer> actualForeignKeyFlags = mForeignKeyFlags.stream().sorted().toList();
176         List<Integer> expectedForeignKeyFlags =
177                 expectedForeignkeyInfo.mForeignKeyFlags.stream().sorted().toList();
178 
179         if (!actualForeignKeyFlags.equals(expectedForeignKeyFlags)) {
180             modificationOfForeignKey.add(
181                     "Foreign Key Flags have been changed for Foreign Key: "
182                             + mForeignKeyName
183                             + " of table: "
184                             + tableName);
185         }
186     }
187 }
188