[Telepathy-commits] [telepathy-qt4/master] New architecture of the high-level proxies; Autointrospect for Connection implemented

Olli Salli olli.salli at collabora.co.uk
Mon Sep 22 04:58:09 PDT 2008


---
 TelepathyQt4/cli-connection.cpp |  193 +++++++++++++++++++++++++++++----------
 TelepathyQt4/cli-connection.h   |  196 +++++++++++++++++++++------------------
 2 files changed, 252 insertions(+), 137 deletions(-)

diff --git a/TelepathyQt4/cli-connection.cpp b/TelepathyQt4/cli-connection.cpp
index 4a385bd..232f841 100644
--- a/TelepathyQt4/cli-connection.cpp
+++ b/TelepathyQt4/cli-connection.cpp
@@ -38,18 +38,25 @@ namespace Client
 
 struct Connection::Private
 {
+    // Public object
     Connection& parent;
+
+    // Optional interface proxies
     ConnectionInterfaceAliasingInterface aliasing;
     ConnectionInterfacePresenceInterface presence;
     ConnectionInterfaceSimplePresenceInterface simplePresence;
     DBus::PropertiesInterface properties;
+
+    // Introspection
+    bool initialIntrospection;
+    Readiness readiness;
+    QStringList interfaces;
     QQueue<void (Private::*)()> introspectQueue;
 
-    bool ready;
-    long status;
+    // Introspected properties
+    uint status;
     uint statusReason;
-    QStringList interfaces;
-    ConnectionAliasFlags aliasFlags;
+    uint aliasFlags;
     StatusSpecMap presenceStatuses;
     SimpleStatusSpecMap simplePresenceStatuses;
 
@@ -60,9 +67,13 @@ struct Connection::Private
           simplePresence(parent),
           properties(parent)
     {
-        ready = false;
-        status = -1;
+        initialIntrospection = false;
+        readiness = ReadinessJustCreated;
+        status = ConnectionStatusDisconnected;
         statusReason = ConnectionStatusReasonNoneSpecified;
+        aliasFlags = 0;
+
+        debug() << "Connecting to StatusChanged()";
 
         parent.connect(&parent,
                        SIGNAL(StatusChanged(uint, uint)),
@@ -79,6 +90,12 @@ struct Connection::Private
 
     void introspectAliasing()
     {
+        // The Aliasing interface is not usable before the connection is established
+        if (initialIntrospection) {
+            continueIntrospection();
+            return;
+        }
+
         debug() << "Calling GetAliasFlags()";
         QDBusPendingCallWatcher* watcher =
             new QDBusPendingCallWatcher(aliasing.GetAliasFlags(), &parent);
@@ -87,8 +104,27 @@ struct Connection::Private
                        SLOT(gotAliasFlags(QDBusPendingCallWatcher*)));
     }
 
+    void introspectMain()
+    {
+        // Introspecting the main interface is currently just calling
+        // GetInterfaces(), but it might include other stuff in the future if we
+        // gain GetAll-able properties on the connection
+        debug() << "Calling GetInterfaces()";
+        QDBusPendingCallWatcher* watcher =
+            new QDBusPendingCallWatcher(parent.GetInterfaces(), &parent);
+        parent.connect(watcher,
+                       SIGNAL(finished(QDBusPendingCallWatcher*)),
+                       SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
+    }
+
     void introspectPresence()
     {
+        // The Presence interface is not usable before the connection is established
+        if (initialIntrospection) {
+            continueIntrospection();
+            return;
+        }
+
         debug() << "Calling GetStatuses() (legacy)";
         QDBusPendingCallWatcher* watcher =
             new QDBusPendingCallWatcher(presence.GetStatuses(), &parent);
@@ -113,13 +149,46 @@ struct Connection::Private
     void continueIntrospection()
     {
         if (introspectQueue.isEmpty()) {
-            debug() << "Connection ready";
-            ready = true;
-            emit parent.nowReady();
+            if (initialIntrospection) {
+                initialIntrospection = false;
+                if (readiness < ReadinessNotYetConnected)
+                    changeReadiness(ReadinessNotYetConnected);
+            } else {
+                if (readiness != ReadinessDead)
+                    changeReadiness(ReadinessFull);
+            }
         } else {
             (this->*introspectQueue.dequeue())();
         }
     }
+
+    void changeReadiness(Readiness newReadiness)
+    {
+        Q_ASSERT(newReadiness != readiness);
+
+        switch (readiness) {
+            case ReadinessJustCreated:
+                break;
+            case ReadinessNotYetConnected:
+                Q_ASSERT(newReadiness == ReadinessConnecting
+                        || newReadiness == ReadinessDead);
+                break;
+            case ReadinessConnecting:
+                Q_ASSERT(newReadiness == ReadinessFull
+                        || newReadiness == ReadinessDead);
+                break;
+            case ReadinessFull:
+                Q_ASSERT(newReadiness == ReadinessDead);
+                break;
+            case ReadinessDead:
+                Q_ASSERT(false);
+                break;
+        }
+
+        debug() << "Readiness changed from" << readiness << "to" << newReadiness;
+        readiness = newReadiness;
+        emit parent.readinessChanged(newReadiness);
+    }
 };
 
 Connection::Connection(const QString& serviceName,
@@ -144,12 +213,12 @@ Connection::~Connection()
     delete mPriv;
 }
 
-bool Connection::ready() const
+Connection::Readiness Connection::readiness() const
 {
-    return mPriv->ready;
+    return mPriv->readiness;
 }
 
-long Connection::status() const
+uint Connection::status() const
 {
     return mPriv->status;
 }
@@ -164,9 +233,7 @@ QStringList Connection::interfaces() const
     return mPriv->interfaces;
 }
 
-#if 0
-
-ConnectionAliasFlags Connection::aliasFlags() const
+uint Connection::aliasFlags() const
 {
     return mPriv->aliasFlags;
 }
@@ -176,37 +243,42 @@ StatusSpecMap Connection::presenceStatuses() const
     return mPriv->presenceStatuses;
 }
 
-
 SimpleStatusSpecMap Connection::simplePresenceStatuses() const
 {
-    if (!ready() && mPriv->simplePresenceStatuses.isEmpty()) {
-        debug() << "Getting the simple presence statuses available before connecting";
-        mPriv->simplePresenceStatuses = mPriv->simplePresence.statuses();
-    }
-
     return mPriv->simplePresenceStatuses;
 }
-#endif
 
 void Connection::onStatusChanged(uint status, uint reason)
 {
-    if (mPriv->status == -1) {
-        // We've got a StatusChanged before the initial GetStatus reply, ignore it
-        return;
-    }
-
-    debug().nospace() << "New status (" << status << ", " << reason << ')';
+    debug() << "Status changed from" << mPriv->status << "to" << status << "because of" << reason;
 
     mPriv->status = status;
     mPriv->statusReason = reason;
 
-    if (status == ConnectionStatusConnected) {
-        debug() << "Calling GetInterfaces()";
-        QDBusPendingCallWatcher* watcher =
-            new QDBusPendingCallWatcher(GetInterfaces(), this);
-        connect(watcher,
-                SIGNAL(finished(QDBusPendingCallWatcher*)),
-                SLOT(gotInterfaces(QDBusPendingCallWatcher*)));
+    switch (status) {
+        case ConnectionStatusConnected:
+            debug() << "Performing introspection for the Connected status";
+            mPriv->introspectQueue.enqueue(&Private::introspectMain);
+            mPriv->continueIntrospection();
+            break;
+
+        case ConnectionStatusConnecting:
+            if (mPriv->readiness < ReadinessConnecting)
+                mPriv->changeReadiness(ReadinessConnecting);
+            else
+                warning() << "Got unexpected status change to Connecting";
+            break;
+
+        case ConnectionStatusDisconnected:
+            if (mPriv->readiness != ReadinessDead)
+                mPriv->changeReadiness(ReadinessDead);
+            else
+                warning() << "Got unexpected status change to Disconnected";
+            break;
+
+        default:
+            warning() << "Unknown connection status" << status;
+            break;
     }
 }
 
@@ -214,14 +286,40 @@ void Connection::gotStatus(QDBusPendingCallWatcher* watcher)
 {
     QDBusPendingReply<uint> reply = *watcher;
 
-    if (!reply.isError()) {
-        debug() << "Got reply to initial GetStatus()";
-        // Avoid early return in onStatusChanged()
-        mPriv->status = reply.argumentAt<0>();
-        onStatusChanged(reply.argumentAt<0>(), ConnectionStatusReasonNoneSpecified);
-    } else {
+    if (reply.isError()) {
         warning().nospace() << "GetStatus() failed with " << reply.error().name() << ": " << reply.error().message();
+        mPriv->changeReadiness(ReadinessDead);
+        return;
     }
+
+    uint status = reply.value();
+
+    debug() << "Got connection status" << status;
+    mPriv->status = status;
+
+    // Don't do any introspection yet if the connection is in the Connecting
+    // state; the StatusChanged handler will take care of doing that, if the
+    // connection ever gets to the Connected state.
+    if (status == ConnectionStatusConnecting) {
+        debug() << "Not introspecting yet because the connection is currently Connecting";
+        mPriv->changeReadiness(ReadinessConnecting);
+        return;
+    }
+
+    if (status == ConnectionStatusDisconnected) {
+        debug() << "Performing introspection for the Disconnected status";
+        mPriv->initialIntrospection = true;
+    } else {
+        if (status != ConnectionStatusConnected) {
+            warning() << "Not performing introspection for unknown status" << status;
+            return;
+        } else {
+            debug() << "Performing introspection for the Connected status";
+        }
+    }
+
+    mPriv->introspectQueue.enqueue(&Private::introspectMain);
+    mPriv->continueIntrospection();
 }
 
 void Connection::gotInterfaces(QDBusPendingCallWatcher* watcher)
@@ -230,10 +328,9 @@ void Connection::gotInterfaces(QDBusPendingCallWatcher* watcher)
 
     if (!reply.isError()) {
         mPriv->interfaces = reply.value();
-        debug() << "Got reply to initial GetInterfaces():";
-        debug() << mPriv->interfaces;
+        debug() << "Got reply to GetInterfaces():" << mPriv->interfaces;
     } else {
-        warning().nospace() << "GetInterfaces() failed with " << reply.error().name() << ": " << reply.error().message() << " - assuming no interfaces";
+        warning().nospace() << "GetInterfaces() failed with " << reply.error().name() << ": " << reply.error().message() << " - assuming no new interfaces";
     }
 
     for (QStringList::const_iterator i  = mPriv->interfaces.constBegin();
@@ -261,7 +358,7 @@ void Connection::gotAliasFlags(QDBusPendingCallWatcher* watcher)
 
     if (!reply.isError()) {
         mPriv->aliasFlags = static_cast<ConnectionAliasFlag>(reply.value());
-        debug().nospace() << "Got initial alias flags 0x" << hex << mPriv->aliasFlags;
+        debug().nospace() << "Got alias flags 0x" << hex << mPriv->aliasFlags;
     } else {
         warning().nospace() << "GetAliasFlags() failed with " << reply.error().name() << ": " << reply.error().message();
     }
@@ -275,7 +372,7 @@ void Connection::gotStatuses(QDBusPendingCallWatcher* watcher)
 
     if (!reply.isError()) {
         mPriv->presenceStatuses = reply.value();
-        debug() << "Got initial legacy presence statuses";
+        debug() << "Got" << mPriv->presenceStatuses.size() << "legacy presence statuses";
     } else {
         warning().nospace() << "GetStatuses() failed with " << reply.error().name() << ": " << reply.error().message();
     }
@@ -289,9 +386,9 @@ void Connection::gotSimpleStatuses(QDBusPendingCallWatcher* watcher)
 
     if (!reply.isError()) {
         mPriv->simplePresenceStatuses = qdbus_cast<SimpleStatusSpecMap>(reply.value().variant());
-        debug() << "Got initial simple presence statuses";
+        debug() << "Got" << mPriv->simplePresenceStatuses.size() << "simple presence statuses";
     } else {
-        warning().nospace() << "Getting initial simple presence statuses failed with " << reply.error().name() << ": " << reply.error().message();
+        warning().nospace() << "Getting simple presence statuses failed with " << reply.error().name() << ": " << reply.error().message();
     }
 
     mPriv->continueIntrospection();
diff --git a/TelepathyQt4/cli-connection.h b/TelepathyQt4/cli-connection.h
index 959e18e..1908f2b 100644
--- a/TelepathyQt4/cli-connection.h
+++ b/TelepathyQt4/cli-connection.h
@@ -63,15 +63,77 @@ namespace Client
  * directly:
  * <ul>
  *  <li>%Connection status tracking</li>
- *  <li>Calling #GetInterfaces automatically</li>
- *  <li>Calling #GetAliasFlags automatically</li>
+ *  <li>Calling GetInterfaces() automatically</li>
+ *  <li>Calling GetAliasFlags() automatically</li>
+ *  <li>Calling GetStatuses() automatically</li>
+ *  <li>Getting the SimplePresence Statuses property automatically</li>
  * </ul>
+ *
+ * The remote object state accessor functions on this object (status(),
+ * statusReason(), aliasFlags(), presenceStatuses(), simplePresenceStatuses())
+ * don't make any DBus calls; instead, they return values cached from a previous
+ * introspection run.
  */
 class Connection : public ConnectionInterface
 {
     Q_OBJECT
+    Q_ENUMS(Readiness);
+
 public:
     /**
+     * Describes readiness of the Connection for usage. The readiness depends
+     * on the state of the remote object. In suitable states, an asynchronous
+     * introspection process is started, and the Connection becomes more ready
+     * when that process is completed.
+     */
+    enum Readiness {
+        /**
+         * The object has just been created and introspection is still in
+         * progress. No functionality is available.
+         *
+         * The readiness can change to any other state depending on the result
+         * of the initial state query to the remote object.
+         */
+        ReadinessJustCreated,
+
+        /**
+         * The remote object is in the Disconnected state and introspection
+         * relevant to that state has been completed.
+         *
+         * This state is useful for being able to set your presence status
+         * (through the SimplePresence interface) before connecting. Most other
+         * functionality is unavailable, though.
+         *
+         * The readiness can change to ReadinessConnecting and ReadinessDead.
+         */
+        ReadinessNotYetConnected,
+
+        /**
+         * The remote object is in the Connecting state. Most functionality is
+         * unavailable.
+         *
+         * The readiness can change to ReadinessFull and ReadinessDead.
+         */
+        ReadinessConnecting,
+
+        /**
+         * The connection is in the Connected state and all introspection
+         * has been completed. Most functionality is available.
+         *
+         * The readiness can change to ReadinessDead.
+         */
+        ReadinessFull,
+
+        /**
+         * The remote object has gone into a state where it can no longer be
+         * used. No functionality is available.
+         *
+         * No further readiness changes are possible.
+         */
+        ReadinessDead
+    };
+
+    /**
      * Creates a Connection associated with the given object on the session bus.
      *
      * \param serviceName Name of the service the object is on.
@@ -101,127 +163,83 @@ public:
     ~Connection();
 
     /**
-     * Initially <code>false</code>, changes to <code>true</code> when the
-     * connection has gone to the Connected status, introspection is finished
-     * and it's ready for use.
+     * Returns the current readiness of the Connection.
      *
-     * By the time this property becomes true, the property #interfaces will
-     * have been populated.
+     * \return The readiness, as defined in #Readiness.
      */
-    Q_PROPERTY(bool ready READ ready)
+    Readiness readiness() const;
 
     /**
-     * Getter for the property #ready.
+     * Returns the connection's status. The returned status is undefined if the
+     * object has readiness #ReadinessJustCreated.
      *
-     * \return If the object is ready for use.
+     * \return The status, as defined in #ConnectionStatus.
      */
-    bool ready() const;
+    uint status() const;
 
     /**
-     * The connection's status (one of the values of #ConnectionStatus), or -1
-     * if we don't know it yet.
+     * Returns the reason for the connection's status. The returned reason is
+     * undefined if the object has readiness #ReadinessJustCreated.
      *
-     * Connect to the signal <code>statusChanged</code> on the underlying
-     * #interface to watch for changes.
-     */
-    Q_PROPERTY(long status READ status)
-
-    /**
-     * Getter for the property #status.
-     *
-     * \return The status of the connection.
-     */
-    long status() const;
-
-    /**
-     * The reason why #status changed to its current value, or
-     * #ConnectionStatusReasonNoneSpecified if the status is still unknown.
-     */
-    Q_PROPERTY(uint statusReason READ statusReason)
-
-    /**
-     * Getter for the property #statusReason.
-     *
-     * \return The reason for the current status.
+     * \return The reason, as defined in #ConnectionStatusReason.
      */
     uint statusReason() const;
 
     /**
-     * List of interfaces implemented by the connection.
+     * Returns a list of optional interfaces supported by this object. The
+     * contents of the list is undefined unless the Connection has readiness
+     * #ReadinessNotYetConnected or #ReadinessFull. The returned value stays
+     * constant for the entire time the connection spends in each of these
+     * states; however interfaces might be added to the supported set at the
+     * time #ReadinessFull is reached.
      *
-     * The value is undefined until the property #ready is <code>true</code>.
-     */
-    Q_PROPERTY(QStringList interfaces READ interfaces)
-
-    /**
-     * Getter for the property #interfaces.
-     *
-     * \return D-Bus interface names of the implemented interfaces.
+     * \return Names of the supported interfaces.
      */
     QStringList interfaces() const;
 
-#if 0
-    /**
-     * Bitwise OR of flags detailing the behaviour of aliases on the
-     * connection.
-     *
-     * The value is undefined if the value of the property #ready is
-     * <code>false</code> or the connection doesn't support the Aliasing
-     * interface.
-     */
-    Q_PROPERTY(Telepathy::ConnectionAliasFlags aliasFlags READ aliasFlags)
-
-    /**
-     * Getter for the property #aliasFlags.
-     *
-     * \return Bitfield of the alias flags, as specified in
-     * #ConnectionAliasFlag.
-     */
-    ConnectionAliasFlags aliasFlags() const;
-
     /**
-     * Dictionary of the valid presence statuses for the connection for use with
-     * the legacy Telepathy Presence interface.
+     * Returns the bitwise OR of flags detailing the behavior of the Aliasing
+     * interface on the remote object. The value is undefined if the connection
+     * doesn't have readiness #ReadinessFull or if the remote object doesn't
+     * implement the Aliasing interface.
      *
-     * The value is undefined if the value of the property #ready is
-     * <code>false</code> or the connection doesn't support the Presence
-     * interface.
+     * \return Bitfield of flags, as specified in #ConnectionAliasFlag.
      */
-    Q_PROPERTY(Telepathy::StatusSpecMap presenceStatuses READ presenceStatuses)
+    uint aliasFlags() const;
 
     /**
-     * Getter for the property #presenceStatuses.
+     * Returns a dictionary of presence statuses valid for use with the legacy
+     * Telepathy Presence interface on the remote object. The value is undefined
+     * unless the Connection has readiness #ReadinessFull or if the remote
+     * object doesn't implement the legacy Presence interface.
      *
-     * \return Dictionary mapping string identifiers to structs for each status.
+     * \return Dictionary from string identifiers to structs for each valid status.
      */
     StatusSpecMap presenceStatuses() const;
 
     /**
-     * Dictionary of the valid presence statuses for the connection for use with
-     * the new simplified Telepathy SimplePresence interface.
-     *
-     * Getting the value of this property before the connection is ready (as
-     * signified by the property #ready) may cause a synchronous call to the
-     * remote service to be made.
-     *
-     * The value is undefined if the connection doesn't support the
+     * Returns a dictionary of presence statuses valid for use with the new(er)
+     * Telepathy SimplePresence interface on the remote object. The value is
+     * undefined unless the Connection has readiness #ReadinessNotYetConnected
+     * or #ReadinessFull, or if the remote object doesn't implement the
      * SimplePresence interface.
-     */
-    Q_PROPERTY(Telepathy::SimpleStatusSpecMap simplePresenceStatuses READ simplePresenceStatuses)
-
-    /**
-     * Getter for the property #simplePresenceStatuses.
      *
-     * \return Dictionary mapping string identifiers to structs for each status.
+     * The value will stay fixed for the whole time the connection stays with
+     * readiness #ReadinessNotYetConnected, but may change arbitrarily at the
+     * time #ReadinessFull is reached, staying fixed for the remaining lifetime
+     * of the Connection.
+     *
+     * \return Dictionary from string identifiers to structs for each valid status.
      */
     SimpleStatusSpecMap simplePresenceStatuses() const;
-#endif
+
 Q_SIGNALS:
     /**
-     * Emitted when the value of the property #ready changes to
-     * <code>true</code>.
+     * Emitted whenever the readiness of the Connection changes.
+     *
+     * \param newReadiness The new readiness, as defined in #Readiness.
      */
-    void nowReady();
+    void readinessChanged(Telepathy::Client::Connection::Readiness newReadiness);
 
 private Q_SLOTS:
     void onStatusChanged(uint, uint);
-- 
1.5.6.5




More information about the Telepathy-commits mailing list