1 | // Copyright (C) 2019 The Qt Company Ltd. |
2 | // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR GPL-3.0-only WITH Qt-GPL-exception-1.0 |
3 | |
4 | #ifndef QQMLJSSCOPESBYID_P_H |
5 | #define QQMLJSSCOPESBYID_P_H |
6 | |
7 | // |
8 | // W A R N I N G |
9 | // ------------- |
10 | // |
11 | // This file is not part of the Qt API. It exists purely as an |
12 | // implementation detail. This header file may change from version to |
13 | // version without notice, or even be removed. |
14 | // |
15 | // We mean it. |
16 | |
17 | |
18 | #include "qqmljsscope_p.h" |
19 | |
20 | #include <QtCore/qhash.h> |
21 | #include <QtCore/qstring.h> |
22 | |
23 | QT_BEGIN_NAMESPACE |
24 | |
25 | enum QQmlJSScopesByIdOption: char { |
26 | Default = 0, |
27 | AssumeComponentsAreBound = 1, |
28 | }; |
29 | Q_DECLARE_FLAGS(QQmlJSScopesByIdOptions, QQmlJSScopesByIdOption); |
30 | |
31 | class QQmlJSScopesById |
32 | { |
33 | public: |
34 | bool componentsAreBound() const { return m_componentsAreBound; } |
35 | void setComponentsAreBound(bool bound) { m_componentsAreBound = bound; } |
36 | |
37 | void setSignaturesAreEnforced(bool enforced) { m_signaturesAreEnforced = enforced; } |
38 | bool signaturesAreEnforced() const { return m_signaturesAreEnforced; } |
39 | |
40 | void setValueTypesAreAddressable(bool addressable) { m_valueTypesAreAddressable = addressable; } |
41 | bool valueTypesAreAddressable() const { return m_valueTypesAreAddressable; } |
42 | |
43 | QString id(const QQmlJSScope::ConstPtr &scope, const QQmlJSScope::ConstPtr &referrer, |
44 | QQmlJSScopesByIdOptions options = Default) const |
45 | { |
46 | const QQmlJSScope::ConstPtr referrerRoot = componentRoot(inner: referrer); |
47 | for (auto it = m_scopesById.begin(), end = m_scopesById.end(); it != end; ++it) { |
48 | if (*it == scope && isComponentVisible(observed: componentRoot(inner: *it), observer: referrerRoot, options)) |
49 | return it.key(); |
50 | } |
51 | return QString(); |
52 | } |
53 | |
54 | /*! |
55 | \internal |
56 | Returns the scope that has id \a id in the component to which \a referrer belongs to. |
57 | If no such scope exists, a null scope is returned. |
58 | */ |
59 | QQmlJSScope::ConstPtr scope(const QString &id, const QQmlJSScope::ConstPtr &referrer, |
60 | QQmlJSScopesByIdOptions options = Default) const |
61 | { |
62 | Q_ASSERT(!id.isEmpty()); |
63 | const auto range = m_scopesById.equal_range(key: id); |
64 | if (range.first == range.second) |
65 | return QQmlJSScope::ConstPtr(); |
66 | const QQmlJSScope::ConstPtr referrerRoot = componentRoot(inner: referrer); |
67 | |
68 | for (auto it = range.first; it != range.second; ++it) { |
69 | if (isComponentVisible(observed: componentRoot(inner: *it), observer: referrerRoot, options)) |
70 | return *it; |
71 | } |
72 | |
73 | return QQmlJSScope::ConstPtr(); |
74 | } |
75 | |
76 | void insert(const QString &id, const QQmlJSScope::ConstPtr &scope) |
77 | { |
78 | Q_ASSERT(!id.isEmpty()); |
79 | m_scopesById.insert(key: id, value: scope); |
80 | } |
81 | |
82 | void clear() { m_scopesById.clear(); } |
83 | |
84 | /*! |
85 | \internal |
86 | Returns \c true if \a id exists anywhere in the current document. |
87 | This is still allowed if the other occurrence is in a different (inline) component. |
88 | Check the return value of scope to know whether the id has already been assigned |
89 | in a givne scope. |
90 | */ |
91 | bool existsAnywhereInDocument(const QString &id) const { return m_scopesById.contains(key: id); } |
92 | |
93 | private: |
94 | static QQmlJSScope::ConstPtr componentRoot(const QQmlJSScope::ConstPtr &inner) |
95 | { |
96 | QQmlJSScope::ConstPtr scope = inner; |
97 | while (scope && !scope->isComponentRootElement() && !scope->isInlineComponent()) { |
98 | if (QQmlJSScope::ConstPtr parent = scope->parentScope()) |
99 | scope = parent; |
100 | else |
101 | break; |
102 | } |
103 | return scope; |
104 | } |
105 | |
106 | bool isComponentVisible(const QQmlJSScope::ConstPtr &observed, |
107 | const QQmlJSScope::ConstPtr &observer, |
108 | QQmlJSScopesByIdOptions options) const |
109 | { |
110 | if (!m_componentsAreBound && !options.testAnyFlag(flag: AssumeComponentsAreBound)) |
111 | return observed == observer; |
112 | |
113 | for (QQmlJSScope::ConstPtr scope = observer; scope; scope = scope->parentScope()) { |
114 | if (scope == observed) |
115 | return true; |
116 | } |
117 | |
118 | return false; |
119 | } |
120 | |
121 | QMultiHash<QString, QQmlJSScope::ConstPtr> m_scopesById; |
122 | bool m_componentsAreBound = false; |
123 | bool m_signaturesAreEnforced = true; |
124 | bool m_valueTypesAreAddressable = false; |
125 | }; |
126 | |
127 | QT_END_NAMESPACE |
128 | |
129 | #endif // QQMLJSSCOPESBYID_P_H |
130 | |