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     * Remove a single value.
208     *
209     * @param key the name of the value to remove
210     */
211    public void remove(String key) {
212        mValues.remove(key);
213    }
214
215    /**
216     * Removes all values.
217     */
218    public void clear() {
219        mValues.clear();
220    }
221
222    /**
223     * Returns true if this object has the named value.
224     *
225     * @param key the value to check for
226     * @return {@code true} if the value is present, {@code false} otherwise
227     */
228    public boolean containsKey(String key) {
229        return mValues.containsKey(key);
230    }
231
232    /**
233     * Gets a value. Valid value types are {@link String}, {@link Boolean},
234     * {@link Number}, and {@code byte[]} implementations.
235     *
236     * @param key the value to get
237     * @return the data for the value, or {@code null} if the value is missing or if {@code null}
238     *         was previously added with the given {@code key}
239     */
240    public Object get(String key) {
241        return mValues.get(key);
242    }
243
244    /**
245     * Gets a value and converts it to a String.
246     *
247     * @param key the value to get
248     * @return the String for the value
249     */
250    public String getAsString(String key) {
251        Object value = mValues.get(key);
252        return value != null ? value.toString() : null;
253    }
254
255    /**
256     * Gets a value and converts it to a Long.
257     *
258     * @param key the value to get
259     * @return the Long value, or {@code null} if the value is missing or cannot be converted
260     */
261    public Long getAsLong(String key) {
262        Object value = mValues.get(key);
263        try {
264            return value != null ? ((Number) value).longValue() : null;
265        } catch (ClassCastException e) {
266            if (value instanceof CharSequence) {
267                try {
268                    return Long.valueOf(value.toString());
269                } catch (NumberFormatException e2) {
270                    Log.e(TAG, "Cannot parse Long value for " + value + " at key " + key);
271                    return null;
272                }
273            } else {
274                Log.e(TAG, "Cannot cast value for " + key + " to a Long: " + value, e);
275                return null;
276            }
277        }
278    }
279
280    /**
281     * Gets a value and converts it to an Integer.
282     *
283     * @param key the value to get
284     * @return the Integer value, or {@code null} if the value is missing or cannot be converted
285     */
286    public Integer getAsInteger(String key) {
287        Object value = mValues.get(key);
288        try {
289            return value != null ? ((Number) value).intValue() : null;
290        } catch (ClassCastException e) {
291            if (value instanceof CharSequence) {
292                try {
293                    return Integer.valueOf(value.toString());
294                } catch (NumberFormatException e2) {
295                    Log.e(TAG, "Cannot parse Integer value for " + value + " at key " + key);
296                    return null;
297                }
298            } else {
299                Log.e(TAG, "Cannot cast value for " + key + " to a Integer: " + value, e);
300                return null;
301            }
302        }
303    }
304
305    /**
306     * Gets a value and converts it to a Short.
307     *
308     * @param key the value to get
309     * @return the Short value, or {@code null} if the value is missing or cannot be converted
310     */
311    public Short getAsShort(String key) {
312        Object value = mValues.get(key);
313        try {
314            return value != null ? ((Number) value).shortValue() : null;
315        } catch (ClassCastException e) {
316            if (value instanceof CharSequence) {
317                try {
318                    return Short.valueOf(value.toString());
319                } catch (NumberFormatException e2) {
320                    Log.e(TAG, "Cannot parse Short value for " + value + " at key " + key);
321                    return null;
322                }
323            } else {
324                Log.e(TAG, "Cannot cast value for " + key + " to a Short: " + value, e);
325                return null;
326            }
327        }
328    }
329
330    /**
331     * Gets a value and converts it to a Byte.
332     *
333     * @param key the value to get
334     * @return the Byte value, or {@code null} if the value is missing or cannot be converted
335     */
336    public Byte getAsByte(String key) {
337        Object value = mValues.get(key);
338        try {
339            return value != null ? ((Number) value).byteValue() : null;
340        } catch (ClassCastException e) {
341            if (value instanceof CharSequence) {
342                try {
343                    return Byte.valueOf(value.toString());
344                } catch (NumberFormatException e2) {
345                    Log.e(TAG, "Cannot parse Byte value for " + value + " at key " + key);
346                    return null;
347                }
348            } else {
349                Log.e(TAG, "Cannot cast value for " + key + " to a Byte: " + value, e);
350                return null;
351            }
352        }
353    }
354
355    /**
356     * Gets a value and converts it to a Double.
357     *
358     * @param key the value to get
359     * @return the Double value, or {@code null} if the value is missing or cannot be converted
360     */
361    public Double getAsDouble(String key) {
362        Object value = mValues.get(key);
363        try {
364            return value != null ? ((Number) value).doubleValue() : null;
365        } catch (ClassCastException e) {
366            if (value instanceof CharSequence) {
367                try {
368                    return Double.valueOf(value.toString());
369                } catch (NumberFormatException e2) {
370                    Log.e(TAG, "Cannot parse Double value for " + value + " at key " + key);
371                    return null;
372                }
373            } else {
374                Log.e(TAG, "Cannot cast value for " + key + " to a Double: " + value, e);
375                return null;
376            }
377        }
378    }
379
380    /**
381     * Gets a value and converts it to a Float.
382     *
383     * @param key the value to get
384     * @return the Float value, or {@code null} if the value is missing or cannot be converted
385     */
386    public Float getAsFloat(String key) {
387        Object value = mValues.get(key);
388        try {
389            return value != null ? ((Number) value).floatValue() : null;
390        } catch (ClassCastException e) {
391            if (value instanceof CharSequence) {
392                try {
393                    return Float.valueOf(value.toString());
394                } catch (NumberFormatException e2) {
395                    Log.e(TAG, "Cannot parse Float value for " + value + " at key " + key);
396                    return null;
397                }
398            } else {
399                Log.e(TAG, "Cannot cast value for " + key + " to a Float: " + value, e);
400                return null;
401            }
402        }
403    }
404
405    /**
406     * Gets a value and converts it to a Boolean.
407     *
408     * @param key the value to get
409     * @return the Boolean value, or {@code null} if the value is missing or cannot be converted
410     */
411    public Boolean getAsBoolean(String key) {
412        Object value = mValues.get(key);
413        try {
414            return (Boolean) value;
415        } catch (ClassCastException e) {
416            if (value instanceof CharSequence) {
417                return Boolean.valueOf(value.toString());
418            } else if (value instanceof Number) {
419                return ((Number) value).intValue() != 0;
420            } else {
421                Log.e(TAG, "Cannot cast value for " + key + " to a Boolean: " + value, e);
422                return null;
423            }
424        }
425    }
426
427    /**
428     * Gets a value that is a byte array. Note that this method will not convert
429     * any other types to byte arrays.
430     *
431     * @param key the value to get
432     * @return the {@code byte[]} value, or {@code null} is the value is missing or not a
433     *         {@code byte[]}
434     */
435    public byte[] getAsByteArray(String key) {
436        Object value = mValues.get(key);
437        if (value instanceof byte[]) {
438            return (byte[]) value;
439        } else {
440            return null;
441        }
442    }
443
444    /**
445     * Returns a set of all of the keys and values
446     *
447     * @return a set of all of the keys and values
448     */
449    public Set<Map.Entry<String, Object>> valueSet() {
450        return mValues.entrySet();
451    }
452
453    /**
454     * Returns a set of all of the keys
455     *
456     * @return a set of all of the keys
457     */
458    public Set<String> keySet() {
459        return mValues.keySet();
460    }
461
462    public static final Parcelable.Creator<ContentValues> CREATOR =
463            new Parcelable.Creator<ContentValues>() {
464        @SuppressWarnings({"deprecation", "unchecked"})
465        public ContentValues createFromParcel(Parcel in) {
466            // TODO - what ClassLoader should be passed to readHashMap?
467            HashMap<String, Object> values = in.readHashMap(null);
468            return new ContentValues(values);
469        }
470
471        public ContentValues[] newArray(int size) {
472            return new ContentValues[size];
473        }
474    };
475
476    public int describeContents() {
477        return 0;
478    }
479
480    @SuppressWarnings("deprecation")
481    public void writeToParcel(Parcel parcel, int flags) {
482        parcel.writeMap(mValues);
483    }
484
485    /**
486     * Unsupported, here until we get proper bulk insert APIs.
487     * {@hide}
488     */
489    @Deprecated
490    public void putStringArrayList(String key, ArrayList<String> value) {
491        mValues.put(key, value);
492    }
493
494    /**
495     * Unsupported, here until we get proper bulk insert APIs.
496     * {@hide}
497     */
498    @SuppressWarnings("unchecked")
499    @Deprecated
500    public ArrayList<String> getStringArrayList(String key) {
501        return (ArrayList<String>) mValues.get(key);
502    }
503
504    /**
505     * Returns a string containing a concise, human-readable description of this object.
506     * @return a printable representation of this object.
507     */
508    @Override
509    public String toString() {
510        StringBuilder sb = new StringBuilder();
511        for (String name : mValues.keySet()) {
512            String value = getAsString(name);
513            if (sb.length() > 0) sb.append(" ");
514            sb.append(name + "=" + value);
515        }
516        return sb.toString();
517    }
518}
519