1 /* 2 * Copyright (C) 2007 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.content; 18 19 import android.compat.annotation.UnsupportedAppUsage; 20 import android.os.Parcel; 21 import android.os.Parcelable; 22 import android.util.ArrayMap; 23 import android.util.Log; 24 25 import com.android.internal.util.Preconditions; 26 27 import java.util.ArrayList; 28 import java.util.HashMap; 29 import java.util.Map; 30 import java.util.Objects; 31 import java.util.Set; 32 33 /** 34 * This class is used to store a set of values that the {@link ContentResolver} 35 * can process. 36 */ 37 public final class ContentValues implements Parcelable { 38 public static final String TAG = "ContentValues"; 39 40 /** 41 * @hide 42 * @deprecated kept around for lame people doing reflection 43 */ 44 @Deprecated 45 @UnsupportedAppUsage 46 private HashMap<String, Object> mValues; 47 48 private final ArrayMap<String, Object> mMap; 49 50 /** 51 * Creates an empty set of values using the default initial size 52 */ ContentValues()53 public ContentValues() { 54 mMap = new ArrayMap<>(); 55 } 56 57 /** 58 * Creates an empty set of values using the given initial size 59 * 60 * @param size the initial size of the set of values 61 */ ContentValues(int size)62 public ContentValues(int size) { 63 Preconditions.checkArgumentNonnegative(size); 64 mMap = new ArrayMap<>(size); 65 } 66 67 /** 68 * Creates a set of values copied from the given set 69 * 70 * @param from the values to copy 71 */ ContentValues(ContentValues from)72 public ContentValues(ContentValues from) { 73 Objects.requireNonNull(from); 74 mMap = new ArrayMap<>(from.mMap); 75 } 76 77 /** 78 * @hide 79 * @deprecated kept around for lame people doing reflection 80 */ 81 @Deprecated 82 @UnsupportedAppUsage ContentValues(HashMap<String, Object> from)83 private ContentValues(HashMap<String, Object> from) { 84 mMap = new ArrayMap<>(); 85 mMap.putAll(from); 86 } 87 88 /** {@hide} */ ContentValues(Parcel in)89 private ContentValues(Parcel in) { 90 mMap = new ArrayMap<>(in.readInt()); 91 in.readArrayMap(mMap, null); 92 } 93 94 @Override equals(Object object)95 public boolean equals(Object object) { 96 if (!(object instanceof ContentValues)) { 97 return false; 98 } 99 return mMap.equals(((ContentValues) object).mMap); 100 } 101 102 /** {@hide} */ getValues()103 public ArrayMap<String, Object> getValues() { 104 return mMap; 105 } 106 107 @Override hashCode()108 public int hashCode() { 109 return mMap.hashCode(); 110 } 111 112 /** 113 * Adds a value to the set. 114 * 115 * @param key the name of the value to put 116 * @param value the data for the value to put 117 */ put(String key, String value)118 public void put(String key, String value) { 119 mMap.put(key, value); 120 } 121 122 /** 123 * Adds all values from the passed in ContentValues. 124 * 125 * @param other the ContentValues from which to copy 126 */ putAll(ContentValues other)127 public void putAll(ContentValues other) { 128 mMap.putAll(other.mMap); 129 } 130 131 /** 132 * Adds a value to the set. 133 * 134 * @param key the name of the value to put 135 * @param value the data for the value to put 136 */ put(String key, Byte value)137 public void put(String key, Byte value) { 138 mMap.put(key, value); 139 } 140 141 /** 142 * Adds a value to the set. 143 * 144 * @param key the name of the value to put 145 * @param value the data for the value to put 146 */ put(String key, Short value)147 public void put(String key, Short value) { 148 mMap.put(key, value); 149 } 150 151 /** 152 * Adds a value to the set. 153 * 154 * @param key the name of the value to put 155 * @param value the data for the value to put 156 */ put(String key, Integer value)157 public void put(String key, Integer value) { 158 mMap.put(key, value); 159 } 160 161 /** 162 * Adds a value to the set. 163 * 164 * @param key the name of the value to put 165 * @param value the data for the value to put 166 */ put(String key, Long value)167 public void put(String key, Long value) { 168 mMap.put(key, value); 169 } 170 171 /** 172 * Adds a value to the set. 173 * 174 * @param key the name of the value to put 175 * @param value the data for the value to put 176 */ put(String key, Float value)177 public void put(String key, Float value) { 178 mMap.put(key, value); 179 } 180 181 /** 182 * Adds a value to the set. 183 * 184 * @param key the name of the value to put 185 * @param value the data for the value to put 186 */ put(String key, Double value)187 public void put(String key, Double value) { 188 mMap.put(key, value); 189 } 190 191 /** 192 * Adds a value to the set. 193 * 194 * @param key the name of the value to put 195 * @param value the data for the value to put 196 */ put(String key, Boolean value)197 public void put(String key, Boolean value) { 198 mMap.put(key, value); 199 } 200 201 /** 202 * Adds a value to the set. 203 * 204 * @param key the name of the value to put 205 * @param value the data for the value to put 206 */ put(String key, byte[] value)207 public void put(String key, byte[] value) { 208 mMap.put(key, value); 209 } 210 211 /** 212 * Adds a null value to the set. 213 * 214 * @param key the name of the value to make null 215 */ putNull(String key)216 public void putNull(String key) { 217 mMap.put(key, null); 218 } 219 220 /** 221 * Returns the number of values. 222 * 223 * @return the number of values 224 */ size()225 public int size() { 226 return mMap.size(); 227 } 228 229 /** 230 * Indicates whether this collection is empty. 231 * 232 * @return true iff size == 0 233 * {@hide} 234 * TODO: consider exposing this new method publicly 235 */ isEmpty()236 public boolean isEmpty() { 237 return mMap.isEmpty(); 238 } 239 240 /** 241 * Remove a single value. 242 * 243 * @param key the name of the value to remove 244 */ remove(String key)245 public void remove(String key) { 246 mMap.remove(key); 247 } 248 249 /** 250 * Removes all values. 251 */ clear()252 public void clear() { 253 mMap.clear(); 254 } 255 256 /** 257 * Returns true if this object has the named value. 258 * 259 * @param key the value to check for 260 * @return {@code true} if the value is present, {@code false} otherwise 261 */ containsKey(String key)262 public boolean containsKey(String key) { 263 return mMap.containsKey(key); 264 } 265 266 /** 267 * Gets a value. Valid value types are {@link String}, {@link Boolean}, 268 * {@link Number}, and {@code byte[]} implementations. 269 * 270 * @param key the value to get 271 * @return the data for the value, or {@code null} if the value is missing or if {@code null} 272 * was previously added with the given {@code key} 273 */ get(String key)274 public Object get(String key) { 275 return mMap.get(key); 276 } 277 278 /** 279 * Gets a value and converts it to a String. 280 * 281 * @param key the value to get 282 * @return the String for the value 283 */ getAsString(String key)284 public String getAsString(String key) { 285 Object value = mMap.get(key); 286 return value != null ? value.toString() : null; 287 } 288 289 /** 290 * Gets a value and converts it to a Long. 291 * 292 * @param key the value to get 293 * @return the Long value, or {@code null} if the value is missing or cannot be converted 294 */ getAsLong(String key)295 public Long getAsLong(String key) { 296 Object value = mMap.get(key); 297 try { 298 return value != null ? ((Number) value).longValue() : null; 299 } catch (ClassCastException e) { 300 if (value instanceof CharSequence) { 301 try { 302 return Long.valueOf(value.toString()); 303 } catch (NumberFormatException e2) { 304 Log.e(TAG, "Cannot parse Long value for " + value + " at key " + key); 305 return null; 306 } 307 } else { 308 Log.e(TAG, "Cannot cast value for " + key + " to a Long: " + value, e); 309 return null; 310 } 311 } 312 } 313 314 /** 315 * Gets a value and converts it to an Integer. 316 * 317 * @param key the value to get 318 * @return the Integer value, or {@code null} if the value is missing or cannot be converted 319 */ getAsInteger(String key)320 public Integer getAsInteger(String key) { 321 Object value = mMap.get(key); 322 try { 323 return value != null ? ((Number) value).intValue() : null; 324 } catch (ClassCastException e) { 325 if (value instanceof CharSequence) { 326 try { 327 return Integer.valueOf(value.toString()); 328 } catch (NumberFormatException e2) { 329 Log.e(TAG, "Cannot parse Integer value for " + value + " at key " + key); 330 return null; 331 } 332 } else { 333 Log.e(TAG, "Cannot cast value for " + key + " to a Integer: " + value, e); 334 return null; 335 } 336 } 337 } 338 339 /** 340 * Gets a value and converts it to a Short. 341 * 342 * @param key the value to get 343 * @return the Short value, or {@code null} if the value is missing or cannot be converted 344 */ getAsShort(String key)345 public Short getAsShort(String key) { 346 Object value = mMap.get(key); 347 try { 348 return value != null ? ((Number) value).shortValue() : null; 349 } catch (ClassCastException e) { 350 if (value instanceof CharSequence) { 351 try { 352 return Short.valueOf(value.toString()); 353 } catch (NumberFormatException e2) { 354 Log.e(TAG, "Cannot parse Short value for " + value + " at key " + key); 355 return null; 356 } 357 } else { 358 Log.e(TAG, "Cannot cast value for " + key + " to a Short: " + value, e); 359 return null; 360 } 361 } 362 } 363 364 /** 365 * Gets a value and converts it to a Byte. 366 * 367 * @param key the value to get 368 * @return the Byte value, or {@code null} if the value is missing or cannot be converted 369 */ getAsByte(String key)370 public Byte getAsByte(String key) { 371 Object value = mMap.get(key); 372 try { 373 return value != null ? ((Number) value).byteValue() : null; 374 } catch (ClassCastException e) { 375 if (value instanceof CharSequence) { 376 try { 377 return Byte.valueOf(value.toString()); 378 } catch (NumberFormatException e2) { 379 Log.e(TAG, "Cannot parse Byte value for " + value + " at key " + key); 380 return null; 381 } 382 } else { 383 Log.e(TAG, "Cannot cast value for " + key + " to a Byte: " + value, e); 384 return null; 385 } 386 } 387 } 388 389 /** 390 * Gets a value and converts it to a Double. 391 * 392 * @param key the value to get 393 * @return the Double value, or {@code null} if the value is missing or cannot be converted 394 */ getAsDouble(String key)395 public Double getAsDouble(String key) { 396 Object value = mMap.get(key); 397 try { 398 return value != null ? ((Number) value).doubleValue() : null; 399 } catch (ClassCastException e) { 400 if (value instanceof CharSequence) { 401 try { 402 return Double.valueOf(value.toString()); 403 } catch (NumberFormatException e2) { 404 Log.e(TAG, "Cannot parse Double value for " + value + " at key " + key); 405 return null; 406 } 407 } else { 408 Log.e(TAG, "Cannot cast value for " + key + " to a Double: " + value, e); 409 return null; 410 } 411 } 412 } 413 414 /** 415 * Gets a value and converts it to a Float. 416 * 417 * @param key the value to get 418 * @return the Float value, or {@code null} if the value is missing or cannot be converted 419 */ getAsFloat(String key)420 public Float getAsFloat(String key) { 421 Object value = mMap.get(key); 422 try { 423 return value != null ? ((Number) value).floatValue() : null; 424 } catch (ClassCastException e) { 425 if (value instanceof CharSequence) { 426 try { 427 return Float.valueOf(value.toString()); 428 } catch (NumberFormatException e2) { 429 Log.e(TAG, "Cannot parse Float value for " + value + " at key " + key); 430 return null; 431 } 432 } else { 433 Log.e(TAG, "Cannot cast value for " + key + " to a Float: " + value, e); 434 return null; 435 } 436 } 437 } 438 439 /** 440 * Gets a value and converts it to a Boolean. 441 * 442 * @param key the value to get 443 * @return the Boolean value, or {@code null} if the value is missing or cannot be converted 444 */ getAsBoolean(String key)445 public Boolean getAsBoolean(String key) { 446 Object value = mMap.get(key); 447 try { 448 return (Boolean) value; 449 } catch (ClassCastException e) { 450 if (value instanceof CharSequence) { 451 // Note that we also check against 1 here because SQLite's internal representation 452 // for booleans is an integer with a value of 0 or 1. Without this check, boolean 453 // values obtained via DatabaseUtils#cursorRowToContentValues will always return 454 // false. 455 return Boolean.valueOf(value.toString()) || "1".equals(value); 456 } else if (value instanceof Number) { 457 return ((Number) value).intValue() != 0; 458 } else { 459 Log.e(TAG, "Cannot cast value for " + key + " to a Boolean: " + value, e); 460 return null; 461 } 462 } 463 } 464 465 /** 466 * Gets a value that is a byte array. Note that this method will not convert 467 * any other types to byte arrays. 468 * 469 * @param key the value to get 470 * @return the {@code byte[]} value, or {@code null} is the value is missing or not a 471 * {@code byte[]} 472 */ getAsByteArray(String key)473 public byte[] getAsByteArray(String key) { 474 Object value = mMap.get(key); 475 if (value instanceof byte[]) { 476 return (byte[]) value; 477 } else { 478 return null; 479 } 480 } 481 482 /** 483 * Returns a set of all of the keys and values 484 * 485 * @return a set of all of the keys and values 486 */ valueSet()487 public Set<Map.Entry<String, Object>> valueSet() { 488 return mMap.entrySet(); 489 } 490 491 /** 492 * Returns a set of all of the keys 493 * 494 * @return a set of all of the keys 495 */ keySet()496 public Set<String> keySet() { 497 return mMap.keySet(); 498 } 499 500 public static final @android.annotation.NonNull Parcelable.Creator<ContentValues> CREATOR = 501 new Parcelable.Creator<ContentValues>() { 502 @Override 503 public ContentValues createFromParcel(Parcel in) { 504 return new ContentValues(in); 505 } 506 507 @Override 508 public ContentValues[] newArray(int size) { 509 return new ContentValues[size]; 510 } 511 }; 512 513 @Override describeContents()514 public int describeContents() { 515 return 0; 516 } 517 518 @Override writeToParcel(Parcel parcel, int flags)519 public void writeToParcel(Parcel parcel, int flags) { 520 parcel.writeInt(mMap.size()); 521 parcel.writeArrayMap(mMap); 522 } 523 524 /** 525 * Unsupported, here until we get proper bulk insert APIs. 526 * {@hide} 527 */ 528 @Deprecated 529 @UnsupportedAppUsage putStringArrayList(String key, ArrayList<String> value)530 public void putStringArrayList(String key, ArrayList<String> value) { 531 mMap.put(key, value); 532 } 533 534 /** 535 * Unsupported, here until we get proper bulk insert APIs. 536 * {@hide} 537 */ 538 @SuppressWarnings("unchecked") 539 @Deprecated 540 @UnsupportedAppUsage getStringArrayList(String key)541 public ArrayList<String> getStringArrayList(String key) { 542 return (ArrayList<String>) mMap.get(key); 543 } 544 545 /** 546 * Returns a string containing a concise, human-readable description of this object. 547 * @return a printable representation of this object. 548 */ 549 @Override toString()550 public String toString() { 551 StringBuilder sb = new StringBuilder(); 552 for (String name : mMap.keySet()) { 553 String value = getAsString(name); 554 if (sb.length() > 0) sb.append(" "); 555 sb.append(name + "=" + value); 556 } 557 return sb.toString(); 558 } 559 } 560