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