Warning, file /libraries/baloo-widgets/src/filemetadatautil.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* 0002 SPDX-FileCopyrightText: 2019 Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de> 0003 0004 SPDX-License-Identifier: LGPL-2.0-or-later 0005 */ 0006 0007 #include "filemetadatautil_p.h" 0008 #include <KFileMetaData/PropertyInfo> 0009 #include <KFileMetaData/UserMetaData> 0010 #include <QSet> 0011 0012 #include <QCollator> 0013 #include <QString> 0014 #include <QStringList> 0015 0016 #include <algorithm> 0017 0018 namespace 0019 { 0020 QVariant intersect(const QVariant &v1, const QVariant &v2) 0021 { 0022 if (!v1.isValid() || !v2.isValid()) { 0023 return {}; 0024 } 0025 0026 // List and String 0027 if (v1.type() == QVariant::StringList && v2.type() == QVariant::String) { 0028 QStringList list = v1.toStringList(); 0029 QString str = v2.toString(); 0030 0031 if (!list.contains(str)) { 0032 list << str; 0033 } 0034 0035 return QVariant(list); 0036 } 0037 0038 // String and List 0039 if (v1.type() == QVariant::String && v2.type() == QVariant::StringList) { 0040 QStringList list = v2.toStringList(); 0041 QString str = v1.toString(); 0042 0043 if (!list.contains(str)) { 0044 list << str; 0045 } 0046 0047 return QVariant(list); 0048 } 0049 0050 // List and List 0051 if (v1.type() == QVariant::StringList && v2.type() == QVariant::StringList) { 0052 QSet<QString> s1(v1.toStringList().cbegin(), v1.toStringList().cend()); 0053 QSet<QString> s2(v2.toStringList().cbegin(), v2.toStringList().cend()); 0054 0055 return QVariant(s1.intersect(s2).values()); 0056 } 0057 0058 if (v1 == v2) { 0059 return v1; 0060 } 0061 0062 return {}; 0063 } 0064 0065 // Precondition: 0066 // if commonProperties contains <prop>, all <files> must also provide <prop> 0067 void totalProperties(QVariantMap& target, const QString &prop, const QList<QVariantMap> &files, QList<QString> &commonProperties) 0068 { 0069 auto propIndex = commonProperties.indexOf(prop); 0070 0071 if (propIndex >= 0) { 0072 int total = 0; 0073 for (const QVariantMap &file : files) { 0074 QVariantMap::const_iterator it = file.constFind(prop); 0075 Q_ASSERT(it != file.constEnd()); 0076 0077 total += it.value().toInt(); 0078 } 0079 0080 target.insert(prop, QVariant(total)); 0081 0082 commonProperties.removeAt(propIndex); 0083 } 0084 } 0085 } // anonymous namespace 0086 0087 namespace Baloo 0088 { 0089 namespace Private 0090 { 0091 QMap<KFileMetaData::UserMetaData::Attribute, QVariant> 0092 fetchUserMetaData(const KFileMetaData::UserMetaData &metaData, KFileMetaData::UserMetaData::Attributes wantedAttributes) 0093 { 0094 using Attribute = KFileMetaData::UserMetaData::Attribute; 0095 0096 auto attributes = metaData.queryAttributes(wantedAttributes); 0097 0098 QMap<Attribute, QVariant> properties; 0099 0100 if (attributes & Attribute::Tags) { 0101 QStringList tags = metaData.tags(); 0102 if (!tags.isEmpty()) { 0103 properties.insert(Attribute::Tags, tags); 0104 } 0105 } 0106 0107 if (attributes & Attribute::Rating) { 0108 int rating = metaData.rating(); 0109 if (rating) { 0110 properties.insert(Attribute::Rating, rating); 0111 } 0112 } 0113 0114 if (attributes & Attribute::Comment) { 0115 QString comment = metaData.userComment(); 0116 if (!comment.isEmpty()) { 0117 properties.insert(Attribute::Comment, comment); 0118 } 0119 } 0120 0121 if (attributes & Attribute::OriginUrl) { 0122 const QString originUrl = metaData.originUrl().toDisplayString(); 0123 if (!originUrl.isEmpty()) { 0124 properties.insert(Attribute::OriginUrl, originUrl); 0125 } 0126 } 0127 0128 return properties; 0129 } 0130 0131 QVariantMap convertUserMetaData(const QMap<KFileMetaData::UserMetaData::Attribute, QVariant>& metaData) 0132 { 0133 using Attribute = KFileMetaData::UserMetaData::Attribute; 0134 0135 QVariantMap properties; 0136 for (auto it = metaData.begin(); it != metaData.end(); ++it) { 0137 if (it.key() == Attribute::Tags) { 0138 properties.insert(QStringLiteral("tags"), it.value()); 0139 0140 } else if (it.key() == Attribute::Rating) { 0141 properties.insert(QStringLiteral("rating"), it.value()); 0142 0143 } else if (it.key() == Attribute::Comment) { 0144 properties.insert(QStringLiteral("userComment"), it.value()); 0145 0146 } else if (it.key() == Attribute::OriginUrl) { 0147 properties.insert(QStringLiteral("originUrl"), it.value()); 0148 } 0149 } 0150 0151 return properties; 0152 } 0153 0154 QVariantMap convertUserMetaData(const KFileMetaData::UserMetaData &metaData) 0155 { 0156 using Attribute = KFileMetaData::UserMetaData::Attribute; 0157 0158 auto attributeData = fetchUserMetaData(metaData, Attribute::Tags | Attribute::Rating | Attribute::Comment | Attribute::OriginUrl); 0159 0160 return convertUserMetaData(attributeData); 0161 } 0162 0163 QVariantMap toNamedVariantMap(const KFileMetaData::PropertyMultiMap &propMap) 0164 { 0165 QVariantMap map; 0166 if (propMap.isEmpty()) { 0167 return map; 0168 } 0169 0170 using entry = std::pair<const KFileMetaData::Property::Property &, const QVariant &>; 0171 0172 auto begin = propMap.constKeyValueBegin(); 0173 0174 while (begin != propMap.constKeyValueEnd()) { 0175 auto key = (*begin).first; 0176 KFileMetaData::PropertyInfo property(key); 0177 auto rangeEnd = std::find_if(begin, propMap.constKeyValueEnd(), [key](const entry &e) { 0178 return e.first != key; 0179 }); 0180 0181 auto distance = std::distance(begin, rangeEnd); 0182 if (distance > 1) { 0183 QVariantList list; 0184 list.reserve(static_cast<int>(distance)); 0185 std::for_each(begin, rangeEnd, [&list](const entry &s) { 0186 list.append(s.second); 0187 }); 0188 map.insert(property.name(), list); 0189 } else { 0190 map.insert(property.name(), (*begin).second); 0191 } 0192 begin = rangeEnd; 0193 } 0194 0195 return map; 0196 } 0197 0198 void mergeCommonData(QVariantMap& target, const QList<QVariantMap> &files) 0199 { 0200 if (files.empty()) { 0201 target.clear(); 0202 return; 0203 } 0204 0205 if (files.size() == 1) { 0206 target = files[0]; 0207 return; 0208 } 0209 0210 // 0211 // Only report the stuff that is common to all the files 0212 // 0213 QList<QString> commonProperties = files[0].keys(); 0214 auto end = commonProperties.end(); 0215 for (const QVariantMap &fileData : files) { 0216 end = std::remove_if(commonProperties.begin(), end, 0217 [&fileData](const QString& prop) { return !fileData.contains(prop); } 0218 ); 0219 } 0220 commonProperties.erase(end, commonProperties.end()); 0221 0222 // Special handling for certain properties 0223 totalProperties(target, QStringLiteral("duration"), files, commonProperties); 0224 totalProperties(target, QStringLiteral("characterCount"), files, commonProperties); 0225 totalProperties(target, QStringLiteral("wordCount"), files, commonProperties); 0226 totalProperties(target, QStringLiteral("lineCount"), files, commonProperties); 0227 0228 for (const QString &propUri : std::as_const(commonProperties)) { 0229 QVariant value = files[0][propUri]; 0230 for (const QVariantMap &file : files) { 0231 QVariantMap::const_iterator it = file.find(propUri); 0232 Q_ASSERT(it != file.constEnd()); 0233 0234 value = intersect(it.value(), value); 0235 if (!value.isValid()) { 0236 break; 0237 } 0238 } 0239 0240 if (value.isValid()) 0241 target[propUri] = value; 0242 } 0243 } 0244 0245 QStringList sortTags(const QStringList &tags) 0246 { 0247 QCollator coll; 0248 coll.setNumericMode(true); 0249 QStringList sortedTags = tags; 0250 std::sort(sortedTags.begin(), sortedTags.end(), [&](const QString &s1, const QString &s2) { 0251 return coll.compare(s1, s2) < 0; 0252 }); 0253 return sortedTags; 0254 } 0255 0256 } // namespace KFMPrivate 0257 } // namespace Baloo