Line data Source code
1 : // SPDX-FileCopyrightText: 2013-2025 Paul Colby <git@colby.id.au>
2 : // SPDX-License-Identifier: LGPL-3.0-or-later
3 :
4 : /*!
5 : * \file
6 : * Defines the ShapeId and ShapeIdPrivate classes.
7 : */
8 :
9 : #include <qtsmithy/shapeid.h>
10 : #include "shapeid_p.h"
11 :
12 : #include <QRegularExpression>
13 :
14 : QTSMITHY_BEGIN_NAMESPACE
15 :
16 : /*!
17 : * \class ShapeId
18 : *
19 : * The ShapeId class provides a Qt representation of a Smithy Shape ID.
20 : *
21 : * \see https://awslabs.github.io/smithy/2.0/spec/model.html#shape-id
22 : */
23 :
24 : /*!
25 : * Constructs an empty ShapeId object.
26 : */
27 9372 : ShapeId::ShapeId() : d_ptr(new ShapeIdPrivate(this))
28 7656 : {
29 :
30 14124 : }
31 :
32 : /*!
33 : * Constructs a ShapeId object by moving resources from \a other.
34 : */
35 852 : ShapeId::ShapeId(ShapeId &&other) : d_ptr(new ShapeIdPrivate(this))
36 696 : {
37 696 : Q_D(ShapeId);
38 1284 : d->memberName = std::move(other.d_ptr->memberName);
39 696 : d->nameSpace = std::move(other.d_ptr->nameSpace);
40 696 : d->shapeName = std::move(other.d_ptr->shapeName);
41 1284 : }
42 :
43 : /*!
44 : * Constructs a ShapeId object by copying \a other.
45 : */
46 852 : ShapeId::ShapeId(const ShapeId &other) : d_ptr(new ShapeIdPrivate(this))
47 696 : {
48 696 : Q_D(ShapeId);
49 1284 : d->memberName = other.d_ptr->memberName;
50 1284 : d->nameSpace = other.d_ptr->nameSpace;
51 1284 : d->shapeName = other.d_ptr->shapeName;
52 1284 : }
53 :
54 : /*!
55 : * Constructs a ShapeId object by parsing the Smithy Shape ID given by \a shapeId.
56 : *
57 : * To be considered valid, \a shapeId must contain at least a valid shape name, but may also contain
58 : * optional namespace and member name components. Use isValid() to verify \a shapeId's validity.
59 : *
60 : * \see isValid
61 : * \see https://awslabs.github.io/smithy/2.0/spec/model.html#shape-id
62 : */
63 13916 : ShapeId::ShapeId(const QString &shapeId) : d_ptr(new ShapeIdPrivate(this))
64 11368 : {
65 11368 : Q_D(ShapeId);
66 20972 : d->setShapeId(shapeId);
67 20972 : }
68 :
69 : /*!
70 : * Assigns the specified \a shapeId to this object.
71 : */
72 588 : ShapeId& ShapeId::operator=(const ShapeId &shapeId)
73 696 : {
74 696 : Q_D(ShapeId);
75 1284 : d->memberName = shapeId.d_ptr->memberName;
76 1284 : d->nameSpace = shapeId.d_ptr->nameSpace;
77 1284 : d->shapeName = shapeId.d_ptr->shapeName;
78 1284 : return *this;
79 696 : }
80 :
81 : /*!
82 : * Moves the specified \a shapeId to this object.
83 : */
84 588 : ShapeId& ShapeId::operator=(const ShapeId &&shapeId)
85 696 : {
86 696 : Q_D(ShapeId);
87 1284 : d->memberName = std::move(shapeId.d_ptr->memberName);
88 696 : d->nameSpace = std::move(shapeId.d_ptr->nameSpace);
89 696 : d->shapeName = std::move(shapeId.d_ptr->shapeName);
90 1284 : return *this;
91 696 : }
92 :
93 : /*!
94 : * Assigns the specified \a shapeId to this object.
95 : */
96 588 : ShapeId& ShapeId::operator=(const QString &shapeId)
97 696 : {
98 696 : Q_D(ShapeId);
99 1284 : d->setShapeId(shapeId);
100 1284 : return *this;
101 696 : }
102 :
103 : /*!
104 : * Destroys this ShapeId object.
105 : */
106 17248 : ShapeId::~ShapeId()
107 20416 : {
108 37664 : delete d_ptr;
109 37664 : }
110 :
111 : /*!
112 : * Returns the Shape ID's member name, if it has one, otherwise a null string.
113 : */
114 12322 : QString ShapeId::memberName() const
115 17110 : {
116 17110 : Q_D(const ShapeId);
117 29432 : return d->memberName;
118 17110 : }
119 :
120 : /*!
121 : * Returns the Shape ID's namespace, if it has one, otherwise a null string.
122 : */
123 12212 : QString ShapeId::nameSpace() const
124 16820 : {
125 16820 : Q_D(const ShapeId);
126 29032 : return d->nameSpace;
127 16820 : }
128 :
129 : /*!
130 : * Returns the Shape ID's shape name, if it has one, otherwise a null string.
131 : *
132 : * Note, a Shape ID is considered invalid if it has no shape name.
133 : *
134 : * \see isValid
135 : */
136 11266 : QString ShapeId::shapeName() const
137 14326 : {
138 14326 : Q_D(const ShapeId);
139 23337 : return d->shapeName;
140 14326 : }
141 :
142 : /*!
143 : * Set the Shape ID's member name to \a name, which may be an empty or null string.
144 : */
145 588 : void ShapeId::setMemberName(const QString &name)
146 696 : {
147 696 : Q_D(ShapeId);
148 1284 : d->memberName = name;
149 1284 : }
150 :
151 : /*!
152 : * Set the Shape ID's namespace to \a name, which may be an empty or null string.
153 : */
154 588 : void ShapeId::setNameSpace(const QString &name)
155 696 : {
156 696 : Q_D(ShapeId);
157 1284 : d->nameSpace = name;
158 1284 : }
159 :
160 : /*!
161 : * Set the Shape ID's shape name to \a name.
162 : *
163 : * Note, a Shape ID is considered invalid if it has no shape name, so \a name should typically be
164 : * non-empty.
165 : */
166 588 : void ShapeId::setShapeName(const QString &name)
167 696 : {
168 696 : Q_D(ShapeId);
169 1284 : d->shapeName = name;
170 1284 : }
171 :
172 : /*!
173 : * Returns this object as an absolute Smithy Shape ID if this object has a namespace, otherwise a
174 : * null string.
175 : *
176 : * \note, Smithy defines an absolute Shape ID as one that begins with a namespace, therefore it is
177 : * not possible to return an absolute Shape ID if no namespace has been set.
178 : *
179 : * \note, if the Shape ID is invalid (ie isValid() returns \c false) it still safe to invoke this
180 : * method, but the result is undefined.
181 : *
182 : * \see setNameSpace
183 : * \see isValid
184 : */
185 931 : QString ShapeId::absoluteShapeId() const
186 1102 : {
187 3818 : return hasNameSpace() ? QStringLiteral("%1#%2").arg(nameSpace(), relativeShapeId()) : QString();
188 1102 : }
189 :
190 : /*!
191 : * Returns this object as a relative Smithy Shape ID, that one without a leading namespace.
192 : *
193 : * \note, if the Shape ID is invalid (ie isValid() returns \c false) it still safe to invoke this
194 : * method, but the result is undefined.
195 : *
196 : * \see isValid
197 : */
198 1519 : QString ShapeId::relativeShapeId() const
199 1798 : {
200 5814 : return hasMemberName() ? QStringLiteral("%1$%2").arg(shapeName(), memberName()) : shapeName();
201 1798 : }
202 :
203 : /*!
204 : * Returns this object as an *absolute* Smithy Shape ID if this object has a namespace, otherwise a
205 : * *relative* Smithy Shape ID.
206 : *
207 : * \note, if the Shape ID is invalid (ie isValid() returns \c false) it still safe to invoke this
208 : * method, but the result is undefined.
209 : *
210 : * \see absoluteShapeId
211 : * \see relativeShapeId
212 : * \see isValid
213 : */
214 588 : QString ShapeId::toString() const
215 696 : {
216 1284 : return hasNameSpace() ? absoluteShapeId() : relativeShapeId();
217 696 : }
218 :
219 : /*!
220 : * Returns \c true if this Shape ID has a non-empty namespace, otherwise \c false otherwise.
221 : *
222 : * \see nameSpace.
223 : * \see setNameSpace.
224 : */
225 2940 : bool ShapeId::hasNameSpace() const
226 3480 : {
227 6420 : return !nameSpace().isEmpty();
228 3480 : }
229 :
230 : /*!
231 : * Returns \c true if this Shape ID has a non-empty member name, otherwise \c false otherwise.
232 : *
233 : * \see memberName.
234 : * \see setMemberName.
235 : */
236 2891 : bool ShapeId::hasMemberName() const
237 3422 : {
238 6313 : return !memberName().isEmpty();
239 3422 : }
240 :
241 : /*!
242 : * Returns \c true if this Shape ID is a *root* Shape ID, and has a namespace, \c false otherwise.
243 : *
244 : * \see isRootShapeId.
245 : * \see hasNameSpace.
246 : */
247 588 : bool ShapeId::isAbsoluteRootShapeId() const
248 696 : {
249 1284 : return isRootShapeId() && hasNameSpace();
250 696 : }
251 :
252 : /*!
253 : * Returns \c true if this Shape ID is a *root* Shape ID, \c false otherwise.
254 : *
255 : * \note, Smithy defines a root Shape ID as one that does not have a member name.
256 : *
257 : * \see hasMemberName.
258 : */
259 460 : bool ShapeId::isRootShapeId() const
260 928 : {
261 1712 : return !hasMemberName();
262 928 : }
263 :
264 : /*!
265 : * Returns \c true if this Shape ID is a *relative* Shape ID, \c false otherwise.
266 : *
267 : * \note, Smithy defines a relative Shape ID as one that does not have a namespace.
268 : *
269 : * \see hasNameSpace.
270 : */
271 588 : bool ShapeId::isRelativeShapeId() const
272 696 : {
273 1284 : return !hasNameSpace();
274 696 : }
275 :
276 : /*!
277 : * Returns true if this object represents a valid, non-empty Smithy Shape ID.
278 : *
279 : * \see https://awslabs.github.io/smithy/2.0/spec/model.html#shape-id
280 : */
281 8820 : bool ShapeId::isValid() const
282 10440 : {
283 10440 : Q_D(const ShapeId);
284 : // Validate the (optional) namespace.
285 19260 : if (!d->nameSpace.isEmpty()) {
286 8268 : static QRegularExpression namespacePattern(QStringLiteral("^(_*[a-zA-Z]\\w*\\.?)+(?<!\\.)$"));
287 4466 : Q_ASSERT(namespacePattern.isValid());
288 8239 : if (!namespacePattern.match(d->nameSpace).hasMatch()) {
289 1276 : return false;
290 1276 : }
291 4466 : }
292 :
293 : // Validate the (required) shape name.
294 16935 : static QRegularExpression identifierPattern(QStringLiteral("^_*[a-zA-Z]\\w*$"));
295 9164 : Q_ASSERT(identifierPattern.isValid());
296 19601 : if ((d->shapeName.isEmpty()) || (!identifierPattern.match(d->shapeName).hasMatch())) {
297 5974 : return false;
298 5974 : }
299 :
300 : // Validate the (optional) member name.
301 6424 : if ((!d->memberName.isEmpty()) && (!identifierPattern.match(d->memberName).hasMatch())) {
302 638 : return false;
303 638 : }
304 2552 : return true; // Valid.
305 3190 : }
306 :
307 0 : bool ShapeId::operator==(const ShapeId &other) const
308 0 : {
309 0 : Q_D(const ShapeId);
310 0 : return (d->memberName == other.d_ptr->memberName) &&
311 0 : (d->nameSpace == other.d_ptr->nameSpace) &&
312 0 : (d->shapeName == other.d_ptr->shapeName);
313 0 : }
314 :
315 : #if (QT_VERSION < QT_VERSION_CHECK(6, 0, 0))
316 0 : uint qHash(const ShapeId &key, uint seed)
317 : #else
318 0 : size_t qHash(const ShapeId &key, size_t seed)
319 : #endif
320 0 : {
321 0 : return ::qHash(key.toString(), seed);
322 0 : }
323 :
324 : /*!
325 : * \cond internal
326 : * \class ShapeIdPrivate
327 : *
328 : * The ShapeIdPrivate class provides private implementation for ShapeId.
329 : */
330 :
331 : /*!
332 : * \internal
333 : * Constructs a new ShapeIdPrivate object with public implementation \a q.
334 : */
335 21104 : ShapeIdPrivate::ShapeIdPrivate(ShapeId * const q) : q_ptr(q)
336 20416 : {
337 :
338 28160 : }
339 :
340 : /*!
341 : * Splits \a shapeId into its components (namespace, shape name and member name) and assigns them
342 : * to the equivalent object members.
343 : *
344 : * Both the namespace and member name are optional; their equivalent object members will be set to
345 : * empty strings if not present in \a shapeId.
346 : */
347 10780 : void ShapeIdPrivate::setShapeId(const QString &shapeId)
348 12760 : {
349 23540 : const int sep1 = shapeId.indexOf(QLatin1Char('#'));
350 23540 : const int sep2 = shapeId.lastIndexOf(QLatin1Char('$'));
351 26868 : if (sep1 > 0) nameSpace = shapeId.mid(0, sep1);
352 27336 : if (sep2 > 0) memberName = shapeId.mid(sep2+1);
353 23540 : shapeName = shapeId.mid(sep1+1, sep2-sep1-1);
354 23540 : }
355 :
356 : /// \endcond
357 :
358 : QTSMITHY_END_NAMESPACE
|