1// Copyright (C) 2020 The Qt Company Ltd.
2// SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only
3
4#include "qqmldomfieldfilter_p.h"
5#include "qqmldompath_p.h"
6#include "qqmldomitem_p.h"
7#include "QtCore/qglobal.h"
8
9QT_BEGIN_NAMESPACE
10
11namespace QQmlJS {
12namespace Dom {
13
14/*!
15\internal
16\class QQmljs::Dom::FieldFilter
17
18\brief Class that represent a filter on DomItem, when dumping or comparing
19
20DomItem can be duped or compared, but often one is interested only in a subset
21of them, FieldFilter is a simple way to select a subset of them.
22It uses two basic elements: the type of the object (internalKind) and the
23name of fields.
24
25A basic filter can be represented by <op><typeName>:<fieldName> or <op><fieldName>
26where op is either + or - (if the matching elements should be added or removed)
27Both typeName and fieldName can be the empty string (meaning any value matches).
28
29Basic filters are ordered from the most specific to the least specific as follow:
30type+field > type > field > empty.
31When combining several filters the most specific always wins, so
32-code,+ScriptExpression:code is the same as +ScriptExpression:code,-code and means
33that normally the field code is not outputted but for a ScriptExpression DomItem
34it is.
35
36It is possible to get the string representation of the current filter with
37FieldFilter::describeFieldsFilter(), and change the current filter with
38FieldFilter::addFilter(), but after it one should call FieldFilter::setFiltred()
39to ensure that the internal cache used to speed up comparisons is correct.
40*/
41
42QString FieldFilter::describeFieldsFilter() const
43{
44 QString fieldFilterStr;
45 {
46 auto it = m_fieldFilterRemove.begin();
47 while (it != m_fieldFilterRemove.end()) {
48 if (!fieldFilterStr.isEmpty())
49 fieldFilterStr.append(v: u",");
50 fieldFilterStr.append(s: QLatin1String("-%1:%2").arg(args: it.key(), args: it.value()));
51 ++it;
52 }
53 }
54 {
55 auto it = m_fieldFilterAdd.begin();
56 while (it != m_fieldFilterAdd.end()) {
57 if (!fieldFilterStr.isEmpty())
58 fieldFilterStr.append(v: u",");
59 fieldFilterStr.append(s: QLatin1String("+%1:%2").arg(args: it.key(), args: it.value()));
60 ++it;
61 }
62 }
63 return fieldFilterStr;
64}
65
66bool FieldFilter::operator()(const DomItem &obj, const Path &p, const DomItem &i) const
67{
68 if (p)
69 return this->operator()(obj, c: p.component(i: 0), i);
70 else
71 return this->operator()(obj, c: PathEls::Empty(), i);
72}
73
74bool FieldFilter::operator()(const DomItem &base, const PathEls::PathComponent &c, const DomItem &obj) const
75{
76 DomType baseK = base.internalKind();
77 if (c.kind() == Path::Kind::Field) {
78 DomType objK = obj.internalKind();
79 if (!m_filtredTypes.contains(value: baseK) && !m_filtredTypes.contains(value: objK)
80 && !m_filtredFields.contains(value: qHash(key: c.stringView())))
81 return m_filtredDefault;
82 QString typeStr = domTypeToString(k: baseK);
83 QList<QString> tVals = m_fieldFilterRemove.values(key: typeStr);
84 QString name = c.name();
85 if (tVals.contains(str: name))
86 return false;
87 if (tVals.contains(str: QString())
88 || m_fieldFilterRemove.values(key: domTypeToString(k: objK)).contains(str: QString())
89 || m_fieldFilterRemove.values(key: QString()).contains(str: name)) {
90 return m_fieldFilterAdd.values(key: typeStr).contains(str: name);
91 }
92 } else if (m_filtredTypes.contains(value: baseK)) {
93 QString typeStr = domTypeToString(k: baseK);
94 QList<QString> tVals = m_fieldFilterRemove.values(key: typeStr);
95 return !tVals.contains(str: QString());
96 }
97 return true;
98}
99
100bool FieldFilter::addFilter(const QString &fFields)
101{
102 // parses a base filter of the form <op><typeName>:<fieldName> or <op><fieldName>
103 // as described in this class documentation
104 QRegularExpression fieldRe(QRegularExpression::anchoredPattern(QStringLiteral(
105 uR"((?<op>[-+])?(?:(?<type>[a-zA-Z0-9_]*):)?(?<field>[a-zA-Z0-9_]*))")));
106 for (const QString &fField : fFields.split(sep: QLatin1Char(','))) {
107 QRegularExpressionMatch m = fieldRe.matchView(subjectView: fField);
108 if (m.hasMatch()) {
109 if (m.capturedView(name: u"op") == u"+") {
110 m_fieldFilterRemove.remove(key: m.captured(name: u"type"), value: m.captured(name: u"field"));
111 m_fieldFilterAdd.insert(key: m.captured(name: u"type"), value: m.captured(name: u"field"));
112 } else {
113 m_fieldFilterRemove.insert(key: m.captured(name: u"type"), value: m.captured(name: u"field"));
114 m_fieldFilterAdd.remove(key: m.captured(name: u"type"), value: m.captured(name: u"field"));
115 }
116 } else {
117 qCWarning(domLog) << "could not extract filter from" << fField;
118 return false;
119 }
120 }
121 return true;
122}
123
124FieldFilter FieldFilter::noFilter()
125{
126 return FieldFilter{ {}, {} };
127}
128
129FieldFilter FieldFilter::defaultFilter()
130{
131 QMultiMap<QString, QString> fieldFilterAdd { { QLatin1String("ScriptExpression"),
132 QLatin1String("code") } };
133 QMultiMap<QString, QString> fieldFilterRemove {
134 { QString(), QString::fromUtf16(Fields::code) },
135 { QString(), QString::fromUtf16(Fields::postCode) },
136 { QString(), QString::fromUtf16(Fields::preCode) },
137 { QString(), QString::fromUtf16(Fields::importScope) },
138 { QString(), QString::fromUtf16(Fields::fileLocationsTree) },
139 { QString(), QString::fromUtf16(Fields::astComments) },
140 { QString(), QString::fromUtf16(Fields::comments) },
141 { QString(), QString::fromUtf16(Fields::exports) },
142 { QString(), QString::fromUtf16(Fields::propertyInfos) },
143 { QLatin1String("AttachedInfo"), QString::fromUtf16(Fields::parent) }
144 };
145 return FieldFilter { fieldFilterAdd, fieldFilterRemove };
146}
147
148QQmlJS::Dom::FieldFilter QQmlJS::Dom::FieldFilter::noLocationFilter()
149{
150 QMultiMap<QString, QString> fieldFilterAdd {};
151 QMultiMap<QString, QString> fieldFilterRemove {
152 { QString(), QLatin1String("code") },
153 { QString(), QLatin1String("propertyInfos") },
154 { QString(), QLatin1String("fileLocationsTree") },
155 { QString(), QLatin1String("location") },
156 { QLatin1String("ScriptExpression"), QLatin1String("localOffset") },
157 { QLatin1String("ScriptExpression"), QLatin1String("preCode") },
158 { QLatin1String("ScriptExpression"), QLatin1String("postCode") },
159 { QLatin1String("AttachedInfo"), QLatin1String("parent") },
160 { QLatin1String("Reference"), QLatin1String("get") },
161 { QLatin1String("QmlComponent"), QLatin1String("ids") },
162 { QLatin1String("QmlObject"), QLatin1String("prototypes") }
163 };
164 return FieldFilter { fieldFilterAdd, fieldFilterRemove };
165}
166
167FieldFilter FieldFilter::compareFilter()
168{
169 QMultiMap<QString, QString> fieldFilterAdd {};
170 QMultiMap<QString, QString> fieldFilterRemove {
171 { QString(), QLatin1String("propertyInfos") },
172 { QLatin1String("ScriptExpression"), QLatin1String("localOffset") },
173 { QLatin1String("FileLocations"), QLatin1String("regions") },
174 { QLatin1String("AttachedInfo"), QLatin1String("parent") },
175 { QLatin1String("QmlComponent"), QLatin1String("ids") },
176 { QLatin1String("QmlObject"), QLatin1String("prototypes") },
177 { QLatin1String("Reference"), QLatin1String("get") }
178 };
179 return FieldFilter { fieldFilterAdd, fieldFilterRemove };
180}
181
182FieldFilter FieldFilter::compareNoCommentsFilter()
183{
184 QMultiMap<QString, QString> fieldFilterAdd {};
185 QMultiMap<QString, QString> fieldFilterRemove {
186 { QString(), QLatin1String("propertyInfos") },
187 { QLatin1String("FileLocations"), QLatin1String("regions") },
188 { QLatin1String("Reference"), QLatin1String("get") },
189 { QLatin1String("QmlComponent"), QLatin1String("ids") },
190 { QLatin1String("QmlObject"), QLatin1String("prototypes") },
191 { QLatin1String(), QLatin1String("code") },
192 { QLatin1String("ScriptExpression"), QLatin1String("localOffset") },
193 { QLatin1String("AttachedInfo"), QLatin1String("parent") },
194 { QString(), QLatin1String("fileLocationsTree") },
195 { QString(), QLatin1String("preCode") },
196 { QString(), QLatin1String("postCode") },
197 { QString(), QLatin1String("comments") },
198 { QString(), QLatin1String("preCommentLocations") },
199 { QString(), QLatin1String("postCommentLocations") },
200 { QString(), QLatin1String("astComments") },
201 { QString(), QLatin1String("location") }
202 };
203 return FieldFilter { fieldFilterAdd, fieldFilterRemove };
204}
205
206void FieldFilter::setFiltred()
207{
208 auto types = domTypeToStringMap();
209 QSet<QString> filtredFieldStrs;
210 QSet<QString> filtredTypeStrs;
211 static QHash<QString, DomType> fieldToId = []() {
212 QHash<QString, DomType> res;
213 auto reverseMap = domTypeToStringMap();
214 auto it = reverseMap.cbegin();
215 auto end = reverseMap.cend();
216 while (it != end) {
217 res[it.value()] = it.key();
218 ++it;
219 }
220 return res;
221 }();
222 auto addFilteredOfMap = [&](const QMultiMap<QString, QString> &map) {
223 auto it = map.cbegin();
224 auto end = map.cend();
225 while (it != end) {
226 filtredTypeStrs.insert(value: it.key());
227 ++it;
228 }
229 for (auto f : map.values(key: QString()))
230 filtredFieldStrs.insert(value: f);
231 };
232 addFilteredOfMap(m_fieldFilterAdd);
233 addFilteredOfMap(m_fieldFilterRemove);
234 m_filtredDefault = true;
235 if (m_fieldFilterRemove.values(key: QString()).contains(str: QString()))
236 m_filtredDefault = false;
237 m_filtredFields.clear();
238 for (auto s : filtredFieldStrs)
239 if (!s.isEmpty())
240 m_filtredFields.insert(value: qHash(key: QStringView(s)));
241 m_filtredTypes.clear();
242 for (auto s : filtredTypeStrs) {
243 if (s.isEmpty())
244 continue;
245 if (fieldToId.contains(key: s)) {
246 m_filtredTypes.insert(value: fieldToId.value(key: s));
247 } else {
248 qCWarning(domLog) << "Filter on unknown type " << s << " will be ignored";
249 }
250 }
251}
252
253} // end namespace Dom
254} // end namespace QQmlJS
255
256QT_END_NAMESPACE
257
258#include "moc_qqmldomfieldfilter_p.cpp"
259

Provided by KDAB

Privacy Policy
Learn Advanced QML with KDAB
Find out more

source code of qtdeclarative/src/qmldom/qqmldomfieldfilter.cpp