1/*
2 * Copyright (C) 2014 Apple Inc. All rights reserved.
3 *
4 * Redistribution and use in source and binary forms, with or without
5 * modification, are permitted provided that the following conditions
6 * are met:
7 * 1. Redistributions of source code must retain the above copyright
8 *    notice, this list of conditions and the following disclaimer.
9 * 2. Redistributions in binary form must reproduce the above copyright
10 *    notice, this list of conditions and the following disclaimer in the
11 *    documentation and/or other materials provided with the distribution.
12 *
13 * THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
14 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
15 * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
16 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
17 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
18 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
19 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
20 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
21 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
22 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
23 * THE POSSIBILITY OF SUCH DAMAGE.
24 */
25
26#include "config.h"
27#include "IDBKeyData.h"
28
29#if ENABLE(INDEXED_DATABASE)
30
31#include "KeyedCoding.h"
32#include <wtf/text/StringBuilder.h>
33
34namespace WebCore {
35
36IDBKeyData::IDBKeyData(const IDBKey* key)
37    : type(IDBKey::InvalidType)
38    , numberValue(0)
39    , isNull(false)
40{
41    if (!key) {
42        isNull = true;
43        return;
44    }
45
46    type = key->type();
47    numberValue = 0;
48
49    switch (type) {
50    case IDBKey::InvalidType:
51        break;
52    case IDBKey::ArrayType:
53        for (auto& key2 : key->array())
54            arrayValue.append(IDBKeyData(key2.get()));
55        break;
56    case IDBKey::StringType:
57        stringValue = key->string();
58        break;
59    case IDBKey::DateType:
60        numberValue = key->date();
61        break;
62    case IDBKey::NumberType:
63        numberValue = key->number();
64        break;
65    case IDBKey::MaxType:
66    case IDBKey::MinType:
67        break;
68    }
69}
70
71PassRefPtr<IDBKey> IDBKeyData::maybeCreateIDBKey() const
72{
73    if (isNull)
74        return nullptr;
75
76    switch (type) {
77    case IDBKey::InvalidType:
78        return IDBKey::createInvalid();
79    case IDBKey::ArrayType:
80        {
81            IDBKey::KeyArray array;
82            for (auto& keyData : arrayValue) {
83                array.append(keyData.maybeCreateIDBKey());
84                ASSERT(array.last());
85            }
86            return IDBKey::createArray(array);
87        }
88    case IDBKey::StringType:
89        return IDBKey::createString(stringValue);
90    case IDBKey::DateType:
91        return IDBKey::createDate(numberValue);
92    case IDBKey::NumberType:
93        return IDBKey::createNumber(numberValue);
94    case IDBKey::MaxType:
95    case IDBKey::MinType:
96        ASSERT_NOT_REACHED();
97        return nullptr;
98    }
99
100    ASSERT_NOT_REACHED();
101    return nullptr;
102}
103
104IDBKeyData IDBKeyData::isolatedCopy() const
105{
106    IDBKeyData result;
107    result.type = type;
108    result.isNull = isNull;
109
110    switch (type) {
111    case IDBKey::InvalidType:
112        return result;
113    case IDBKey::ArrayType:
114        for (auto& key : arrayValue)
115            result.arrayValue.append(key.isolatedCopy());
116        return result;
117    case IDBKey::StringType:
118        result.stringValue = stringValue.isolatedCopy();
119        return result;
120    case IDBKey::DateType:
121    case IDBKey::NumberType:
122        result.numberValue = numberValue;
123        return result;
124    case IDBKey::MaxType:
125    case IDBKey::MinType:
126        return result;
127    }
128
129    ASSERT_NOT_REACHED();
130    return result;
131}
132
133void IDBKeyData::encode(KeyedEncoder& encoder) const
134{
135    encoder.encodeBool("null", isNull);
136    if (isNull)
137        return;
138
139    encoder.encodeEnum("type", type);
140
141    switch (type) {
142    case IDBKey::InvalidType:
143        return;
144    case IDBKey::ArrayType:
145        encoder.encodeObjects("array", arrayValue.begin(), arrayValue.end(), [](KeyedEncoder& encoder, const IDBKeyData& key) {
146            key.encode(encoder);
147        });
148        return;
149    case IDBKey::StringType:
150        encoder.encodeString("string", stringValue);
151        return;
152    case IDBKey::DateType:
153    case IDBKey::NumberType:
154        encoder.encodeDouble("number", numberValue);
155        return;
156    case IDBKey::MaxType:
157    case IDBKey::MinType:
158        return;
159    }
160
161    ASSERT_NOT_REACHED();
162}
163
164bool IDBKeyData::decode(KeyedDecoder& decoder, IDBKeyData& result)
165{
166    if (!decoder.decodeBool("null", result.isNull))
167        return false;
168
169    if (result.isNull)
170        return true;
171
172    auto enumFunction = [](int64_t value) {
173        return value == IDBKey::MaxType
174            || value == IDBKey::InvalidType
175            || value == IDBKey::ArrayType
176            || value == IDBKey::StringType
177            || value == IDBKey::DateType
178            || value == IDBKey::NumberType
179            || value == IDBKey::MinType;
180    };
181    if (!decoder.decodeEnum("type", result.type, enumFunction))
182        return false;
183
184    if (result.type == IDBKey::InvalidType)
185        return true;
186
187    if (result.type == IDBKey::MaxType)
188        return true;
189
190    if (result.type == IDBKey::MinType)
191        return true;
192
193    if (result.type == IDBKey::StringType)
194        return decoder.decodeString("string", result.stringValue);
195
196    if (result.type == IDBKey::NumberType || result.type == IDBKey::DateType)
197        return decoder.decodeDouble("number", result.numberValue);
198
199    ASSERT(result.type == IDBKey::ArrayType);
200
201    auto arrayFunction = [](KeyedDecoder& decoder, IDBKeyData& result) {
202        return decode(decoder, result);
203    };
204    return decoder.decodeObjects("array", result.arrayValue, arrayFunction);
205}
206
207int IDBKeyData::compare(const IDBKeyData& other) const
208{
209    if (type == IDBKey::InvalidType) {
210        if (other.type != IDBKey::InvalidType)
211            return -1;
212        if (other.type == IDBKey::InvalidType)
213            return 0;
214    } else if (other.type == IDBKey::InvalidType)
215        return 1;
216
217    // The IDBKey::Type enum is in reverse sort order.
218    if (type != other.type)
219        return type < other.type ? 1 : -1;
220
221    // The types are the same, so handle actual value comparison.
222    switch (type) {
223    case IDBKey::InvalidType:
224        // InvalidType should have been fully handled above
225        ASSERT_NOT_REACHED();
226        return 0;
227    case IDBKey::ArrayType:
228        for (size_t i = 0; i < arrayValue.size() && i < other.arrayValue.size(); ++i) {
229            if (int result = arrayValue[i].compare(other.arrayValue[i]))
230                return result;
231        }
232        if (arrayValue.size() < other.arrayValue.size())
233            return -1;
234        if (arrayValue.size() > other.arrayValue.size())
235            return 1;
236        return 0;
237    case IDBKey::StringType:
238        return codePointCompare(stringValue, other.stringValue);
239    case IDBKey::DateType:
240    case IDBKey::NumberType:
241        if (numberValue == other.numberValue)
242            return 0;
243        return numberValue > other.numberValue ? 1 : -1;
244    case IDBKey::MaxType:
245    case IDBKey::MinType:
246        return 0;
247    }
248
249    ASSERT_NOT_REACHED();
250    return 0;
251}
252
253#ifndef NDEBUG
254String IDBKeyData::loggingString() const
255{
256    if (isNull)
257        return "<null>";
258
259    switch (type) {
260    case IDBKey::InvalidType:
261        return "<invalid>";
262    case IDBKey::ArrayType:
263        {
264            StringBuilder result;
265            result.appendLiteral("<array> - { ");
266            for (size_t i = 0; i < arrayValue.size(); ++i) {
267                result.append(arrayValue[i].loggingString());
268                if (i < arrayValue.size() - 1)
269                    result.append(", ");
270            }
271            result.append(" }");
272            return result.toString();
273        }
274    case IDBKey::StringType:
275        return "<string> - " + stringValue;
276    case IDBKey::DateType:
277        return String::format("Date type - %f", numberValue);
278    case IDBKey::NumberType:
279        return String::format("<number> - %f", numberValue);
280    case IDBKey::MaxType:
281        return "<maximum>";
282    case IDBKey::MinType:
283        return "<minimum>";
284    default:
285        return String();
286    }
287    ASSERT_NOT_REACHED();
288}
289#endif
290
291void IDBKeyData::setArrayValue(const Vector<IDBKeyData>& value)
292{
293    *this = IDBKeyData();
294    arrayValue = value;
295    type = IDBKey::ArrayType;
296    isNull = false;
297}
298
299void IDBKeyData::setStringValue(const String& value)
300{
301    *this = IDBKeyData();
302    stringValue = value;
303    type = IDBKey::StringType;
304    isNull = false;
305}
306
307void IDBKeyData::setDateValue(double value)
308{
309    *this = IDBKeyData();
310    numberValue = value;
311    type = IDBKey::DateType;
312    isNull = false;
313}
314
315void IDBKeyData::setNumberValue(double value)
316{
317    *this = IDBKeyData();
318    numberValue = value;
319    type = IDBKey::NumberType;
320    isNull = false;
321}
322
323}
324
325#endif // ENABLE(INDEXED_DATABASE)
326