OSDN Git Service

Update license.
[qt-creator-jp/qt-creator-jp.git] / src / plugins / coreplugin / settingsdatabase.cpp
1 /**************************************************************************
2 **
3 ** This file is part of Qt Creator
4 **
5 ** Copyright (c) 2011 Nokia Corporation and/or its subsidiary(-ies).
6 **
7 ** Contact: Nokia Corporation (info@qt.nokia.com)
8 **
9 **
10 ** GNU Lesser General Public License Usage
11 **
12 ** This file may be used under the terms of the GNU Lesser General Public
13 ** License version 2.1 as published by the Free Software Foundation and
14 ** appearing in the file LICENSE.LGPL included in the packaging of this file.
15 ** Please review the following information to ensure the GNU Lesser General
16 ** Public License version 2.1 requirements will be met:
17 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
18 **
19 ** In addition, as a special exception, Nokia gives you certain additional
20 ** rights. These rights are described in the Nokia Qt LGPL Exception
21 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
22 **
23 ** Other Usage
24 **
25 ** Alternatively, this file may be used in accordance with the terms and
26 ** conditions contained in a signed written agreement between you and Nokia.
27 **
28 ** If you have questions regarding the use of this file, please contact
29 ** Nokia at qt-info@nokia.com.
30 **
31 **************************************************************************/
32
33 #include "settingsdatabase.h"
34
35 #include <QtCore/QDir>
36 #include <QtCore/QMap>
37 #include <QtCore/QString>
38 #include <QtCore/QStringList>
39 #include <QtCore/QVariant>
40
41 #include <QtSql/QSqlDatabase>
42 #include <QtSql/QSqlError>
43 #include <QtSql/QSqlQuery>
44 #include <QtCore/QDebug>
45
46 /*!
47     \class Core::SettingsDatabase
48     \brief An alternative to the application-wide QSettings that is more
49     suitable for storing large amounts of data.
50
51     The settings database is SQLite based, and lazily retrieves data when it
52     is asked for. It also does incremental updates of the database rather than
53     rewriting the whole file each time one of the settings change.
54
55     The SettingsDatabase API mimics that of QSettings.
56 */
57
58 using namespace Core;
59 using namespace Core::Internal;
60
61 enum { debug_settings = 0 };
62
63 namespace Core {
64 namespace Internal {
65
66 typedef QMap<QString, QVariant> SettingsMap;
67
68 class SettingsDatabasePrivate
69 {
70 public:
71     QString effectiveGroup() const
72     {
73         return m_groups.join(QString(QLatin1Char('/')));
74     }
75
76     QString effectiveKey(const QString &key) const
77     {
78         QString g = effectiveGroup();
79         if (!g.isEmpty() && !key.isEmpty())
80             g += QLatin1Char('/');
81         g += key;
82         return g;
83     }
84
85     SettingsMap m_settings;
86
87     QStringList m_groups;
88     QStringList m_dirtyKeys;
89
90     QSqlDatabase m_db;
91 };
92
93 } // namespace Internal
94 } // namespace Core
95
96 SettingsDatabase::SettingsDatabase(const QString &path,
97                                    const QString &application,
98                                    QObject *parent)
99     : QObject(parent)
100     , d(new SettingsDatabasePrivate)
101 {
102     const QLatin1Char slash('/');
103
104     // TODO: Don't rely on a path, but determine automatically
105     QDir pathDir(path);
106     if (!pathDir.exists())
107         pathDir.mkpath(pathDir.absolutePath());
108
109     QString fileName = path;
110     if (!fileName.endsWith(slash))
111         fileName += slash;
112     fileName += application;
113     fileName += QLatin1String(".db");
114
115     d->m_db = QSqlDatabase::addDatabase("QSQLITE", QLatin1String("settings"));
116     d->m_db.setDatabaseName(fileName);
117     if (!d->m_db.open()) {
118         qWarning().nospace() << "Warning: Failed to open settings database at " << fileName << " ("
119                              << d->m_db.lastError().driverText() << ")";
120     } else {
121         // Create the settings table if it doesn't exist yet
122         QSqlQuery query(d->m_db);
123         query.prepare(QLatin1String("CREATE TABLE IF NOT EXISTS settings ("
124                                     "key PRIMARY KEY ON CONFLICT REPLACE, "
125                                     "value)"));
126         if (!query.exec())
127             qWarning().nospace() << "Warning: Failed to prepare settings database! ("
128                                  << query.lastError().driverText() << ")";
129
130         // Retrieve all available keys (values are retrieved lazily)
131         if (query.exec(QLatin1String("SELECT key FROM settings"))) {
132             while (query.next()) {
133                 d->m_settings.insert(query.value(0).toString(), QVariant());
134             }
135         }
136     }
137 }
138
139 SettingsDatabase::~SettingsDatabase()
140 {
141     sync();
142
143     delete d;
144     QSqlDatabase::removeDatabase(QLatin1String("settings"));
145 }
146
147 void SettingsDatabase::setValue(const QString &key, const QVariant &value)
148 {
149     const QString effectiveKey = d->effectiveKey(key);
150
151     // Add to cache
152     d->m_settings.insert(effectiveKey, value);
153
154     if (!d->m_db.isOpen())
155         return;
156
157     // Instant apply (TODO: Delay writing out settings)
158     QSqlQuery query(d->m_db);
159     query.prepare(QLatin1String("INSERT INTO settings VALUES (?, ?)"));
160     query.addBindValue(effectiveKey);
161     query.addBindValue(value);
162     query.exec();
163
164     if (debug_settings)
165         qDebug() << "Stored:" << effectiveKey << "=" << value;
166 }
167
168 QVariant SettingsDatabase::value(const QString &key, const QVariant &defaultValue) const
169 {
170     const QString effectiveKey = d->effectiveKey(key);
171     QVariant value = defaultValue;
172
173     SettingsMap::const_iterator i = d->m_settings.constFind(effectiveKey);
174     if (i != d->m_settings.constEnd() && i.value().isValid()) {
175         value = i.value();
176     } else if (d->m_db.isOpen()) {
177         // Try to read the value from the database
178         QSqlQuery query(d->m_db);
179         query.prepare(QLatin1String("SELECT value FROM settings WHERE key = ?"));
180         query.addBindValue(effectiveKey);
181         query.exec();
182         if (query.next()) {
183             value = query.value(0);
184
185             if (debug_settings)
186                 qDebug() << "Retrieved:" << effectiveKey << "=" << value;
187         }
188
189         // Cache the result
190         d->m_settings.insert(effectiveKey, value);
191     }
192
193     return value;
194 }
195
196 bool SettingsDatabase::contains(const QString &key) const
197 {
198     return d->m_settings.contains(d->effectiveKey(key));
199 }
200
201 void SettingsDatabase::remove(const QString &key)
202 {
203     const QString effectiveKey = d->effectiveKey(key);
204
205     // Remove keys from the cache
206     foreach (const QString &k, d->m_settings.keys()) {
207         // Either it's an exact match, or it matches up to a /
208         if (k.startsWith(effectiveKey)
209             && (k.length() == effectiveKey.length()
210                 || k.at(effectiveKey.length()) == QLatin1Char('/')))
211         {
212             d->m_settings.remove(k);
213         }
214     }
215
216     if (!d->m_db.isOpen())
217         return;
218
219     // Delete keys from the database
220     QSqlQuery query(d->m_db);
221     query.prepare(QLatin1String("DELETE FROM settings WHERE key = ? OR key LIKE ?"));
222     query.addBindValue(effectiveKey);
223     query.addBindValue(QString(effectiveKey + QLatin1String("/%")));
224     query.exec();
225 }
226
227 void SettingsDatabase::beginGroup(const QString &prefix)
228 {
229     d->m_groups.append(prefix);
230 }
231
232 void SettingsDatabase::endGroup()
233 {
234     d->m_groups.removeLast();
235 }
236
237 QString SettingsDatabase::group() const
238 {
239     return d->effectiveGroup();
240 }
241
242 QStringList SettingsDatabase::childKeys() const
243 {
244     QStringList children;
245
246     const QString g = group();
247     QMapIterator<QString, QVariant> i(d->m_settings);
248     while (i.hasNext()) {
249         const QString &key = i.next().key();
250         if (key.startsWith(g) && key.indexOf(QLatin1Char('/'), g.length() + 1) == -1) {
251             children.append(key.mid(g.length() + 1));
252         }
253     }
254
255     return children;
256 }
257
258 void SettingsDatabase::sync()
259 {
260     // TODO: Delay writing of dirty keys and save them here
261 }