Qt
Internal/Contributor docs for the Qt SDK. Note: These are NOT official API docs; those are found at https://doc.qt.io/
Loading...
Searching...
No Matches
qqmldomcompare.cpp
Go to the documentation of this file.
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// Qt-Security score:significant
4
6#include "QtCore/qglobal.h"
7
9
10namespace QQmlJS {
11namespace Dom {
12
13bool domCompare(const DomItem &i1, const DomItem &i2, function_ref<bool(Path, const DomItem &, const DomItem &)> change,
14 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter,
15 Path basePath)
16{
17 DomKind k1 = i1.domKind();
18 DomKind k2 = i2.domKind();
19 if (k1 != k2) {
20 if (!change(basePath, i1, i2))
21 return false;
22 } else {
23 switch (k1) {
24 case DomKind::Empty:
25 return true;
26 case DomKind::Object: {
27 QStringList f1 = i1.fields();
28 QStringList f2 = i2.fields();
29 f1.sort();
30 f2.sort();
31 qsizetype it1 = 0;
32 qsizetype it2 = 0;
33 while (it1 != f1.size() || it2 != f2.size()) {
34 QString k1, k2;
35 DomItem el1, el2;
36 bool hasK1 = it1 != f1.size();
37 bool filt1 = hasK1;
38 if (hasK1) {
39 k1 = f1.at(it1);
40 el1 = i1.field(k1);
41 filt1 = i1.isCanonicalChild(el1) && filter(i1, PathEls::Field(k1), el1);
42 }
43 bool hasK2 = it2 != f2.size();
44 bool filt2 = hasK2;
45 if (hasK2) {
46 k2 = f2.at(it2);
47 el2 = i2.field(k2);
48 filt2 = i2.isCanonicalChild(el2) && filter(i2, PathEls::Field(k2), el2);
49 }
50 // continue when filtering out
51 if (hasK1 && !filt1) {
52 ++it1;
53 if (hasK2 && !filt2)
54 ++it2;
55 continue;
56 } else if (hasK2 && !filt2) {
57 ++it2;
58 continue;
59 }
60 if (filt1 && filt2 && k1 == k2) {
61 if (!domCompare(el1, el2, change, filter, basePath.withField(k1)))
62 return false;
63 ++it1;
64 ++it2;
65 } else if (!hasK1 || (hasK2 && k1 > k2)) {
66 if (!change(basePath.withField(k1), DomItem::empty, el2))
67 return false;
68 ++it2;
69 } else {
70 if (!change(basePath.withField(k1), el1, DomItem::empty))
71 return false;
72 ++it1;
73 }
74 }
75 } break;
76 case DomKind::Map: {
77 QStringList f1 = i1.sortedKeys();
78 QStringList f2 = i2.sortedKeys();
79 qsizetype it1 = 0;
80 qsizetype it2 = 0;
81 while (it1 != f1.size() || it2 != f2.size()) {
82 QString k1, k2;
83 DomItem el1, el2;
84 bool hasK1 = it1 != f1.size();
85 bool filt1 = hasK1;
86 if (hasK1) {
87 k1 = f1.at(it1);
88 el1 = i1.key(k1);
89 filt1 = i1.isCanonicalChild(el1) && filter(i1, PathEls::Key(k1), el1);
90 }
91 bool hasK2 = it2 != f2.size();
92 bool filt2 = hasK2;
93 if (hasK2) {
94 k2 = f2.at(it2);
95 el2 = i2.key(k2);
96 filt2 = i2.isCanonicalChild(el2) && filter(i2, PathEls::Key(k2), el2);
97 }
98 // continue when filtering out
99 if (hasK1 && !filt1) {
100 ++it1;
101 if (hasK2 && !filt2)
102 ++it2;
103 continue;
104 } else if (hasK2 && !filt2) {
105 ++it2;
106 continue;
107 }
108 if (filt1 && filt2 && k1 == k2) {
109 if (!domCompare(el1, el2, change, filter, basePath.withKey(k1)))
110 return false;
111 ++it1;
112 ++it2;
113 } else if (!hasK1 || (hasK2 && k1 > k2)) {
114 if (!change(basePath.withKey(k1), DomItem::empty, el2))
115 return false;
116 ++it2;
117 } else {
118 if (!change(basePath.withKey(k1), el1, DomItem::empty))
119 return false;
120 ++it1;
121 }
122 }
123 } break;
124 case DomKind::List: {
125 // we could support smarter matching keeping filtering in account like map
126 // currently it is just a simple index by index comparison
127 index_type len1 = i1.indexes();
128 index_type len2 = i2.indexes();
129 if (len1 != len2)
130 return change(basePath, i1, i2);
131 for (index_type i = 0; i < len1; ++i) {
132 DomItem v1 = i1.index(i);
133 DomItem v2 = i2.index(i);
134 if (filter(i1, PathEls::Index(i), v1) && filter(i2, PathEls::Index(i), v2)) {
135 DomItem el1 = i1.index(i);
136 DomItem el2 = i2.index(i);
137 if (i1.isCanonicalChild(el1) && i2.isCanonicalChild(el2)
138 && !domCompare(el1, el2, change, filter, basePath.withIndex(i)))
139 return false;
140 }
141 }
142 } break;
143 case DomKind::Value: {
144 QCborValue v1 = i1.value();
145 QCborValue v2 = i2.value();
146 if (v1 != v2)
147 return change(basePath, i1, i2);
148 } break;
149 case DomKind::ScriptElement: {
150 // TODO: implement me
151 return false;
152
153 } break;
154 }
155 }
156 return true;
157}
158
160domCompareStrList(const DomItem &i1, const DomItem &i2,
161 function_ref<bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const> filter,
162 DomCompareStrList stopAtFirstDiff)
163{
164 QStringList res;
165 bool hasDiff = false;
166 domCompare(
167 i1, i2,
168 [&res, &hasDiff, stopAtFirstDiff](const Path &p, const DomItem &j1, const DomItem &j2) {
169 hasDiff = true;
170 if (!j1) {
171 res.append(QStringLiteral("- %1\n").arg(p.toString()));
172 } else if (!j2) {
173 res.append(QStringLiteral("+ %1\n").arg(p.toString()));
174 } else {
175 DomKind k1 = j1.domKind();
176 DomKind k2 = j2.domKind();
177 if (k1 != k2) {
178 res.append(
179 QStringLiteral("- %1 %2\n").arg(p.toString(), domKindToString(k1)));
180 res.append(
181 QStringLiteral("+ %1 %2\n").arg(p.toString(), domKindToString(k2)));
182 } else {
183 switch (k1) {
184 case DomKind::Empty:
185 case DomKind::Object:
186 case DomKind::Map:
187 Q_ASSERT(false);
188 break;
189 case DomKind::List: {
190 index_type len1 = j1.indexes();
191 index_type len2 = j2.indexes();
192 res.append(QStringLiteral("- %1 #%2\n").arg(p.toString()).arg(len1));
193 res.append(QStringLiteral("+ %1 #%2\n").arg(p.toString()).arg(len2));
194 } break;
195 case DomKind::Value: {
196 QCborValue v1 = j1.value();
197 QCborValue v2 = j2.value();
198 auto t1 = v1.type();
199 auto t2 = v2.type();
200 if (t1 != t2) {
201 res.append(QStringLiteral("- %1 type(%2)\n")
202 .arg(p.toString())
203 .arg(int(t1)));
204 res.append(QStringLiteral("+ %1 type(%2)\n")
205 .arg(p.toString())
206 .arg(int(t2)));
207 } else {
208 res.append(QStringLiteral("- %1 value(%2)\n")
209 .arg(p.toString())
210 .arg(j1.toString()));
211 res.append(QStringLiteral("+ %1 value(%2)\n")
212 .arg(p.toString())
213 .arg(j2.toString()));
214 }
215 } break;
216 case DomKind::ScriptElement: {
217 // implement me
218 break;
219 }
220 }
221 }
222 }
223 return (stopAtFirstDiff == DomCompareStrList::AllDiffs);
224 },
225 filter);
226 if (hasDiff && res.isEmpty()) // should never happen
227 res.append(QStringLiteral(u"Had changes!"));
228 return res;
229}
230
231} // end namespace Dom
232} // end namespace QQmlJS
233
234QT_END_NAMESPACE
A value type that references any element of the Dom.
bool isCanonicalChild(const DomItem &child) const
static DomItem empty
bool domCompare(const DomItem &i1, const DomItem &i2, function_ref< bool(Path, const DomItem &, const DomItem &)> change, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &)> filter, Path basePath)
QStringList domCompareStrList(const DomItem &i1, const DomItem &i2, function_ref< bool(const DomItem &, const PathEls::PathComponent &, const DomItem &) const > filter, DomCompareStrList stopAtFirstDiff)