1 /* 2 * Copyright (C) 2021 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.queryable.queries; 18 19 import static com.android.queryable.annotations.StringQuery.DEFAULT_STRING_QUERY_PARAMETERS_VALUE; 20 import static com.android.queryable.util.ParcelableUtils.writeStringSet; 21 22 import android.os.Parcel; 23 import android.os.Parcelable; 24 25 import com.android.bedstead.nene.types.OptionalBoolean; 26 import com.android.queryable.Queryable; 27 import com.android.queryable.QueryableBaseWithMatch; 28 import com.android.queryable.util.ParcelableUtils; 29 30 import com.google.auto.value.AutoAnnotation; 31 32 import java.io.Serializable; 33 import java.util.ArrayList; 34 import java.util.HashSet; 35 import java.util.List; 36 import java.util.Objects; 37 import java.util.Set; 38 39 /** Implementation of {@link StringQuery}. */ 40 public final class StringQueryHelper<E extends Queryable> 41 implements StringQuery<E>, Serializable{ 42 43 private static final long serialVersionUID = 1; 44 45 private final transient E mQuery; 46 // This is required because null is a valid value 47 private boolean mEqualsIsSpecified = false; 48 private String mEqualsValue; 49 private Set<String> mNotEqualsValues = new HashSet<>(); 50 private String mStartsWithValue; 51 52 public static final class StringQueryBase extends 53 QueryableBaseWithMatch<String, StringQueryHelper<StringQueryBase>> { StringQueryBase()54 StringQueryBase() { 55 super(); 56 setQuery(new StringQueryHelper<>(this)); 57 } 58 StringQueryBase(Parcel in)59 StringQueryBase(Parcel in) { 60 super(in); 61 } 62 63 public static final Parcelable.Creator<StringQueryHelper.StringQueryBase> CREATOR = 64 new Parcelable.Creator<>() { 65 public StringQueryHelper.StringQueryBase createFromParcel(Parcel in) { 66 return new StringQueryHelper.StringQueryBase(in); 67 } 68 69 public StringQueryHelper.StringQueryBase[] newArray(int size) { 70 return new StringQueryHelper.StringQueryBase[size]; 71 } 72 }; 73 } 74 StringQueryHelper(E query)75 public StringQueryHelper(E query) { 76 mQuery = query; 77 } 78 StringQueryHelper(Parcel in)79 private StringQueryHelper(Parcel in) { 80 mQuery = null; 81 mEqualsIsSpecified = in.readBoolean(); 82 mEqualsValue = in.readString(); 83 mNotEqualsValues = ParcelableUtils.readStringSet(in); 84 mStartsWithValue = in.readString(); 85 } 86 87 @Override isEqualTo(String string)88 public E isEqualTo(String string) { 89 mEqualsIsSpecified = true; 90 mEqualsValue = string; 91 return mQuery; 92 } 93 94 @Override isNotEqualTo(String string)95 public E isNotEqualTo(String string) { 96 mNotEqualsValues.add(string); 97 return mQuery; 98 } 99 100 @Override startsWith(String string)101 public E startsWith(String string) { 102 mStartsWithValue = string; 103 return mQuery; 104 } 105 106 @Override matchesAnnotation(com.android.queryable.annotations.StringQuery queryAnnotation)107 public E matchesAnnotation(com.android.queryable.annotations.StringQuery queryAnnotation) { 108 if (!queryAnnotation.startsWith().equals(DEFAULT_STRING_QUERY_PARAMETERS_VALUE)) { 109 E unused = startsWith(queryAnnotation.startsWith()); 110 } 111 if (!queryAnnotation.isEqualTo().equals(DEFAULT_STRING_QUERY_PARAMETERS_VALUE)) { 112 E unused = isEqualTo(queryAnnotation.isEqualTo()); 113 } 114 if (!queryAnnotation.isNotEqualTo().equals(DEFAULT_STRING_QUERY_PARAMETERS_VALUE)) { 115 E unused = isNotEqualTo(queryAnnotation.isNotEqualTo()); 116 } 117 if (queryAnnotation.isNull().equals(OptionalBoolean.TRUE)) { 118 E unused = isNull(); 119 } 120 if (queryAnnotation.isNull().equals(OptionalBoolean.FALSE)) { 121 E unused = isNotNull(); 122 } 123 124 return mQuery; 125 } 126 toAnnotation()127 public com.android.queryable.annotations.StringQuery toAnnotation() { 128 return stringQuery( 129 mStartsWithValue == null ? DEFAULT_STRING_QUERY_PARAMETERS_VALUE : mStartsWithValue, 130 mEqualsIsSpecified ? mEqualsValue : DEFAULT_STRING_QUERY_PARAMETERS_VALUE); 131 } 132 133 @AutoAnnotation stringQuery( String startsWith, String isEqualTo)134 private static com.android.queryable.annotations.StringQuery stringQuery( 135 String startsWith, String isEqualTo) { 136 // TODO: Add support for isNotEqualTo 137 // TODO: Add support for isNull/isNotNull 138 return new AutoAnnotation_StringQueryHelper_stringQuery( 139 startsWith, isEqualTo 140 ); 141 } 142 143 @Override isEmptyQuery()144 public boolean isEmptyQuery() { 145 return !mEqualsIsSpecified 146 && mNotEqualsValues.isEmpty() 147 && (mStartsWithValue == null || mStartsWithValue.isEmpty()); 148 } 149 150 @Override matches(String value)151 public boolean matches(String value) { 152 if (mEqualsIsSpecified && !Objects.equals(mEqualsValue, value)) { 153 return false; 154 } 155 if (mNotEqualsValues.contains(value)) { 156 return false; 157 } 158 if (mStartsWithValue != null && (value == null || !value.startsWith(mStartsWithValue))) { 159 return false; 160 } 161 162 return true; 163 } 164 matches(StringQueryHelper<?> stringQueryHelper, String value)165 public static boolean matches(StringQueryHelper<?> stringQueryHelper, String value) { 166 return stringQueryHelper.matches(value); 167 } 168 169 /** 170 * True if this query has not been configured. 171 */ isEmpty()172 public boolean isEmpty() { 173 return mEqualsValue == null && mNotEqualsValues.isEmpty(); 174 } 175 176 /** 177 * True if this query is for an exact string match. 178 */ isQueryingForExactMatch()179 public boolean isQueryingForExactMatch() { 180 return mEqualsValue != null; 181 } 182 183 @Override describeQuery(String fieldName)184 public String describeQuery(String fieldName) { 185 List<String> queryStrings = new ArrayList<>(); 186 if (mEqualsIsSpecified) { 187 queryStrings.add(fieldName + "=\"" + mEqualsValue + "\""); 188 } 189 190 for (String notEquals : mNotEqualsValues) { 191 queryStrings.add(fieldName + "!=\"" + notEquals + "\""); 192 } 193 194 if (mStartsWithValue != null) { 195 queryStrings.add(fieldName + " starts with \"" + mStartsWithValue + "\""); 196 } 197 198 return Queryable.joinQueryStrings(queryStrings); 199 } 200 201 @Override describeContents()202 public int describeContents() { 203 return 0; 204 } 205 206 @Override writeToParcel(Parcel out, int flags)207 public void writeToParcel(Parcel out, int flags) { 208 out.writeBoolean(mEqualsIsSpecified); 209 out.writeString(mEqualsValue); 210 writeStringSet(out, mNotEqualsValues); 211 out.writeString(mStartsWithValue); 212 } 213 214 public static final Parcelable.Creator<StringQueryHelper> CREATOR = 215 new Parcelable.Creator<StringQueryHelper>() { 216 public StringQueryHelper createFromParcel(Parcel in) { 217 return new StringQueryHelper(in); 218 } 219 220 public StringQueryHelper[] newArray(int size) { 221 return new StringQueryHelper[size]; 222 } 223 }; 224 225 @Override equals(Object o)226 public boolean equals(Object o) { 227 if (this == o) return true; 228 if (!(o instanceof StringQueryHelper)) return false; 229 StringQueryHelper<?> that = (StringQueryHelper<?>) o; 230 return Objects.equals(mEqualsValue, that.mEqualsValue) && Objects.equals( 231 mNotEqualsValues, that.mNotEqualsValues) && Objects.equals( 232 mStartsWithValue, that.mStartsWithValue); 233 } 234 235 @Override hashCode()236 public int hashCode() { 237 return Objects.hash(mEqualsValue, mNotEqualsValues, mStartsWithValue); 238 } 239 } 240