// Copyright (C) 2020 The Qt Company Ltd. // SPDX-License-Identifier: LicenseRef-Qt-Commercial OR LGPL-3.0-only OR GPL-2.0-only OR GPL-3.0-only #ifndef QMLDOM_PATH_H #define QMLDOM_PATH_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 "qqmldomconstants_p.h" #include "qqmldomstringdumper_p.h" #include "qqmldom_global.h" #include #include #include #include #include #include #include #include #include QT_BEGIN_NAMESPACE namespace QQmlJS { namespace Dom { class ErrorGroups; class ErrorMessage; class DomItem; class Path; using ErrorHandler = std::function ; using index_type = qint64; namespace PathEls { enum class Kind{ Empty, Field, Index, Key, Root, Current, Any, Filter }; class TestPaths; class Empty; class Field; class Index; class Key; class Root; class Current; class Any; class Filter; class Base { public: QStringView stringView() const { return QStringView(); } index_type index(index_type defaultValue = -1) const { return defaultValue; } bool hasSquareBrackets() const { return false; } protected: void dump(const Sink &sink, const QString &name, bool hasSquareBrackets) const; }; class Empty final : public Base { public: Empty() = default; QString name() const { return QString(); } bool checkName(QStringView s) const { return s.isEmpty(); } void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); } }; class Field final : public Base { public: Field() = default; Field(QStringView n): fieldName(n) {} QString name() const { return fieldName.toString(); } bool checkName(QStringView s) const { return s == fieldName; } QStringView stringView() const { return fieldName; } void dump(const Sink &sink) const { sink(fieldName); } QStringView fieldName; }; class Index final : public Base { public: Index() = default; Index(index_type i): indexValue(i) {} QString name() const { return QString::number(indexValue); } bool checkName(QStringView s) const { return s == name(); } index_type index(index_type = -1) const { return indexValue; } void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); } bool hasSquareBrackets() const { return true; } index_type indexValue = -1; }; class Key final : public Base { public: Key() = default; Key(const QString &n) : keyValue(n) { } QString name() const { return keyValue; } bool checkName(QStringView s) const { return s == keyValue; } QStringView stringView() const { return keyValue; } void dump(const Sink &sink) const { sink(u"["); sinkEscaped(sink, keyValue); sink(u"]"); } bool hasSquareBrackets() const { return true; } QString keyValue; }; class Root final : public Base { public: Root() = default; Root(PathRoot r): contextKind(r), contextName() {} Root(QStringView n) { QMetaEnum metaEnum = QMetaEnum::fromType(); contextKind = PathRoot::Other; for (int i = 0; i < metaEnum.keyCount(); ++ i) if (n.compare(QString::fromUtf8(metaEnum.key(i)), Qt::CaseInsensitive) == 0) contextKind = PathRoot(metaEnum.value(i)); if (contextKind == PathRoot::Other) contextName = n; } QString name() const { switch (contextKind) { case PathRoot::Modules: return QStringLiteral(u"$modules"); case PathRoot::Cpp: return QStringLiteral(u"$cpp"); case PathRoot::Libs: return QStringLiteral(u"$libs"); case PathRoot::Top: return QStringLiteral(u"$top"); case PathRoot::Env: return QStringLiteral(u"$env"); case PathRoot::Universe: return QStringLiteral(u"$universe"); case PathRoot::Other: return QString::fromUtf8("$").append(contextName.toString()); } Q_ASSERT(false && "Unexpected contextKind in name"); return QString(); } bool checkName(QStringView s) const { if (contextKind != PathRoot::Other) return s.compare(name(), Qt::CaseInsensitive) == 0; return s.startsWith(QChar::fromLatin1('$')) && s.mid(1) == contextName; } QStringView stringView() const { return contextName; } void dump(const Sink &sink) const { sink(name()); } PathRoot contextKind = PathRoot::Other; QStringView contextName; }; class Current final : public Base { public: Current() = default; Current(PathCurrent c): contextKind(c) {} Current(QStringView n) { QMetaEnum metaEnum = QMetaEnum::fromType(); contextKind = PathCurrent::Other; for (int i = 0; i < metaEnum.keyCount(); ++ i) if (n.compare(QString::fromUtf8(metaEnum.key(i)), Qt::CaseInsensitive) == 0) contextKind = PathCurrent(metaEnum.value(i)); if (contextKind == PathCurrent::Other) contextName = n; } QString name() const { switch (contextKind) { case PathCurrent::Other: return QString::fromUtf8("@").append(contextName.toString()); case PathCurrent::Obj: return QStringLiteral(u"@obj"); case PathCurrent::ObjChain: return QStringLiteral(u"@objChain"); case PathCurrent::ScopeChain: return QStringLiteral(u"@scopeChain"); case PathCurrent::Component: return QStringLiteral(u"@component"); case PathCurrent::Module: return QStringLiteral(u"@module"); case PathCurrent::Ids: return QStringLiteral(u"@ids"); case PathCurrent::Types: return QStringLiteral(u"@types"); case PathCurrent::LookupStrict: return QStringLiteral(u"@lookupStrict"); case PathCurrent::LookupDynamic: return QStringLiteral(u"@lookupDynamic"); case PathCurrent::Lookup: return QStringLiteral(u"@lookup"); } Q_ASSERT(false && "Unexpected contextKind in Current::name"); return QString(); } bool checkName(QStringView s) const { if (contextKind != PathCurrent::Other) return s.compare(name(), Qt::CaseInsensitive) == 0; return s.startsWith(QChar::fromLatin1('@')) && s.mid(1) == contextName; } QStringView stringView() const { return contextName; } void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); } PathCurrent contextKind = PathCurrent::Other; QStringView contextName; }; class Any final : public Base { public: Any() = default; QString name() const { return QLatin1String("*"); } bool checkName(QStringView s) const { return s == u"*"; } void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); } bool hasSquareBrackets() const { return true; } }; class QMLDOM_EXPORT Filter final : public Base { public: Filter() = default; Filter(const std::function &f, QStringView filterDescription = u""); QString name() const; bool checkName(QStringView s) const; QStringView stringView() const { return filterDescription; } void dump(const Sink &sink) const { Base::dump(sink, name(), hasSquareBrackets()); } bool hasSquareBrackets() const { return true; } std::function filterFunction; QStringView filterDescription; }; class QMLDOM_EXPORT PathComponent { public: PathComponent() = default; PathComponent(const PathComponent &) = default; PathComponent(PathComponent &&) = default; PathComponent &operator=(const PathComponent &) = default; PathComponent &operator=(PathComponent &&) = default; ~PathComponent() = default; Kind kind() const { return Kind(m_data.index()); } QString name() const { return std::visit([](auto &&d) { return d.name(); }, m_data); } bool checkName(QStringView s) const { return std::visit([s](auto &&d) { return d.checkName(s); }, m_data); } QStringView stringView() const { return std::visit([](auto &&d) { return d.stringView(); }, m_data); } index_type index(index_type defaultValue=-1) const { return std::visit([defaultValue](auto &&d) { return d.index(defaultValue); }, m_data); } void dump(const Sink &sink) const { return std::visit([sink](auto &&d) { return d.dump(sink); }, m_data); } bool hasSquareBrackets() const { return std::visit([](auto &&d) { return d.hasSquareBrackets(); }, m_data); } const Empty *asEmpty() const { return std::get_if(&m_data); } const Field *asField() const { return std::get_if(&m_data); } const Index *asIndex() const { return std::get_if(&m_data); } const Key *asKey() const { return std::get_if(&m_data); } const Root *asRoot() const { return std::get_if(&m_data); } const Current *asCurrent() const { return std::get_if(&m_data); } const Any *asAny() const { return std::get_if(&m_data); } const Filter *asFilter() const { return std::get_if(&m_data); } static int cmp(const PathComponent &p1, const PathComponent &p2); PathComponent(Empty &&o): m_data(std::move(o)) {} PathComponent(Field &&o): m_data(std::move(o)) {} PathComponent(Index &&o): m_data(std::move(o)) {} PathComponent(Key &&o): m_data(std::move(o)) {} PathComponent(Root &&o): m_data(std::move(o)) {} PathComponent(Current &&o): m_data(std::move(o)) {} PathComponent(Any &&o): m_data(std::move(o)) {} PathComponent(Filter &&o): m_data(std::move(o)) {} private: friend class QQmlJS::Dom::Path; friend class QQmlJS::Dom::PathEls::TestPaths; using Variant = std::variant; template static constexpr bool variantTypeMatches = std::is_same_v, T>; static_assert(size_t(Kind::Empty) == 0); static_assert(variantTypeMatches); static_assert(variantTypeMatches); static_assert(variantTypeMatches); static_assert(variantTypeMatches); static_assert(variantTypeMatches); static_assert(variantTypeMatches); static_assert(variantTypeMatches); static_assert(std::variant_size_v == size_t(Kind::Filter) + 1); Variant m_data; }; inline bool operator==(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) == 0; } inline bool operator!=(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) != 0; } inline bool operator< (const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) < 0; } inline bool operator> (const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) > 0; } inline bool operator<=(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) <= 0; } inline bool operator>=(const PathComponent& lhs, const PathComponent& rhs){ return PathComponent::cmp(lhs,rhs) >= 0; } class PathData { public: PathData(const QStringList &strData, const QVector &components) : strData(strData), components(components) {} PathData(const QStringList &strData, const QVector &components, const std::shared_ptr &parent) : strData(strData), components(components), parent(parent) {} QStringList strData; QVector components; std::shared_ptr parent; }; } // namespace PathEls #define QMLDOM_USTRING(s) u##s #define QMLDOM_FIELD(name) inline constexpr const auto name = QMLDOM_USTRING(#name) /*! \internal In an ideal world, the Fields namespace would be an enum, not strings. Use FieldType whenever you expect a static String from the Fields namespace instead of an arbitrary QStringView. */ using FieldType = QStringView; // namespace, so it cam be reopened to add more entries namespace Fields{ QMLDOM_FIELD(access); QMLDOM_FIELD(accessSemantics); QMLDOM_FIELD(allSources); QMLDOM_FIELD(alternative); QMLDOM_FIELD(annotations); QMLDOM_FIELD(arguments); QMLDOM_FIELD(astComments); QMLDOM_FIELD(astRelocatableDump); QMLDOM_FIELD(attachedType); QMLDOM_FIELD(attachedTypeName); QMLDOM_FIELD(autoExports); QMLDOM_FIELD(base); QMLDOM_FIELD(binaryExpression); QMLDOM_FIELD(bindable); QMLDOM_FIELD(bindingElement); QMLDOM_FIELD(bindingIdentifiers); QMLDOM_FIELD(bindingType); QMLDOM_FIELD(bindings); QMLDOM_FIELD(block); QMLDOM_FIELD(body); QMLDOM_FIELD(callee); QMLDOM_FIELD(canonicalFilePath); QMLDOM_FIELD(canonicalPath); QMLDOM_FIELD(caseBlock); QMLDOM_FIELD(caseClause); QMLDOM_FIELD(caseClauses); QMLDOM_FIELD(catchBlock); QMLDOM_FIELD(catchParameter); QMLDOM_FIELD(children); QMLDOM_FIELD(classNames); QMLDOM_FIELD(code); QMLDOM_FIELD(commentedElements); QMLDOM_FIELD(comments); QMLDOM_FIELD(components); QMLDOM_FIELD(condition); QMLDOM_FIELD(consequence); QMLDOM_FIELD(contents); QMLDOM_FIELD(contentsDate); QMLDOM_FIELD(cppType); QMLDOM_FIELD(currentExposedAt); QMLDOM_FIELD(currentIsValid); QMLDOM_FIELD(currentItem); QMLDOM_FIELD(currentRevision); QMLDOM_FIELD(declarations); QMLDOM_FIELD(defaultClause); QMLDOM_FIELD(defaultPropertyName); QMLDOM_FIELD(defaultValue); QMLDOM_FIELD(designerSupported); QMLDOM_FIELD(elLocation); QMLDOM_FIELD(elements); QMLDOM_FIELD(elementCanonicalPath); QMLDOM_FIELD(enumerations); QMLDOM_FIELD(errors); QMLDOM_FIELD(exportSource); QMLDOM_FIELD(exports); QMLDOM_FIELD(expr); QMLDOM_FIELD(expression); QMLDOM_FIELD(expressionType); QMLDOM_FIELD(extensionTypeName); QMLDOM_FIELD(fileLocationsTree); QMLDOM_FIELD(fileName); QMLDOM_FIELD(finallyBlock); QMLDOM_FIELD(forStatement); QMLDOM_FIELD(fullRegion); QMLDOM_FIELD(get); QMLDOM_FIELD(globalScopeName); QMLDOM_FIELD(globalScopeWithName); QMLDOM_FIELD(hasCallback); QMLDOM_FIELD(hasCustomParser); QMLDOM_FIELD(idStr); QMLDOM_FIELD(identifier); QMLDOM_FIELD(ids); QMLDOM_FIELD(implicit); QMLDOM_FIELD(import); QMLDOM_FIELD(importId); QMLDOM_FIELD(importScope); QMLDOM_FIELD(importSources); QMLDOM_FIELD(imported); QMLDOM_FIELD(imports); QMLDOM_FIELD(inProgress); QMLDOM_FIELD(infoItem); QMLDOM_FIELD(inheritVersion); QMLDOM_FIELD(initializer); QMLDOM_FIELD(interfaceNames); QMLDOM_FIELD(isAlias); QMLDOM_FIELD(isComposite); QMLDOM_FIELD(isConstructor); QMLDOM_FIELD(isCreatable); QMLDOM_FIELD(isDefaultMember); QMLDOM_FIELD(isFinal); QMLDOM_FIELD(isInternal); QMLDOM_FIELD(isLatest); QMLDOM_FIELD(isList); QMLDOM_FIELD(isPointer); QMLDOM_FIELD(isReadonly); QMLDOM_FIELD(isRequired); QMLDOM_FIELD(isSignalHandler); QMLDOM_FIELD(isSingleton); QMLDOM_FIELD(isValid); QMLDOM_FIELD(jsFileWithPath); QMLDOM_FIELD(kind); QMLDOM_FIELD(lastRevision); QMLDOM_FIELD(label); QMLDOM_FIELD(lastValidRevision); QMLDOM_FIELD(left); QMLDOM_FIELD(loadInfo); QMLDOM_FIELD(loadOptions); QMLDOM_FIELD(loadPaths); QMLDOM_FIELD(loadsWithWork); QMLDOM_FIELD(localOffset); QMLDOM_FIELD(location); QMLDOM_FIELD(logicalPath); QMLDOM_FIELD(majorVersion); QMLDOM_FIELD(metaRevisions); QMLDOM_FIELD(methodType); QMLDOM_FIELD(methods); QMLDOM_FIELD(minorVersion); QMLDOM_FIELD(moduleIndex); QMLDOM_FIELD(moduleIndexWithUri); QMLDOM_FIELD(moduleScope); QMLDOM_FIELD(moreCaseClauses); QMLDOM_FIELD(nAllLoadedCallbacks); QMLDOM_FIELD(nCallbacks); QMLDOM_FIELD(nLoaded); QMLDOM_FIELD(nNotdone); QMLDOM_FIELD(name); QMLDOM_FIELD(nameIdentifiers); QMLDOM_FIELD(newlinesBefore); QMLDOM_FIELD(nextComponent); QMLDOM_FIELD(nextScope); QMLDOM_FIELD(notify); QMLDOM_FIELD(objects); QMLDOM_FIELD(onAttachedObject); QMLDOM_FIELD(operation); QMLDOM_FIELD(options); QMLDOM_FIELD(parameters); QMLDOM_FIELD(parent); QMLDOM_FIELD(parentObject); QMLDOM_FIELD(path); QMLDOM_FIELD(plugins); QMLDOM_FIELD(postCode); QMLDOM_FIELD(postCommentLocations); QMLDOM_FIELD(postComments); QMLDOM_FIELD(pragma); QMLDOM_FIELD(pragmas); QMLDOM_FIELD(preCode); QMLDOM_FIELD(preCommentLocations); QMLDOM_FIELD(preComments); QMLDOM_FIELD(properties); QMLDOM_FIELD(propertyDef); QMLDOM_FIELD(propertyDefRef); QMLDOM_FIELD(propertyDefs); QMLDOM_FIELD(propertyInfos); QMLDOM_FIELD(propertyName); QMLDOM_FIELD(prototypes); QMLDOM_FIELD(qmlDirectoryWithPath); QMLDOM_FIELD(qmlFileWithPath); QMLDOM_FIELD(qmlFiles); QMLDOM_FIELD(qmldirFileWithPath); QMLDOM_FIELD(qmldirWithPath); QMLDOM_FIELD(qmltypesFileWithPath); QMLDOM_FIELD(qmltypesFiles); QMLDOM_FIELD(qualifiedImports); QMLDOM_FIELD(rawComment); QMLDOM_FIELD(read); QMLDOM_FIELD(referredObject); QMLDOM_FIELD(referredObjectPath); QMLDOM_FIELD(regionComments); QMLDOM_FIELD(regions); QMLDOM_FIELD(requestedAt); QMLDOM_FIELD(requestingUniverse); QMLDOM_FIELD(returnType); QMLDOM_FIELD(returnTypeName); QMLDOM_FIELD(right); QMLDOM_FIELD(rootComponent); QMLDOM_FIELD(scopeType); QMLDOM_FIELD(scriptElement); QMLDOM_FIELD(sources); QMLDOM_FIELD(statement); QMLDOM_FIELD(statements); QMLDOM_FIELD(status); QMLDOM_FIELD(stringValue); QMLDOM_FIELD(subComponents); QMLDOM_FIELD(subImports); QMLDOM_FIELD(subItems); QMLDOM_FIELD(symbol); QMLDOM_FIELD(symbols); QMLDOM_FIELD(target); QMLDOM_FIELD(targetPropertyName); QMLDOM_FIELD(text); QMLDOM_FIELD(type); QMLDOM_FIELD(typeArgument); QMLDOM_FIELD(typeArgumentName); QMLDOM_FIELD(typeName); QMLDOM_FIELD(types); QMLDOM_FIELD(universe); QMLDOM_FIELD(updatedScriptExpressions); QMLDOM_FIELD(uri); QMLDOM_FIELD(uris); QMLDOM_FIELD(validExposedAt); QMLDOM_FIELD(validItem); QMLDOM_FIELD(value); QMLDOM_FIELD(valueTypeName); QMLDOM_FIELD(values); QMLDOM_FIELD(version); QMLDOM_FIELD(when); QMLDOM_FIELD(write); } // namespace Fields class Source; size_t qHash(const Path &, size_t); class PathIterator; // Define a iterator for it? // begin() can basically be itself, end() the empty path (zero length), iteration though dropFront() class QMLDOM_EXPORT Path{ Q_GADGET Q_DECLARE_TR_FUNCTIONS(ErrorGroup); public: using Kind = PathEls::Kind; using Component = PathEls::PathComponent; static ErrorGroups myErrors(); // use static consts and central registration instead? Path() = default; explicit Path(const PathEls::PathComponent &c) : m_endOffset(0), m_length(0) { *this = appendComponent(c); } int length() const { return m_length; } Path operator[](int i) const; explicit operator bool() const; PathIterator begin() const; PathIterator end() const; PathRoot headRoot() const; PathCurrent headCurrent() const; Kind headKind() const; QString headName() const; bool checkHeadName(QStringView name) const; index_type headIndex(index_type defaultValue=-1) const; std::function headFilter() const; Path head() const; Path last() const; Source split() const; void dump(const Sink &sink) const; QString toString() const; Path dropFront(int n = 1) const; Path dropTail(int n = 1) const; Path mid(int offset, int length) const; Path mid(int offset) const; Path appendComponent(const PathEls::PathComponent &c); // # Path construction static Path fromString(const QString &s, const ErrorHandler &errorHandler = nullptr); static Path fromString(QStringView s, const ErrorHandler &errorHandler = nullptr); static Path Root(PathRoot r); static Path Root(QStringView s=u""); static Path Root(const QString &s); static Path Index(index_type i); static Path Field(QStringView s=u""); static Path Field(const QString &s); static Path Key(QStringView s=u""); static Path Key(const QString &s); static Path Current(PathCurrent c); static Path Current(QStringView s=u""); static Path Current(const QString &s); static Path Empty(); // add Path empty() const; Path field(const QString &name) const; Path field(QStringView name) const; Path key(const QString &name) const; Path key(QStringView name) const; Path index(index_type i) const; Path any() const; Path filter(const std::function &, const QString &) const; Path filter(const std::function &, QStringView desc=u"") const; Path current(PathCurrent s) const; Path current(const QString &s) const; Path current(QStringView s=u"") const; Path path(const Path &toAdd, bool avoidToAddAsBase = false) const; Path expandFront() const; Path expandBack() const; Path &operator++(); Path operator ++(int); // iterator traits using difference_type = long; using value_type = Path; using pointer = const Component*; using reference = const Path&; using iterator_category = std::forward_iterator_tag; static int cmp(const Path &p1, const Path &p2); private: const Component &component(int i) const; explicit Path(quint16 endOffset, quint16 length, const std::shared_ptr &data); friend class QQmlJS::Dom::PathEls::TestPaths; friend class FieldFilter; friend size_t qHash(const Path &, size_t); Path noEndOffset() const; quint16 m_endOffset = 0; quint16 m_length = 0; std::shared_ptr m_data = {}; }; inline bool operator==(const Path &lhs, const Path &rhs) { return lhs.length() == rhs.length() && Path::cmp(lhs, rhs) == 0; } inline bool operator!=(const Path &lhs, const Path &rhs) { return lhs.length() != rhs.length() || Path::cmp(lhs, rhs) != 0; } inline bool operator<(const Path &lhs, const Path &rhs) { return Path::cmp(lhs, rhs) < 0; } inline bool operator>(const Path &lhs, const Path &rhs) { return Path::cmp(lhs, rhs) > 0; } inline bool operator<=(const Path &lhs, const Path &rhs) { return Path::cmp(lhs, rhs) <= 0; } inline bool operator>=(const Path &lhs, const Path &rhs) { return Path::cmp(lhs, rhs) >= 0; } class PathIterator { public: Path currentEl; Path operator *() const { return currentEl.head(); } PathIterator operator ++() { currentEl = currentEl.dropFront(); return *this; } PathIterator operator ++(int) { PathIterator res{currentEl}; currentEl = currentEl.dropFront(); return res; } bool operator ==(const PathIterator &o) const { return currentEl == o.currentEl; } bool operator !=(const PathIterator &o) const { return currentEl != o.currentEl; } }; class Source { public: Path pathToSource; Path pathFromSource; }; inline size_t qHash(const Path &path, size_t seed) { const size_t bufSize = 256; size_t buf[bufSize]; size_t *it = &buf[0]; *it++ = path.length(); if (path.length()>0) { int iPath = path.length(); size_t maxPath = bufSize / 2 - 1; size_t endPath = (size_t(iPath) > maxPath) ? maxPath - iPath : 0; while (size_t(iPath) > endPath) { Path p = path[--iPath]; Path::Kind k = p.headKind(); *it++ = size_t(k); *it++ = qHash(p.component(0).stringView(), seed)^size_t(p.headRoot())^size_t(p.headCurrent()); } } // TODO: Get rid of the reinterpret_cast. // Rather hash the path components in a more structured way. return qHash(QByteArray::fromRawData(reinterpret_cast(&buf[0]), (it - &buf[0])*sizeof(size_t)), seed); } inline QDebug operator<<(QDebug debug, const Path &p) { debug << p.toString(); return debug; } } // end namespace Dom } // end namespace QQmlJS QT_END_NAMESPACE #endif // QMLDOM_PATH_H