/**************************************************************************** ** ** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). ** All rights reserved. ** Contact: Nokia Corporation (qt-info@nokia.com) ** ** This file is part of the QtXmlPatterns module of the Qt Toolkit. ** ** $QT_BEGIN_LICENSE:LGPL$ ** No Commercial Usage ** This file contains pre-release code and may not be distributed. ** You may use this file in accordance with the terms and conditions ** contained in the Technology Preview License Agreement accompanying ** this package. ** ** GNU Lesser General Public License Usage ** Alternatively, this file may be used under the terms of the GNU Lesser ** General Public License version 2.1 as published by the Free Software ** Foundation and appearing in the file LICENSE.LGPL included in the ** packaging of this file. Please review the following information to ** ensure the GNU Lesser General Public License version 2.1 requirements ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. ** ** In addition, as a special exception, Nokia gives you certain additional ** rights. These rights are described in the Nokia Qt LGPL Exception ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. ** ** If you have questions regarding the use of this file, please contact ** Nokia at qt-info@nokia.com. ** ** ** ** ** ** ** ** ** $QT_END_LICENSE$ ** ****************************************************************************/ // // 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. #ifndef Patternist_NamePool_H #define Patternist_NamePool_H #include #include #include #include #include #include #include #include QT_BEGIN_HEADER QT_BEGIN_NAMESPACE namespace QPatternist { /** * @short Store names such as namespace bindings and QNames and allows them to * be referenced in efficient ways. * * Once a string have been inserted it stays there and cannot be removed. The * only way to deallocate any string in the NamePool is to deallocate the * NamePool itself, as a whole. * * This class is not only reentrant, it is thread-safe in all sense of the * word. All functions of this class can be called concurrently. This is * achieved by internal locking. * * @author Frans Englich * @todo Use QSubStrings, we can save very many heap allocations by that. * @todo Check limits */ class Q_AUTOTEST_EXPORT NamePool : public QSharedData { public: typedef QExplicitlySharedDataPointer Ptr; private: friend class StandardNamespaces; enum { NoSuchValue = -1, /** * This must be identical to the amount of members in * StandardNamespaces. */ StandardNamespaceCount = 11, StandardPrefixCount = 9, StandardLocalNameCount = 141 }; QVector m_prefixes; QVector m_namespaces; QVector m_localNames; /** * This hash contains no essential data, but speeds up * finding a prefix in m_prefixes by mapping a prefix(the key) to * the index into m_prefixes(which the value is). * * In other words, one can skip this variable at the cost of having * to linearly loop over prefixes, in order to find the entry. */ QHash m_prefixMapping; /** * Same as m_prefixMapping but applies for URIs, and hence m_namespaces instead * of m_prefixes. */ QHash m_namespaceMapping; QHash m_localNameMapping; enum DefaultCapacities { DefaultPrefixCapacity = 10, DefaultURICapacity = DefaultPrefixCapacity, /** * It looks like it's quite common with 40-60 different local names per XML * vocabulary. For background, see: * * - http://englich.wordpress.com/2007/01/11/representing-xml/ * - http://englich.wordpress.com/2007/01/09/xmlstat/ */ DefaultLocalNameCapacity = 60 }; public: NamePool(); /** * @short Allocates a namespace binding for @p prefix and @p uri. * * In the returned QXmlName, the local name is * StandardLocalNames::empty, and QXmlName::prefix() and * QXmlName::namespaceUri() returns @p prefix and @p uri, respectively. * * In older versions of this code, the class NamespaceBinding existed, * but as part of having the public class QXmlName, it was dropped and * a special interpretation/convention involving use of QXmlName was * adopted. */ QXmlName allocateBinding(const QString &prefix, const QString &uri); QXmlName allocateQName(const QString &uri, const QString &localName, const QString &prefix = QString()); inline QXmlName allocateQName(const QXmlName::NamespaceCode uri, const QString &ln) { /* We don't lock here, but we do in allocateLocalName(). */ return QXmlName(uri, allocateLocalName(ln)); } inline const QString &stringForLocalName(const QXmlName::LocalNameCode code) const { const QReadLocker l(&lock); return m_localNames.at(code); } inline const QString &stringForPrefix(const QXmlName::PrefixCode code) const { const QReadLocker l(&lock); return m_prefixes.at(code); } inline const QString &stringForNamespace(const QXmlName::NamespaceCode code) const { const QReadLocker l(&lock); return m_namespaces.at(code); } QString displayName(const QXmlName qName) const; inline QString toLexical(const QXmlName qName) const { const QReadLocker l(&lock); Q_ASSERT_X(!qName.isNull(), "", "It makes no sense to call toLexical() on a null name."); if(qName.hasPrefix()) { const QString &p = m_prefixes.at(qName.prefix()); return p + QLatin1Char(':') + m_localNames.at(qName.localName()); } else return m_localNames.at(qName.localName()); } inline QXmlName::NamespaceCode allocateNamespace(const QString &uri) { const QWriteLocker l(&lock); return unlockedAllocateNamespace(uri); } inline QXmlName::LocalNameCode allocateLocalName(const QString &ln) { const QWriteLocker l(&lock); return unlockedAllocateLocalName(ln); } inline QXmlName::PrefixCode allocatePrefix(const QString &prefix) { const QWriteLocker l(&lock); return unlockedAllocatePrefix(prefix); } QString toClarkName(const QXmlName &name) const; QXmlName fromClarkName(const QString &clarkName); private: /** * @note This function can not be called concurrently. */ QXmlName::NamespaceCode unlockedAllocateNamespace(const QString &uri); /** * @note This function can not be called concurrently. */ QXmlName::LocalNameCode unlockedAllocateLocalName(const QString &ln); /** * It's assumed that @p prefix is a valid @c NCName. * * @note This function can not be called concurrently. */ QXmlName::PrefixCode unlockedAllocatePrefix(const QString &prefix); Q_DISABLE_COPY(NamePool) /** * @note This function can not be called concurrently. */ const QString &displayPrefix(const QXmlName::NamespaceCode nc) const; mutable QReadWriteLock lock; }; /** * @short Formats QName. * * @relates QXmlName */ static inline QString formatKeyword(const NamePool::Ptr &np, const QXmlName name) { return QLatin1String("") + escape(np->displayName(name)) + QLatin1String(""); } /** * @see NamespaceResolver::Constants */ class StandardNamespaces { public: enum ID { /** * This does not mean empty in the sense of "empty", but * in the sense of an empty string, "". * * Its value, zero, is significant. */ empty = 0, fn, local, xml, xmlns, xs, xsi, xslt, /** * @short A special value that when passed as the namespace part * to NamespaceResolver::addBinding(), undeclares the prefix. * * This is used by the namespace prolog declaration. * * A dummy value is added to the name pool. */ UndeclarePrefix, /** * Signals that a node shouldn't inherit namespaces from its parent. Must be used * with StandardPrefixes::StopNamespaceInheritance. */ StopNamespaceInheritance, /** * A namespace used to identify for instance @c \#all template * mode in XSL-T. */ InternalXSLT }; }; // const QString * a = &*qset.insert("foo"); class StandardLocalNames { public: enum { abs, adjust_dateTime_to_timezone, adjust_date_to_timezone, adjust_time_to_timezone, all, arity, avg, base, base_uri, boolean, ceiling, codepoint_equal, codepoints_to_string, collection, compare, concat, contains, count, current, current_date, current_dateTime, current_time, data, dateTime, day_from_date, day_from_dateTime, days_from_duration, deep_equal, Default, default_collation, distinct_values, doc, doc_available, document, document_uri, element_available, empty, encode_for_uri, ends_with, error, escape_html_uri, exactly_one, exists, False, floor, function_available, function_name, generate_id, generic_string_join, hours_from_dateTime, hours_from_duration, hours_from_time, id, idref, implicit_timezone, index_of, in_scope_prefixes, insert_before, iri_to_uri, is_schema_aware, key, lang, last, local_name, local_name_from_QName, lower_case, matches, max, min, minutes_from_dateTime, minutes_from_duration, minutes_from_time, month_from_date, month_from_dateTime, months_from_duration, name, namespace_uri, namespace_uri_for_prefix, namespace_uri_from_QName, nilled, node_name, normalize_space, normalize_unicode, Not, number, one_or_more, position, prefix_from_QName, product_name, product_version, property_name, QName, remove, replace, resolve_QName, resolve_uri, reverse, root, round, round_half_to_even, seconds_from_dateTime, seconds_from_duration, seconds_from_time, sourceValue, starts_with, static_base_uri, string, string_join, string_length, string_to_codepoints, subsequence, substring, substring_after, substring_before, sum, supports_backwards_compatibility, supports_serialization, system_property, timezone_from_date, timezone_from_dateTime, timezone_from_time, tokenize, trace, translate, True, type_available, unordered, unparsed_entity_public_id, unparsed_entity_uri, unparsed_text, unparsed_text_available, upper_case, vendor, vendor_url, version, xml, xmlns, year_from_date, year_from_dateTime, years_from_duration, zero_or_one }; }; class StandardPrefixes { public: enum { /** * This does not mean empty in the sense of "empty", but * in the sense of an empty string, "". * * Its value, zero, is significant. */ empty = 0, fn, local, xml, xmlns, xs, xsi, ns0, StopNamespaceInheritance }; }; } inline QXmlName::LocalNameCode QXmlName::localName() const { return (m_qNameCode & LocalNameMask) >> LocalNameOffset; } inline QXmlName::PrefixCode QXmlName::prefix() const { return (m_qNameCode & PrefixMask) >> PrefixOffset; } inline bool QXmlName::hasPrefix() const { return prefix() != 0; } inline bool QXmlName::hasNamespace() const { return namespaceURI() != 0; } inline QXmlName::NamespaceCode QXmlName::namespaceURI() const { return (m_qNameCode & NamespaceMask) >> NamespaceOffset; } inline bool QXmlName::isLexicallyEqual(const QXmlName &other) const { return (m_qNameCode & LexicalQNameMask) == (other.m_qNameCode & LexicalQNameMask); } inline void QXmlName::setPrefix(const PrefixCode c) { m_qNameCode |= (c << PrefixOffset); } inline void QXmlName::setNamespaceURI(const NamespaceCode c) { m_qNameCode |= (c << NamespaceOffset); } inline void QXmlName::setLocalName(const LocalNameCode c) { m_qNameCode |= (c << LocalNameOffset); } inline QXmlName::Code QXmlName::code() const { return m_qNameCode; } inline QXmlName::QXmlName(const NamespaceCode uri, const LocalNameCode ln, const PrefixCode p) : m_qNameCode((uri << NamespaceOffset) + (ln << LocalNameOffset) + (p << PrefixOffset)) { /* We can't use members like prefix() here because if one of the * values are to large, they would overflow into the others. */ Q_ASSERT_X(p <= MaximumPrefixes, "", qPrintable(QString::fromLatin1("NamePool prefix limits: max is %1, therefore %2 exceeds.").arg(MaximumPrefixes).arg(p))); Q_ASSERT_X(ln <= MaximumLocalNames, "", qPrintable(QString::fromLatin1("NamePool local name limits: max is %1, therefore %2 exceeds.").arg(MaximumLocalNames).arg(ln))); Q_ASSERT_X(uri <= MaximumNamespaces, "", qPrintable(QString::fromLatin1("NamePool namespace limits: max is %1, therefore %2 exceeds.").arg(MaximumNamespaces).arg(uri))); } Q_DECLARE_TYPEINFO(QPatternist::NamePool::Ptr, Q_MOVABLE_TYPE); QT_END_NAMESPACE QT_END_HEADER #endif