Baloo Widgets

filemetadatautil.cpp
1/*
2 SPDX-FileCopyrightText: 2019 Stefan BrĂ¼ns <stefan.bruens@rwth-aachen.de>
3
4 SPDX-License-Identifier: LGPL-2.0-or-later
5*/
6
7#include "filemetadatautil_p.h"
8#include <KFileMetaData/PropertyInfo>
9#include <KFileMetaData/UserMetaData>
10#include <QSet>
11
12#include <QCollator>
13#include <QString>
14#include <QStringList>
15
16#include <algorithm>
17
18namespace
19{
20QVariant intersect(const QVariant &v1, const QVariant &v2)
21{
22 if (!v1.isValid() || !v2.isValid()) {
23 return {};
24 }
25
26 // List and String
27 if (v1.type() == QVariant::StringList && v2.type() == QVariant::String) {
29 QString str = v2.toString();
30
31 if (!list.contains(str)) {
32 list << str;
33 }
34
35 return QVariant(list);
36 }
37
38 // String and List
39 if (v1.type() == QVariant::String && v2.type() == QVariant::StringList) {
41 QString str = v1.toString();
42
43 if (!list.contains(str)) {
44 list << str;
45 }
46
47 return QVariant(list);
48 }
49
50 // List and List
51 if (v1.type() == QVariant::StringList && v2.type() == QVariant::StringList) {
54
55 return QVariant(s1.intersect(s2).values());
56 }
57
58 if (v1 == v2) {
59 return v1;
60 }
61
62 return {};
63}
64
65// Precondition:
66// if commonProperties contains <prop>, all <files> must also provide <prop>
67void totalProperties(QVariantMap& target, const QString &prop, const QList<QVariantMap> &files, QList<QString> &commonProperties)
68{
69 auto propIndex = commonProperties.indexOf(prop);
70
71 if (propIndex >= 0) {
72 int total = 0;
73 for (const QVariantMap &file : files) {
74 QVariantMap::const_iterator it = file.constFind(prop);
75 Q_ASSERT(it != file.constEnd());
76
77 total += it.value().toInt();
78 }
79
80 target.insert(prop, QVariant(total));
81
82 commonProperties.removeAt(propIndex);
83 }
84}
85} // anonymous namespace
86
87namespace Baloo
88{
89namespace Private
90{
92fetchUserMetaData(const KFileMetaData::UserMetaData &metaData, KFileMetaData::UserMetaData::Attributes wantedAttributes)
93{
94 using Attribute = KFileMetaData::UserMetaData::Attribute;
95
96 auto attributes = metaData.queryAttributes(wantedAttributes);
97
99
100 if (attributes & Attribute::Tags) {
101 QStringList tags = metaData.tags();
102 if (!tags.isEmpty()) {
103 properties.insert(Attribute::Tags, tags);
104 }
105 }
106
107 if (attributes & Attribute::Rating) {
108 int rating = metaData.rating();
109 if (rating) {
110 properties.insert(Attribute::Rating, rating);
111 }
112 }
113
114 if (attributes & Attribute::Comment) {
115 QString comment = metaData.userComment();
116 if (!comment.isEmpty()) {
117 properties.insert(Attribute::Comment, comment);
118 }
119 }
120
121 if (attributes & Attribute::OriginUrl) {
122 const QString originUrl = metaData.originUrl().toDisplayString();
123 if (!originUrl.isEmpty()) {
124 properties.insert(Attribute::OriginUrl, originUrl);
125 }
126 }
127
128 return properties;
129}
130
131QVariantMap convertUserMetaData(const QMap<KFileMetaData::UserMetaData::Attribute, QVariant>& metaData)
132{
133 using Attribute = KFileMetaData::UserMetaData::Attribute;
134
135 QVariantMap properties;
136 for (auto it = metaData.begin(); it != metaData.end(); ++it) {
137 if (it.key() == Attribute::Tags) {
138 properties.insert(QStringLiteral("tags"), it.value());
139
140 } else if (it.key() == Attribute::Rating) {
141 properties.insert(QStringLiteral("rating"), it.value());
142
143 } else if (it.key() == Attribute::Comment) {
144 properties.insert(QStringLiteral("userComment"), it.value());
145
146 } else if (it.key() == Attribute::OriginUrl) {
147 properties.insert(QStringLiteral("originUrl"), it.value());
148 }
149 }
150
151 return properties;
152}
153
154QVariantMap convertUserMetaData(const KFileMetaData::UserMetaData &metaData)
155{
156 using Attribute = KFileMetaData::UserMetaData::Attribute;
157
158 auto attributeData = fetchUserMetaData(metaData, Attribute::Tags | Attribute::Rating | Attribute::Comment | Attribute::OriginUrl);
159
160 return convertUserMetaData(attributeData);
161}
162
163QVariantMap toNamedVariantMap(const KFileMetaData::PropertyMultiMap &propMap)
164{
165 QVariantMap map;
166 if (propMap.isEmpty()) {
167 return map;
168 }
169
170 using entry = std::pair<const KFileMetaData::Property::Property &, const QVariant &>;
171
172 auto begin = propMap.constKeyValueBegin();
173
174 while (begin != propMap.constKeyValueEnd()) {
175 auto key = (*begin).first;
176 KFileMetaData::PropertyInfo property(key);
177 auto rangeEnd = std::find_if(begin, propMap.constKeyValueEnd(), [key](const entry &e) {
178 return e.first != key;
179 });
180
181 auto distance = std::distance(begin, rangeEnd);
182 if (distance > 1) {
183 QVariantList list;
184 list.reserve(static_cast<int>(distance));
185 std::for_each(begin, rangeEnd, [&list](const entry &s) {
186 list.append(s.second);
187 });
188 map.insert(property.name(), list);
189 } else {
190 map.insert(property.name(), (*begin).second);
191 }
192 begin = rangeEnd;
193 }
194
195 return map;
196}
197
198void mergeCommonData(QVariantMap& target, const QList<QVariantMap> &files)
199{
200 if (files.empty()) {
201 target.clear();
202 return;
203 }
204
205 if (files.size() == 1) {
206 target = files[0];
207 return;
208 }
209
210 //
211 // Only report the stuff that is common to all the files
212 //
213 QList<QString> commonProperties = files[0].keys();
214 auto end = commonProperties.end();
215 for (const QVariantMap &fileData : files) {
216 end = std::remove_if(commonProperties.begin(), end,
217 [&fileData](const QString& prop) { return !fileData.contains(prop); }
218 );
219 }
220 commonProperties.erase(end, commonProperties.end());
221
222 // Special handling for certain properties
223 totalProperties(target, QStringLiteral("duration"), files, commonProperties);
224 totalProperties(target, QStringLiteral("characterCount"), files, commonProperties);
225 totalProperties(target, QStringLiteral("wordCount"), files, commonProperties);
226 totalProperties(target, QStringLiteral("lineCount"), files, commonProperties);
227
228 for (const QString &propUri : std::as_const(commonProperties)) {
229 QVariant value = files[0][propUri];
230 for (const QVariantMap &file : files) {
231 QVariantMap::const_iterator it = file.find(propUri);
232 Q_ASSERT(it != file.constEnd());
233
234 value = intersect(it.value(), value);
235 if (!value.isValid()) {
236 break;
237 }
238 }
239
240 if (value.isValid())
241 target[propUri] = value;
242 }
243}
244
245QStringList sortTags(const QStringList &tags)
246{
247 QCollator coll;
248 coll.setNumericMode(true);
249 QStringList sortedTags = tags;
250 std::sort(sortedTags.begin(), sortedTags.end(), [&](const QString &s1, const QString &s2) {
251 return coll.compare(s1, s2) < 0;
252 });
253 return sortedTags;
254}
255
256} // namespace KFMPrivate
257} // namespace Baloo
KIOCORE_EXPORT QStringList list(const QString &fileClass)
KGuiItem properties()
const QList< QKeySequence > & begin()
const QList< QKeySequence > & end()
KOSM_EXPORT double distance(const std::vector< const OSM::Node * > &path, Coordinate coord)
void setNumericMode(bool on)
void append(QList< T > &&value)
iterator begin()
const_iterator cbegin() const const
const_iterator cend() const const
bool empty() const const
iterator end()
iterator erase(const_iterator begin, const_iterator end)
qsizetype indexOf(const AT &value, qsizetype from) const const
bool isEmpty() const const
void removeAt(qsizetype i)
void reserve(qsizetype size)
qsizetype size() const const
iterator begin()
iterator end()
bool isEmpty() const const
bool contains(QLatin1StringView str, Qt::CaseSensitivity cs) const const
QFuture< void > map(Iterator begin, Iterator end, MapFunctor &&function)
Type type() const const
bool isValid() const const
QString toString() const const
QStringList toStringList() const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:20:21 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.