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

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