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
qqmlsignalnames.cpp
Go to the documentation of this file.
1// Copyright (C) 2023 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 <iterator>
7#include <algorithm>
8#include <optional>
9#include <string>
10
11QT_BEGIN_NAMESPACE
12
13using namespace Qt::Literals;
14
15static constexpr const QLatin1String On("on");
16static constexpr const QLatin1String Changed("Changed");
17
18static constexpr const qsizetype StrlenOn = On.length();
19static constexpr const qsizetype StrlenChanged = Changed.length();
20
21static std::optional<qsizetype> firstLetterIdx(QStringView name, qsizetype removePrefix = 0,
22 qsizetype removeSuffix = 0)
23{
24 auto end = std::prev(name.cend(), removeSuffix);
25 auto result = std::find_if(std::next(name.cbegin(), removePrefix), end,
26 [](const QChar &c) { return c.isLetter(); });
27 if (result != end)
28 return std::distance(name.begin(), result);
29
30 return {};
31}
32
33static std::optional<QChar> firstLetter(QStringView name, qsizetype removePrefix = 0,
34 qsizetype removeSuffix = 0)
35{
36 if (auto idx = firstLetterIdx(name, removePrefix, removeSuffix))
37 return name[*idx];
38 return {};
39}
40
42static void changeCaseOfFirstLetter(QString &name, ChangeCase option, qsizetype removePrefix = 0,
43 qsizetype removeSuffix = 0)
44{
45 auto idx = firstLetterIdx(name, removePrefix, removeSuffix);
46 if (!idx)
47 return;
48
49 QChar &changeMe = name[*idx];
50 changeMe = option == ToUpper ? changeMe.toUpper() : changeMe.toLower();
51};
52
53static std::optional<QString> toQStringData(std::optional<QStringView> view)
54{
55 if (view)
56 return view->toString();
57 return std::nullopt;
58}
59
60static QByteArray toUtf8Data(QUtf8StringView view)
61{
62 return QByteArray(view.data(), view.size());
63}
64
65static std::optional<QByteArray> toUtf8Data(std::optional<QUtf8StringView> view)
66{
67 if (view)
68 return toUtf8Data(*view);
69 return std::nullopt;
70}
71
72/*!
73\internal
74\class QQmlSignalNames
75
76QQmlSignalNames contains a list of helper methods to manipulate signal names.
77Always try to use the most specific one, as combining them might lead to incorrect
78results like wrong upper/lower case, for example.
79*/
80
81/*!
82\internal
83Concatenate a prefix to a property name and uppercases the first letter of the property name.
84*/
85QString QQmlSignalNames::addPrefixToPropertyName(QStringView prefix, QStringView propertyName)
86{
87 QString result = prefix.toString().append(propertyName);
88 changeCaseOfFirstLetter(result, ToUpper, prefix.size());
89 return result;
90}
91
92QString QQmlSignalNames::propertyNameToChangedSignalName(QStringView property)
93{
94 return property.toString().append(Changed);
95}
96
97QByteArray QQmlSignalNames::propertyNameToChangedSignalName(QUtf8StringView property)
98{
99 return toUtf8Data(property).append(QByteArrayView(Changed));
100}
101
102QString QQmlSignalNames::propertyNameToChangedHandlerName(QStringView property)
103{
104 return propertyNameToChangedSignalName(signalNameToHandlerName(property));
105}
106
107template<typename View>
108std::optional<View> changedSignalNameToPropertyNameTemplate(View changeSignal)
109{
110 const qsizetype changeSignalSize = changeSignal.size();
111 if (changeSignalSize < StrlenChanged || changeSignal.last(StrlenChanged).compare(Changed) != 0)
112 return std::nullopt;
113
114 const View result = changeSignal.sliced(0, changeSignalSize - StrlenChanged);
115 if (!result.isEmpty())
116 return result;
117
118 return {};
119}
120
121/*!
122\internal
123Obtain a propertyName from its changed signal handler.
124Do not call this on a value obtained from handlerNameToSignalName! Instead use
125changedHandlerNameToPropertyName() directly. Otherwise you might end up with a wrong
126capitalization of _Changed for "on_Changed", for example.
127*/
128
129std::optional<QString> QQmlSignalNames::changedSignalNameToPropertyName(QStringView signalName)
130{
131 return toQStringData(changedSignalNameToPropertyNameTemplate(signalName));
132}
133std::optional<QByteArray>
134QQmlSignalNames::changedSignalNameToPropertyName(QUtf8StringView signalName)
135{
136 return toUtf8Data(changedSignalNameToPropertyNameTemplate(signalName));
137}
138
139/*!
140\internal
141Returns a property name from \a changedHandler.
142This fails for property names starting with an upper-case letter, as it will lower-case it in the
143process.
144*/
145std::optional<QString> QQmlSignalNames::changedHandlerNameToPropertyName(QStringView handler)
146{
147 if (!isChangedHandlerName(handler))
148 return {};
149
150 if (auto withoutChangedSuffix = changedSignalNameToPropertyName(handler)) {
151 return handlerNameToSignalName(*withoutChangedSuffix);
152 }
153 return {};
154}
155
156QString QQmlSignalNames::signalNameToHandlerName(QAnyStringView signal)
157{
158 QString handlerName;
159 handlerName.reserve(StrlenOn + signal.length());
160 handlerName.append(On);
161
162 signal.visit([&handlerName](auto &&s) { handlerName.append(s); });
163
164 changeCaseOfFirstLetter(handlerName, ToUpper, StrlenOn);
165 return handlerName;
166}
167
169
170template<HandlerType type>
172{
173 if (!QQmlSignalNames::isHandlerName(handler))
174 return {};
175
176 QString signalName = handler.sliced(StrlenOn).toString();
177 Q_ASSERT(!signalName.isEmpty());
178
179 changeCaseOfFirstLetter(signalName, ToLower, 0, type == ChangedHandler ? StrlenChanged : 0);
180 return signalName;
181}
182
183/*!
184\internal
185Returns a signal name from \a handlerName string. Do not use it on changed handlers, see
186changedHandlerNameToSignalName for that!
187*/
188std::optional<QString> QQmlSignalNames::handlerNameToSignalName(QStringView handler)
189{
190 return handlerNameToSignalNameHelper<Handler>(handler);
191}
192
193/*!
194\internal
195Returns a signal name from \a handlerName string. Do not use it on changed handlers, see
196changedHandlerNameToSignalName for that! Accepts improperly capitalized handler names and
197incorrectly resolves signal names that start with '_' or '$'.
198*/
199std::optional<QString> QQmlSignalNames::badHandlerNameToSignalName(QStringView handler)
200{
201 if (handler.size() <= StrlenOn || !handler.startsWith(On))
202 return {};
203
204 QString signalName = handler.sliced(StrlenOn).toString();
205
206 // This is quite wrong. But we need it for backwards compatibility.
207 signalName.front() = signalName.front().toLower();
208
209 return signalName;
210}
211
212/*!
213\internal
214Returns a signal name from \a changedHandlerName string. Makes sure not to lowercase the 'C' from
215Changed.
216*/
217std::optional<QString> QQmlSignalNames::changedHandlerNameToSignalName(QStringView handler)
218{
219 return handlerNameToSignalNameHelper<ChangedHandler>(handler);
220}
221
222bool QQmlSignalNames::isChangedSignalName(QStringView signalName)
223{
224 if (signalName.size() <= StrlenChanged || !signalName.endsWith(Changed))
225 return false;
226
227 if (auto letter = firstLetter(signalName, 0, StrlenChanged))
228 return letter->isLower();
229
230 return true;
231}
232
233bool QQmlSignalNames::isChangedHandlerName(QStringView signalName)
234{
235 if (signalName.size() <= (StrlenOn + StrlenChanged)
236 || !signalName.startsWith(On)
237 || !signalName.endsWith(Changed)) {
238 return false;
239 }
240
241 if (auto letter = firstLetter(signalName, StrlenOn, StrlenChanged))
242 return letter->isUpper();
243
244 return true;
245}
246
247bool QQmlSignalNames::isHandlerName(QStringView signalName)
248{
249 if (signalName.size() <= StrlenOn || !signalName.startsWith(On))
250 return false;
251
252 if (auto letter = firstLetter(signalName, StrlenOn))
253 return letter->isUpper();
254
255 return true;
256}
257
258QT_END_NAMESPACE
static QByteArray toUtf8Data(QUtf8StringView view)
static constexpr const QLatin1String Changed("Changed")
static constexpr const QLatin1String On("on")
static std::optional< QChar > firstLetter(QStringView name, qsizetype removePrefix=0, qsizetype removeSuffix=0)
static void changeCaseOfFirstLetter(QString &name, ChangeCase option, qsizetype removePrefix=0, qsizetype removeSuffix=0)
static std::optional< QString > toQStringData(std::optional< QStringView > view)
@ ToLower
@ ToUpper
static std::optional< qsizetype > firstLetterIdx(QStringView name, qsizetype removePrefix=0, qsizetype removeSuffix=0)
static constexpr const qsizetype StrlenOn
static std::optional< QString > handlerNameToSignalNameHelper(QStringView handler)
@ Handler
@ ChangedHandler
static constexpr const qsizetype StrlenChanged
std::optional< View > changedSignalNameToPropertyNameTemplate(View changeSignal)