1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Contact: https://www.qt.io/licensing/
5**
6** This file is part of the QtQml module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial License Usage
10** Licensees holding valid commercial Qt licenses may use this file in
11** accordance with the commercial license agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and The Qt Company. For licensing terms
14** and conditions see https://www.qt.io/terms-conditions. For further
15** information use the contact form at https://www.qt.io/contact-us.
16**
17** GNU Lesser General Public License Usage
18** Alternatively, this file may be used under the terms of the GNU Lesser
19** General Public License version 3 as published by the Free Software
20** Foundation and appearing in the file LICENSE.LGPL3 included in the
21** packaging of this file. Please review the following information to
22** ensure the GNU Lesser General Public License version 3 requirements
23** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
24**
25** GNU General Public License Usage
26** Alternatively, this file may be used under the terms of the GNU
27** General Public License version 2.0 or (at your option) the GNU General
28** Public license version 3 or any later version approved by the KDE Free
29** Qt Foundation. The licenses are as published by the Free Software
30** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
31** included in the packaging of this file. Please review the following
32** information to ensure the GNU General Public License requirements will
33** be met: https://www.gnu.org/licenses/gpl-2.0.html and
34** https://www.gnu.org/licenses/gpl-3.0.html.
35**
36** $QT_END_LICENSE$
37**
38****************************************************************************/
39
40#include "qqmlcustomparser_p.h"
41
42#include <private/qv4compileddata_p.h>
43#include <private/qqmlsourcecoordinate_p.h>
44
45#include <QtCore/qdebug.h>
46
47QT_BEGIN_NAMESPACE
48
49/*!
50 \class QQmlCustomParser
51 \brief The QQmlCustomParser class allows you to add new arbitrary types to QML.
52 \internal
53
54 By subclassing QQmlCustomParser, you can add a parser for
55 building a particular type.
56
57 The subclass must implement compile() and setCustomData(), and register
58 itself in the meta type system by calling the macro:
59
60 \code
61 QML_REGISTER_CUSTOM_TYPE(Module, MajorVersion, MinorVersion, Name, TypeClass, ParserClass)
62 \endcode
63*/
64
65/*
66 \fn QByteArray QQmlCustomParser::compile(const QList<QQmlCustomParserProperty> & properties)
67
68 The custom parser processes \a properties, and returns
69 a QByteArray containing data meaningful only to the
70 custom parser; the type engine will pass this same data to
71 setCustomData() when making an instance of the data.
72
73 Errors must be reported via the error() functions.
74
75 The QByteArray may be cached between executions of the system, so
76 it must contain correctly-serialized data (not, for example,
77 pointers to stack objects).
78*/
79
80/*
81 \fn void QQmlCustomParser::setCustomData(QObject *object, const QByteArray &data)
82
83 This function sets \a object to have the properties defined
84 by \a data, which is a block of data previously returned by a call
85 to compile().
86
87 Errors should be reported using qmlWarning(object).
88
89 The \a object will be an instance of the TypeClass specified by QML_REGISTER_CUSTOM_TYPE.
90*/
91
92void QQmlCustomParser::clearErrors()
93{
94 exceptions.clear();
95}
96
97/*!
98 Reports an error with the given \a description.
99
100 An error is generated referring to the \a location in the source file.
101*/
102void QQmlCustomParser::error(const QV4::CompiledData::Location &location, const QString &description)
103{
104 QQmlError error;
105 error.setLine(qmlConvertSourceCoordinate<quint32, int>(n: location.line));
106 error.setColumn(qmlConvertSourceCoordinate<quint32, int>(n: location.column));
107 error.setDescription(description);
108
109 exceptions << error;
110}
111
112struct StaticQtMetaObject : public QObject
113{
114 static const QMetaObject *get()
115 { return &staticQtMetaObject; }
116};
117
118/*!
119 If \a script is a simple enumeration expression (eg. Text.AlignLeft),
120 returns the integer equivalent (eg. 1), and sets \a ok to true.
121
122 Otherwise sets \a ok to false.
123
124 A valid \a ok must be provided, or the function will assert.
125*/
126int QQmlCustomParser::evaluateEnum(const QByteArray& script, bool *ok) const
127{
128 Q_ASSERT_X(ok, "QQmlCustomParser::evaluateEnum", "ok must not be a null pointer");
129 *ok = false;
130
131 // we support one or two '.' in the enum phrase:
132 // * <TypeName>.<EnumValue>
133 // * <TypeName>.<ScopedEnumName>.<EnumValue>
134
135 auto nextDot = [&](int dot) {
136 const int nextDot = script.indexOf(c: '.', from: dot + 1);
137 return (nextDot == script.length() - 1) ? -1 : nextDot;
138 };
139
140 int dot = nextDot(-1);
141 if (dot == -1)
142 return -1;
143
144 QString scope = QString::fromUtf8(str: script.left(len: dot));
145
146 if (scope != QLatin1String("Qt")) {
147 if (imports.isNull())
148 return -1;
149 QQmlType type;
150
151 if (imports.isT1()) {
152 QQmlImportNamespace *ns = nullptr;
153 if (!imports.asT1()->resolveType(type: scope, type_return: &type, version_major: nullptr, version_minor: nullptr, ns_return: &ns))
154 return -1;
155 if (!type.isValid() && ns != nullptr) {
156 dot = nextDot(dot);
157 if (dot == -1 || !imports.asT1()->resolveType(type: QString::fromUtf8(str: script.left(len: dot)),
158 type_return: &type, version_major: nullptr, version_minor: nullptr, ns_return: nullptr)) {
159 return -1;
160 }
161 }
162 } else {
163 QQmlTypeNameCache::Result result = imports.asT2()->query(scope);
164 if (result.isValid()) {
165 type = result.type;
166 } else if (result.importNamespace) {
167 dot = nextDot(dot);
168 if (dot != -1)
169 type = imports.asT2()->query(QString::fromUtf8(str: script.left(len: dot))).type;
170 }
171 }
172
173 if (!type.isValid())
174 return -1;
175
176 const int dot2 = nextDot(dot);
177 const bool dot2Valid = (dot2 != -1);
178 QByteArray enumValue = script.mid(index: dot2Valid ? dot2 + 1 : dot + 1);
179 QByteArray scopedEnumName = (dot2Valid ? script.mid(index: dot + 1, len: dot2 - dot - 1) : QByteArray());
180 if (!scopedEnumName.isEmpty())
181 return type.scopedEnumValue(engine, scopedEnumName, enumValue, ok);
182 else
183 return type.enumValue(engine, QHashedCStringRef(enumValue.constData(), enumValue.length()), ok);
184 }
185
186 QByteArray enumValue = script.mid(index: dot + 1);
187 const QMetaObject *mo = StaticQtMetaObject::get();
188 int i = mo->enumeratorCount();
189 while (i--) {
190 int v = mo->enumerator(index: i).keyToValue(key: enumValue.constData(), ok);
191 if (*ok)
192 return v;
193 }
194 return -1;
195}
196
197/*!
198 Resolves \a name to a type, or 0 if it is not a type. This can be used
199 to type-check object nodes.
200*/
201const QMetaObject *QQmlCustomParser::resolveType(const QString& name) const
202{
203 if (!imports.isT1())
204 return nullptr;
205 QQmlType qmltype;
206 if (!imports.asT1()->resolveType(type: name, type_return: &qmltype, version_major: nullptr, version_minor: nullptr, ns_return: nullptr))
207 return nullptr;
208 return qmltype.metaObject();
209}
210
211QT_END_NAMESPACE
212

source code of qtdeclarative/src/qml/qml/qqmlcustomparser.cpp