File indexing completed on 2024-05-12 05:44:35

0001 /***************************************************************************
0002  *   Copyright (C) 2005-2009 by Rajko Albrecht                             *
0003  *   ral@alwins-world.de                                                   *
0004  *                                                                         *
0005  * This program is free software; you can redistribute it and/or           *
0006  * modify it under the terms of the GNU Lesser General Public              *
0007  * License as published by the Free Software Foundation; either            *
0008  * version 2.1 of the License, or (at your option) any later version.      *
0009  *                                                                         *
0010  * This program is distributed in the hope that it will be useful,         *
0011  * but WITHOUT ANY WARRANTY; without even the implied warranty of          *
0012  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU       *
0013  * Lesser General Public License for more details.                         *
0014  *                                                                         *
0015  * You should have received a copy of the GNU Lesser General Public        *
0016  * License along with this program (in the file LGPL.txt); if not,         *
0017  * write to the Free Software Foundation, Inc., 51 Franklin St,            *
0018  * Fifth Floor, Boston, MA  02110-1301  USA                                *
0019  *                                                                         *
0020  * This software consists of voluntary contributions made by many          *
0021  * individuals.  For exact contribution history, see the revision          *
0022  * history and logs, available at https://commits.kde.org/kdesvn.          *
0023  ***************************************************************************/
0024 
0025 /***************************************************************************
0026  * Lot of code and ideas taken from KDE 4 KConfigGroup source code         *
0027  * ksvn://anonsvn.kde.org/home/kde/branches/KDE/4.2/kdelibs/kdecore/config *
0028  ***************************************************************************/
0029 #include "ReposConfig.h"
0030 #include "LogCache.h"
0031 #include "svnqt/path.h"
0032 
0033 namespace svn
0034 {
0035 namespace cache
0036 {
0037 
0038 class ReposConfigPrivate
0039 {
0040 public:
0041     static QByteArray serializeList(const QList<QByteArray> &list);
0042     static QStringList deserializeList(const QByteArray &data);
0043     static QVariant convertToQVariant(const QByteArray &value, const QVariant &aDefault);
0044 };
0045 
0046 static QVector<int> asIntVec(const QByteArray &string)
0047 {
0048     const QList<QByteArray> strList = string.split(',');
0049     QVector<int> vec;
0050     vec.reserve(strList.size());
0051     for (const QByteArray &s : strList)
0052         vec << s.toInt();
0053     return vec;
0054 }
0055 
0056 QByteArray ReposConfigPrivate::serializeList(const QList<QByteArray> &list)
0057 {
0058     QByteArray value;
0059 
0060     if (!list.isEmpty()) {
0061         QList<QByteArray>::ConstIterator it = list.constBegin();
0062         const QList<QByteArray>::ConstIterator end = list.constEnd();
0063 
0064         value = QByteArray(*it).replace('\\', "\\\\").replace(',', "\\,");
0065 
0066         while (++it != end) {
0067             // In the loop, so it is not done when there is only one element.
0068             // Doing it repeatedly is a pretty cheap operation.
0069             value.reserve(4096);
0070 
0071             value += ',';
0072             value += QByteArray(*it).replace('\\', "\\\\").replace(',', "\\,");
0073         }
0074 
0075         // To be able to distinguish an empty list from a list with one empty element.
0076         if (value.isEmpty()) {
0077             value = "\\0";
0078         }
0079     }
0080 
0081     return value;
0082 }
0083 
0084 QStringList ReposConfigPrivate::deserializeList(const QByteArray &data)
0085 {
0086     if (data.isEmpty()) {
0087         return QStringList();
0088     }
0089     if (data == "\\0") {
0090         return QStringList(QString());
0091     }
0092     QStringList value;
0093     QString val;
0094     val.reserve(data.size());
0095     bool quoted = false;
0096     for (int p = 0; p < data.length(); p++) {
0097         if (quoted) {
0098             val += QLatin1Char(data[p]);
0099             quoted = false;
0100         } else if (data[p] == '\\') {
0101             quoted = true;
0102         } else if (data[p] == ',') {
0103             val.squeeze(); // release any unused memory
0104             value.append(val);
0105             val.clear();
0106             val.reserve(data.size() - p);
0107         } else {
0108             val += QLatin1Char(data[p]);
0109         }
0110     }
0111     value.append(val);
0112     return value;
0113 }
0114 
0115 QVariant ReposConfigPrivate::convertToQVariant(const QByteArray &value, const QVariant &aDefault)
0116 {
0117     // if a type handler is added here you must add a QVConversions definition
0118     // to conversion_check.h, or ConversionCheck::to_QVariant will not allow
0119     // readEntry<T> to convert to QVariant.
0120     switch (aDefault.type()) {
0121     case QVariant::Invalid:
0122         return QVariant();
0123     case QVariant::String:
0124         // this should return the raw string not the dollar expanded string.
0125         // imho if processed string is wanted should call
0126         // readEntry(key, QString) not readEntry(key, QVariant)
0127         return QString::fromUtf8(value);
0128     case QVariant::List:
0129     case QVariant::StringList:
0130         return deserializeList(value);
0131     case QVariant::ByteArray:
0132         return value;
0133     case QVariant::Bool: {
0134         const QByteArray lower(value.toLower());
0135         if (lower == "false" || lower == "no" || lower == "off" || lower == "0") {
0136             return false;
0137         }
0138         return true;
0139     }
0140     case QVariant::Double:
0141     case QMetaType::Float:
0142     case QVariant::Int:
0143     case QVariant::UInt:
0144     case QVariant::LongLong:
0145     case QVariant::ULongLong: {
0146         QVariant tmp = value;
0147         if (!tmp.convert(aDefault.type())) {
0148             tmp = aDefault;
0149         }
0150         return tmp;
0151     }
0152     case QVariant::DateTime: {
0153         const QVector<int> list = asIntVec(value);
0154         if (list.count() != 6) {
0155             return aDefault;
0156         }
0157         const QDate date(list.at(0), list.at(1), list.at(2));
0158         const QTime time(list.at(3), list.at(4), list.at(5));
0159         const QDateTime dt(date, time);
0160         if (!dt.isValid()) {
0161             return aDefault;
0162         }
0163         return dt;
0164     }
0165     case QVariant::Date: {
0166         QVector<int> list = asIntVec(value);
0167         if (list.count() == 6) {
0168             list = list.mid(0, 3); // don't break config files that stored QDate as QDateTime
0169         }
0170         if (list.count() != 3) {
0171             return aDefault;
0172         }
0173         const QDate date(list.at(0), list.at(1), list.at(2));
0174         if (!date.isValid()) {
0175             return aDefault;
0176         }
0177         return date;
0178     }
0179     default:
0180         break;
0181     }
0182 
0183     qWarning("unhandled type %s", aDefault.typeName());
0184     return QVariant();
0185 }
0186 
0187 ReposConfig *ReposConfig::mSelf = nullptr;
0188 
0189 ReposConfig *ReposConfig::self()
0190 {
0191     if (!mSelf) {
0192         mSelf = new ReposConfig();
0193     }
0194     return mSelf;
0195 }
0196 
0197 ReposConfig::ReposConfig()
0198 {
0199 }
0200 
0201 void ReposConfig::setValue(const QString &repository, const QString &key, const QVariant &value)
0202 {
0203     QByteArray data;
0204     switch (value.type()) {
0205     case QVariant::Invalid:
0206         break;
0207     case QVariant::ByteArray:
0208         data = value.toByteArray();
0209         break;
0210     case QVariant::String:
0211     case QVariant::Int:
0212     case QVariant::UInt:
0213     case QVariant::Double:
0214     case QMetaType::Float:
0215     case QVariant::Bool:
0216     case QVariant::LongLong:
0217     case QVariant::ULongLong:
0218         data = value.toString().toUtf8();
0219         break;
0220     case QVariant::List:
0221     case QVariant::StringList:
0222         setValue(repository, key, value.toList());
0223         return;
0224     case QVariant::Date: {
0225         QVariantList list;
0226         const QDate date = value.toDate();
0227 
0228         list.insert(0, date.year());
0229         list.insert(1, date.month());
0230         list.insert(2, date.day());
0231 
0232         setValue(repository, key, list);
0233         return;
0234     }
0235     case QVariant::DateTime: {
0236         QVariantList list;
0237         const QDateTime rDateTime = value.toDateTime();
0238 
0239         const QTime time = rDateTime.time();
0240         const QDate date = rDateTime.date();
0241 
0242         list.insert(0, date.year());
0243         list.insert(1, date.month());
0244         list.insert(2, date.day());
0245 
0246         list.insert(3, time.hour());
0247         list.insert(4, time.minute());
0248         list.insert(5, time.second());
0249 
0250         setValue(repository, key, list);
0251         return;
0252     }
0253 
0254     default:
0255         qWarning("ReposConfig: Unhandled type");
0256         return;
0257     }
0258 
0259     svn::cache::LogCache::self()->setRepositoryParameter(svn::Path(repository), key, data);
0260 }
0261 
0262 void ReposConfig::eraseValue(const QString &repository, const QString &key)
0263 {
0264     svn::cache::LogCache::self()->setRepositoryParameter(svn::Path(repository), key, QVariant());
0265 }
0266 
0267 void ReposConfig::setValue(const QString &repository, const QString &key, const QVariantList &list)
0268 {
0269     QList<QByteArray> data;
0270 
0271     for (const QVariant &v : list) {
0272         if (v.type() == QVariant::ByteArray) {
0273             data << v.toByteArray();
0274         } else {
0275             data << v.toString().toUtf8();
0276         }
0277     }
0278 
0279     setValue(repository, key, ReposConfigPrivate::serializeList(data));
0280 }
0281 
0282 void ReposConfig::setValue(const QString &repository, const QString &key, const QStringList &list)
0283 {
0284     QList<QByteArray> balist;
0285     for (const QString &entry : list)
0286         balist.append(entry.toUtf8());
0287     setValue(repository, key, ReposConfigPrivate::serializeList(balist));
0288 }
0289 
0290 void ReposConfig::setValue(const QString &repository, const QString &key, const QString &value)
0291 {
0292     setValue(repository, key, value.toUtf8());
0293 }
0294 
0295 QVariant ReposConfig::readEntry(const QString &repository, const QString &key, const QVariant &aDefault)
0296 {
0297     QVariant v = svn::cache::LogCache::self()->getRepositoryParameter(svn::Path(repository), key);
0298     if (!v.isValid()) {
0299         return aDefault;
0300     }
0301     return ReposConfigPrivate::convertToQVariant(v.toByteArray(), aDefault);
0302 }
0303 
0304 int ReposConfig::readEntry(const QString &repository, const QString &key, int aDefault)
0305 {
0306     return readEntry(repository, key, QVariant(aDefault)).toInt();
0307 }
0308 
0309 bool ReposConfig::readEntry(const QString &repository, const QString &key, bool aDefault)
0310 {
0311     return readEntry(repository, key, QVariant(aDefault)).toBool();
0312 }
0313 
0314 QStringList ReposConfig::readEntry(const QString &repository, const QString &key, const QStringList &aDefault)
0315 {
0316     return readEntry(repository, key, QVariant(aDefault)).toStringList();
0317 }
0318 
0319 } // namespace cache
0320 } // namespace svn