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