aboutsummaryrefslogtreecommitdiffstats
path: root/src/qmldom
diff options
context:
space:
mode:
authorFawzi Mohamed <fawzi.mohamed@qt.io>2020-10-10 23:14:02 +0200
committerFawzi Mohamed <fawzi.mohamed@qt.io>2020-12-03 16:42:24 +0100
commit9f56f7f7863427445d74c5d7580e92075b767dc2 (patch)
treeaf6d339ba7899f6806e38fc400e0d37faf2b2460 /src/qmldom
parent5e491b77b91f835801d6520f3c5c4ecbb33ef9e5 (diff)
qmldom: DomItem and common API
This commit adds the main API of the Dom model: The idea of the Dom Model is to expose a simplified an uniform model, while keeping typed objects (as one is used in C++, and ideally using the modern C++ approach with containers, and avoiding manual memory management). In particular this approach avoids reducing everything to an untyped model. The central object in this is the DomItem, it is a value type that keeps a pointer to the actual element. A non empty DomItem always keeps some context (a canonicalPath) and one can explore its containers and its contents. Non existing or missing values simply translate to empty DomItems and one does not need to check at each step for invalid things. See the DomItem documentation for more information. The as() ad ownerAs() templates let one cast back the DomItem to the underlying elements. To expose an element with minimal effort one has to mainly implement the method iterateDirectSubpaths(), and iterate through all elements contained in that element (see the documentation of DomBase for more details). Containers, simple data and references can be wrapped in DomItem elements just when accessed, so the underlying C++ object can use the usual containers/datatypes. A good model should not just be easy to browse, reference and extend with new elements, it should also have a clear parallelizazion/ multithreading policy. This is less relevant for a single pass compiler, but for is relevannt for a code model. The fact that the current code model now somehow works only from the GUI thread show the importance of a clear policy when multiple people can contribute. In the Dom model there is the following idea: - During build time pointers to the elements can change (MutableDomItem should be used) - Access to sub elements normally has no locks, they should be either accessed by a single thread or immutable. - OwningItem are the unit of multithread safe updates, the have a mutex and protect the parts that need protection - A DomEnvironment can refer to a parent Environment, perform several changes (in a separate thread), and commit them all at once (in a sparate commit). This commit contains all the main API, and tests for it. The bulk of the concrete element will be in followup patches. Task-number: QTBUG-74840 Change-Id: I9e14e8560e3b4aa3268a733fed37e9090c18e3ab Reviewed-by: Fabian Kosmale <fabian.kosmale@qt.io>
Diffstat (limited to 'src/qmldom')
-rw-r--r--src/qmldom/CMakeLists.txt3
-rw-r--r--src/qmldom/qmldom.pro5
-rw-r--r--src/qmldom/qqmldom_fwd_p.h104
-rw-r--r--src/qmldom/qqmldomconstants_p.h102
-rw-r--r--src/qmldom/qqmldomexternalitems.cpp90
-rw-r--r--src/qmldom/qqmldomexternalitems_p.h114
-rw-r--r--src/qmldom/qqmldomitem.cpp1791
-rw-r--r--src/qmldom/qqmldomitem_p.h1101
-rw-r--r--src/qmldom/qqmldomtop.cpp471
-rw-r--r--src/qmldom/qqmldomtop_p.h421
10 files changed, 4186 insertions, 16 deletions
diff --git a/src/qmldom/CMakeLists.txt b/src/qmldom/CMakeLists.txt
index ceefec2dae..df3da266f8 100644
--- a/src/qmldom/CMakeLists.txt
+++ b/src/qmldom/CMakeLists.txt
@@ -9,11 +9,14 @@ qt_internal_add_module(QmlDom
INTERNAL_MODULE
SOURCES
qqmldom_global.h
+ qqmldom_fwd_p.h
qqmldomconstants_p.h
qqmldomerrormessage.cpp qqmldomerrormessage_p.h
+ qqmldomexternalitems.cpp qqmldomexternalitems_p.h
qqmldomitem.cpp qqmldomitem_p.h
qqmldompath.cpp qqmldompath_p.h
qqmldomstringdumper.cpp qqmldomstringdumper_p.h
+ qqmldomtop.cpp qqmldomtop_p.h
DEFINES
QMLDOM_LIBRARY
PUBLIC_LIBRARIES
diff --git a/src/qmldom/qmldom.pro b/src/qmldom/qmldom.pro
index d83d74e273..9f64ff599f 100644
--- a/src/qmldom/qmldom.pro
+++ b/src/qmldom/qmldom.pro
@@ -7,16 +7,21 @@ DEFINES += QMLDOM_LIBRARY
SOURCES += \
$$PWD/qqmldomerrormessage.cpp \
+ $$PWD/qqmldomexternalitems.cpp \
$$PWD/qqmldomitem.cpp \
$$PWD/qqmldompath.cpp \
+ $$PWD/qqmldomtop.cpp \
$$PWD/qqmldomstringdumper.cpp
HEADERS += \
+ $$PWD/qqmldom_fwd_p.h \
$$PWD/qqmldom_global.h \
$$PWD/qqmldomconstants_p.h \
$$PWD/qqmldomerrormessage_p.h \
+ $$PWD/qqmldomexternalitems_p.h \
$$PWD/qqmldomitem_p.h \
$$PWD/qqmldompath_p.h \
+ $$PWD/qqmldomtop_p.h \
$$PWD/qqmldomstringdumper_p.h
load(qt_module)
diff --git a/src/qmldom/qqmldom_fwd_p.h b/src/qmldom/qqmldom_fwd_p.h
new file mode 100644
index 0000000000..a8b54b1e51
--- /dev/null
+++ b/src/qmldom/qqmldom_fwd_p.h
@@ -0,0 +1,104 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef QQMLDOM_FWD_P_H
+#define QQMLDOM_FWD_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldom_global.h"
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+class ExternalItemInfoBase;
+class ExternalItemPairBase;
+class ExternalOwningItem;
+class OwningItem;
+class DomBase;
+class DomItem;
+class Source;
+class Empty;
+class QmlDirectory;
+class JsFile;
+class QmlFile;
+class QmltypesFile;
+class ModuleIndex;
+class ModuleScope;
+class Export;
+class JsResource;
+class QmltypesComponent;
+class QmlComponent;
+class EnumDecl;
+class Import;
+class Pragma;
+class Id;
+class QmlObject;
+class ConstantData;
+class ScriptExpression;
+class Reference;
+class Binding;
+class PropertyDefinition;
+class RequiredProperty;
+class Version;
+class MethodInfo;
+class GenericObject;
+class Map;
+class List;
+class Version;
+class DomTop;
+class DomEnvironment;
+class DomUniverse;
+
+class Subpath;
+class ObserversTrie;
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // QQMLDOM_FWD_P_H
diff --git a/src/qmldom/qqmldomconstants_p.h b/src/qmldom/qqmldomconstants_p.h
index c7e7de5918..3fcd4a639b 100644
--- a/src/qmldom/qqmldomconstants_p.h
+++ b/src/qmldom/qqmldomconstants_p.h
@@ -86,6 +86,108 @@ enum class PathCurrent {
Lookup
};
Q_ENUM_NS(PathCurrent)
+enum class ResolveOption{
+ None=0,
+ TraceVisit=0x1 // call the function along all elements of the path, not just for the target (the function might be called even if the target is never reached)
+};
+Q_ENUM_NS(ResolveOption)
+Q_DECLARE_FLAGS(ResolveOptions, ResolveOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(ResolveOptions)
+
+enum class VisitOption{
+ None=0,
+ VisitAdopted=0x1, // Visit adopted types (but never recurses)
+ Recurse=0x2, // recurse non adopted types
+ NoPath=0x4 // does not generate path consistent with visit
+};
+Q_ENUM_NS(VisitOption)
+Q_DECLARE_FLAGS(VisitOptions, VisitOption)
+Q_DECLARE_OPERATORS_FOR_FLAGS(VisitOptions)
+
+enum class DomKind {
+ Empty,
+ Object,
+ List,
+ Map,
+ Value
+};
+Q_ENUM_NS(DomKind)
+
+enum class DomType {
+ Empty,
+
+ ExternalItemInfo,
+ ExternalItemPair,
+ // ExternalOwningItems refer to an external path and can be shared between environments
+ QmlDirectory, // dir
+ QmldirFile, // qmldir
+ JsFile, // file
+ QmlFile, // file
+ QmltypesFile, // qmltypes
+ GlobalScope, // language dependent
+ EnumItem,
+
+ // types
+ EnumDecl,
+ JsResource,
+ QmltypesComponent,
+ QmlComponent,
+ GlobalComponent,
+
+ ModuleAutoExport, // dependent imports to automatically load when a module is imported
+ ModuleIndex, // index for all the imports of a major version
+ ModuleScope, // a specific import with full version
+ Export, // An exported type
+
+ // header stuff
+ Import, // wrapped
+ Pragma,
+
+ // qml elements
+ Id,
+ QmlObject,
+ ConstantData,
+ SimpleObjectWrap,
+ ScriptExpression,
+ Reference,
+ Binding,
+ PropertyDefinition,
+ RequiredProperty,
+ MethodParameter,
+ MethodInfo,
+ Version, // wrapped
+
+ // generic objects, mainly for debug support
+ GenericObject,
+ GenericOwner,
+
+ // containers
+ Map,
+ List,
+
+ // supporting objects
+ LoadInfo, // owning
+ ErrorMessage, // wrapped
+
+ // Dom top level
+ DomEnvironment,
+ DomUniverse
+};
+Q_ENUM_NS(DomType)
+
+enum class ListOptions {
+ Normal,
+ Reverse
+};
+Q_ENUM_NS(ListOptions)
+
+enum class LoadOption {
+ DefaultLoad = 0x0,
+ ForceLoad = 0x1,
+};
+Q_ENUM_NS(LoadOption)
+Q_DECLARE_FLAGS(LoadOptions, LoadOption);
+
enum class EscapeOptions{
OuterQuotes,
NoOuterQuotes
diff --git a/src/qmldom/qqmldomexternalitems.cpp b/src/qmldom/qqmldomexternalitems.cpp
new file mode 100644
index 0000000000..2d71cbc170
--- /dev/null
+++ b/src/qmldom/qqmldomexternalitems.cpp
@@ -0,0 +1,90 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#include "qqmldomexternalitems_p.h"
+
+#include "qqmldomtop_p.h"
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtCore/QDir>
+#include <QtCore/QScopeGuard>
+#include <QtCore/QFileInfo>
+
+#include <algorithm>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+ExternalOwningItem::ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path path, int derivedFrom):
+ OwningItem(derivedFrom, lastDataUpdateAt), m_canonicalFilePath(filePath), m_path(path)
+{}
+
+ExternalOwningItem::ExternalOwningItem(const ExternalOwningItem &o):
+ OwningItem(o), m_canonicalFilePath(o.m_canonicalFilePath),
+ m_path(o.m_path), m_isValid(o.m_isValid)
+{}
+
+QString ExternalOwningItem::canonicalFilePath(const DomItem &) const
+{
+ return m_canonicalFilePath;
+}
+
+QString ExternalOwningItem::canonicalFilePath() const
+{
+ return m_canonicalFilePath;
+}
+
+Path ExternalOwningItem::canonicalPath(const DomItem &) const
+{
+ return m_path;
+}
+
+Path ExternalOwningItem::canonicalPath() const
+{
+ return m_path;
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomexternalitems_p.h b/src/qmldom/qqmldomexternalitems_p.h
new file mode 100644
index 0000000000..fcb15ba2ac
--- /dev/null
+++ b/src/qmldom/qqmldomexternalitems_p.h
@@ -0,0 +1,114 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef QQMLDOMEXTERNALITEMS_P_H
+#define QQMLDOMEXTERNALITEMS_P_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldomitem_p.h"
+
+#include <QtQml/private/qqmljsast_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmldirparser_p.h>
+#include <QtCore/QMetaType>
+
+#include <limits>
+
+Q_DECLARE_METATYPE(QQmlDirParser::Plugin)
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+/*!
+\internal
+\class QQmlJS::Dom::ExternalOwningItem
+
+\brief A OwningItem that refers to an external resource (file,...)
+
+Every owning item has a file or directory it refers to.
+
+
+*/
+class QMLDOM_EXPORT ExternalOwningItem: public OwningItem {
+public:
+ ExternalOwningItem(QString filePath, QDateTime lastDataUpdateAt, Path pathFromTop, int derivedFrom=0);
+ ExternalOwningItem(const ExternalOwningItem &o);
+ QString canonicalFilePath(const DomItem &) const override;
+ QString canonicalFilePath() const;
+ Path canonicalPath(const DomItem &) const override;
+ Path canonicalPath() const;
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)> visitor) override {
+ bool cont = OwningItem::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.subDataField(Fields::canonicalFilePath, canonicalFilePath()).visit(visitor);
+ cont = cont && self.subDataField(Fields::isValid, isValid()).visit(visitor);
+// if (!code().isNull())
+// cont = cont && self.subDataField(Fields::code, code()).visit(visitor);
+ return cont;
+ }
+
+ bool isValid() const {
+ QMutexLocker l(mutex());
+ return m_isValid;
+ }
+ void setIsValid(bool val) {
+ QMutexLocker l(mutex());
+ m_isValid = val;
+ }
+ // null code means invalid
+ virtual QString code() const { return QString(); }
+protected:
+ QString m_canonicalFilePath;
+ Path m_path;
+ bool m_isValid = false;
+};
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // QQMLDOMEXTERNALITEMS_P_H
diff --git a/src/qmldom/qqmldomitem.cpp b/src/qmldom/qqmldomitem.cpp
index e572c52f8f..c2acbaebf2 100644
--- a/src/qmldom/qqmldomitem.cpp
+++ b/src/qmldom/qqmldomitem.cpp
@@ -36,31 +36,1806 @@
** $QT_END_LICENSE$
**/
#include "qqmldomitem_p.h"
-#include "qqmldompath_p.h"
+#include "qqmldomtop_p.h"
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+
+#include <QtCore/QDebug>
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QPair>
+#include <QtCore/QScopeGuard>
+#include <QtCore/QMutexLocker>
+#include <QtCore/QCborMap>
+#include <QtCore/QCborArray>
+#include <QtCore/QJsonValue>
+#include <QtCore/QJsonDocument>
QT_BEGIN_NAMESPACE
+
+
namespace QQmlJS {
namespace Dom {
-Path DomItem::canonicalPath() const
+using std::shared_ptr;
+/*!
+\internal
+\class QQmljs::Dom::DomBase
+
+\brief Abstract class common to all elements of the Qml code model
+
+DomBase represents the base class common to all objects exposed by the Dom Model
+through a DomItem.
+Single inheritence is mandatory for the inline items (Empty, Map, List, ConstantData, Reference),
+so that the base pointer of the object can be used a base pointer of the superclass directly.
+
+The subclass *must* have a
+\code
+ constexpr static Kind kindValue
+\endcode
+entry with its kind to enable casting usng the DomItem::as DomItem::ownerAs templates.
+
+The minimal overload set to be usable is:
+\code
+ Kind kind() const override { return kindValue; } // returns the kind of the current element
+ Path pathFromOwner(const DomItem &self) const override; // returns the path from the owner to the current element
+ Path canonicalPath(const DomItem &self) const override; // returns the path from
+ virtual bool iterateDirectSubpaths(const DomItem &self, std::function<bool(Path, DomItem)>) const = 0; // iterates the *direct* subpaths, returns false if a quick end was requested
+\endcode
+But you probably want to subclass either DomElement of OwningItem for your element.
+DomElement stores its pathFromOwner, and computes the canonicalPath from it and its owner.
+OwningItem is the unit for updates to the Dom model, exposed changes always change at least one OwningItem.
+They have their lifetime handled with shared_ptr and own (i.e. are responsible of freeing) other items in them.
+
+\sa QQml::Dom::DomItem, QQml::Dom::DomElement, QQml::Dom::OwningItem
+*/
+
+QMap<DomType,QString> domTypeToStringMap()
{
- return Path();
+ static QMap<DomType,QString> map = [](){
+ QMetaEnum metaEnum = QMetaEnum::fromType<DomType>();
+ QMap<DomType,QString> res;
+ for (int i = 0; i < metaEnum.keyCount(); ++ i) {
+ res[DomType(metaEnum.value(i))] = QString::fromUtf8(metaEnum.key(i));
+ }
+ return res;
+ }();
+ return map;
+}
+
+QString domTypeToString(DomType k)
+{
+ return domTypeToStringMap().value(k, QString::number(int(k)));
+}
+
+bool domTypeIsObjWrap(DomType k)
+{
+ switch (k) {
+ case DomType::ModuleAutoExport:
+ case DomType::Import:
+ case DomType::Export:
+ case DomType::SimpleObjectWrap:
+ case DomType::Version:
+ case DomType::ErrorMessage:
+ case DomType::PropertyDefinition:
+ case DomType::MethodParameter:
+ case DomType::MethodInfo:
+ case DomType::Pragma:
+ case DomType::Id:
+ case DomType::EnumItem:
+ case DomType::Binding:
+ case DomType::RequiredProperty:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool domTypeIsDomElement(DomType k)
+{
+ switch (k) {
+ case DomType::ModuleScope:
+ case DomType::QmlObject:
+ case DomType::ConstantData:
+ case DomType::SimpleObjectWrap:
+ case DomType::ScriptExpression:
+ case DomType::Reference:
+ case DomType::Map:
+ case DomType::List:
+ case DomType::EnumDecl:
+ case DomType::JsResource:
+ case DomType::QmltypesComponent:
+ case DomType::QmlComponent:
+ case DomType::GlobalComponent:
+ case DomType::GenericObject:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool domTypeIsOwningItem(DomType k)
+{
+ switch (k) {
+ case DomType::ModuleIndex:
+
+ case DomType::GenericOwner:
+
+ case DomType::QmlDirectory:
+ case DomType::JsFile:
+ case DomType::QmlFile:
+ case DomType::QmltypesFile:
+ case DomType::GlobalScope:
+
+ case DomType::LoadInfo:
+
+ case DomType::DomEnvironment:
+ case DomType::DomUniverse:
+ return true;
+ default:
+ return false;
+ }
+};
+
+bool domTypeIsExternalItem(DomType k)
+{
+ switch (k) {
+ case DomType::QmlDirectory:
+ case DomType::JsFile:
+ case DomType::QmlFile:
+ case DomType::QmltypesFile:
+ case DomType::GlobalScope:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool domTypeIsTopItem(DomType k)
+{
+ switch (k) {
+ case DomType::DomEnvironment:
+ case DomType::DomUniverse:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+bool domTypeIsContainer(DomType k)
+{
+ switch (k) {
+ case DomType::Map:
+ case DomType::List:
+ return true;
+ default:
+ return false;
+ }
+}
+
+bool domTypeCanBeInline(DomType k)
+{
+ switch (k) {
+ case DomType::Empty:
+ case DomType::Map:
+ case DomType::List:
+ case DomType::ConstantData:
+ case DomType::SimpleObjectWrap:
+ case DomType::Reference:
+ return true;
+ default:
+ return false;
+ }
+};
+
+DomKind DomBase::domKind() const
+{
+ return kind2domKind(kind());
+}
+
+bool DomBase::iterateDirectSubpathsConst(const DomItem &self, std::function<bool (Path, const DomItem &)>visitor) const
+{
+ return const_cast<DomBase *>(this)->iterateDirectSubpaths(
+ *const_cast<DomItem *>(&self),
+ [visitor](Path p, DomItem &item) {
+ return visitor(p, item);
+ });
+}
+
+DomItem DomBase::containingObject(const DomItem &self) const
+{
+ Path path = pathFromOwner(self);
+ DomItem base = self.owner();
+ if (!path) {
+ path = canonicalPath(self);
+ base = self;
+ }
+ Source source = path.split();
+ return base.path(source.pathToSource);
+}
+
+quintptr DomBase::id() const
+{
+ return quintptr(this);
+}
+
+QString DomBase::typeName() const
+{
+ return domTypeToStringMap()[kind()];
+}
+
+QList<QString> const DomBase::fields(const DomItem &self) const
+{
+ QList<QString> res;
+ iterateDirectSubpathsConst(self, [&res](Path p, const DomItem &){
+ if (p.headKind() == Path::Kind::Field)
+ res.append(p.headName());
+ return true;
+ });
+ return res;
+}
+
+DomItem DomBase::field(const DomItem &self, QStringView name) const
+{
+ DomItem res;
+ iterateDirectSubpathsConst(self, [&res, name](Path p, const DomItem & i){
+ if (p.headKind() == Path::Kind::Field && p.checkHeadName(name)) {
+ res = i;
+ return false;
+ }
+ return true;
+ });
+ return res;
+}
+
+index_type DomBase::indexes(const DomItem &self) const
+{
+ index_type res = 0;
+ iterateDirectSubpathsConst(self, [&res](Path p, const DomItem &){
+ if (p.headKind() == Path::Kind::Index) {
+ index_type i = p.headIndex() + 1;
+ if (res < i)
+ res = i;
+ }
+ return true;
+ });
+ return res;
}
-QString DomItem::canonicalFilePath() const
+DomItem DomBase::index(const DomItem &self, qint64 index) const
{
+ DomItem res;
+ iterateDirectSubpathsConst(self, [&res, index](Path p, const DomItem &i){
+ if (p.headKind() == Path::Kind::Index && p.headIndex() == index) {
+ res = i;
+ return false;
+ }
+ return true;
+ });
+ return res;
+}
+
+QSet<QString> const DomBase::keys(const DomItem &self) const
+{
+ QSet<QString> res;
+ iterateDirectSubpathsConst(self, [&res](Path p, const DomItem &){
+ if (p.headKind() == Path::Kind::Key)
+ res.insert(p.headName());
+ return true;
+ });
+ return res;
+}
+
+DomItem DomBase::key(const DomItem &self, QString name) const
+{
+ DomItem res;
+ iterateDirectSubpathsConst(self, [&res, name](Path p, const DomItem &i){
+ if (p.headKind() == Path::Kind::Key && p.checkHeadName(name)) {
+ res = i;
+ return false;
+ }
+ return true;
+ });
+ return res;
+}
+
+QString DomBase::canonicalFilePath(const DomItem & self) const
+{
+ auto parent = containingObject(self);
+ if (parent)
+ return parent.canonicalFilePath();
return QString();
}
-SourceLocation DomItem::location() const
+SourceLocation DomBase::location(const DomItem & self) const
{
+ auto parent = containingObject(self);
+ if (parent)
+ return parent.location();
return SourceLocation();
}
-DomItem::DomItem()
+ConstantData::ConstantData(Path pathFromOwner, QCborValue value, Options options, const SourceLocation & loc):
+ DomElement(pathFromOwner, loc), m_value(value), m_options(options)
+{}
+
+bool ConstantData::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ if (m_value.isMap()) {
+ QCborMap map = m_value.toMap();
+ auto it = map.cbegin();
+ auto end = map.cend();
+ while (it != end) {
+ QString key = it.key().toString();
+ Path path;
+ switch (m_options) {
+ case ConstantData::Options::MapIsMap:
+ path = Path::key(key);
+ break;
+ case ConstantData::Options::FirstMapIsFields:
+ path = Path::field(key);
+ break;
+ }
+ if (!self.subDataPath(path, it.value()).visit(visitor))
+ return false;
+ ++it;
+ }
+ return true;
+ } else if (m_value.isArray()){
+ QCborArray array = m_value.toArray();
+ auto it = array.cbegin();
+ auto end = array.cend();
+ index_type i = 0;
+ while (it != end) {
+ if (!self.subDataPath(Path::index(i++), *it++).visit(visitor))
+ return false;
+ }
+ return true;
+ } else {
+ return true;
+ }
+}
+
+quintptr ConstantData::id() const
+{
+ return quintptr(0);
+}
+
+DomKind ConstantData::domKind() const
+{
+ if (m_value.isMap()) {
+ switch (m_options) {
+ case ConstantData::Options::MapIsMap:
+ return DomKind::Map;
+ case ConstantData::Options::FirstMapIsFields:
+ return DomKind::Object;
+ }
+ }
+ if (m_value.isArray())
+ return DomKind::List;
+ return DomKind::Value;
+}
+
+SimpleObjectWrap::SimpleObjectWrap(Path pathFromOwner, QVariant value,
+ std::function<bool(DomItem &, QVariant, std::function<bool(Path, DomItem &)>)> directSubpathsIterate,
+ DomType kind,
+ DomKind domKind,
+ QString typeName,
+ const SourceLocation & loc):
+ DomElement(pathFromOwner, loc), m_kind(kind), m_domKind(domKind), m_typeName(typeName), m_value(value),
+ m_directSubpathsIterate(directSubpathsIterate)
+{}
+
+bool SimpleObjectWrap::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ return m_directSubpathsIterate(self, m_value, visitor);
+}
+
+quintptr SimpleObjectWrap::id() const
+{
+ return quintptr(m_value.value<void *>());
+}
+
+/*!
+\internal
+\class QQmlJS::Dom::DomItem
+
+\brief A value type that references any element of the Dom.
+
+This class is the central element in the Dom, it is how any element can be identfied in a uniform
+way, and provides the API to explore the Dom, and Path based operations.
+
+The DomItem (unless it is Empty) keeps a pointer to the element, and a shared pointer to its owner
+and to the DomEnvironment or DomUniverse that contains them. This means that:
+\list
+\li A DomItem always has some context: you can get the canonicalPath(), go up along it with
+ containingObject() and container().
+\li the indexing operator [], or the path(), field(), key() and index() methods (along with their
+ fields(), keys(), and indexes() contreparts) let one visit the contents of the current element.
+\li visitChildren can be used to visit all subEments, if preferred on the top of it a visitor
+ pattern can also be used.
+\li If element specific attributes are wanted the two template casting as and ownerAs allow safe casting
+of the DomItem to a specific concrete type (cast to superclasses is not supported).
+\li Multithreading does not create issues, because even if an update replacing an OwningItem takes
+ place the DomItem keeps a shared_ptr to the current owner as long as you use it
+\li Some elements (Empty, List, Map, ConstantData, Reference) might be inline, meaning that they are
+ generated on the fly, wrapping data of the original object.
+\endlist
+
+One of the goals of the DomItem is to allow one to use real typed objects, as one is used to in C++,
+and also let one use modern C++ patterns, meaning container that contain the actual object (without
+pointer indirection).
+Exposed OwningItems are basically immutable, but during construction, objects can be modified.
+This will typically happen from a single thread, so there aren't locking issues, but pointers to
+inner elements might become invalid.
+In this case the use of the MutableDomItem is required.
+It does not keep any pointers to internal elements, but rather the path to them, and it resolves
+it every time it needs.
+*/
+
+ErrorGroup DomItem::domErrorGroup = NewErrorGroup("Dom");
+
+ErrorGroups DomItem::myErrors()
+{
+ static ErrorGroups res = {{domErrorGroup}};
+ return res;
+}
+
+ErrorGroups DomItem::myResolveErrors()
+{
+ static ErrorGroups res = {{domErrorGroup, NewErrorGroup("Resolve")}};
+ return res;
+}
+
+Path DomItem::canonicalPath() const
+{
+ Path res = base()->canonicalPath(*this);
+ Q_ASSERT((!res || res.headKind() == Path::Kind::Root) && "non anchored canonical path");
+ return res;
+
+}
+
+DomItem DomItem::containingObject() const
+{
+ return base()->containingObject(*this);
+}
+
+DomItem DomItem::container() const
+{
+ Path path = pathFromOwner();
+ if (!path)
+ path = canonicalPath();
+ Source s = path.split();
+ if (s.pathFromSource.length() > 1)
+ return containingObject().path(s.pathFromSource.dropTail());
+ return containingObject();
+}
+
+DomItem DomItem::owner() const {
+ return DomItem(m_top, m_owner, m_owner.get());
+}
+
+DomItem DomItem::top() const
+{
+ return DomItem(m_top, m_top, m_top.get());
+}
+
+DomItem DomItem::environment() const
+{
+ DomItem res = top();
+ if (res.internalKind() == DomType::DomEnvironment)
+ return res;
+ return DomItem(); // we are in the universe, and cannot go back to the environment...
+}
+
+DomItem DomItem::universe() const
+{
+ DomItem res = top();
+ if (res.internalKind() == DomType::DomUniverse)
+ return res;
+ if (res.internalKind() == DomType::DomEnvironment)
+ return res.field(Fields::universe);
+ return DomItem(); // we should be in an empty DomItem already...
+}
+
+QString DomItem::name() const
+{
+ return field(Fields::name).value().toString();
+}
+
+DomItem DomItem::qmlChildren() const
{
+ return field(Fields::children);
}
-} // namespace Dom
-} // namespace QQmlJS
+DomItem DomItem::annotations() const
+{
+ return field(Fields::annotations);
+}
+
+DomItem DomItem::component() const
+{
+ DomItem item = *this;
+ while (item) {
+ DomType kind = item.internalKind() ;
+ if (kind == DomType::QmlComponent || kind == DomType::QmltypesComponent || kind == DomType::GlobalComponent)
+ return item;
+ item = item.containingObject();
+ }
+ return DomItem();
+}
+
+struct ResolveToDo {
+ DomItem item;
+ int pathIndex;
+};
+
+bool DomItem::resolve(Path path,
+ DomItem::Visitor visitor,
+ ErrorHandler errorHandler,
+ ResolveOptions options,
+ Path fullPath,
+ QList<Path> *visitedRefs) const
+{
+ QList<Path> vRefs;
+ Path fPath = fullPath;
+ if (fullPath.length() == 0)
+ fPath = path;
+ if (path.length()==0)
+ return visitor(fPath, *this);
+ QSet<QPair<quintptr,int> > visited;
+ Path myPath = path;
+ QVector<ResolveToDo> toDos(1); // invariant: always increase pathIndex to guarantee end even with only partial visited match
+ if (path.headKind() == Path::Kind::Root) {
+ DomItem root = *this;
+ PathRoot contextId = path.headRoot();
+ switch (contextId) {
+ case PathRoot::Modules:
+ root = root.environment().field(Fields::moduleIndexWithUri);
+ break;
+ case PathRoot::Cpp:
+ root = root.environment()[Fields::qmltypesFileWithPath];
+ break;
+ case PathRoot::Libs:
+ root = root.environment()[Fields::plugins];
+ break;
+ case PathRoot::Top:
+ root = root.top();
+ break;
+ case PathRoot::Env:
+ root = root.environment();
+ break;
+ case PathRoot::Universe:
+ root = root.environment()[u"universe"];
+ break;
+ case PathRoot::Other:
+ myResolveErrors().error(tr("Root context %1 is not knwon").arg(path.headName())).handle(errorHandler);
+ return false;
+ }
+ toDos[0] = {root, 1};
+ } else {
+ toDos[0] = {*this, 0};
+ }
+ while (!toDos.isEmpty()) {
+ auto toDo = toDos.last();
+ toDos.removeLast();
+ {
+ auto idNow = toDo.item.base()->id();
+ if (idNow == quintptr(0) && toDo.item == *this)
+ idNow = quintptr(base());
+ if (idNow != quintptr(0) && visited.contains(qMakePair(idNow,0)))
+ continue;
+ }
+ int iPath = toDo.pathIndex;
+ DomItem it = toDo.item;
+ bool branchExhausted = false;
+ while (iPath < path.length() && it && !branchExhausted) {
+ auto idNow = it.base()->id();
+ if (idNow == quintptr() && toDo.item == *this)
+ idNow = quintptr(base());
+ if (idNow != quintptr(0)) {
+ auto vPair = qMakePair(idNow, iPath);
+ if (visited.contains(vPair))
+ break;
+ visited.insert(vPair);
+ }
+ if (options & ResolveOption::TraceVisit && !visitor(path.mid(0,iPath), it))
+ return false;
+ auto cNow = path[iPath++];
+ switch (cNow.headKind()) {
+ case Path::Kind::Key:
+ it = it.key(cNow.headName());
+ break;
+ case Path::Kind::Field:
+ if (cNow.checkHeadName(Fields::get) && it.internalKind() == DomType::Reference) {
+ Path toResolve = it.as<Reference>()->referredObjectPath;
+ if (visitedRefs == nullptr) {
+ visitedRefs = &vRefs;
+ visitedRefs->append(fPath);
+ }
+ if (visitedRefs->contains(toResolve)) {
+ myResolveErrors().error([visitedRefs, toResolve](Sink sink) {
+ sink(tr("Circular reference:\n"));
+ for (const Path &vPath : *visitedRefs) {
+ sink(u" ");
+ vPath.dump(sink);
+ sink(u" >\n");
+ }
+ toResolve.dump(sink);
+ }).handle(errorHandler);
+ it = DomItem();
+ } else {
+ DomItem resolveRes;
+ it.resolve(toResolve, [&resolveRes](Path, const DomItem &r) {
+ resolveRes = r;
+ return false;
+ }, errorHandler, ResolveOption::None, toResolve, visitedRefs);
+ it = resolveRes;
+ }
+ } else {
+ it = it.field(cNow.headName()); // avoid instantiation of QString?
+ }
+ break;
+ case Path::Kind::Index:
+ it = it.index(cNow.headIndex());
+ break;
+ case Path::Kind::Empty:
+ {
+ // immediate expansion, might use extra memory, but simplifies code (no suspended evaluation)
+ Path toFind;
+ do {
+ if (iPath >= path.length()) {
+ myResolveErrors().warning(tr("Resolve with path ending with empty path, matches nothing."))
+ .handle(errorHandler);
+ branchExhausted = true; // allow and visit all?
+ break;
+ }
+ toFind = path[iPath++];
+ } while (toFind.headKind() == Path::Kind::Empty);
+ QVector<Path::Kind> validFind({Path::Kind::Key, Path::Kind::Field, Path::Kind::Field, Path::Kind::Index});
+ if (!validFind.contains(toFind.headKind())) {
+ myResolveErrors().error(tr("After an empty path only key, field or indexes are supported, not %1.").arg(toFind.toString()))
+ .handle(errorHandler);
+ branchExhausted = true; // allow and visit all?
+ return false;
+ }
+ if (!branchExhausted)
+ visitChildren(Path(),[toFind, &toDos, iPath](Path, const DomItem &item, bool) {
+ // avoid non directly attached?
+ DomItem newItem = item[toFind];
+ if (newItem)
+ toDos.append({newItem, iPath});
+ return true;
+ }, VisitOption::Recurse | VisitOption::VisitAdopted | VisitOption::NoPath);
+ branchExhausted = true;
+ break;
+ }
+ case Path::Kind::Root:
+ myResolveErrors().error(tr("Root path is supported only at the beginning, and only once, found %1 at %2 in %3")
+ .arg(cNow.toString()).arg(iPath -1).arg(path.toString())).handle(errorHandler);
+ return false;
+ case Path::Kind::Current:
+ {
+ PathCurrent current = cNow.headCurrent();
+ switch (current) {
+ case PathCurrent::Other:
+ // todo
+ case PathCurrent::Obj:
+ if (domKind() != DomKind::Object)
+ it = it.containingObject();
+ break;
+ case PathCurrent::ObjChain:
+
+ case PathCurrent::ScopeChain:
+ case PathCurrent::Component:
+ it = it.component();
+ break;
+ case PathCurrent::Module:
+ case PathCurrent::Ids:
+ it = it.component().field(Fields::ids);
+ break;
+ case PathCurrent::Types:
+ it = it.component()[Fields::exports];
+ break;
+ case PathCurrent::LookupStrict:
+ case PathCurrent::LookupDynamic:
+ case PathCurrent::Lookup:
+ if (current == PathCurrent::Lookup) {
+ DomItem strict = it.component().field(u"~strictLookup~");
+ if (!strict)
+ strict = it.environment().field(u"defaultStrictLookup");
+ if (strict && strict.value().toBool())
+ current = PathCurrent::LookupStrict;
+ else
+ current = PathCurrent::LookupDynamic;
+ }
+ if (current == PathCurrent::LookupStrict) {
+ myResolveErrors().error(tr("@lookupStrinct unimplemented"))
+ .handle(errorHandler);
+ return false;
+ } else if (current == PathCurrent::LookupDynamic) {
+ // expand, add self, prototype, components, prototype, global
+ DomItem proto = it.component()[u"~prototype~"];
+ if (proto) {
+ DomItem pVal = proto[u"get"];
+ if (!pVal) {
+ myResolveErrors().warning(tr("Could not find prototype %1").arg(proto.toString()))
+ .handle(errorHandler);
+ } else {
+ toDos.append({proto, iPath - 1});
+ }
+ }
+ myResolveErrors().error(tr("@lookupDynamic unimplemented"))
+ .handle(errorHandler);
+ return false;
+ } else {
+ myResolveErrors().error(tr("Unexpected Current path component %1").arg(cNow.headName()))
+ .handle(errorHandler);
+ return false;
+ }
+ break;
+ }
+ break;
+ }
+ case Path::Kind::Any:
+ visitChildren(Path(), [&toDos, iPath](Path, const DomItem &item, bool) {
+ toDos.append({item, iPath});
+ return true;
+ }, VisitOption::VisitAdopted);
+ branchExhausted = true;
+ break;
+ case Path::Kind::Filter:
+ if (cNow.headFilter() && !cNow.headFilter()(it))
+ branchExhausted = true;
+ break;
+ }
+ }
+ // visit the resolved path
+ if (!branchExhausted && iPath == path.length() && !visitor(fPath, it))
+ return false;
+ }
+ return true;
+}
+
+DomItem DomItem::path(Path p, ErrorHandler errorHandler) const
+{
+ if (!p)
+ return *this;
+ DomItem res;
+ resolve(p, [&res](Path, DomItem it) {
+ res = it;
+ return false;
+ }, errorHandler);
+ return res;
+}
+
+DomItem DomItem::path(QString p, ErrorHandler errorHandler) const
+{
+ return path(Path::fromString(p, errorHandler));
+}
+
+DomItem DomItem::path(QStringView p, ErrorHandler errorHandler) const
+{
+ return path(Path::fromString(p, errorHandler));
+}
+
+QList<QString> const DomItem::fields() const
+{
+ return base()->fields(*this);
+}
+
+DomItem DomItem::field(QStringView name) const
+{
+ return base()->field(*this, name);
+}
+
+index_type DomItem::indexes() const
+{
+ return base()->indexes(*this);
+}
+
+DomItem DomItem::index(index_type i) const
+{
+ return base()->index(*this, i);
+}
+
+QSet<QString> const DomItem::keys() const
+{
+ return base()->keys(*this);
+}
+
+DomItem DomItem::key(QString name) const
+{
+ return base()->key(*this, name);
+}
+
+bool DomItem::visitChildren(
+ Path basePath,
+ DomItem::ChildrenVisitor visitor,
+ VisitOptions options,
+ DomItem::ChildrenVisitor openingVisitor,
+ DomItem::ChildrenVisitor closingVisitor) const
+{
+ if (!*this)
+ return true;
+ if (visitor && ! visitor(basePath, *this, true))
+ return false;
+ if (openingVisitor && !openingVisitor(basePath, *this, true))
+ return true;
+ auto atEnd = qScopeGuard([closingVisitor, basePath, this](){
+ if (closingVisitor)
+ closingVisitor(basePath, *this, true);
+ });
+ return base()->iterateDirectSubpathsConst(*this,
+ [this, basePath, visitor, openingVisitor, closingVisitor, options]
+ (Path p, const DomItem &item)
+ {
+ Path pNow;
+ if (!(options & VisitOption::NoPath))
+ pNow = basePath.subPath(p);
+ if (item.containingObject() != *this) {
+ if (!(options & VisitOption::VisitAdopted))
+ return true;
+ if (visitor && !visitor(pNow, item, false))
+ return false;
+ if (openingVisitor && !openingVisitor(pNow, item, false))
+ return true;
+ if (closingVisitor)
+ closingVisitor(pNow, item, false);
+ } else {
+ return item.visitChildren(pNow, visitor, options, openingVisitor, closingVisitor);
+ }
+ return true;
+ });
+}
+
+DomItem DomItem::operator[](const QString &cName) const
+{
+ if (internalKind() == DomType::Map)
+ return key(cName);
+ return field(cName);
+}
+
+DomItem DomItem::operator[](QStringView cName) const
+{
+ if (internalKind() == DomType::Map)
+ return key(cName.toString());
+ return field(cName);
+}
+
+DomItem DomItem::operator[](Path p) const
+{
+ return path(p);
+}
+
+QCborValue DomItem::value() const
+{
+ if (internalKind() == DomType::ConstantData)
+ return static_cast<ConstantData const *>(base())->value();
+ return QCborValue();
+}
+
+void DomItem::dumpPtr(Sink sink) const
+{
+ sink(u"DomItem{ topPtr:");
+ sink(QString::number((quintptr)m_top.get(),16));
+ sink(u", ownerPtr:");
+ sink(QString::number((quintptr)m_owner.get(),16));
+ sink(u", m_basePtr:");
+ sink(QString::number((quintptr)m_base,16));
+ sink(u", basePtr:");
+ sink(QString::number((quintptr)base(),16));
+ sink(u"}");
+}
+
+void DomItem::dump(Sink s, int indent) const
+{
+ base()->dump(*this, s, indent);
+}
+
+QString DomItem::toString() const
+{
+ return dumperToString([this](Sink s){ dump(s); });
+}
+
+int DomItem::derivedFrom() const
+{
+ if (m_owner)
+ return m_owner->derivedFrom();
+ return 0;
+}
+
+int DomItem::revision() const {
+ if (m_owner)
+ return m_owner->revision();
+ else
+ return -1;
+}
+
+QDateTime DomItem::createdAt() const
+{
+ if (m_owner)
+ return m_owner->createdAt();
+ else
+ return QDateTime::fromMSecsSinceEpoch(0);
+}
+
+QDateTime DomItem::frozenAt() const
+{
+ if (m_owner)
+ return m_owner->frozenAt();
+ else
+ return QDateTime::fromMSecsSinceEpoch(0);
+}
+
+QDateTime DomItem::lastDataUpdateAt() const
+{
+ if (m_owner)
+ return m_owner->lastDataUpdateAt();
+ else
+ return QDateTime::fromMSecsSinceEpoch(0);
+}
+
+void DomItem::addError(ErrorMessage msg) const
+{
+ if (m_owner)
+ m_owner->addError(this->copy(m_owner), msg.withItem(*this));
+ else
+ defaultErrorHandler(msg.withItem(*this));
+}
+
+ErrorHandler DomItem::errorHandler() const
+{
+ DomItem self = *this;
+ return [self](ErrorMessage m){
+ self.addError(m);
+ };
+}
+
+void DomItem::clearErrors(ErrorGroups groups, bool iterate) const
+{
+ if (m_owner) {
+ m_owner->clearErrors(groups);
+ if (iterate)
+ iterateSubOwners([groups](DomItem i){
+ i.clearErrors(groups, true);
+ return true;
+ });
+ }
+}
+
+bool DomItem::iterateErrors(function<bool (DomItem, ErrorMessage)> visitor, bool iterate,
+ Path inPath) const
+{
+ if (m_owner) {
+ if (!m_owner->iterateErrors(owner(), visitor, inPath))
+ return false;
+ if (iterate && !iterateSubOwners([inPath, visitor](DomItem i){
+ return i.iterateErrors(visitor, true, inPath);
+ }))
+ return false;
+ }
+ return true;
+}
+
+bool DomItem::iterateSubOwners(function<bool (DomItem)> visitor) const
+{
+ if (m_owner)
+ return m_owner->iterateSubOwners(owner(), visitor);
+ return true;
+}
+
+shared_ptr<DomTop> DomItem::topPtr() const
+{
+ return m_top;
+}
+
+shared_ptr<OwningItem> DomItem::owningItemPtr() const
+{
+ return m_owner;
+}
+
+DomItem DomItem::copy(shared_ptr<OwningItem> owner, DomBase *base) const
+{
+ return DomItem(m_top, owner, base);
+}
+
+DomItem DomItem::copy(shared_ptr<OwningItem> owner) const
+{
+ return DomItem(m_top, owner, owner.get());
+}
+
+DomItem DomItem::copy(DomBase *base) const
+{
+ return DomItem(m_top, m_owner, base);
+}
+
+Subpath DomItem::subDataField(QStringView fieldName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+{
+ return subDataPath(Path::field(fieldName), value, options, loc);
+}
+
+Subpath DomItem::subDataField(QString fieldName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+{
+ return subDataPath(Path::field(fieldName), value, options, loc);
+}
+
+Subpath DomItem::subDataIndex(index_type i, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+{
+ return subDataPath(Path::index(i), value, options, loc);
+}
+
+Subpath DomItem::subDataKey(QStringView keyName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+{
+ return subDataPath(Path::key(keyName), value, options, loc);
+}
+
+Subpath DomItem::subDataKey(QString keyName, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+{
+ return subDataPath(Path::key(keyName), value, options, loc);
+}
+
+Subpath DomItem::subDataPath(Path path, QCborValue value, ConstantData::Options options, const SourceLocation & loc) const
+{
+ if (domTypeIsOwningItem(internalKind()))
+ return Subpath{path, DomItem(m_top, m_owner,
+ ConstantData(path, value, options, loc))};
+ else
+ return Subpath{path, DomItem(m_top, m_owner,
+ ConstantData(pathFromOwner().subPath(path), value, options, loc))};
+}
+
+Subpath DomItem::subReferenceField(QStringView fieldName, Path referencedObject,
+ const SourceLocation & loc) const
+{
+ return subReferencePath(Path::field(fieldName), referencedObject, loc);
+}
+
+Subpath DomItem::subReferenceField(QString fieldName, Path referencedObject, const SourceLocation & loc) const
+{
+ return subReferencePath(Path::field(fieldName), referencedObject, loc);
+}
+
+Subpath DomItem::subReferenceKey(QStringView keyName, Path referencedObject, const SourceLocation & loc) const
+{
+ return subReferencePath(Path::key(keyName), referencedObject,loc);
+}
+
+Subpath DomItem::subReferenceKey(QString keyName, Path referencedObject, const SourceLocation & loc) const
+{
+ return subReferencePath(Path::key(keyName), referencedObject, loc);
+}
+
+Subpath DomItem::subReferenceIndex(index_type i, Path referencedObject, const SourceLocation & loc) const
+{
+ return subReferencePath(Path::index(i), referencedObject, loc);
+}
+
+Subpath DomItem::subReferencePath(Path path, Path referencedObject, const SourceLocation & loc) const
+{
+ if (domTypeIsOwningItem(internalKind()))
+ return Subpath{path, DomItem(m_top, m_owner,
+ Reference(referencedObject, path, loc))};
+ else
+ return Subpath{path, DomItem(m_top, m_owner,
+ Reference(referencedObject, pathFromOwner().subPath(path), loc))};
+}
+
+Subpath DomItem::toSubField(QStringView fieldName) const
+{
+ return Subpath{Path::field(fieldName), *this};
+}
+
+Subpath DomItem::toSubField(QString fieldName) const
+{
+ return Subpath{Path::field(fieldName), *this};
+}
+
+Subpath DomItem::toSubKey(QStringView keyName) const
+{
+ return Subpath{Path::key(keyName), *this};
+}
+
+Subpath DomItem::toSubKey(QString keyName) const
+{
+ return Subpath{Path::key(keyName), *this};
+}
+
+Subpath DomItem::toSubIndex(index_type i) const
+{
+ return Subpath{Path::index(i), *this};
+}
+
+Subpath DomItem::toSubPath(Path subPath) const
+{
+ return Subpath{subPath, *this};
+}
+
+Subpath DomItem::subList(const List &list) const
+{
+ return Subpath{list.pathFromOwner().last(), DomItem(m_top, m_owner, list)};
+}
+
+Subpath DomItem::subMap(const Map &map) const
+{
+ return Subpath{map.pathFromOwner().last(), DomItem(m_top, m_owner, map)};
+}
+
+Subpath DomItem::subObjectWrap(const SimpleObjectWrap &obj) const
+{
+ return Subpath{obj.pathFromOwner().last(), DomItem(m_top, m_owner, obj)};
+}
+
+DomItem::DomItem():
+ DomItem(shared_ptr<DomTop>(), shared_ptr<OwningItem>(), nullptr)
+{
+}
+
+DomItem::DomItem(std::shared_ptr<DomEnvironment> envPtr):
+ DomItem(envPtr, envPtr, envPtr.get())
+{}
+
+DomItem::DomItem(std::shared_ptr<DomUniverse> universePtr):
+ DomItem(universePtr, universePtr, universePtr.get())
+{}
+
+DomItem::DomItem(std::shared_ptr<DomTop> top, std::shared_ptr<OwningItem> owner, DomBase *base):
+ m_top(top), m_owner(owner), m_base(base)
+{
+ if (m_base == nullptr || m_base->kind() == DomType::Empty) { // avoid null ptr, and allow only a single kind of Empty
+ m_top.reset();
+ m_owner.reset();
+ m_base = nullptr;
+ }
+}
+
+DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, Map map):
+ m_top(top), m_owner(owner), m_base(nullptr),
+ inlineEl(map)
+{}
+
+DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, List list):
+ m_top(top), m_owner(owner), m_base(nullptr),
+ inlineEl(list)
+{}
+
+DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, ConstantData data):
+ m_top(top), m_owner(owner), m_base(nullptr),
+ inlineEl(data)
+{}
+
+DomItem::DomItem(shared_ptr<DomTop> top, shared_ptr<OwningItem> owner, Reference reference):
+ m_top(top), m_owner(owner), m_base(nullptr), inlineEl(reference)
+{}
+
+DomItem::DomItem(std::shared_ptr<DomTop> top, std::shared_ptr<OwningItem> owner, SimpleObjectWrap wrapper):
+ m_top(top), m_owner(owner), m_base(nullptr), inlineEl(wrapper)
+{}
+
+Empty::Empty()
+{}
+
+Path Empty::pathFromOwner(const DomItem &) const
+{
+ return Path();
+}
+
+Path Empty::canonicalPath(const DomItem &) const
+{
+ return Path();
+}
+
+bool Empty::iterateDirectSubpaths(DomItem &, function<bool (Path, DomItem &)>)
+{
+ return true;
+}
+
+DomItem Empty::containingObject(const DomItem &self) const
+{
+ return self;
+}
+
+void Empty::dump(const DomItem &, Sink s, int) const
+{
+ s(u"null");
+}
+
+Map::Map(Path pathFromOwner, Map::LookupFunction lookup, Keys keys, QString targetType)
+ : DomElement(pathFromOwner), m_lookup(lookup), m_keys(keys), m_targetType(targetType)
+{}
+
+quintptr Map::id() const
+{
+ return quintptr(0);
+}
+
+bool Map::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)>visitor)
+{
+ for (QString k:keys(self)) {
+ DomItem el = key(self, k);
+ if (!visitor(Path::key(k), el))
+ return false;
+ }
+ return true;
+}
+
+const QSet<QString> Map::keys(const DomItem &self) const
+{
+ return m_keys(self);
+}
+
+DomItem Map::key(const DomItem &self, QString name) const
+{
+ return m_lookup(self, name);
+}
+
+void DomBase::dump(const DomItem &self, Sink sink, int indent) const
+{
+ bool comma = false;
+ DomKind dK = self.domKind();
+ switch (dK) {
+ case DomKind::Object:
+ sink(u"{ \"~type~\":");
+ sinkEscaped(sink, typeName());
+ comma = true;
+ break;
+ case DomKind::Value:
+ {
+ QJsonValue v = value().toJsonValue();
+ if (v.isString())
+ sinkEscaped(sink, v.toString());
+ else if (v.isBool())
+ if (v.toBool())
+ sink(u"true");
+ else
+ sink(u"false");
+ else if (v.isDouble())
+ if (value().isInteger())
+ sink(QString::number(value().toInteger()));
+ else
+ sink(QString::number(v.toDouble()));
+ else {
+ sink(QString::fromUtf8(QJsonDocument::fromVariant(v.toVariant()).toJson()));
+ }
+ break;
+ }
+ case DomKind::Empty:
+ sink(u"null");
+ break;
+ case DomKind::List:
+ sink(u"[");
+ break;
+ case DomKind::Map:
+ sink(u"{");
+ break;
+ }
+ auto closeParens = qScopeGuard(
+ [dK, sink, indent]{
+ switch (dK) {
+ case DomKind::Object:
+ sinkNewline(sink, indent);
+ sink(u"}");
+ break;
+ case DomKind::Value:
+ break;
+ case DomKind::Empty:
+ break;
+ case DomKind::List:
+ sinkNewline(sink, indent);
+ sink(u"]");
+ break;
+ case DomKind::Map:
+ sinkNewline(sink, indent);
+ sink(u"}");
+ break;
+ }
+ });
+ index_type idx = 0;
+ iterateDirectSubpathsConst(self, [&comma, &idx, dK, sink, indent, self](Path p, const DomItem &i) {
+ if (comma)
+ sink(u",");
+ else
+ comma = true;
+ switch (p.headKind()) {
+ case Path::Kind::Field:
+ sinkNewline(sink, indent + 2);
+ if (dK != DomKind::Object)
+ sink(u"UNEXPECTED ENTRY ERROR:");
+ sinkEscaped(sink, p.headName());
+ sink(u":");
+ break;
+ case Path::Kind::Key:
+ sinkNewline(sink, indent + 2);
+ if (dK != DomKind::Map)
+ sink(u"UNEXPECTED ENTRY ERROR:");
+ sinkEscaped(sink, p.headName());
+ sink(u":");
+ break;
+ case Path::Kind::Index:
+ sinkNewline(sink, indent + 2);
+ if (dK != DomKind::List)
+ sink(u"UNEXPECTED ENTRY ERROR:");
+ else if (idx++ != p.headIndex())
+ sink(u"OUT OF ORDER ARRAY:");
+ break;
+ default:
+ sinkNewline(sink, indent + 2);
+ sink(u"UNEXPECTED PATH KIND ERROR (ignored)");
+ break;
+ }
+ DomItem cObj=i.container();
+ if (cObj == self) {
+ i.dump(sink, indent + 2);
+ } else {
+ sink(uR"({ "~type~": "Reference", "immediate": true, "referredObjectPath":")");
+ i.canonicalPath().dump([sink](QStringView s){ sinkEscaped(sink, s, EscapeOptions::NoOuterQuotes); });
+ sink(u"\"}");
+ }
+ return true;
+ });
+}
+
+List::List(Path pathFromOwner, List::LookupFunction lookup, List::Length length,
+ List::IteratorFunction iterator, QString elType):
+ DomElement(pathFromOwner), m_lookup(lookup), m_length(length), m_iterator(iterator),
+ m_elType(elType)
+{}
+
+quintptr List::id() const
+{
+ return quintptr(0);
+}
+
+void List::dump(const DomItem &self, Sink sink, int indent) const
+{
+ bool first = true;
+ sink(u"[");
+ iterateDirectSubpathsConst(self, [indent, &first, sink](Path, const DomItem &item) {
+ if (first)
+ first = false;
+ else
+ sink(u",");
+ sinkNewline(sink, indent + 2);
+ item.dump(sink, indent + 2);
+ return true;
+ });
+ sink(u"]");
+}
+
+bool List::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)>visitor)
+{
+ if (m_iterator) {
+ return m_iterator(self, [visitor](index_type i, DomItem &item){
+ return visitor(Path::empty().subIndex(i), item);
+ });
+ }
+ index_type len = indexes(self);
+ for (index_type i = 0; i < len; ++i) {
+ DomItem idx = index(self, i);
+ if (!visitor(Path::empty().subIndex(i), idx))
+ return false;
+ }
+ return true;
+}
+
+index_type List::indexes(const DomItem &self) const
+{
+ return m_length(self);
+}
+
+DomItem List::index(const DomItem &self, index_type index) const
+{
+ return m_lookup(self, index);
+}
+
+DomElement::DomElement(Path pathFromOwner, const SourceLocation & loc):
+ loc(loc), m_pathFromOwner(pathFromOwner)
+{
+}
+
+Path DomElement::pathFromOwner(const DomItem &) const
+{
+ Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
+ return m_pathFromOwner;
+}
+
+Path DomElement::canonicalPath(const DomItem &self) const
+{
+ Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
+ return self.owner().canonicalPath().subPath(m_pathFromOwner);
+}
+
+DomItem DomElement::containingObject(const DomItem &self) const
+{
+ Q_ASSERT(m_pathFromOwner && "uninitialized DomElement");
+ return DomBase::containingObject(self);
+}
+
+void DomElement::updatePathFromOwner(Path newPath)
+{
+ //if (!domTypeCanBeInline(kind()))
+ m_pathFromOwner = newPath;
+}
+
+SourceLocation DomElement::location(const DomItem &) const
+{
+ return loc;
+}
+
+Reference::Reference(Path referredObject, Path pathFromOwner, const SourceLocation & loc):
+ DomElement(pathFromOwner, loc), referredObjectPath(referredObject)
+{
+}
+
+quintptr Reference::id() const
+{
+ return quintptr(0);
+}
+
+
+bool Reference::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ if (!self.subDataField(Fields::referredObjectPath, referredObjectPath.toString()).visit(visitor))
+ return false;
+ DomItem res = get(self);
+ if (!visitor(Path::field(Fields::get), res))
+ return false;
+ return true;
+}
+
+DomItem Reference::field(const DomItem &self, QStringView name) const
+{
+ if (Fields::referredObjectPath == name)
+ return self.subDataField(Fields::referredObjectPath, referredObjectPath.toString()).item;
+ if (Fields::get == name)
+ return get(self);
+ return DomItem();
+}
+
+const QList<QString> Reference::fields(const DomItem &) const
+{
+ return QList<QString>({QString::fromUtf16(Fields::referredObjectPath), QString::fromUtf16(Fields::get)});
+}
+
+DomItem Reference::index(const DomItem &, index_type) const {
+ return DomItem();
+}
+
+DomItem Reference::key(const DomItem &, QString) const {
+ return DomItem();
+}
+
+DomItem Reference::get(const DomItem &self) const
+{
+ if (!referredObjectPath)
+ return DomItem();
+ return self[referredObjectPath];
+}
+
+/*!
+\internal
+\class QQmlJS::Dom::OwningItem
+
+\brief A DomItem that owns other DomItems and is managed through a shared pointer
+
+This is the unit of update of the Dom model, only OwningItem can be updated after
+exposed, to update a single element one has to copy its owner, change it, and expose an new one.
+The non owning contents of an exposed OwiningItem can safely be considered immutable, and pointers
+to them must remain valid for the whole lifetime of the OwningItem (that is managed with
+shared_ptr), only the sub OwningItems *might* be change.
+The OwningItem has a mutex that controls it access (mainly for the errors, observers, and sub
+OwningItems), Access to the rest is *not* controlled, it should either be by a single thread
+(during construction), or immutable (after pubblication).
+
+*/
+
+OwningItem::OwningItem(int derivedFrom):
+ m_derivedFrom(derivedFrom), m_revision(nextRevision()),
+ m_createdAt(QDateTime::currentDateTime()), m_lastDataUpdateAt(m_createdAt), m_frozenAt(QDateTime::fromMSecsSinceEpoch(0))
+{}
+
+OwningItem::OwningItem(int derivedFrom, QDateTime lastDataUpdateAt):
+ m_derivedFrom(derivedFrom), m_revision(nextRevision()),
+ m_createdAt(QDateTime::currentDateTime()), m_lastDataUpdateAt(lastDataUpdateAt), m_frozenAt(QDateTime::fromMSecsSinceEpoch(0))
+{}
+
+OwningItem::OwningItem(const OwningItem &o):
+ m_derivedFrom(o.revision()), m_revision(nextRevision()),
+ m_createdAt(QDateTime::currentDateTime()), m_lastDataUpdateAt(o.lastDataUpdateAt()), m_frozenAt(QDateTime::fromMSecsSinceEpoch(0))
+{
+ QMultiMap<Path, ErrorMessage> my_errors;
+ {
+ QMutexLocker l1(o.mutex());
+ my_errors = o.m_errors;
+
+ }
+ {
+ QMutexLocker l2(mutex());
+ m_errors = my_errors;
+ }
+}
+
+
+int OwningItem::nextRevision()
+{
+ static QAtomicInt nextRev(0);
+ return ++nextRev;
+}
+
+bool OwningItem::iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)> visitor)
+{
+ bool cont = true;
+ QMultiMap<Path, ErrorMessage> myErrors = localErrors();
+ cont = cont && self.subMap(
+ Map(
+ self.pathFromOwner().subField(Fields::errors),
+ [myErrors](const DomItem &map, QString key) {
+ auto it = myErrors.find(Path::fromString(key));
+ if (it != myErrors.end())
+ return map.subDataKey(
+ key, it->toCbor(),
+ ConstantData::Options::FirstMapIsFields, it->location).item;
+ else
+ return DomItem();
+ }, [myErrors](const DomItem &) {
+ QSet<QString> res;
+ auto it = myErrors.keyBegin();
+ auto end = myErrors.keyEnd();
+ while (it != end)
+ res.insert(it++->toString());
+ return res;
+ }, QLatin1String("ErrorMessages"))).visit(visitor);
+ return cont;
+}
+
+Path OwningItem::pathFromOwner(const DomItem &) const
+{
+ return Path();
+}
+
+DomItem OwningItem::containingObject(const DomItem &self) const
+{
+ Source s = self.canonicalPath().split();
+ if (s.pathFromSource) {
+ if (!s.pathToSource)
+ return DomItem();
+ return self.path(s.pathToSource);
+ }
+ return DomItem();
+}
+
+int OwningItem::derivedFrom() const
+{
+ return m_derivedFrom;
+}
+
+int OwningItem::revision() const
+{
+ return m_revision;
+}
+
+bool OwningItem::frozen() const
+{
+ return m_frozenAt > m_createdAt;
+}
+
+bool OwningItem::freeze()
+{
+ if (!frozen()) {
+ m_frozenAt = QDateTime::currentDateTime();
+ if (m_frozenAt <= m_createdAt)
+ m_frozenAt = m_createdAt.addSecs(1);
+ return true;
+ }
+ return false;
+}
+
+QDateTime OwningItem::createdAt() const
+{
+ return m_createdAt;
+}
+
+QDateTime OwningItem::lastDataUpdateAt() const
+{
+ return m_lastDataUpdateAt;
+}
+
+QDateTime OwningItem::frozenAt() const
+{
+ return m_frozenAt;
+}
+
+void OwningItem::refreshedDataAt(QDateTime tNew)
+{
+ if (m_lastDataUpdateAt < tNew) // remove check?
+ m_lastDataUpdateAt = tNew;
+}
+
+void OwningItem::addError(const DomItem &, ErrorMessage msg)
+{
+ addErrorLocal(msg);
+}
+
+void OwningItem::addErrorLocal(ErrorMessage msg)
+{
+ QMutexLocker l(mutex());
+ auto it = m_errors.constFind(msg.path);
+ while (it != m_errors.constEnd() && it->path == msg.path) {
+ if (*it++ == msg)
+ return;
+ }
+ m_errors.insert(msg.path, msg);
+}
+
+void OwningItem::clearErrors(ErrorGroups groups)
+{
+ QMutexLocker l(mutex());
+ auto it = m_errors.begin();
+ while (it != m_errors.end()) {
+ if (it->errorGroups == groups)
+ it = m_errors.erase(it);
+ else
+ ++it;
+ }
+}
+
+bool OwningItem::iterateErrors(const DomItem &self, function<bool (DomItem, ErrorMessage)> visitor,
+ Path inPath)
+{
+ QMultiMap<Path, ErrorMessage> myErrors;
+ {
+ QMutexLocker l(mutex());
+ myErrors = m_errors;
+ }
+ auto it = myErrors.lowerBound(inPath);
+ auto end = myErrors.end();
+ while (it != end && it.key().mid(0, inPath.length()) == inPath) {
+ if (!visitor(self, *it))
+ return false;
+ }
+ return true;
+}
+
+bool OwningItem::iterateSubOwners(const DomItem &self, function<bool (const DomItem &)>visitor)
+{
+ return iterateDirectSubpathsConst(self,[self, visitor](Path, const DomItem &i) {
+ if (i.owningItemPtr() != self.owningItemPtr() && i.container().id() == self.id())
+ return visitor(i);
+ return true;
+ });
+}
+
+GenericObject GenericObject::copy() const
+{
+ QMap<QString, GenericObject> newObjs;
+ auto objs = subObjects;
+ auto itO = objs.cbegin();
+ auto endO = objs.cend();
+ while (itO != endO) {
+ newObjs.insert(itO.key(),itO->copy());
+ ++itO;
+ }
+ return GenericObject(pathFromOwner(), loc, newObjs, subValues);
+}
+
+std::pair<QString, GenericObject> GenericObject::asStringPair() const
+{
+ return std::make_pair(pathFromOwner().last().headName(), *this);
+}
+
+bool GenericObject::iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)> visitor)
+{
+ bool cont = true;
+ auto itV = subValues.begin();
+ auto endV = subValues.end();
+ while (itV != endV) {
+ cont = cont && self.subDataField(itV.key(), *itV).visit(visitor);
+ ++itV;
+ }
+ auto itO = subObjects.begin();
+ auto endO = subObjects.end();
+ while (itO != endO) {
+ cont = cont && self.copy(&(*itO)).toSubField(itO.key()).visit(visitor);
+ ++itO;
+ }
+ return cont;
+}
+
+std::shared_ptr<OwningItem> GenericOwner::doCopy(const DomItem &)
+{
+ return std::make_shared<GenericOwner>(*this);
+}
+
+GenericOwner::GenericOwner(const GenericOwner &o):
+ OwningItem(o), pathFromTop(o.pathFromTop),
+ subValues(o.subValues)
+{
+ auto objs = o.subObjects;
+ auto itO = objs.cbegin();
+ auto endO = objs.cend();
+ while (itO != endO) {
+ subObjects.insert(itO.key(),itO->copy());
+ ++itO;
+ }
+}
+
+std::shared_ptr<GenericOwner> GenericOwner::makeCopy(const DomItem &self)
+{
+ return std::static_pointer_cast<GenericOwner>(doCopy(self));
+}
+
+Path GenericOwner::canonicalPath(const DomItem &) const
+{
+ return pathFromTop;
+}
+
+bool GenericOwner::iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)> visitor)
+{
+ bool cont = true;
+ auto itV = subValues.begin();
+ auto endV = subValues.end();
+ while (itV != endV) {
+ cont = cont && self.subDataField(itV.key(), *itV).visit(visitor);
+ ++itV;
+ }
+ auto itO = subObjects.begin();
+ auto endO = subObjects.end();
+ while (itO != endO) {
+ cont = cont && self.copy(&(*itO)).toSubField(itO.key()).visit(visitor);
+ ++itO;
+ }
+ return cont;
+}
+
+bool operator ==(const DomItem &o1, const DomItem &o2) {
+ if (o1.base() == o2.base())
+ return true;
+ quintptr i1 = o1.id();
+ quintptr i2 = o2.id();
+ if (i1 != i2)
+ return false;
+ if (i1 != quintptr(0))
+ return true;
+ Path p1 = o1.pathFromOwner();
+ Path p2 = o2.pathFromOwner();
+ if (p1 != p2)
+ return false;
+ return o1.owningItemPtr() == o2.owningItemPtr();
+}
+
+QString MutableDomItem::name() const
+{
+ return base().name();
+}
+
+ErrorHandler MutableDomItem::errorHandler() const
+{
+ MutableDomItem self;
+ return [self](ErrorMessage m){
+ self.addError(m);
+ };
+}
+
+QDebug operator<<(QDebug debug, const DomItem &c)
+{
+ dumperToQDebug([&c](Sink s) {
+ c.dump(s);
+ }, debug);
+ return debug;
+}
+
+QDebug operator<<(QDebug debug, const MutableDomItem &c)
+{
+ return debug.noquote().nospace() << "MutableDomItem(" << domTypeToString(c.internalKind())
+ << ", " << c.canonicalPath().toString() << ")";
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomitem_p.h b/src/qmldom/qqmldomitem_p.h
index 59199bd9af..92535b87ac 100644
--- a/src/qmldom/qqmldomitem_p.h
+++ b/src/qmldom/qqmldomitem_p.h
@@ -35,8 +35,8 @@
**
** $QT_END_LICENSE$
**/
-#ifndef QQMLDOMITEM_P_H
-#define QQMLDOMITEM_P_H
+#ifndef QMLDOMITEM_H
+#define QMLDOMITEM_H
//
// W A R N I N G
@@ -50,28 +50,1113 @@
//
#include "qqmldom_global.h"
+#include "qqmldom_fwd_p.h"
+#include "qqmldomconstants_p.h"
+#include "qqmldomstringdumper_p.h"
+#include "qqmldompath_p.h"
+#include "qqmldomerrormessage_p.h"
-#include <QtQml/private/qqmljsast_p.h>
+#include <QtCore/QMap>
+#include <QtCore/QMultiMap>
+#include <QtCore/QString>
+#include <QtCore/QStringView>
+#include <QtCore/QDebug>
+#include <QtCore/QDateTime>
+#include <QtCore/QMutex>
+#include <QtCore/QCborValue>
+#include <QtQml/private/qqmljssourcelocation_p.h>
+#include <memory>
+#include <typeinfo>
+#include <utility>
QT_BEGIN_NAMESPACE
namespace QQmlJS {
// we didn't have enough 'O's to properly name everything...
namespace Dom {
+
class Path;
-class DomItem {
+bool domTypeIsObjWrap(DomType k);
+bool domTypeIsDomElement(DomType);
+bool domTypeIsOwningItem(DomType);
+bool domTypeIsExternalItem(DomType k);
+bool domTypeIsTopItem(DomType k);
+bool domTypeIsContainer(DomType k);
+bool domTypeCanBeInline(DomType k);
+
+QMLDOM_EXPORT QMap<DomType,QString> domTypeToStringMap();
+QMLDOM_EXPORT QString domTypeToString(DomType k);
+
+class QMLDOM_EXPORT DomBase{
+public:
+ virtual ~DomBase() = default;
+
+ // minimal overload set:
+ virtual DomType kind() const = 0;
+ virtual DomKind domKind() const;
+ virtual Path pathFromOwner(const DomItem &self) const = 0;
+ virtual Path canonicalPath(const DomItem &self) const = 0;
+ virtual bool iterateDirectSubpaths(DomItem &self, std::function<bool(Path, DomItem &)>) = 0; // iterates the *direct* subpaths, returns false if a quick end was requested
+ bool iterateDirectSubpathsConst(const DomItem &self, std::function<bool(Path, const DomItem &)>) const; // iterates the *direct* subpaths, returns false if a quick end was requested
+
+ virtual DomItem containingObject(const DomItem &self) const; // the DomItem corresponding to the canonicalSource source
+ virtual void dump(const DomItem &, Sink sink, int indent) const;
+ virtual quintptr id() const;
+ virtual QString typeName() const;
+
+ virtual QList<QString> const fields(const DomItem &self) const;
+ virtual DomItem field(const DomItem &self, QStringView name) const;
+
+ virtual index_type indexes(const DomItem &self) const;
+ virtual DomItem index(const DomItem &self, index_type index) const;
+
+ virtual QSet<QString> const keys(const DomItem &self) const;
+ virtual DomItem key(const DomItem &self, QString name) const;
+
+ virtual QString canonicalFilePath(const DomItem &self) const;
+ virtual SourceLocation location(const DomItem &self) const;
+
+ virtual QCborValue value() const {
+ return QCborValue();
+ }
+
+};
+
+inline DomKind kind2domKind(DomType k)
+{
+ switch (k) {
+ case DomType::Empty:
+ return DomKind::Empty;
+ case DomType::List:
+ return DomKind::List;
+ case DomType::Map:
+ return DomKind::Map;
+ case DomType::ConstantData:
+ return DomKind::Value;
+ default:
+ return DomKind::Object;
+ }
+}
+
+class QMLDOM_EXPORT Empty: public DomBase {
+public:
+ constexpr static DomType kindValue = DomType::Empty;
+ DomType kind() const override { return kindValue; }
+
+ Empty();
+ Path pathFromOwner(const DomItem &self) const override;
+ Path canonicalPath(const DomItem &self) const override;
+ DomItem containingObject(const DomItem &self) const override;
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)>) override;
+ void dump(const DomItem &, Sink s, int indent) const override;
+};
+
+class QMLDOM_EXPORT DomElement: public DomBase {
+protected:
+ DomElement& operator=(const DomElement&) = default;
+public:
+ DomElement(Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation());
+ DomElement(const DomElement &o) = default;
+ Path pathFromOwner(const DomItem &self) const override;
+ Path pathFromOwner() const { return m_pathFromOwner; }
+ Path canonicalPath(const DomItem &self) const override;
+ DomItem containingObject(const DomItem &self) const override;
+ virtual void updatePathFromOwner(Path newPath);
+ SourceLocation location(const DomItem &self) const override;
+
+ SourceLocation loc;
+private:
+ Path m_pathFromOwner;
+};
+
+class QMLDOM_EXPORT Map: public DomElement {
+public:
+ constexpr static DomType kindValue = DomType::Map;
+ DomType kind() const override { return kindValue; }
+
+ using LookupFunction = std::function<DomItem (const DomItem&, QString)>;
+ using Keys = std::function<QSet<QString> (const DomItem &)>;
+ Map(Path pathFromOwner, LookupFunction lookup, Keys keys, QString targetType);
+ quintptr id() const override;
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool(Path, DomItem &)>) override;
+ QSet<QString> const keys(const DomItem &self) const override;
+ DomItem key(const DomItem &self, QString name) const override;
+
+ template <typename T>
+ static Map fromMultiMapRef(Path pathFromOwner, QMultiMap<QString,T> &mmap, std::function<DomItem(const DomItem &, Path, T&)> elWrapper);
+ template <typename T>
+ static Map fromMapRef(Path pathFromOwner, QMap<QString,T> &mmap, std::function<DomItem(const DomItem &, Path, T&)> elWrapper);
+private:
+ LookupFunction m_lookup;
+ Keys m_keys;
+ QString m_targetType;
+};
+
+class QMLDOM_EXPORT List: public DomElement {
+public:
+ constexpr static DomType kindValue = DomType::List;
+ DomType kind() const override { return kindValue; }
+
+ using LookupFunction = std::function<DomItem (const DomItem &, index_type)>;
+ using Length = std::function<index_type (const DomItem &)>;
+ using IteratorFunction = std::function<bool (const DomItem &, std::function<bool(index_type,DomItem &)>)>;
+
+ List(Path pathFromOwner, LookupFunction lookup, Length length, IteratorFunction iterator, QString elType);
+ quintptr id() const override;
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool(Path, DomItem &)>) override;
+ void dump(const DomItem &, Sink s, int indent) const override;
+ index_type indexes(const DomItem &self) const override;
+ DomItem index(const DomItem &self, index_type index) const override;
+
+ template<typename T>
+ static List fromQList(Path pathFromOwner, QList<T> list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options = ListOptions::Normal);
+ template<typename T>
+ static List fromQListRef(Path pathFromOwner, QList<T> &list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options = ListOptions::Normal);
+private:
+ LookupFunction m_lookup;
+ Length m_length;
+ IteratorFunction m_iterator;
+ QString m_elType;
+};
+
+class QMLDOM_EXPORT ConstantData: public DomElement {
+public:
+ constexpr static DomType kindValue = DomType::ConstantData;
+ DomType kind() const override { return kindValue; }
+
+ enum class Options {
+ MapIsMap,
+ FirstMapIsFields
+ };
+
+ ConstantData(Path pathFromOwner, QCborValue value, Options options = Options::MapIsMap, const SourceLocation & loc = SourceLocation());
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool(Path, DomItem &)>) override;
+ quintptr id() const override;
+ DomKind domKind() const override;
+ QCborValue value() const override { return m_value; }
+ Options options() const { return m_options; }
+private:
+ QCborValue m_value;
+ Options m_options;
+};
+
+class QMLDOM_EXPORT SimpleObjectWrap: public DomElement {
+public:
+ constexpr static DomType kindValue = DomType::SimpleObjectWrap;
+ DomType kind() const override { return kindValue; }
+
+ template <typename T>
+ static SimpleObjectWrap fromDataObject(
+ Path pathFromOwner, T const & val,
+ std::function<QCborValue(T const &)> toData,
+ const SourceLocation & loc = SourceLocation(),
+ DomType kind = kindValue,
+ DomKind domKind = DomKind::Object,
+ QString typeName = QString());
+
+ template <typename T>
+ static SimpleObjectWrap fromObjectRef(
+ Path pathFromOwner, T &value,
+ std::function<bool(DomItem &, T &val, std::function<bool(Path, DomItem &)>)> directSubpathsIterate,
+ const SourceLocation & loc = SourceLocation(),
+ DomType kind = kindValue,
+ QString typeName = QString(),
+ DomKind domKind = DomKind::Object);
+
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool(Path, DomItem &)>) override;
+ quintptr id() const override;
+ QString typeName() const override { return m_typeName; }
+ DomType internalKind() const { return m_kind; }
+ DomKind domKind() const override { return m_domKind; }
+ template <typename T>
+ T const *as() const
+ {
+ return m_value.value<T*>();
+ }
+ template <typename T>
+ T *mutableAs()
+ {
+ return m_value.value<T*>();
+ }
+private:
+ SimpleObjectWrap(
+ Path pathFromOwner, QVariant value,
+ std::function<bool(DomItem &, QVariant, std::function<bool(Path, DomItem &)>)> directSubpathsIterate,
+ DomType kind = kindValue,
+ DomKind domKind = DomKind::Object,
+ QString typeName = QString(),
+ const SourceLocation & loc = SourceLocation());
+
+ DomType m_kind;
+ DomKind m_domKind;
+ QString m_typeName;
+ QVariant m_value;
+ std::function<bool(DomItem &, QVariant, std::function<bool(Path, DomItem &)>)> m_directSubpathsIterate;
+};
+
+class QMLDOM_EXPORT Reference: public DomElement {
+public:
+ constexpr static DomType kindValue = DomType::Reference;
+ DomType kind() const override { return kindValue; }
+
+ Reference(Path referredObject = Path(), Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation());
+ quintptr id() const override;
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool(Path, DomItem &)>) override;
+ DomItem field(const DomItem &self, QStringView name) const override;
+ QList<QString> const fields(const DomItem &self) const override;
+ index_type indexes(const DomItem &) const override {
+ return 0;
+ }
+ DomItem index(const DomItem &, index_type) const override;
+ QSet<QString> const keys(const DomItem &) const override {
+ return {};
+ }
+ DomItem key(const DomItem &, QString) const override;
+
+ DomItem get(const DomItem &self) const;
+
+ Path referredObjectPath;
+};
+
+class MutableDomItem;
+
+class QMLDOM_EXPORT DomItem {
+ Q_DECLARE_TR_FUNCTIONS(DomItem);
public:
+ using Callback = function<void(Path, const DomItem &, const DomItem &)>;
+
+ using InternalKind = DomType;
+ using Visitor = std::function<bool(Path, const DomItem &)>;
+ using ChildrenVisitor = std::function<bool(Path, const DomItem &, bool)>;
+
+ static ErrorGroup domErrorGroup;
+ static ErrorGroups myErrors();
+ static ErrorGroups myResolveErrors();
+
+ operator bool() const { return base()->kind() != DomType::Empty; }
+ InternalKind internalKind() const {
+ InternalKind res = base()->kind();
+ if (res == InternalKind::SimpleObjectWrap)
+ return static_cast<SimpleObjectWrap const *>(base())->internalKind();
+ return res;
+ }
+ DomKind domKind() const {
+ return base()->domKind();
+ }
+
Path canonicalPath() const;
- QString canonicalFilePath() const;
- SourceLocation location() const;
+ DomItem containingObject() const;
+ DomItem container() const;
+ DomItem component() const;
+ DomItem owner() const;
+ DomItem top() const;
+ DomItem environment() const;
+ DomItem universe() const;
+
+ // convenience getters
+ QString name() const;
+ DomItem qmlChildren() const;
+ DomItem annotations() const;
+
+ bool resolve(Path path, Visitor visitor, ErrorHandler errorHandler, ResolveOptions options = ResolveOption::None, Path fullPath = Path(), QList<Path> *visitedRefs = nullptr) const;
+
+ DomItem operator[](Path path) const;
+ DomItem operator[](QStringView component) const;
+ DomItem operator[](const QString &component) const;
+ DomItem operator[](const char16_t *component) const { return (*this)[QStringView(component)]; } // to avoid clash with stupid builtin ptrdiff_t[DomItem&], coming from C
+ DomItem operator[](index_type i) const { return index(i); }
+ DomItem operator[](int i) const { return index(i); }
+
+ DomItem path(Path p, ErrorHandler h = &defaultErrorHandler) const;
+ DomItem path(QString p, ErrorHandler h = &defaultErrorHandler) const;
+ DomItem path(QStringView p, ErrorHandler h = &defaultErrorHandler) const;
+
+ QList<QString> const fields() const;
+ DomItem field(QStringView name) const;
+
+ index_type indexes() const;
+ DomItem index(index_type) const;
+
+ QSet<QString> const keys() const;
+ DomItem key(QString name) const;
+
+ bool visitChildren(Path basePath, ChildrenVisitor visitor, VisitOptions options = VisitOption::VisitAdopted, ChildrenVisitor openingVisitor = ChildrenVisitor(), ChildrenVisitor closingVisitor = ChildrenVisitor()) const;
+
+ quintptr id() const { return base()->id(); }
+ Path pathFromOwner() const { return base()->pathFromOwner(*this); }
+ QString canonicalFilePath() const { return base()->canonicalFilePath(*this); }
+ SourceLocation location() const { return base()->location(*this); }
+
+ QCborValue value() const;
+
+ void dumpPtr(Sink) const;
+ void dump(Sink, int indent = 0) const;
+ QString toString() const;
+
+ // OwnigItem elements
+ int derivedFrom() const;
+ int revision() const;
+ QDateTime createdAt() const;
+ QDateTime frozenAt() const;
+ QDateTime lastDataUpdateAt() const;
+
+ void addError(ErrorMessage msg) const;
+ ErrorHandler errorHandler() const;
+ void clearErrors(ErrorGroups groups = ErrorGroups({}), bool iterate = true) const;
+ // return false if a quick exit was requested
+ bool iterateErrors(std::function<bool(DomItem source, ErrorMessage msg)> visitor, bool iterate,
+ Path inPath = Path())const;
+
+ bool iterateSubOwners(std::function<bool(DomItem owner)> visitor) const;
+
+ Subpath subDataField(QStringView fieldName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
+ Subpath subDataField(QString fieldName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
+ Subpath subDataIndex(index_type i, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
+ Subpath subDataKey(QStringView keyName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
+ Subpath subDataKey(QString keyName, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
+ Subpath subDataPath(Path path, QCborValue value, ConstantData::Options options = ConstantData::Options::MapIsMap, const SourceLocation &loc = SourceLocation()) const;
+ Subpath subReferenceField(QStringView fieldName, Path referencedObject,
+ const SourceLocation & loc = SourceLocation()) const;
+ Subpath subReferenceField(QString fieldName, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
+ Subpath subReferenceKey(QStringView keyName, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
+ Subpath subReferenceKey(QString keyName, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
+ Subpath subReferenceIndex(index_type i, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
+ Subpath subReferencePath(Path subPath, Path referencedObject, const SourceLocation & loc = SourceLocation()) const;
+
+ Subpath toSubField(QStringView fieldName) const;
+ Subpath toSubField(QString fieldName) const;
+ Subpath toSubKey(QStringView keyName) const;
+ Subpath toSubKey(QString keyName) const;
+ Subpath toSubIndex(index_type i) const;
+ Subpath toSubPath(Path subPath) const;
+ Subpath subList(const List &list) const;
+ Subpath subMap(const Map &map) const;
+ Subpath subObjectWrap(const SimpleObjectWrap &o) const;
+ template <typename T>
+ Subpath subWrapPath(Path p, T &obj, SourceLocation loc = SourceLocation()) const;
+ template <typename T>
+ Subpath subWrapField(QString p, T &obj, SourceLocation loc = SourceLocation()) const;
+ template <typename T>
+ Subpath subWrapField(QStringView p, T &obj, SourceLocation loc = SourceLocation()) const;
+ template <typename T>
+ Subpath subWrapKey(QString p, T &obj, SourceLocation loc = SourceLocation()) const;
+ template <typename T>
+ Subpath subWrapKey(QStringView p, T &obj, SourceLocation loc = SourceLocation()) const;
+ template <typename T>
+ Subpath subWrapIndex(index_type i, T &obj, SourceLocation loc = SourceLocation()) const;
DomItem();
+ DomItem(std::shared_ptr<DomEnvironment>);
+ DomItem(std::shared_ptr<DomUniverse>);
+
+ // --- start of potentially dangerous stuff, make private? ---
+
+ std::shared_ptr<DomTop> topPtr() const;
+ std::shared_ptr<OwningItem> owningItemPtr() const;
+
+ // keep the DomItem around to ensure that it doesn't get deleted
+ template <typename T, typename std::enable_if<std::is_base_of<DomBase, T>::value, bool>::type = true>
+ T const*as() const {
+ InternalKind k = base()->kind();
+ if (k == T::kindValue)
+ return static_cast<T const*>(base());
+ if (k == InternalKind::SimpleObjectWrap)
+ return static_cast<SimpleObjectWrap const *>(base())->as<T>();
+ return nullptr;
+ }
+
+ template <typename T, typename std::enable_if<!std::is_base_of<DomBase, T>::value, bool>::type = true>
+ T const*as() const {
+ InternalKind k = base()->kind();
+ if (k == InternalKind::SimpleObjectWrap)
+ return static_cast<SimpleObjectWrap const *>(base())->as<T>();
+ return nullptr;
+ }
+
+ template <typename T>
+ std::shared_ptr<T> ownerAs() const;
+
+ DomItem copy(std::shared_ptr<OwningItem> owner, DomBase *base) const;
+ DomItem copy(std::shared_ptr<OwningItem> owner) const;
+ DomItem copy(DomBase *base) const;
+private:
+ DomBase const* base() const {
+ if (m_base == nullptr)
+ return reinterpret_cast<DomBase const*>(&inlineEl);
+ return m_base;
+ }
+ template <typename T, typename std::enable_if<std::is_base_of<DomBase, T>::value, bool>::type = true>
+ T *mutableAs() {
+ InternalKind k = base()->kind();
+ if (k == T::kindValue)
+ return static_cast<T*>(mutableBase());
+ if (k == InternalKind::SimpleObjectWrap)
+ return static_cast<SimpleObjectWrap *>(mutableBase())->mutableAs<T>();
+ return nullptr;
+ }
+
+ template <typename T, typename std::enable_if<!std::is_base_of<DomBase, T>::value, bool>::type = true>
+ T *mutableAs() {
+ InternalKind k = base()->kind();
+ if (k == InternalKind::SimpleObjectWrap)
+ return static_cast<SimpleObjectWrap *>(mutableBase())->mutableAs<T>();
+ return nullptr;
+ }
+ DomBase * mutableBase() {
+ if (m_base == nullptr)
+ return reinterpret_cast<DomBase*>(&inlineEl);
+ return m_base;
+ }
+ DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, DomBase *base);
+ DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, Map map);
+ DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, List list);
+ DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, ConstantData data);
+ DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, Reference reference);
+ DomItem(std::shared_ptr<DomTop> env, std::shared_ptr<OwningItem> owner, SimpleObjectWrap wrapper);
+ friend class DomElement;
+ friend class Map;
+ friend class List;
+ friend class QmlObject;
+ friend class DomUniverse;
+ friend class DomEnvironment;
+ friend class ExternalItemInfoBase;
+ friend class ConstantData;
+ friend class MutableDomItem;
+ friend bool operator ==(const DomItem &, const DomItem &);
+ std::shared_ptr<DomTop> m_top;
+ std::shared_ptr<OwningItem> m_owner;
+ DomBase *m_base;
+ union InlineEl {
+ // Should add optimized move ops (should be able to do a bit copy of union)
+ InlineEl(): empty() { }
+ InlineEl(const InlineEl &d) {
+ switch (d.kind()){
+ case DomType::Empty:
+ Q_ASSERT((quintptr)this == (quintptr)&empty && "non C++11 compliant compiler");
+ new (&empty) Empty(d.empty);
+ break;
+ case DomType::Map:
+ Q_ASSERT((quintptr)this == (quintptr)&map && "non C++11 compliant compiler");
+ new (&map) Map(d.map);
+ break;
+ case DomType::List:
+ Q_ASSERT((quintptr)this == (quintptr)&list && "non C++11 compliant compiler");
+ new (&list) List(d.list);
+ break;
+ case DomType::ConstantData:
+ Q_ASSERT((quintptr)this == (quintptr)&data && "non C++11 compliant compiler");
+ new (&data) ConstantData(d.data);
+ break;
+ case DomType::SimpleObjectWrap:
+ Q_ASSERT((quintptr)this == (quintptr)&simpleObjectWrap && "non C++11 compliant compiler");
+ new (&simpleObjectWrap) SimpleObjectWrap(d.simpleObjectWrap);
+ break;
+ case DomType::Reference:
+ Q_ASSERT((quintptr)this == (quintptr)&reference && "non C++11 compliant compiler");
+ new (&reference) Reference(d.reference);
+ break;
+ default:
+ Q_ASSERT(false && "unexpected kind in inline element");
+ break;
+ }
+ }
+ InlineEl(const Empty &o) {
+ Q_ASSERT((quintptr)this == (quintptr)&empty && "non C++11 compliant compiler");
+ new (&empty) Empty(o);
+ }
+ InlineEl(const Map &o) {
+ Q_ASSERT((quintptr)this == (quintptr)&map && "non C++11 compliant compiler");
+ new (&map) Map(o);
+ }
+ InlineEl(const List &o){
+ Q_ASSERT((quintptr)this == (quintptr)&list && "non C++11 compliant compiler");
+ new (&list) List(o);
+ }
+ InlineEl(const ConstantData &o) {
+ Q_ASSERT((quintptr)this == (quintptr)&data && "non C++11 compliant compiler");
+ new (&data) ConstantData(o);
+ }
+ InlineEl(const SimpleObjectWrap &o) {
+ Q_ASSERT((quintptr)this == (quintptr)&simpleObjectWrap && "non C++11 compliant compiler");
+ new (&simpleObjectWrap) SimpleObjectWrap(o);
+ }
+ InlineEl(const Reference &o) {
+ Q_ASSERT((quintptr)this == (quintptr)&reference && "non C++11 compliant compiler");
+ new (&reference) Reference(o);
+ }
+ InlineEl &operator=(const InlineEl &d) {
+ Q_ASSERT(this != &d);
+ this->~InlineEl(); // destruct & construct new...
+ new (this)InlineEl(d);
+ return *this;
+ }
+ DomType kind() const {
+ return reinterpret_cast<const DomBase*>(this)->kind();
+ }
+ ~InlineEl() {
+ reinterpret_cast<const DomBase*>(this)->~DomBase();
+ }
+ Empty empty;
+ Map map;
+ List list;
+ ConstantData data;
+ SimpleObjectWrap simpleObjectWrap;
+ Reference reference;
+ } inlineEl;
};
+bool operator ==(const DomItem &o1, const DomItem &o2);
+inline bool operator !=(const DomItem &o1, const DomItem &o2) {
+ return !(o1 == o2);
}
+
+Q_DECLARE_OPERATORS_FOR_FLAGS(LoadOptions)
+
+class Subpath {
+public:
+ Path path;
+ DomItem item;
+
+ bool visit(std::function <bool(Path, DomItem &)> visitor){
+ return visitor(path, item);
+ }
+};
+
+template<typename T>
+Map Map::fromMultiMapRef(Path pathFromOwner, QMultiMap<QString,T> &mmap, std::function<DomItem(const DomItem &, Path, T&)> elWrapper)
+{
+ return Map(pathFromOwner, [&mmap, elWrapper](const DomItem &self, QString key) {
+ auto it = mmap.find(key);
+ auto end = mmap.cend();
+ if (it == end)
+ return DomItem();
+ else {
+ QList<T *> values;
+ while (it != end && it.key() == key)
+ values.append(&(*it++));
+ return self.subList(List::fromQList<T*>(self.pathFromOwner().subKey(key), values, [elWrapper](const DomItem &l, Path p,T * &el) {
+ return elWrapper(l,p,*el);
+ }, ListOptions::Reverse)).item;
+ }
+ }, [&mmap](const DomItem&){
+ return QSet<QString>(mmap.keyBegin(), mmap.keyEnd());
+ }, QLatin1String(typeid(T).name()));
}
-QT_END_NAMESPACE
-#endif // QQMLDOMITEM_P_H
+template<typename T>
+Map Map::fromMapRef(Path pathFromOwner, QMap<QString,T> &map,
+ std::function<DomItem(const DomItem &, Path, T&)> elWrapper)
+{
+ return Map(pathFromOwner, [&map, elWrapper](const DomItem &self, QString key) {
+ if (!map.contains(key))
+ return DomItem();
+ else {
+ return elWrapper(self, Path::key(key), map[key]);
+ }
+ }, [&map](const DomItem&){
+ return QSet<QString>(map.keyBegin(), map.keyEnd());
+ }, QLatin1String(typeid(T).name()));
+}
+
+template<typename T>
+List List::fromQList(Path pathFromOwner, QList<T> list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options)
+{
+ index_type len = list.length();
+ if (options == ListOptions::Reverse) {
+ return List(
+ pathFromOwner,
+ [list, elWrapper](const DomItem &self, index_type i) mutable {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, Path::index(i), list[list.length() -i - 1]);
+ }, [len](const DomItem &) {
+ return len;
+ }, nullptr, QLatin1String(typeid(T).name()));
+ } else {
+ return List(pathFromOwner,
+ [list, elWrapper](const DomItem &self, index_type i) mutable {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, Path::index(i), list[i]);
+ }, [len](const DomItem &) {
+ return len;
+ }, nullptr, QLatin1String(typeid(T).name()));
+ }
+}
+
+template<typename T>
+List List::fromQListRef(Path pathFromOwner, QList<T> &list, std::function<DomItem(const DomItem &, Path, T&)> elWrapper, ListOptions options)
+{
+ if (options == ListOptions::Reverse) {
+ return List(
+ pathFromOwner,
+ [&list, elWrapper](const DomItem &self, index_type i) {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, Path::index(i), list[list.length() -i - 1]);
+ }, [&list](const DomItem &) {
+ return list.length();
+ }, nullptr, QLatin1String(typeid(T).name()));
+ } else {
+ return List(pathFromOwner,
+ [&list, elWrapper](const DomItem &self, index_type i) {
+ if (i < 0 || i >= list.length())
+ return DomItem();
+ return elWrapper(self, Path::index(i), list[i]);
+ }, [&list](const DomItem &) {
+ return list.length();
+ }, nullptr, QLatin1String(typeid(T).name()));
+ }
+}
+
+class QMLDOM_EXPORT OwningItem: public DomBase {
+protected:
+ virtual std::shared_ptr<OwningItem> doCopy(const DomItem &self) = 0;
+public:
+ OwningItem(const OwningItem &o);
+ OwningItem(int derivedFrom=0);
+ OwningItem(int derivedFrom, QDateTime lastDataUpdateAt);
+ static int nextRevision();
+
+ Path canonicalPath(const DomItem &self) const override = 0;
+
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)>) override;
+ std::shared_ptr<OwningItem> makeCopy(const DomItem &self) {
+ return doCopy(self);
+ }
+ Path pathFromOwner(const DomItem &self) const override;
+ DomItem containingObject(const DomItem &self) const override;
+ int derivedFrom() const;
+ virtual int revision() const;
+
+ QDateTime createdAt() const;
+ virtual QDateTime lastDataUpdateAt() const;
+ virtual void refreshedDataAt(QDateTime tNew);
+
+ // explicit freeze handling needed?
+ virtual bool frozen() const;
+ virtual bool freeze();
+ QDateTime frozenAt() const;
+
+ virtual void addError(const DomItem &self, ErrorMessage msg);
+ void addErrorLocal(ErrorMessage msg);
+ void clearErrors(ErrorGroups groups = ErrorGroups({}));
+ // return false if a quick exit was requested
+ bool iterateErrors(const DomItem &self, std::function<bool(DomItem source, ErrorMessage msg)> visitor, Path inPath = Path());
+ QMultiMap<Path, ErrorMessage> localErrors() const {
+ QMutexLocker l(mutex());
+ return m_errors;
+ }
+
+
+ virtual bool iterateSubOwners(const DomItem &self, std::function<bool(const DomItem &owner)> visitor);
+
+ QBasicMutex *mutex() const { return &m_mutex; }
+private:
+ mutable QBasicMutex m_mutex;
+ int m_derivedFrom;
+ int m_revision;
+ QDateTime m_createdAt;
+ QDateTime m_lastDataUpdateAt;
+ QDateTime m_frozenAt;
+ QMultiMap<Path, ErrorMessage> m_errors;
+};
+
+template <typename T>
+std::shared_ptr<T> DomItem::ownerAs() const {
+ if (m_owner && m_owner->kind() == T::kindValue)
+ return std::static_pointer_cast<T>(m_owner);
+ return nullptr;
+}
+
+template <typename T>
+SimpleObjectWrap SimpleObjectWrap::fromDataObject(
+ Path pathFromOwner, T const & val,
+ std::function<QCborValue(T const &)> toData,
+ const SourceLocation &loc,
+ DomType kind,
+ DomKind domKind,
+ QString typeName)
+{
+ QString objectName;
+ if (!typeName.isEmpty())
+ objectName = typeName;
+ else if (kind != kindValue)
+ objectName = domTypeToStringMap()[kind];
+ else
+ objectName = QLatin1String("SimpleObjectWrap<%1>").arg(QLatin1String(typeid(T).name()));
+ return SimpleObjectWrap(
+ pathFromOwner, QVariant::fromValue(&val),
+ [toData, pathFromOwner](DomItem &self, QVariant v, std::function<bool(Path, DomItem &)> visitor){
+ ConstantData data = ConstantData(pathFromOwner, toData(*v.value<T const*>()), ConstantData::Options::FirstMapIsFields);
+ return data.iterateDirectSubpaths(self, visitor);
+ }, kind, domKind, objectName, loc);
+}
+
+template <typename T>
+SimpleObjectWrap SimpleObjectWrap::fromObjectRef(
+ Path pathFromOwner, T &value,
+ std::function<bool(DomItem &, T &val, std::function<bool(Path, DomItem &)>)> directSubpathsIterate,
+ const SourceLocation &loc,
+ DomType kind,
+ QString typeName,
+ DomKind domKind)
+{
+ return SimpleObjectWrap(
+ pathFromOwner, QVariant::fromValue(&value),
+ [directSubpathsIterate](DomItem &self, QVariant v, std::function<bool(Path, DomItem &)> visitor){
+ return directSubpathsIterate(self, *v.value<T *>(), visitor);
+ },
+ kind, domKind,
+ ((!typeName.isEmpty()) ? typeName :
+ (kind != kindValue) ? domTypeToStringMap()[kind] :
+ QStringLiteral(u"SimpleObjectWrap<%1>").arg(QLatin1String(typeid(T).name()))),
+ loc);
+}
+
+template <typename T>
+Subpath DomItem::subWrapPath(Path p, T &obj, SourceLocation loc) const {
+ return this->subObjectWrap(SimpleObjectWrap::fromObjectRef<T>(
+ this->pathFromOwner().subPath(p),
+ obj,
+ [](DomItem &self, T &fDef, std::function<bool(Path, DomItem &)> visitor) {
+ return fDef.iterateDirectSubpaths(self, visitor);
+ },loc,
+ T::kindValue));
+}
+
+template <typename T>
+Subpath DomItem::subWrapField(QString p, T &obj, SourceLocation loc) const {
+ return this->subWrapPath<T>(Path::field(p), obj, loc);
+}
+template <typename T>
+Subpath DomItem::subWrapField(QStringView p, T &obj, SourceLocation loc) const {
+ return this->subWrapPath<T>(Path::field(p), obj, loc);
+}
+template <typename T>
+Subpath DomItem::subWrapKey(QString p, T &obj, SourceLocation loc) const {
+ return this->subWrapPath<T>(Path::key(p), obj, loc);
+}
+template <typename T>
+Subpath DomItem::subWrapKey(QStringView p, T &obj, SourceLocation loc) const {
+ return this->subWrapPath<T>(Path::key(p), obj, loc);
+}
+template <typename T>
+Subpath DomItem::subWrapIndex(index_type i, T &obj, SourceLocation loc) const {
+ return this->subWrapPath<T>(Path::index(i), obj, loc);
+}
+
+// mainly for debugging purposes
+class GenericObject: public DomElement {
+public:
+ constexpr static DomType kindValue = DomType::GenericObject;
+ DomType kind() const override { return kindValue; }
+
+ GenericObject(Path pathFromOwner = Path(), const SourceLocation & loc = SourceLocation(),
+ QMap<QString, GenericObject> subObjects = {},
+ QMap<QString, QCborValue> subValues = {}):
+ DomElement(pathFromOwner, loc), subObjects(subObjects), subValues(subValues) {}
+
+ GenericObject copy() const;
+ std::pair<QString, GenericObject> asStringPair() const;
+
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)>) override;
+
+ QMap<QString, GenericObject> subObjects;
+ QMap<QString, QCborValue> subValues;
+};
+
+// mainly for debugging purposes
+class GenericOwner: public OwningItem {
+protected:
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) override;
+public:
+ constexpr static DomType kindValue = DomType::GenericOwner;
+ DomType kind() const override { return kindValue; }
+
+ GenericOwner(Path pathFromTop = Path(), int derivedFrom = 0,
+ QMap<QString, GenericObject> subObjects = {},
+ QMap<QString, QCborValue> subValues = {}):
+ OwningItem(derivedFrom), pathFromTop(pathFromTop), subObjects(subObjects),
+ subValues(subValues)
+ {}
+
+ GenericOwner(Path pathFromTop, int derivedFrom, QDateTime dataRefreshedAt,
+ QMap<QString, GenericObject> subObjects = {},
+ QMap<QString, QCborValue> subValues = {}):
+ OwningItem(derivedFrom, dataRefreshedAt), pathFromTop(pathFromTop), subObjects(subObjects),
+ subValues(subValues)
+ {}
+
+ GenericOwner(const GenericOwner &o);
+
+ std::shared_ptr<GenericOwner> makeCopy(const DomItem &self);
+ Path canonicalPath(const DomItem &self) const override;
+
+ bool iterateDirectSubpaths(DomItem &self, std::function<bool (Path, DomItem &)>) override;
+
+ Path pathFromTop;
+ QMap<QString, GenericObject> subObjects;
+ QMap<QString, QCborValue> subValues;
+};
+
+QDebug operator<<(QDebug debug, const DomItem &c);
+
+
+class MutableDomItem {
+public:
+ operator bool() const {
+ return m_owner && base();
+ }
+ DomType internalKind() const {
+ return base().internalKind();
+ }
+ DomKind domKind() const { return kind2domKind(internalKind()); }
+
+ Path canonicalPath() const
+ {
+ return m_owner.canonicalPath().subPath(m_pathFromOwner);
+ }
+ MutableDomItem containingObject() const {
+ if (m_pathFromOwner)
+ return MutableDomItem(m_owner, m_pathFromOwner.split().pathToSource);
+ else {
+ DomItem cObj = m_owner.containingObject();
+ return MutableDomItem(cObj.owner(), (domTypeIsOwningItem(cObj.internalKind()) ? Path() :cObj.pathFromOwner()));
+ }
+ }
+
+ MutableDomItem container() const {
+ if (m_pathFromOwner)
+ return MutableDomItem(m_owner, m_pathFromOwner.dropTail());
+ else {
+ return MutableDomItem(base().container());
+ }
+ }
+
+ MutableDomItem component() const {
+ return MutableDomItem{base().component()};
+ }
+ MutableDomItem owner() const {
+ return MutableDomItem(m_owner);
+ }
+ MutableDomItem top() const {
+ return MutableDomItem(base().top());
+ }
+ MutableDomItem environment() const {
+ return MutableDomItem(base().environment());
+ }
+ MutableDomItem universe() const {
+ return MutableDomItem(base().universe());
+ }
+ Path pathFromOwner() const {
+ return m_pathFromOwner;
+ }
+ MutableDomItem operator[](const Path &path) const {
+ return MutableDomItem(base()[path]);
+ }
+ MutableDomItem operator[](QStringView component) const {
+ return MutableDomItem(base()[component]);
+ }
+ MutableDomItem operator[](const QString &component) const {
+ return MutableDomItem(base()[component]);
+ }
+ MutableDomItem operator[](const char16_t *component) const {
+ // to avoid clash with stupid builtin ptrdiff_t[MutableDomItem&], coming from C
+ return MutableDomItem(base()[QStringView(component)]);
+ }
+ MutableDomItem operator[](index_type i) const {
+ return MutableDomItem(base().index(i));
+ }
+
+ MutableDomItem path(const Path &p) const {
+ return MutableDomItem(base().path(p));
+ }
+ MutableDomItem path(const QString &p) const {
+ return path(Path::fromString(p));
+ }
+ MutableDomItem path(QStringView p) const {
+ return path(Path::fromString(p));
+ }
+
+ QList<QString> const fields() const {
+ return base().fields();
+ }
+ MutableDomItem field(QStringView name) const {
+ return MutableDomItem(base().field(name));
+ }
+ index_type indexes() const {
+ return base().indexes();
+ }
+ MutableDomItem index(index_type i) const {
+ return MutableDomItem(base().index(i));
+ }
+
+ QSet<QString> const keys() const {
+ return base().keys();
+ }
+ MutableDomItem key(QString name) const {
+ return MutableDomItem(base().key(name));
+ }
+
+ QString canonicalFilePath() const { return base().canonicalFilePath(); }
+ SourceLocation location() const { return base().location(); }
+
+ QCborValue value() const {
+ return base().value();
+ }
+
+ void dump(Sink sink, int indent = 0) const {
+ return base().dump(sink, indent);
+ }
+ QString toString() const {
+ return base().toString();
+ }
+
+ // convenience getters
+ QString name() const;
+ MutableDomItem qmlChildren() const {
+ return MutableDomItem(base().qmlChildren());
+ }
+ MutableDomItem annotations() const {
+ return MutableDomItem(base().annotations());
+ }
+
+ QMultiMap<QString, RequiredProperty> extraRequired() const;
+
+
+// // OwnigItem elements
+ int derivedFrom() const {
+ return m_owner.derivedFrom();
+ }
+ int revision() const {
+ return m_owner.revision();
+ }
+ QDateTime createdAt() const {
+ return m_owner.createdAt();
+ }
+ QDateTime frozenAt() const {
+ return m_owner.frozenAt();
+ }
+ QDateTime lastDataUpdateAt() const {
+ return m_owner.lastDataUpdateAt();
+ }
+
+ void addError(ErrorMessage msg) const {
+ base().addError(msg);
+ }
+ ErrorHandler errorHandler() const;
+
+ MutableDomItem() = default;
+ MutableDomItem(DomItem owner, Path pathFromOwner):
+ m_owner(owner), m_pathFromOwner(pathFromOwner)
+ {}
+ MutableDomItem(DomItem item):
+ m_owner(item.owner()), m_pathFromOwner(item.pathFromOwner())
+ {}
+
+ std::shared_ptr<DomTop> topPtr() const {
+ return m_owner.topPtr();
+ }
+ std::shared_ptr<OwningItem> owningItemPtr() const {
+ return m_owner.owningItemPtr();
+ }
+
+ template <typename T>
+ T const*as() const {
+ return base().as<T>();
+ }
+
+ template <typename T>
+ T *mutableAs() {
+ Q_ASSERT(!m_owner.owningItemPtr()->frozen());
+ return base().mutableAs<T>();
+ }
+
+ template <typename T>
+ std::shared_ptr<T> ownerAs() const {
+ return m_owner.ownerAs<T>();
+ }
+ // it is dangerous to assume it stays valid when updates are preformed...
+ DomItem base() const {
+ return m_owner.path(m_pathFromOwner);
+ }
+private:
+ DomItem m_owner;
+ Path m_pathFromOwner;
+};
+
+QDebug operator<<(QDebug debug, const MutableDomItem &c);
+
+template <typename K, typename T>
+Path insertUpdatableElementInMultiMap(Path mapPathFromOwner, QMultiMap<K, T> &mmap, K key, const T&value) {
+ mmap.insert(key, value);
+ auto it = mmap.find(key);
+ auto it2 = it;
+ int nVal = 0;
+ while (it2 != mmap.end() && it2.key() == key) {
+ ++nVal;
+ ++it2;
+ }
+ Path newPath = mapPathFromOwner.subKey(key).subIndex(nVal-1);
+ T &newComp = *it;
+ newComp.updatePathFromOwner(newPath);
+ return newPath;
+}
+
+template <typename T>
+Path appendUpdatableElementInQList(Path listPathFromOwner, QList<T> &list, const T&value) {
+ int idx = list.length();
+ list.append(value);
+ Path newPath = listPathFromOwner.subIndex(idx);
+ list[idx].updatePathFromOwner(newPath);
+ return newPath;
+}
+
+
+template <typename T, typename K = QString>
+void updatePathFromOwnerMultiMap(QMultiMap<K, T> &mmap, Path newPath)
+{
+ auto it = mmap.begin();
+ auto end = mmap.end();
+ index_type i = 0;
+ K name;
+ QList<T*> els;
+ while (it != end) {
+ if (i > 0 && name != it.key()) {
+ Path pName = newPath.subKey(QString(name));
+ foreach (T *el, els)
+ el->updatePathFromOwner(pName.subIndex(--i));
+ els.clear();
+ els.append(&(*it));
+ name = it.key();
+ i = 1;
+ } else {
+ els.append(&(*it));
+ name = it.key();
+ ++i;
+ }
+ ++it;
+ }
+ Path pName = newPath.subKey(name);
+ foreach (T *el, els)
+ el->updatePathFromOwner(pName.subIndex(--i));
+}
+
+template <typename T>
+void updatePathFromOwnerQList(QList<T> &list, Path newPath)
+{
+ auto it = list.begin();
+ auto end = list.end();
+ index_type i = 0;
+ while (it != end)
+ (it++)->updatePathFromOwner(newPath.subIndex(i++));
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
+#endif // QMLDOMITEM_H
diff --git a/src/qmldom/qqmldomtop.cpp b/src/qmldom/qqmldomtop.cpp
new file mode 100644
index 0000000000..7777745887
--- /dev/null
+++ b/src/qmldom/qqmldomtop.cpp
@@ -0,0 +1,471 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#include "qqmldomtop_p.h"
+
+#include <QtQml/private/qqmljslexer_p.h>
+#include <QtQml/private/qqmljsparser_p.h>
+#include <QtQml/private/qqmljsengine_p.h>
+#include <QtQml/private/qqmljsastvisitor_p.h>
+#include <QtQml/private/qqmljsast_p.h>
+
+#include <QtCore/QFile>
+#include <QtCore/QFileInfo>
+#include <QtCore/QScopeGuard>
+#include <QtCore/QRegularExpression>
+#include <QtCore/QPair>
+#include <QtCore/QCborArray>
+#include <QtCore/QDebug>
+#include <QtCore/QBasicMutex>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+using std::shared_ptr;
+
+
+/*!
+ \internal
+ \brief QQml::Dom::DomTop::loadFile
+ \param filePath
+ the file path to load
+ \param logicalPath
+ the path from the
+ \param callback
+ a callback called with an canonical path, the old value, and the current value.
+ \param loadOptions are
+ if force is true the file is always read
+ */
+
+Path DomTop::pathFromOwner(const DomItem &) const
+{
+ return Path();
+}
+
+Path DomTop::canonicalPath(const DomItem &) const
+{
+ return canonicalPath();
+}
+
+DomItem DomTop::containingObject(const DomItem &) const
+{
+ return DomItem();
+}
+
+bool DomTop::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ bool cont = true;
+ auto objs = m_extraOwningItems;
+ auto itO = objs.cbegin();
+ auto endO = objs.cend();
+ while (itO != endO) {
+ cont = cont && self.copy(*itO).toSubField(itO.key()).visit(visitor);
+ ++itO;
+ }
+ return cont;
+}
+
+void DomTop::clearExtraOwningItems()
+{
+ QMutexLocker l(mutex());
+ m_extraOwningItems.clear();
+}
+
+QMap<QString, std::shared_ptr<OwningItem> > DomTop::extraOwningItems() const
+{
+ QMutexLocker l(mutex());
+ QMap<QString, std::shared_ptr<OwningItem> > res = m_extraOwningItems;
+ return res;
+}
+
+void DomTop::setExtraOwningItem(QString fieldName, std::shared_ptr<OwningItem> item)
+{
+ QMutexLocker l(mutex());
+ if (!item)
+ m_extraOwningItems.remove(fieldName);
+ else
+ m_extraOwningItems.insert(fieldName, item);
+}
+
+/*!
+\class QQmlJS::Dom::DomUniverse
+
+\brief Represents a set of parsed/loaded modules libraries and a plugins
+
+This can be used to share parsing and updates between several Dom models, and kickstart a model
+without reparsing everything.
+
+The universe is peculiar, because stepping into it from an environment looses the connection with
+the environment.
+
+This implementation is a placeholder, a later patch will introduce it.
+ */
+
+ErrorGroups DomUniverse::myErrors()
+{
+ static ErrorGroups groups = {{ DomItem::domErrorGroup, NewErrorGroup("Universe") }};
+ return groups;
+}
+
+DomUniverse::DomUniverse(QString universeName, Options options):
+ m_name(universeName), m_options(options)
+{}
+
+Path DomUniverse::canonicalPath() const
+{
+ return Path::root(u"universe");
+}
+
+bool DomUniverse::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ bool cont = true;
+ cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
+ cont = cont && self.subDataField(Fields::name, name()).visit(visitor);
+ cont = cont && self.subDataField(Fields::options, int(options())).visit(visitor);
+ QQueue<ParsingTask> q = queue();
+ cont = cont && self.subList(
+ List(Path::field(Fields::queue),
+ [q](const DomItem &list, index_type i){
+ if (i >= 0 && i < q.length())
+ return list.subDataIndex(i, q.at(i).toCbor(), ConstantData::Options::FirstMapIsFields).item;
+ else
+ return DomItem();
+ }, [q](const DomItem &){
+ return index_type(q.length());
+ }, nullptr, QLatin1String("ParsingTask"))
+ ).visit(visitor);
+
+ return cont;
+}
+
+std::shared_ptr<OwningItem> DomUniverse::doCopy(const DomItem &)
+{
+ QRegularExpression r(QRegularExpression::anchoredPattern(QLatin1String(R"(.*Copy([0-9]*)$)")));
+ auto m = r.match(m_name);
+ QString newName;
+ if (m.hasMatch())
+ newName = QStringLiteral(u"%1Copy%2").arg(m_name).arg(m.captured(1).toInt() + 1);
+ else
+ newName = m_name + QLatin1String("Copy");
+ auto res = std::make_shared<DomUniverse>(newName);
+ return res;
+}
+
+void DomUniverse::loadFile(const DomItem &self, QString filePath, QString logicalPath, Callback callback, LoadOptions loadOptions)
+{
+ loadFile(self, filePath, logicalPath, QString(), QDateTime::fromMSecsSinceEpoch(0), callback, loadOptions);
+}
+
+void DomUniverse::loadFile(const DomItem &self, QString canonicalFilePath, QString logicalPath, QString code, QDateTime codeDate, Callback callback, LoadOptions loadOptions)
+{
+ if (canonicalFilePath.endsWith(u".qml", Qt::CaseInsensitive) ||
+ canonicalFilePath.endsWith(u".qmlannotation", Qt::CaseInsensitive) ||
+ canonicalFilePath.endsWith(u".ui", Qt::CaseInsensitive)) {
+ m_queue.enqueue(ParsingTask{
+ QDateTime::currentDateTime(),
+ loadOptions,
+ DomType::QmlFile,
+ canonicalFilePath,
+ logicalPath,
+ code,
+ codeDate,
+ self.ownerAs<DomUniverse>(),
+ callback});
+ } else if (canonicalFilePath.endsWith(u".qmltypes")) {
+ m_queue.enqueue(ParsingTask{
+ QDateTime::currentDateTime(),
+ loadOptions,
+ DomType::QmltypesFile,
+ canonicalFilePath,
+ logicalPath,
+ code,
+ codeDate,
+ self.ownerAs<DomUniverse>(),
+ callback});
+ } else if (QStringView(u"qmldir").compare(QFileInfo(canonicalFilePath).fileName(), Qt::CaseInsensitive) == 0) {
+ m_queue.enqueue(ParsingTask{
+ QDateTime::currentDateTime(),
+ loadOptions,
+ DomType::QmldirFile,
+ canonicalFilePath,
+ logicalPath,
+ code,
+ codeDate,
+ self.ownerAs<DomUniverse>(),
+ callback});
+ } else {
+ self.addError(myErrors().error(tr("Ignoring request to load file of unknown type %1, calling callback immediately").arg(canonicalFilePath)).handle());
+ Q_ASSERT(false && "loading non supported file type");
+ callback(Path(), DomItem(), DomItem());
+ return;
+ }
+ if (m_options & Option::SingleThreaded)
+ execQueue(); // immediate execution in the same thread
+}
+
+template <typename T>
+QPair<std::shared_ptr<ExternalItemPair<T>>,std::shared_ptr<ExternalItemPair<T>>> updateEntry(const DomItem &univ, std::shared_ptr<T> newItem, QMap<QString, std::shared_ptr<ExternalItemPair<T>>> &map, QBasicMutex *mutex)
+{
+ std::shared_ptr<ExternalItemPair<T>> oldValue;
+ std::shared_ptr<ExternalItemPair<T>> newValue;
+ QString canonicalPath = newItem->canonicalFilePath();
+ QDateTime now = QDateTime::currentDateTime();
+ {
+ QMutexLocker l(mutex);
+ auto it = map.find(canonicalPath);
+ if (it != map.cend() && (*it) && (*it)->current) {
+ oldValue = *it;
+ QString oldCode = oldValue->current->code();
+ QString newCode = newItem->code();
+ if (!oldCode.isNull() && !newCode.isNull() && oldCode == newCode) {
+ newValue = oldValue;
+ if (newValue->current->lastDataUpdateAt() < newItem->lastDataUpdateAt())
+ newValue->current->refreshedDataAt(newItem->lastDataUpdateAt());
+ } else if (oldValue->current->lastDataUpdateAt() > newItem->lastDataUpdateAt()) {
+ newValue = oldValue;
+ } else {
+ newValue = oldValue->makeCopy(univ.copy(oldValue));
+ newValue->current = newItem;
+ newValue->currentExposedAt = now;
+ if (newItem->isValid()) {
+ newValue->valid = newItem;
+ newValue->validExposedAt = now;
+ }
+ it = map.insert(it, canonicalPath, newValue);
+ }
+ } else {
+ newValue = std::make_shared<ExternalItemPair<T>>
+ (newItem->isValid() ? newItem : std::shared_ptr<T>(), newItem, now, now);
+ map.insert(canonicalPath, newValue);
+ }
+ }
+ return qMakePair(oldValue, newValue);
+}
+
+void DomUniverse::execQueue()
+{
+ ParsingTask t = m_queue.dequeue();
+ shared_ptr<DomUniverse> topPtr = t.requestingUniverse.lock();
+ if (!topPtr) {
+ myErrors().error(tr("Ignoring calback for loading of %1: universe is not valid anymore").arg(t.canonicalPath)).handle();
+ }
+ Q_ASSERT(false && "Unhandled kind in queue");
+}
+
+/*!
+\class QQmlJS::Dom::DomEnvironment
+
+\brief Represents a consistent set of types organized in modules, it is the top level of the DOM
+ */
+
+ErrorGroups DomEnvironment::myErrors() {
+ static ErrorGroups res = {{NewErrorGroup("Dom")}};
+ return res;
+}
+
+
+Path DomEnvironment::canonicalPath() const
+{
+ return Path::root(u"env");
+}
+
+bool DomEnvironment::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ bool cont = true;
+ cont = cont && DomTop::iterateDirectSubpaths(self, visitor);
+ DomItem univ = universe();
+ cont = cont && visitor(Path::field(Fields::universe), univ);
+ cont = cont && self.subDataField(Fields::options, int(options())).visit(visitor);
+ DomItem baseItem = base();
+ cont = cont && visitor(Path::field(Fields::base), baseItem);
+ cont = cont && self.subList(List::fromQList<QString>(
+ Path::field(Fields::loadPaths), loadPaths(),
+ [](const DomItem &i, Path p, const QString &el){
+ return i.subDataPath(p, el).item;
+ })).visit(visitor);
+ QQueue<Path> loadsWithWork;
+ QQueue<Path> inProgress;
+ int nAllLoadedCallbacks;
+ {
+ QMutexLocker l(mutex());
+ loadsWithWork = m_loadsWithWork;
+ inProgress = m_inProgress;
+ nAllLoadedCallbacks = m_allLoadedCallback.length();
+ }
+ cont = cont && self.subList(
+ List(Path::field(Fields::loadsWithWork),
+ [loadsWithWork](const DomItem &list, index_type i){
+ if (i >= 0 && i < loadsWithWork.length())
+ return list.subDataIndex(i, loadsWithWork.at(i).toString()).item;
+ else
+ return DomItem();
+ }, [loadsWithWork](const DomItem &){
+ return index_type(loadsWithWork.length());
+ }, nullptr, QLatin1String("Path"))
+ ).visit(visitor);
+ cont = cont && self.subDataField(Fields::nAllLoadedCallbacks, nAllLoadedCallbacks).visit(visitor);
+ return cont;
+}
+
+std::shared_ptr<OwningItem> DomEnvironment::doCopy(const DomItem &)
+{
+ shared_ptr<DomEnvironment> res;
+ if (m_base)
+ res = std::make_shared<DomEnvironment>(m_base, m_loadPaths, m_options);
+ else
+ res = std::make_shared<DomEnvironment>(m_universe, m_loadPaths, m_options);
+ return res;
+}
+
+shared_ptr<DomUniverse> DomEnvironment::universe() const {
+ if (m_universe)
+ return m_universe;
+ else if (m_base)
+ return m_base->universe();
+ else
+ return {};
+}
+
+DomEnvironment::DomEnvironment(shared_ptr<DomUniverse> universe, QStringList loadPaths, Options options):
+ m_options(options), m_universe(universe), m_loadPaths(loadPaths)
+{}
+
+DomEnvironment::DomEnvironment(shared_ptr<DomEnvironment> parent, QStringList loadPaths, Options options):
+ m_options(options), m_base(parent), m_loadPaths(loadPaths)
+{}
+
+Path ExternalItemInfoBase::canonicalPath(const DomItem &self) const
+{
+ shared_ptr<ExternalOwningItem> current = currentItem();
+ return current->canonicalPath(self.copy(current, current.get())).dropTail();
+}
+
+QString ExternalItemInfoBase::canonicalFilePath(const DomItem &self) const
+{
+ shared_ptr<ExternalOwningItem> current = currentItem();
+ return current->canonicalFilePath(self.copy(current, current.get()));
+}
+
+Path ExternalItemInfoBase::pathFromOwner(const DomItem &self) const
+{
+ shared_ptr<ExternalOwningItem> current = currentItem();
+ return current->pathFromOwner(self.copy(current, current.get())).dropTail();
+}
+
+bool ExternalItemInfoBase::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ if (!self.subDataField(Fields::currentRevision, currentRevision(self)).visit(visitor))
+ return false;
+ if (!self.subDataField(Fields::lastRevision, lastRevision(self)).visit(visitor))
+ return false;
+ if (!self.subDataField(Fields::lastValidRevision, QCborValue(lastValidRevision(self))).visit(visitor))
+ return false;
+ DomItem cItem = self.copy(currentItem(), currentItem().get());
+ if (!visitor(Path::field(Fields::currentItem), cItem))
+ return false;
+ if (!self.subDataField(Fields::currentExposedAt, QCborValue(currentExposedAt())).visit(visitor))
+ return false;
+ return true;
+}
+
+int ExternalItemInfoBase::currentRevision(const DomItem &) const
+{
+ return currentItem()->revision();
+}
+
+int ExternalItemInfoBase::lastRevision(const DomItem &self) const
+{
+ Path p = currentItem()->canonicalPath();
+ DomItem lastValue = self.universe()[p.mid(1, p.length() - 1)].field(u"revision");
+ return static_cast<int>(lastValue.value().toInteger(0));
+}
+
+int ExternalItemInfoBase::lastValidRevision(const DomItem &self) const
+{
+ Path p = currentItem()->canonicalPath();
+ DomItem lastValidValue = self.universe()[p.mid(1, p.length() - 2)].field(u"validItem").field(u"revision");
+ return static_cast<int>(lastValidValue.value().toInteger(0));
+}
+
+QString ExternalItemPairBase::canonicalFilePath(const DomItem &self) const
+{
+ shared_ptr<ExternalOwningItem> current = currentItem();
+ return current->canonicalFilePath(self.copy(current, current.get()));
+}
+
+Path ExternalItemPairBase::pathFromOwner(const DomItem &self) const
+{
+ shared_ptr<ExternalOwningItem> current = currentItem();
+ return current->pathFromOwner(self.copy(current, current.get())).dropTail();
+}
+
+Path ExternalItemPairBase::canonicalPath(const DomItem &) const
+{
+ shared_ptr<ExternalOwningItem> current = currentItem();
+ return current->canonicalPath().dropTail();
+}
+
+bool ExternalItemPairBase::iterateDirectSubpaths(DomItem &self, function<bool (Path, DomItem &)> visitor)
+{
+ if (!self.subDataField(Fields::currentIsValid, currentIsValid()).visit(visitor))
+ return false;
+ DomItem vItem = self.copy(validItem(), validItem().get());
+ if (!visitor(Path::field(Fields::validItem), vItem))
+ return false;
+ DomItem cItem = self.copy(currentItem(), currentItem().get());
+ if (!visitor(Path::field(Fields::currentItem), cItem))
+ return false;
+ if (!self.subDataField(Fields::validExposedAt, QCborValue(validExposedAt)).visit(visitor))
+ return false;
+ if (!self.subDataField(Fields::currentExposedAt, QCborValue(currentExposedAt)).visit(visitor))
+ return false;
+ return true;
+}
+
+bool ExternalItemPairBase::currentIsValid() const
+{
+ return currentItem() == validItem();
+}
+
+} // end namespace Dom
+} // end namespace QQmlJS
+
+QT_END_NAMESPACE
diff --git a/src/qmldom/qqmldomtop_p.h b/src/qmldom/qqmldomtop_p.h
new file mode 100644
index 0000000000..6145d9edd1
--- /dev/null
+++ b/src/qmldom/qqmldomtop_p.h
@@ -0,0 +1,421 @@
+/****************************************************************************
+**
+** Copyright (C) 2020 The Qt Company Ltd.
+** Contact: https://www.qt.io/licensing/
+**
+** This file is part of the QtQml module of the Qt Toolkit.
+**
+** $QT_BEGIN_LICENSE:LGPL$
+** Commercial License Usage
+** Licensees holding valid commercial Qt licenses may use this file in
+** accordance with the commercial license agreement provided with the
+** Software or, alternatively, in accordance with the terms contained in
+** a written agreement between you and The Qt Company. For licensing terms
+** and conditions see https://www.qt.io/terms-conditions. For further
+** information use the contact form at https://www.qt.io/contact-us.
+**
+** GNU Lesser General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU Lesser
+** General Public License version 3 as published by the Free Software
+** Foundation and appearing in the file LICENSE.LGPL3 included in the
+** packaging of this file. Please review the following information to
+** ensure the GNU Lesser General Public License version 3 requirements
+** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
+**
+** GNU General Public License Usage
+** Alternatively, this file may be used under the terms of the GNU
+** General Public License version 2.0 or (at your option) the GNU General
+** Public license version 3 or any later version approved by the KDE Free
+** Qt Foundation. The licenses are as published by the Free Software
+** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
+** included in the packaging of this file. Please review the following
+** information to ensure the GNU General Public License requirements will
+** be met: https://www.gnu.org/licenses/gpl-2.0.html and
+** https://www.gnu.org/licenses/gpl-3.0.html.
+**
+** $QT_END_LICENSE$
+**/
+#ifndef DOMTOP_H
+#define DOMTOP_H
+
+//
+// W A R N I N G
+// -------------
+//
+// This file is not part of the Qt API. It exists purely as an
+// implementation detail. This header file may change from version to
+// version without notice, or even be removed.
+//
+// We mean it.
+//
+
+#include "qqmldomitem_p.h"
+
+#include "qqmldomexternalitems_p.h"
+
+#include <QtCore/QQueue>
+#include <QtCore/QString>
+#include <QtCore/QDateTime>
+
+#include <QtCore/QCborValue>
+#include <QtCore/QCborMap>
+
+#include <memory>
+
+QT_BEGIN_NAMESPACE
+
+namespace QQmlJS {
+namespace Dom {
+
+class QMLDOM_EXPORT ParsingTask {
+ Q_GADGET
+public:
+ QCborMap toCbor() const {
+ return QCborMap(
+ {{ QString::fromUtf16(Fields::requestedAt), QCborValue(requestedAt)},
+ { QString::fromUtf16(Fields::loadOptions), int(loadOptions)},
+ { QString::fromUtf16(Fields::kind), int(kind)},
+ { QString::fromUtf16(Fields::canonicalPath), canonicalPath},
+ { QString::fromUtf16(Fields::logicalPath), logicalPath},
+ { QString::fromUtf16(Fields::contents), contents},
+ { QString::fromUtf16(Fields::contentsDate), QCborValue(contentsDate)},
+ { QString::fromUtf16(Fields::hasCallback), bool(callback)}});
+ }
+
+ QDateTime requestedAt;
+ LoadOptions loadOptions;
+ DomType kind;
+ QString canonicalPath;
+ QString logicalPath;
+ QString contents;
+ QDateTime contentsDate;
+ std::weak_ptr<DomUniverse> requestingUniverse; // make it a shared_ptr?
+ function<void(Path, DomItem, DomItem)> callback;
+};
+
+class QMLDOM_EXPORT ExternalItemPairBase: public OwningItem { // all access should have the lock of the DomUniverse containing this
+ Q_DECLARE_TR_FUNCTIONS(ExternalItemPairBase);
+public:
+ constexpr static DomType kindValue = DomType::ExternalItemPair;
+ DomType kind() const override { return kindValue; }
+ ExternalItemPairBase(QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom=0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
+ OwningItem(derivedFrom, lastDataUpdateAt), validExposedAt(validExposedAt), currentExposedAt(currentExposedAt)
+ {}
+ ExternalItemPairBase(const ExternalItemPairBase &o):
+ OwningItem(o), validExposedAt(o.validExposedAt), currentExposedAt(o.currentExposedAt)
+ {}
+ virtual std::shared_ptr<ExternalOwningItem> validItem() const = 0;
+ virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
+
+ QString canonicalFilePath(const DomItem &) const override;
+ Path pathFromOwner(const DomItem &self) const override;
+ Path canonicalPath(const DomItem &self) const override;
+ bool iterateDirectSubpaths(DomItem &self, function<bool(Path, DomItem &)>) override;
+
+ bool currentIsValid() const;
+
+ std::shared_ptr<ExternalItemPairBase> makeCopy(const DomItem &self) {
+ return std::static_pointer_cast<ExternalItemPairBase>(doCopy(self));
+ }
+
+ QDateTime lastDataUpdateAt() const override {
+ if (currentItem())
+ return currentItem()->lastDataUpdateAt();
+ return ExternalItemPairBase::lastDataUpdateAt();
+ }
+
+ void refreshedDataAt(QDateTime tNew) override {
+ return OwningItem::refreshedDataAt(tNew);
+ if (currentItem())
+ currentItem()->refreshedDataAt(tNew);
+ }
+
+ friend class DomUniverse;
+
+ QDateTime validExposedAt;
+ QDateTime currentExposedAt;
+};
+
+template<class T>
+class QMLDOM_EXPORT ExternalItemPair: public ExternalItemPairBase { // all access should have the lock of the DomUniverse containing this
+protected:
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) override {
+ return std::make_shared<ExternalItemPair>(*this);
+ }
+
+public:
+ friend class DomUniverse;
+ ExternalItemPair(std::shared_ptr<T> valid = {}, std::shared_ptr<T> current = {},
+ QDateTime validExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom = 0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
+ ExternalItemPairBase(validExposedAt, currentExposedAt, derivedFrom, lastDataUpdateAt),
+ valid(valid), current(current)
+ {}
+ ExternalItemPair(const ExternalItemPair &o):
+ ExternalItemPairBase(o), valid(o.valid), current(o.current)
+ {
+ QMutexLocker l(mutex());
+ }
+ std::shared_ptr<ExternalOwningItem> validItem() const override { return valid; }
+ std::shared_ptr<ExternalOwningItem> currentItem() const override { return current; }
+ std::shared_ptr<ExternalItemPair> makeCopy(const DomItem &self) {
+ return std::static_pointer_cast<ExternalItemPair>(doCopy(self));
+ }
+
+ std::shared_ptr<T> valid;
+ std::shared_ptr<T> current;
+};
+
+class QMLDOM_EXPORT DomTop: public OwningItem {
+public:
+ DomTop(QMap<QString, std::shared_ptr<OwningItem>> extraOwningItems = {}, int derivedFrom=0):
+ OwningItem(derivedFrom), m_extraOwningItems(extraOwningItems)
+ {}
+ DomTop(const DomTop &o):
+ OwningItem(o)
+ {
+ QMap<QString, std::shared_ptr<OwningItem>> items = o.extraOwningItems();
+ {
+ QMutexLocker l(mutex());
+ m_extraOwningItems = items;
+ }
+ }
+ using Callback = DomItem::Callback;
+
+ virtual Path canonicalPath() const = 0;
+
+ Path pathFromOwner(const DomItem &) const override;
+ Path canonicalPath(const DomItem &) const override;
+ DomItem containingObject(const DomItem&) const override;
+ bool iterateDirectSubpaths(DomItem &self, function<bool(Path, DomItem &)>) override;
+
+ void setExtraOwningItem(QString fieldName, std::shared_ptr<OwningItem> item);
+ void clearExtraOwningItems();
+ QMap<QString, std::shared_ptr<OwningItem>> extraOwningItems() const;
+private:
+ QMap<QString, std::shared_ptr<OwningItem>> m_extraOwningItems;
+};
+
+class QMLDOM_EXPORT DomUniverse: public DomTop {
+ Q_DECLARE_TR_FUNCTIONS(DomUniverse);
+protected:
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) override;
+public:
+ enum class Option{
+ Default,
+ SingleThreaded
+ };
+ Q_DECLARE_FLAGS(Options, Option);
+ constexpr static DomType kindValue = DomType::DomUniverse;
+ DomType kind() const override { return kindValue; }
+
+ static ErrorGroups myErrors();
+
+ DomUniverse(QString universeName, Options options = Option::SingleThreaded);
+ DomUniverse(const DomUniverse &) = delete;
+
+ Path canonicalPath() const override;
+ bool iterateDirectSubpaths(DomItem &self, function<bool(Path, DomItem &)>) override;
+ std::shared_ptr<DomUniverse> makeCopy(const DomItem &self) {
+ return std::static_pointer_cast<DomUniverse>(doCopy(self));
+ }
+
+ void loadFile(const DomItem &self, QString filePath, QString logicalPath,
+ Callback callback, LoadOptions loadOptions);
+ void loadFile(const DomItem &self, QString canonicalFilePath, QString logicalPath,
+ QString code, QDateTime codeDate, Callback callback, LoadOptions loadOptions);
+ void execQueue();
+
+ QString name() const {
+ return m_name;
+ }
+ Options options() const {
+ return m_options;
+ }
+ QQueue<ParsingTask> queue() const {
+ QMutexLocker l(mutex());
+ return m_queue;
+ }
+
+private:
+ QString m_name;
+ Options m_options;
+ QQueue<ParsingTask> m_queue;
+};
+
+ Q_DECLARE_OPERATORS_FOR_FLAGS(DomUniverse::Options)
+
+class QMLDOM_EXPORT ExternalItemInfoBase: public OwningItem {
+ Q_DECLARE_TR_FUNCTIONS(ExternalItemInfoBase);
+public:
+ constexpr static DomType kindValue = DomType::ExternalItemInfo;
+ DomType kind() const override { return kindValue; }
+ ExternalItemInfoBase(QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom=0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
+ OwningItem(derivedFrom, lastDataUpdateAt), m_currentExposedAt(currentExposedAt)
+ {}
+ ExternalItemInfoBase(const ExternalItemInfoBase &o):
+ OwningItem(o), m_currentExposedAt(o.currentExposedAt()),
+ m_logicalFilePaths(o.logicalFilePaths())
+ {}
+
+ virtual std::shared_ptr<ExternalOwningItem> currentItem() const = 0;
+
+ QString canonicalFilePath(const DomItem &) const override;
+ Path canonicalPath(const DomItem &) const override;
+ Path pathFromOwner(const DomItem &self) const override;
+ bool iterateDirectSubpaths(DomItem &self, function<bool(Path, DomItem &)>) override;
+
+ int currentRevision(const DomItem &self) const;
+ int lastRevision(const DomItem &self) const;
+ int lastValidRevision(const DomItem &self) const;
+
+ std::shared_ptr<ExternalItemInfoBase> makeCopy(const DomItem &self) {
+ return std::static_pointer_cast<ExternalItemInfoBase>(doCopy(self));
+ }
+
+ QDateTime lastDataUpdateAt() const override {
+ if (currentItem())
+ return currentItem()->lastDataUpdateAt();
+ return OwningItem::lastDataUpdateAt();
+ }
+
+ void refreshedDataAt(QDateTime tNew) override {
+ return OwningItem::refreshedDataAt(tNew);
+ if (currentItem())
+ currentItem()->refreshedDataAt(tNew);
+ }
+
+ void ensureLogicalFilePath(QString path) {
+ QMutexLocker l(mutex());
+ if (!m_logicalFilePaths.contains(path))
+ m_logicalFilePaths.append(path);
+ }
+
+ QDateTime currentExposedAt() const {
+ QMutexLocker l(mutex()); // should not be needed, as it should not change...
+ return m_currentExposedAt;
+ }
+
+ void setCurrentExposedAt(QDateTime d) {
+ QMutexLocker l(mutex()); // should not be needed, as it should not change...
+ m_currentExposedAt = d;
+ }
+
+
+ QStringList logicalFilePaths() const {
+ QMutexLocker l(mutex());
+ return m_logicalFilePaths;
+ }
+
+ private:
+ friend class DomEnvironment;
+ QDateTime m_currentExposedAt;
+ QStringList m_logicalFilePaths;
+};
+
+template <typename T>
+class ExternalItemInfo: public ExternalItemInfoBase {
+protected:
+ std::shared_ptr<OwningItem> doCopy(const DomItem &) override {
+ return std::make_shared<ExternalItemInfo>(*this);
+ }
+public:
+ ExternalItemInfo(std::shared_ptr<T> current = std::shared_ptr<T>(),
+ QDateTime currentExposedAt = QDateTime::fromMSecsSinceEpoch(0),
+ int derivedFrom = 0, QDateTime lastDataUpdateAt = QDateTime::fromMSecsSinceEpoch(0)):
+ ExternalItemInfoBase(currentExposedAt, derivedFrom, lastDataUpdateAt), current(current)
+ {}
+ ExternalItemInfo(QString canonicalPath):
+ current(std::make_shared<T>(canonicalPath))
+ {}
+ ExternalItemInfo(const ExternalItemInfo &o):
+ ExternalItemInfoBase(o), current(o.current)
+ {
+ QMutexLocker l(mutex());
+ }
+
+ std::shared_ptr<ExternalItemInfo> makeCopy(const DomItem &self) {
+ return std::static_pointer_cast<ExternalItemInfo>(doCopy(self));
+ }
+
+ std::shared_ptr<ExternalOwningItem> currentItem() const override {
+ return current;
+ }
+
+ std::shared_ptr<T> current;
+};
+
+enum class EnvLookup { Normal, NoBase, BaseOnly };
+
+enum class Changeable { ReadOnly, Writable };
+
+class QMLDOM_EXPORT DomEnvironment: public DomTop
+{
+ Q_DECLARE_TR_FUNCTIONS(DomEnvironment);
+protected:
+ std::shared_ptr<OwningItem> doCopy(const DomItem &self) override;
+public:
+ enum class Option {
+ Default = 0x0,
+ KeepValid = 0x1, // if there is a previous valid version, use that instead of the latest
+ Exported = 0x2, // the current environment is accessible by multiple threads, one should only modify whole OwningItems, and in general load and do other operations in other (Child) environments
+ NoReload = 0x4, // never reload something that was already loaded by the parent environment
+ WeakLoad = 0x8, // load only the names of the available types, not the types (qml files) themselves
+ SingleThreaded = 0x10, // do all operations in a single thread
+ NoDependencies = 0x20 // will not load dependencies (useful when editing)
+ };
+ Q_DECLARE_FLAGS(Options, Option);
+
+ static ErrorGroups myErrors();
+ constexpr static DomType kindValue = DomType::DomEnvironment;
+ DomType kind() const override { return kindValue; }
+
+ Path canonicalPath() const override;
+ bool iterateDirectSubpaths(DomItem &self, function<bool(Path, DomItem &)>) override;
+ std::shared_ptr<DomEnvironment> makeCopy(const DomItem &self) {
+ return std::static_pointer_cast<DomEnvironment>(doCopy(self));
+ }
+
+ std::shared_ptr<DomUniverse> universe() const;
+
+ DomEnvironment(std::shared_ptr<DomUniverse> universe, QStringList loadPaths, Options options = Option::SingleThreaded);
+ DomEnvironment(std::shared_ptr<DomEnvironment> parent, QStringList loadPaths, Options options = Option::SingleThreaded);
+ DomEnvironment(const DomEnvironment &o) = delete;
+
+ std::shared_ptr<ExternalItemInfo<QmlFile>> addQmlFile(std::shared_ptr<QmlFile> file);
+
+ void commitToBase(const DomItem &self);
+
+ Options options() const {
+ return m_options;
+ }
+
+ std::shared_ptr<DomEnvironment> base() const {
+ return m_base;
+ }
+
+ QStringList loadPaths() const {
+ QMutexLocker l(mutex());
+ return m_loadPaths;
+ }
+
+private:
+ const Options m_options;
+ const std::shared_ptr<DomEnvironment> m_base;
+ const std::shared_ptr<DomUniverse> m_universe;
+ QStringList m_loadPaths; // paths for qml
+ bool m_singleThreadedLoadInProgress = false;
+ QQueue<Path> m_loadsWithWork;
+ QQueue<Path> m_inProgress;
+ QList<Callback> m_allLoadedCallback;
+};
+Q_DECLARE_OPERATORS_FOR_FLAGS(DomEnvironment::Options)
+
+} // end namespace Dom
+} // end namespace QQmlJS
+QT_END_NAMESPACE
+#endif // DOMTOP_H