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 import java.util.Objects; 29 30 /** ColumnInfo contains information about all the attributes that a column can hold. */ 31 class ColumnInfo { 32 private final String mName; 33 private final String mDataType; 34 private final List<Integer> mConstraints; 35 private final List<String> mCheckConstraints; 36 private final String mDefaultValue; 37 38 /** ColumnConstraint contains the constraints on a column. */ 39 @IntDef({UNIQUE_CONSTRAINT, NOT_NULL_CONSTRAINT, AUTO_INCREMENT_CONSTRAINT}) 40 @Retention(RetentionPolicy.SOURCE) 41 public @interface ColumnConstraint {} 42 43 public static final int UNIQUE_CONSTRAINT = 0; 44 public static final int NOT_NULL_CONSTRAINT = 1; 45 public static final int AUTO_INCREMENT_CONSTRAINT = 2; 46 47 /** Creates an instance for ColumnInfo. */ ColumnInfo(Builder builder)48 ColumnInfo(Builder builder) { 49 mName = builder.mName; 50 mDataType = builder.mDataType; 51 mConstraints = builder.mConstraints; 52 mCheckConstraints = builder.mCheckConstraints; 53 mDefaultValue = builder.mDefaultValue; 54 } 55 56 /** Builder pattern for ColumnInfo. */ 57 public static class Builder { 58 private final String mName; 59 private final String mDataType; 60 private final List<Integer> mConstraints; 61 private final List<String> mCheckConstraints; 62 private String mDefaultValue; 63 Builder(@onNull String name, @NonNull String dataType)64 Builder(@NonNull String name, @NonNull String dataType) { 65 Objects.requireNonNull(name); 66 Objects.requireNonNull(dataType); 67 mName = name; 68 mDataType = dataType; 69 mConstraints = new ArrayList<>(); 70 mCheckConstraints = new ArrayList<>(); 71 } 72 73 /** Sets the default value of the column. */ setDefaultValue(String defaultValue)74 public Builder setDefaultValue(String defaultValue) { 75 mDefaultValue = defaultValue; 76 return this; 77 } 78 79 /** Appends constraint to the existing list of constraint. */ addConstraint(@olumnConstraint int constraint)80 public Builder addConstraint(@ColumnConstraint int constraint) { 81 mConstraints.add(constraint); 82 return this; 83 } 84 85 /** Appends check constraint to the existing list of check constraint. */ addCheckConstraint(@onNull String checkConstraint)86 public Builder addCheckConstraint(@NonNull String checkConstraint) { 87 Objects.requireNonNull(checkConstraint); 88 mCheckConstraints.add(checkConstraint); 89 return this; 90 } 91 92 /** Builds the columnInfo object. */ build()93 public ColumnInfo build() { 94 return new ColumnInfo(this); 95 } 96 } 97 98 /** 99 * @return name of the column. 100 */ 101 @NonNull getName()102 public String getName() { 103 return mName; 104 } 105 106 /** 107 * @return datatype of the column. 108 */ 109 @NonNull getDataType()110 public String getDataType() { 111 return mDataType; 112 } 113 114 /** 115 * @return list of all the constraints of the column. 116 */ 117 @Nullable getConstraints()118 public List<Integer> getConstraints() { 119 return mConstraints; 120 } 121 122 /** 123 * @return list of all check constraints of the column. 124 */ 125 @Nullable getCheckConstraints()126 public List<String> getCheckConstraints() { 127 return mCheckConstraints; 128 } 129 130 /** 131 * @return the default value of the column if assigned otherwise null. 132 */ 133 @Nullable getDefaultValue()134 public String getDefaultValue() { 135 return mDefaultValue; 136 } 137 138 /** 139 * @return true if the objects of the two ColumnInfo are same otherwise false. 140 */ 141 @NonNull isEqual(ColumnInfo expectedColumn)142 public Boolean isEqual(ColumnInfo expectedColumn) { 143 if (mName.equals(expectedColumn.mName) && mDataType.equals(expectedColumn.mDataType)) { 144 if (!Objects.equals(mDefaultValue, expectedColumn.mDefaultValue)) { 145 return false; 146 } 147 List<Integer> constraintList = mConstraints; 148 List<Integer> expectedConstraintList = expectedColumn.mConstraints; 149 List<String> checkConstraintList = mCheckConstraints; 150 List<String> expectedCheckConstraintList = expectedColumn.mCheckConstraints; 151 Collections.sort(constraintList); 152 Collections.sort(expectedConstraintList); 153 Collections.sort(checkConstraintList); 154 Collections.sort(expectedCheckConstraintList); 155 return constraintList.equals(expectedConstraintList) 156 && checkConstraintList.equals(expectedCheckConstraintList); 157 } 158 return false; 159 } 160 161 /** 162 * Compares two ColumnInfo and stores any backward incompatible change to the corresponding 163 * ErrorInfo of column. 164 */ checkColumnDiff( ColumnInfo expectedColumn, List<String> modificationOfColumn, String tableName)165 public void checkColumnDiff( 166 ColumnInfo expectedColumn, List<String> modificationOfColumn, String tableName) { 167 168 if (!mDataType.equals(expectedColumn.mDataType)) { 169 modificationOfColumn.add( 170 "Datatype has been changed for column: " 171 + mName 172 + " of the table: " 173 + tableName); 174 } 175 if (!Objects.equals(mDefaultValue, expectedColumn.mDefaultValue)) { 176 modificationOfColumn.add( 177 "Default value has been changed for column: " 178 + mName 179 + " of the table: " 180 + tableName); 181 } 182 List<Integer> constraintList1 = mConstraints.stream().sorted().toList(); 183 List<Integer> constraintList2 = expectedColumn.mConstraints.stream().sorted().toList(); 184 185 if (!constraintList1.equals(constraintList2)) { 186 modificationOfColumn.add( 187 "Constraints have been changed for column: " 188 + mName 189 + " of the table: " 190 + tableName); 191 } 192 List<String> checkConstraintList1 = mCheckConstraints.stream().sorted().toList(); 193 List<String> checkConstraintList2 = 194 expectedColumn.mCheckConstraints.stream().sorted().toList(); 195 196 if (!checkConstraintList1.equals(checkConstraintList2)) { 197 modificationOfColumn.add( 198 "Check constraints has been changed for the column: " 199 + mName 200 + " of the table: " 201 + tableName); 202 } 203 } 204 } 205