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 }