Warning, file /office/calligra/libs/widgets/KoResourceTagStore.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the KDE project 0002 0003 Copyright (c) 2011 Sven Langkamp <sven.langkamp@gmail.com> 0004 Copyright (C) 2011 Srikanth Tiyyagura <srikanth.tulasiram@gmail.com> 0005 0006 This library is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU Lesser General Public 0008 License as published by the Free Software Foundation; either 0009 version 2.1 of the License, or (at your option) any later version. 0010 0011 This library is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0014 Lesser General Public License for more details. 0015 0016 You should have received a copy of the GNU Lesser General Public 0017 License along with this library; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA 0019 */ 0020 0021 #include "KoResourceTagStore.h" 0022 0023 #include <QDebug> 0024 #include <QStringList> 0025 #include <QFile> 0026 #include <QDir> 0027 #include <QStandardPaths> 0028 #include <QDomDocument> 0029 0030 #include <KoResourceServer.h> 0031 0032 0033 class Q_DECL_HIDDEN KoResourceTagStore::Private 0034 { 0035 public: 0036 QMultiHash<QByteArray, QString> md5ToTag; 0037 QMultiHash<QString, QString> identifierToTag; 0038 0039 QHash<QString, int> tagList; 0040 0041 KoResourceServerBase *resourceServer; 0042 }; 0043 0044 KoResourceTagStore::KoResourceTagStore(KoResourceServerBase *resourceServer) 0045 : d(new Private) 0046 { 0047 d->resourceServer = resourceServer; 0048 } 0049 0050 KoResourceTagStore::~KoResourceTagStore() 0051 { 0052 serializeTags(); 0053 delete d; 0054 } 0055 0056 QStringList KoResourceTagStore::assignedTagsList(const KoResource* resource) const 0057 { 0058 if (!resource) return QStringList(); 0059 0060 QStringList tags = d->md5ToTag.values(resource->md5()); 0061 tags += d->identifierToTag.values(resource->filename()); 0062 tags.removeDuplicates(); 0063 return tags; 0064 } 0065 0066 void KoResourceTagStore::removeResource(const KoResource *resource) 0067 { 0068 QStringList tags = assignedTagsList(resource); 0069 0070 d->md5ToTag.remove(resource->md5()); 0071 d->identifierToTag.remove(resource->filename()); 0072 0073 foreach(const QString &tag, tags) { 0074 if (d->tagList.contains(tag)) { 0075 if (d->tagList[tag] > 0) { 0076 d->tagList[tag]--; 0077 } 0078 } 0079 } 0080 } 0081 0082 QStringList KoResourceTagStore::tagNamesList() const 0083 { 0084 return d->tagList.uniqueKeys(); 0085 } 0086 0087 void KoResourceTagStore::addTag(KoResource* resource, const QString& tag) 0088 { 0089 if (!resource) { 0090 d->tagList.insert(tag, 0); 0091 return; 0092 } 0093 0094 bool added = false; 0095 0096 if (!d->md5ToTag.contains(resource->md5(), tag)) { 0097 added = true; 0098 d->md5ToTag.insert(resource->md5(), tag); 0099 } 0100 0101 if (!d->identifierToTag.contains(resource->filename())) { 0102 added = true; 0103 d->identifierToTag.insert(resource->filename(), tag); 0104 } 0105 0106 if (added) { 0107 if (d->tagList.contains(tag)) { 0108 d->tagList[tag]++; 0109 } 0110 else { 0111 d->tagList.insert(tag, 1); 0112 } 0113 } 0114 } 0115 0116 void KoResourceTagStore::delTag(KoResource* resource, const QString& tag) 0117 { 0118 int res = d->md5ToTag.remove(resource->md5(), tag); 0119 res += d->identifierToTag.remove(resource->filename(), tag); 0120 0121 if (res > 0) { // decrease the usecount for this tag 0122 if (d->tagList.contains(tag)) { 0123 if (d->tagList[tag] > 0) { 0124 d->tagList[tag]--; 0125 } 0126 } 0127 } 0128 } 0129 0130 void KoResourceTagStore::delTag(const QString& tag) 0131 { 0132 foreach(const QByteArray &res, d->md5ToTag.keys(tag)) { 0133 d->md5ToTag.remove(res, tag); 0134 } 0135 foreach(const QString &identifier, d->identifierToTag.keys(tag)) { 0136 d->identifierToTag.remove(identifier, tag); 0137 } 0138 0139 Q_ASSERT(!d->md5ToTag.values().contains(tag)); 0140 Q_ASSERT(!d->identifierToTag.values().contains(tag)); 0141 d->tagList.remove(tag); 0142 } 0143 0144 QStringList KoResourceTagStore::searchTag(const QString& query) const 0145 { 0146 QStringList tagsList = query.split(QRegExp("[,]\\s*"), QString::SkipEmptyParts); 0147 if (tagsList.isEmpty()) { 0148 return QStringList(); 0149 } 0150 0151 QSet<const KoResource*> resources; 0152 0153 foreach (QString tag, tagsList) { 0154 foreach(const QByteArray &md5, d->md5ToTag.keys(tag)) { 0155 KoResource *res = d->resourceServer->byMd5(md5); 0156 if (res) 0157 resources << res; 0158 } 0159 foreach(const QString &identifier, d->identifierToTag.keys(tag)) { 0160 KoResource *res = d->resourceServer->byFileName(identifier); 0161 if (res) 0162 resources << res; 0163 } 0164 } 0165 0166 QStringList filenames; 0167 foreach (const KoResource *res, resources) { 0168 if (res) { 0169 filenames << adjustedFileName(res->shortFilename()); 0170 } 0171 } 0172 0173 return removeAdjustedFileNames(filenames); 0174 } 0175 0176 void KoResourceTagStore::loadTags() 0177 { 0178 QStringList tagFiles = QStandardPaths::locateAll(QStandardPaths::AppDataLocation, "tags/" + d->resourceServer->type() + "_tags.xml"); 0179 foreach(const QString &tagFile, tagFiles) { 0180 readXMLFile(tagFile); 0181 } 0182 } 0183 0184 void KoResourceTagStore::writeXMLFile(const QString &tagstore) 0185 { 0186 QDir().mkpath(QFileInfo(tagstore).path()); 0187 QFile f(tagstore); 0188 if (!f.open(QIODevice::WriteOnly | QIODevice::Text)) { 0189 warnWidgets << "Cannot write meta information to '" << tagstore << "'."; 0190 return; 0191 } 0192 QDomDocument doc; 0193 QDomElement root; 0194 0195 QDomDocument docTemp("tags"); 0196 doc = docTemp; 0197 doc.appendChild(doc.createProcessingInstruction("xml", "version=\"1.0\" encoding=\"UTF-8\"")); 0198 root = doc.createElement("tags"); 0199 doc.appendChild(root); 0200 0201 QSet<KoResource*> taggedResources; 0202 foreach (const QByteArray &md5, d->md5ToTag.keys()) { 0203 KoResource *res = d->resourceServer->byMd5(md5); 0204 if (res) { 0205 taggedResources << res; 0206 } 0207 } 0208 0209 foreach (const QString &identifier, d->identifierToTag.keys()) { 0210 KoResource *res = d->resourceServer->byFileName(identifier); 0211 if (res) { 0212 taggedResources << res; 0213 } 0214 } 0215 0216 foreach (const KoResource *res, taggedResources) { 0217 0218 QDomElement resourceEl = doc.createElement("resource"); 0219 resourceEl.setAttribute("identifier", res->filename().replace(QDir::homePath(), QString("~"))); 0220 resourceEl.setAttribute("md5", QString(res->md5().toBase64())); 0221 0222 foreach (const QString &tag, assignedTagsList(res)) { 0223 QDomElement tagEl = doc.createElement("tag"); 0224 tagEl.appendChild(doc.createTextNode(tag)); 0225 resourceEl.appendChild(tagEl); 0226 } 0227 root.appendChild(resourceEl); 0228 0229 } 0230 0231 // Now write empty tags 0232 foreach (const QString &tag, d->tagList.uniqueKeys()) { 0233 if (d->tagList[tag] == 0) { 0234 QDomElement resourceEl = doc.createElement("resource"); 0235 resourceEl.setAttribute("identifier", "dummy"); 0236 QDomElement tagEl = doc.createElement("tag"); 0237 tagEl.appendChild(doc.createTextNode(tag)); 0238 resourceEl.appendChild(tagEl); 0239 root.appendChild(resourceEl); 0240 } 0241 } 0242 0243 QTextStream metastream(&f); 0244 metastream << doc.toString(); 0245 0246 f.close(); 0247 0248 } 0249 0250 void KoResourceTagStore::readXMLFile(const QString &tagstore) 0251 { 0252 QString inputFile; 0253 0254 if (QFile::exists(tagstore)) { 0255 inputFile = tagstore; 0256 } else { 0257 inputFile = QStandardPaths::locate(QStandardPaths::AppDataLocation, "tags.xml"); 0258 } 0259 0260 QFile f(inputFile); 0261 if (!f.open(QIODevice::ReadOnly)) { 0262 return; 0263 } 0264 0265 QDomDocument doc; 0266 if (!doc.setContent(&f)) { 0267 warnWidgets << "The file could not be parsed."; 0268 return; 0269 } 0270 0271 QDomElement root = doc.documentElement(); 0272 if (root.tagName() != "tags") { 0273 warnWidgets << "The file doesn't seem to be of interest."; 0274 return; 0275 } 0276 0277 QDomNodeList resourceNodesList = root.childNodes(); 0278 0279 for (int i = 0; i < resourceNodesList.count(); i++) { 0280 0281 QByteArray resourceMD5; 0282 QString identifier; 0283 0284 QDomElement element = resourceNodesList.at(i).toElement(); 0285 if (element.tagName() == "resource") { 0286 0287 KoResource *resByMd5 = 0; 0288 KoResource *resByFileName = 0; 0289 0290 if (element.hasAttribute("md5")) { 0291 resourceMD5 = QByteArray::fromBase64(element.attribute("md5").toLatin1()); 0292 resByMd5 = d->resourceServer->byMd5(resourceMD5); 0293 } 0294 0295 if (element.hasAttribute("identifier")) { 0296 identifier = element.attribute("identifier"); 0297 QFileInfo fi(identifier); 0298 resByFileName = d->resourceServer->byFileName(fi.fileName()); 0299 } 0300 0301 if (identifier == "dummy" || isServerResource(identifier)) { 0302 0303 QDomNodeList tagNodesList = resourceNodesList.at(i).childNodes(); 0304 0305 for (int j = 0; j < tagNodesList.count() ; j++) { 0306 0307 QDomElement tagEl = tagNodesList.at(j).toElement(); 0308 0309 if (identifier != "dummy") { 0310 QFileInfo fi(identifier); 0311 KoResource *res = d->resourceServer->byFileName(fi.fileName()); 0312 addTag(res, tagEl.text()); 0313 } 0314 else { 0315 addTag(0, tagEl.text()); 0316 } 0317 d->md5ToTag.insert(resourceMD5, tagEl.text()); 0318 d->identifierToTag.insert(identifier, tagEl.text()); 0319 } 0320 } 0321 else { 0322 KoResource *res = 0; 0323 0324 if (resByMd5 && resByFileName && (resByMd5 != resByFileName)) { 0325 warnWidgets << "MD5sum and filename point to different resources -- was the resource renamed? We go with md5"; 0326 res = resByMd5; 0327 } 0328 else if (!resByMd5 && resByFileName) { 0329 // We didn't find the resource by md5, but did find it by filename, so take that one 0330 res = resByFileName; 0331 } 0332 else { 0333 res = resByMd5; 0334 } 0335 0336 QDomNodeList tagNodesList = resourceNodesList.at(i).childNodes(); 0337 for (int j = 0; j < tagNodesList.count() ; j++) { 0338 QDomElement tagEl = tagNodesList.at(j).toElement(); 0339 0340 if (res) { 0341 addTag(res, tagEl.text()); 0342 } 0343 d->md5ToTag.insert(resourceMD5, tagEl.text()); 0344 d->identifierToTag.insert(identifier, tagEl.text()); 0345 } 0346 } 0347 } 0348 } 0349 } 0350 0351 bool KoResourceTagStore::isServerResource(const QString &resourceName) const 0352 { 0353 bool removeChild = false; 0354 QStringList extensionsList = d->resourceServer->extensions().split(':'); 0355 foreach (QString extension, extensionsList) { 0356 if (resourceName.contains(extension.remove('*'))) { 0357 removeChild = true; 0358 break; 0359 } 0360 } 0361 return removeChild; 0362 } 0363 0364 QString KoResourceTagStore::adjustedFileName(const QString &fileName) const 0365 { 0366 if (!isServerResource(fileName)) { 0367 return fileName + "-krita" + d->resourceServer->extensions().split(':').takeFirst().remove('*'); 0368 } 0369 return fileName; 0370 } 0371 0372 QStringList KoResourceTagStore::removeAdjustedFileNames(QStringList fileNamesList) const 0373 { 0374 foreach (const QString & fileName, fileNamesList) { 0375 if (fileName.contains("-krita")) { 0376 fileNamesList.append(fileName.split("-krita").takeFirst()); 0377 fileNamesList.removeAll(fileName); 0378 } 0379 } 0380 return fileNamesList; 0381 } 0382 0383 void KoResourceTagStore::serializeTags() 0384 { 0385 writeXMLFile(QStandardPaths::writableLocation(QStandardPaths::AppDataLocation) + "/tags/" + d->resourceServer->type() + "_tags.xml"); 0386 }