aboutsummaryrefslogtreecommitdiffstats
path: root/SQLiteStudio3/coreSQLiteStudio/common/bistrhash.h
blob: 65c907b0bcaafe5d3c7f310a24c9e0c98e77b1e1 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
#ifndef BISTRHASH_H
#define BISTRHASH_H

#include "bihash.h"
#include <QHash>
#include <QString>

/**
 * @brief Bi-directional string-oriented hash.
 *
 * This hash is very similar to BiHash, except it always uses QString
 * for both left and right values. Given that, it also provides Qt::CaseSensitivity support
 * for any operations accepting values in parameters.
 *
 * Just like BiHash, the BiStrHash doesn't provide operator[]. For more details see BiHash.
 */
class BiStrHash
{
    public:
        /**
         * @brief Creates empty hash.
         */
        BiStrHash() {}

        /**
         * @brief Creates pre-initialized hash.
         * @param list C++11 style initializer list, like: <tt>{{"x"="y"}, {"a"="b"}}</tt>
         */
        BiStrHash(std::initializer_list<std::pair<QString, QString>> list)
        {
            hash = QHash<QString,QString>(list);
            initInvertedAndLower();
        }

        /**
         * @brief Creates BiStrHash basing on QHash.
         * @param other QHash to copy values from.
         *
         * Any conflicting values from the \p other hash will overwrite
         * current values in the hash.
         */
        BiStrHash(const QHash<QString, QString> & other)
        {
            unite(other);
        }

        /**
         * @brief Copy constructor.
         * @param other Other hash to copy.
         */
        BiStrHash(const BiStrHash& other) : hash(other.hash), inverted(other.inverted),
            lowerHash(other.lowerHash), lowerInverted(other.lowerInverted) {}

        /**
         * @brief Inserts entry into the hash.
         * @param left Left-side value to insert.
         * @param right Right-side value to insert.
         *
         * Inserting to the hash is done in case-insensitive manner, hence any conflicting
         * values matched with case insensitive method will be replaced with the new entry.
         */
        void insert(const QString& left, const QString& right)
        {
            if (lowerHash.contains(left.toLower()))
                removeLeft(left, Qt::CaseInsensitive);

            if (lowerInverted.contains(right.toLower()))
                removeRight(right, Qt::CaseInsensitive);

            inverted.insert(right, left);
            hash.insert(left, right);
            lowerHash.insert(left.toLower(), left);
            lowerInverted.insert(right.toLower(), right);
        }

        /**
         * @brief Tests if given value is in the left values of the hash.
         * @param left Left-side value to match.
         * @param cs Case sensitivity flag.
         * @return true if the key was matched in left side values, or false otherwise.
         */
        bool containsLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive)
        {
            if (cs == Qt::CaseSensitive)
                return hash.contains(left);
            else
                return lowerHash.contains(left.toLower());
        }

        /**
         * @brief Tests if given value is in the right values of the hash.
         * @param right Right-side value to match.
         * @param cs Case sensitivity flag.
         * @return true if the key was matched in right side values, or false otherwise.
         */
        bool containsRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive)
        {
            if (cs == Qt::CaseSensitive)
                return inverted.contains(right);
            else
                return lowerInverted.contains(right.toLower());
        }

        /**
         * @brief Removes entry matching given value in left-side values.
         * @param left Left-side value to match.
         * @param cs Case sensitivity flag.
         * @return Number of entries removed.
         */
        int	removeLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive)
        {
            if (cs == Qt::CaseSensitive)
            {
                if (!hash.contains(left))
                    return 0;

                inverted.remove(hash.value(left));
                hash.remove(left);

                return 1;
            }
            else
            {
                QString lowerLeft = left.toLower();
                if (!lowerHash.contains(lowerLeft))
                    return 0;

                QString right = hash.value(lowerHash.value(lowerLeft));

                hash.remove(inverted.value(right));
                inverted.remove(right);
                lowerHash.remove(lowerLeft);
                lowerInverted.remove(right.toLower());

                return 1;
            }
        }

        /**
         * @brief Removes entry matching given value in right-side values.
         * @param right Right-side value to match.
         * @param cs Case sensitivity flag.
         * @return Number of entries removed.
         */
        int	removeRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive)
        {
            if (cs == Qt::CaseSensitive)
            {
                if (!inverted.contains(right))
                    return 0;

                hash.remove(inverted.value(right));
                inverted.remove(right);

                return 1;
            }
            else
            {
                QString lowerRight = right.toLower();
                if (!lowerInverted.contains(lowerRight))
                    return 0;

                QString left = inverted.value(lowerInverted.value(lowerRight));

                inverted.remove(hash.value(left));
                hash.remove(left);
                lowerHash.remove(left.toLower());
                lowerInverted.remove(lowerRight);

                return 1;
            }
        }

        /**
         * @brief Removes entry from hash and returns it.
         * @param left Left-side value to match.
         * @param cs Case sensitivity flag.
         * @return Right side value, or null string if the \p left was not matched.
         */
        QString takeLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive)
        {
            if (cs == Qt::CaseSensitive)
            {
                QString right = hash.take(left);
                inverted.remove(right);
                return right;
            }
            else
            {
                QString right = hash.take(lowerHash.take(left.toLower()));
                inverted.remove(lowerInverted.take(right.toLower()));
                return right;
            }
        }

        /**
         * @brief Removes entry from hash and returns it.
         * @param right Right-side value to match.
         * @param cs Case sensitivity flag.
         * @return Left side value, or null string if the \p left was not matched.
         */
        QString takeRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive)
        {
            if (cs == Qt::CaseSensitive)
            {
                QString left = inverted.take(right);
                hash.remove(left);
                return left;
            }
            else
            {
                QString left = inverted.take(lowerInverted.take(right.toLower()));
                hash.remove(lowerHash.take(left.toLower()));
                return left;
            }
        }

        /**
         * @brief Copies all entries from the other hash to this hash.
         * @param other Other hash to copy values from.
         * @return Reference to this hash, after update.
         */
        BiStrHash& unite(const BiStrHash& other)
        {
            unite(other.hash);
            return *this;
        }

        /**
         * @overload
         */
        BiStrHash& unite(const QHash<QString,QString>& other)
        {
            QHashIterator<QString, QString> it(other);
            while (it.hasNext())
                insert(it.next().key(), it.value());

            return *this;
        }

        /**
         * @brief Finds right-side value by matching the left-side value.
         * @param left Left-side value to match.
         * @param cs Case sensitivity flag.
         * @return Right-side value, or null string if left-side value was not matched.
         */
        QString valueByLeft(const QString& left, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
        {
            if (cs == Qt::CaseSensitive)
                return hash.value(left);
            else
                return hash.value(lowerHash.value(left.toLower()));
        }

        /**
         * @brief Finds left-side value by matching the right-side value.
         * @param right Right-side value to match.
         * @param cs Case sensitivity flag.
         * @return Left-side value, or null string if right-side value was not matched.
         */
        QString valueByRight(const QString& right, Qt::CaseSensitivity cs = Qt::CaseSensitive) const
        {
            if (cs == Qt::CaseSensitive)
                return inverted.value(right);
            else
                return inverted.value(lowerInverted.value(right.toLower()));
        }

        /**
         * @brief Gives all left-side values.
         * @return List of values.
         */
        QStringList leftValues() const
        {
            return hash.keys();
        }

        /**
         * @brief Gives all right-side values.
         * @return List of values.
         */
        QStringList rightValues() const
        {
            return inverted.keys();
        }

        /**
         * @brief Provides java-style iterator for this hash.
         * @return Iterator object.
         */
        QHashIterator<QString,QString> iterator() const
        {
            return QHashIterator<QString,QString>(hash);
        }

        /**
         * @brief Removes all entries from the hash.
         */
        void clear()
        {
            hash.clear();
            inverted.clear();
            lowerHash.clear();
            lowerInverted.clear();
        }

        /**
         * @brief Counts all entries in the hash.
         * @return Number of entries in the hash.
         */
        int count() const
        {
            return hash.count();
        }

        /**
         * @brief Tests whether the hash is empty or not.
         * @return true if the hash is empty, false otherwise.
         */
        bool isEmpty() const
        {
            return hash.isEmpty();
        }

    private:
        /**
         * @brief Fills inverted and lower internal hashes basing on the main hash class member.
         */
        void initInvertedAndLower()
        {
            QHashIterator<QString,QString> it(hash);
            while (it.hasNext())
            {
                it.next();
                inverted[it.value()] = it.key();
                lowerHash[it.key().toLower()] = it.key();
                lowerInverted[it.value().toLower()] = it.value();
            }
        }

        /**
         * @brief Standard mapping - left to right.
         */
        QHash<QString,QString> hash;

        /**
         * @brief Right to left mapping.
         */
        QHash<QString,QString> inverted;

        /**
         * @brief Lower left to true left key mapping.
         */
        QHash<QString,QString> lowerHash;

        /**
         * @brief Lower right to true right key mapping.
         */
        QHash<QString,QString> lowerInverted;
};

#endif // BISTRHASH_H