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