qobjectxmlmodel.cpp Example File

webkitwidgets/xmlpatterns/qobjectxmlmodel/qobjectxmlmodel.cpp

  /****************************************************************************
  **
  ** Copyright (C) 2015 The Qt Company Ltd.
  ** Contact: http://www.qt.io/licensing/
  **
  ** This file is part of the examples of the Qt Toolkit.
  **
  ** $QT_BEGIN_LICENSE:BSD$
  ** You may use this file under the terms of the BSD license as follows:
  **
  ** "Redistribution and use in source and binary forms, with or without
  ** modification, are permitted provided that the following conditions are
  ** met:
  **   * Redistributions of source code must retain the above copyright
  **     notice, this list of conditions and the following disclaimer.
  **   * Redistributions in binary form must reproduce the above copyright
  **     notice, this list of conditions and the following disclaimer in
  **     the documentation and/or other materials provided with the
  **     distribution.
  **   * Neither the name of The Qt Company Ltd nor the names of its
  **     contributors may be used to endorse or promote products derived
  **     from this software without specific prior written permission.
  **
  **
  ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  **
  ** $QT_END_LICENSE$
  **
  ****************************************************************************/

  #include <QVector>
  #include <QtDebug>

  #include <QCoreApplication>
  #include <QMetaProperty>
  #include <QXmlQuery>
  #include <QXmlResultItems>

  #include "qobjectxmlmodel.h"

  /*
  <metaObjects>
      <metaObject className="QObject"/>
      <metaObject className="QWidget" superClass="QObject">
      </metaObject>
      ...
  </metaObjects>
  <QObject objectName="MyWidget" property1="..." property2="..."> <!-- This is root() -->
      <QObject objectName="MyFOO" property1="..."/>
      ....
  </QObject>
  */

  QObjectXmlModel::QObjectXmlModel(QObject *const object, const QXmlNamePool &np)
      : QSimpleXmlNodeModel(np),
        m_baseURI(QUrl::fromLocalFile(QCoreApplication::applicationFilePath())),
        m_root(object),
        m_allMetaObjects(allMetaObjects())
  {
      Q_ASSERT(m_baseURI.isValid());
  }

  QXmlNodeModelIndex QObjectXmlModel::qObjectSibling(const int pos, const QXmlNodeModelIndex &n) const
  {
      Q_ASSERT(pos == 1 || pos == -1);
      Q_ASSERT(asQObject(n));

      const QObject *parent = asQObject(n)->parent();
      if (parent) {
          const QList<QObject *> &children = parent->children();
          const int siblingPos = children.indexOf(asQObject(n)) + pos;

          if (siblingPos >= 0 && siblingPos < children.count())
              return createIndex(children.at(siblingPos));
          else
              return QXmlNodeModelIndex();
      }
      else
          return QXmlNodeModelIndex();
  }

  QObjectXmlModel::QObjectNodeType QObjectXmlModel::toNodeType(const QXmlNodeModelIndex &n)
  {
      return QObjectNodeType(n.additionalData() & (15 << 26));
  }

  QObjectXmlModel::AllMetaObjects QObjectXmlModel::allMetaObjects() const
  {
      QXmlQuery query(namePool());
      query.bindVariable("root", root());
      query.setQuery("declare variable $root external;"
                     "$root/descendant-or-self::QObject");
      Q_ASSERT(query.isValid());

      QXmlResultItems result;
      query.evaluateTo(&result);
      QXmlItem i(result.next());

      AllMetaObjects objects;
      while (!i.isNull()) {
          const QMetaObject *moo = asQObject(i.toNodeModelIndex())->metaObject();
          while (moo) {
              if (!objects.contains(moo))
                  objects.append(moo);
              moo = moo->superClass();
          }
          i = result.next();
      }

      Q_ASSERT(!objects.contains(0));
      return objects;
  }

  QXmlNodeModelIndex QObjectXmlModel::metaObjectSibling(const int pos, const QXmlNodeModelIndex &n) const
  {
      Q_ASSERT(pos == 1 || pos == -1);
      Q_ASSERT(!n.isNull());

      const int indexOf = m_allMetaObjects.indexOf(static_cast<const QMetaObject *>(n.internalPointer())) + pos;

      if (indexOf >= 0 && indexOf < m_allMetaObjects.count())
          return createIndex(const_cast<QMetaObject *>(m_allMetaObjects.at(indexOf)), MetaObject);
      else
          return QXmlNodeModelIndex();
  }

  QXmlNodeModelIndex QObjectXmlModel::nextFromSimpleAxis(SimpleAxis axis, const QXmlNodeModelIndex &n) const
  {
      switch (toNodeType(n))
      {
          case IsQObject:
          {
              switch (axis)
              {
                  case Parent:
                      return createIndex(asQObject(n)->parent());

                  case FirstChild:
                  {
                      if (!asQObject(n) || asQObject(n)->children().isEmpty())
                          return QXmlNodeModelIndex();
                      else
                          return createIndex(asQObject(n)->children().first());
                  }

                  case NextSibling:
                      return qObjectSibling(1, n);

                  case PreviousSibling:
                  {
                      if (asQObject(n) == m_root)
                          return createIndex(qint64(0), MetaObjects);
                      else
                          return qObjectSibling(-1, n);
                  }
              }
              Q_ASSERT(false);
          }

          case QObjectClassName:
          case QObjectProperty:
          {
              Q_ASSERT(axis == Parent);
              return createIndex(asQObject(n));
          }

          case MetaObjects:
          {
              switch (axis)
              {
                  case Parent:
                      return QXmlNodeModelIndex();
                  case PreviousSibling:
                      return QXmlNodeModelIndex();
                  case NextSibling:
                      return root();
                  case FirstChild:
                  {
                      return createIndex(const_cast<QMetaObject*>(m_allMetaObjects.first()),MetaObject);
                  }
              }
              Q_ASSERT(false);
          }

          case MetaObject:
          {
              switch (axis)
              {
                  case FirstChild:
                      return QXmlNodeModelIndex();
                  case Parent:
                      return createIndex(qint64(0), MetaObjects);
                  case PreviousSibling:
                      return metaObjectSibling(-1, n);
                  case NextSibling:
                      return metaObjectSibling(1, n);
              }
          }

          case MetaObjectClassName:
          case MetaObjectSuperClass:
          {
              Q_ASSERT(axis == Parent);
              return createIndex(asQObject(n), MetaObject);
          }
      }

      Q_ASSERT(false);
      return QXmlNodeModelIndex();
  }

  QVector<QXmlNodeModelIndex> QObjectXmlModel::attributes(const QXmlNodeModelIndex& n) const
  {
       QVector<QXmlNodeModelIndex> result;
       QObject *const object = asQObject(n);

       switch(toNodeType(n))
       {
          case IsQObject:
          {
              const QMetaObject *const metaObject = object->metaObject();
              const int count = metaObject->propertyCount();
              result.append(createIndex(object, QObjectClassName));

              for (int i = 0; i < count; ++i) {
                  const QMetaProperty qmp(metaObject->property(i));
                  const int ii = metaObject->indexOfProperty(qmp.name());
                  if (i == ii)
                      result.append(createIndex(object, QObjectProperty | i));
              }
              return result;
          }

          case MetaObject:
          {
              result.append(createIndex(object, MetaObjectClassName));
              result.append(createIndex(object, MetaObjectSuperClass));
              return result;
          }
          default:
              return QVector<QXmlNodeModelIndex>();
       }
  }

  QObject *QObjectXmlModel::asQObject(const QXmlNodeModelIndex &n)
  {
      return static_cast<QObject *>(n.internalPointer());
  }

  bool QObjectXmlModel::isProperty(const QXmlNodeModelIndex n)
  {
      return n.additionalData() & QObjectProperty;
  }

  QUrl QObjectXmlModel::documentUri(const QXmlNodeModelIndex& ) const
  {
      return m_baseURI;
  }

  QXmlNodeModelIndex::NodeKind QObjectXmlModel::kind(const QXmlNodeModelIndex& n) const
  {
      switch (toNodeType(n))
      {
          case IsQObject:
          case MetaObject:
          case MetaObjects:
              return QXmlNodeModelIndex::Element;

          case QObjectProperty:
          case MetaObjectClassName:
          case MetaObjectSuperClass:
          case QObjectClassName:
              return QXmlNodeModelIndex::Attribute;
      }

      Q_ASSERT(false);
      return QXmlNodeModelIndex::Element;
  }

  QXmlNodeModelIndex::DocumentOrder QObjectXmlModel::compareOrder(const QXmlNodeModelIndex& , const QXmlNodeModelIndex& ) const
  {
      return QXmlNodeModelIndex::Follows; // TODO
  }

  QXmlNodeModelIndex QObjectXmlModel::root() const
  {
      return createIndex(m_root);
  }

  QXmlNodeModelIndex QObjectXmlModel::root(const QXmlNodeModelIndex& n) const
  {
      QObject *p = asQObject(n);
      Q_ASSERT(p);

      do {
          QObject *const candidate = p->parent();
          if (candidate)
              p = candidate;
          else
              break;
      }
      while (true);

      return createIndex(p);
  }

  /*!
    We simply throw all of them into a QList and
    return an iterator over it.
   */
  QXmlNodeModelIndex::List QObjectXmlModel::ancestors(const QXmlNodeModelIndex n) const
  {
      const QObject *p = asQObject(n);
      Q_ASSERT(p);

      QXmlNodeModelIndex::List result;
      do {
          QObject *const candidate = p->parent();
          if (candidate) {
              result.append(createIndex(candidate, 0));
              p = candidate;
          }
          else
              break;
      }
      while (true);

      return result;
  }

  QMetaProperty QObjectXmlModel::toMetaProperty(const QXmlNodeModelIndex &n)
  {
      const int propertyOffset = n.additionalData() & (~QObjectProperty);
      const QObject *const qo = asQObject(n);
      return qo->metaObject()->property(propertyOffset);
  }

  QXmlName QObjectXmlModel::name(const QXmlNodeModelIndex &n) const
  {
      switch (toNodeType(n))
      {
          case IsQObject:
              return QXmlName(namePool(), QLatin1String("QObject"));
          case MetaObject:
              return QXmlName(namePool(), QLatin1String("metaObject"));
          case QObjectClassName:
          case MetaObjectClassName:
              return QXmlName(namePool(), QLatin1String("className"));
          case QObjectProperty:
              return QXmlName(namePool(), toMetaProperty(n).name());
          case MetaObjects:
              return QXmlName(namePool(), QLatin1String("metaObjects"));
          case MetaObjectSuperClass:
              return QXmlName(namePool(), QLatin1String("superClass"));
      }

      Q_ASSERT(false);
      return QXmlName();
  }

  QVariant QObjectXmlModel::typedValue(const QXmlNodeModelIndex &n) const
  {
      switch (toNodeType(n))
      {
          case QObjectProperty:
          {
             const QVariant &candidate = toMetaProperty(n).read(asQObject(n));
             if (isTypeSupported(candidate.type()))
                 return candidate;
             else
                 return QVariant();
          }

          case MetaObjectClassName:
              return QVariant(static_cast<QMetaObject*>(n.internalPointer())->className());

          case MetaObjectSuperClass:
          {
              const QMetaObject *const superClass = static_cast<QMetaObject*>(n.internalPointer())->superClass();
              if (superClass)
                  return QVariant(superClass->className());
              else
                  return QVariant();
          }

          case QObjectClassName:
              return QVariant(asQObject(n)->metaObject()->className());

          default:
              return QVariant();
      }
  }

  /*!
   Returns \c true if QVariants of type \a type can be used
   in QtXmlPatterns, otherwise \c false.
   */
  bool QObjectXmlModel::isTypeSupported(QVariant::Type type)
  {
      /* See data/qatomicvalue.cpp too. */
      switch (type)
      {
          /* Fallthrough all these. */
          case QVariant::Char:
          case QVariant::String:
          case QVariant::Url:
          case QVariant::Bool:
          case QVariant::ByteArray:
          case QVariant::Int:
          case QVariant::LongLong:
          case QVariant::ULongLong:
          case QVariant::Date:
          case QVariant::DateTime:
          case QVariant::Time:
          case QVariant::Double:
              return true;
          default:
              return false;
      }
  }