fixup Fix to build with libxml 2.12.x (fixes #505)
[platform/upstream/libzypp.git] / zypp / Callback.h
index d58dbcc..634d22e 100644 (file)
@@ -12,7 +12,8 @@
 #ifndef ZYPP_CALLBACK_H
 #define ZYPP_CALLBACK_H
 
-#include "zypp/base/NonCopyable.h"
+#include <zypp/base/NonCopyable.h>
+#include <zypp/UserData.h>
 
 ///////////////////////////////////////////////////////////////////
 namespace zypp
@@ -31,17 +32,18 @@ namespace zypp
    * \par The task report structure (SENDER SIDE).
    *
    * A default constructible struct derived from callback::ReportBase.
-   * It \b must \b not conatin any data, just virtual methods.
+   * It \b must \b not contain any data, just virtual methods.
    *
    * These are the functions the sender invokes, and which will be forwarded
-   * to some receiver.
+   * to some receiver. If no receiver is present, the defined default
+   * implementations are invoked.
    *
    * For methods returning non-void, define a reasonable return value,
    * because this is what you get back in case no receiver is listening.
    *
    * That way the sending side does not need to know whether some receiver
    * is listening. And it enables the receiver to return a reasonable value,
-   * in case he's got no idea, what to else to return.
+   * in case he's got no idea, what else to return.
    *
    * \code
    *   struct Foo : public callback::ReportBase
@@ -56,7 +58,7 @@ namespace zypp
    *
    * \par Sending a Task report (SENDER SIDE).
    *
-   * Simply create a callback::SendReport<_Report>, where _Report
+   * Simply create a callback::SendReport<TReport>, where TReport
    * is your task report structure. Invoke the callback functions
    * as needed. That's it.
    *
@@ -73,26 +75,116 @@ namespace zypp
    * }
    * \endcode
    *
-  */
+   * \par Receiving Task reports (RECEIVER SIDE).
+   *
+   * To receive task reports of type \c Foo the recipient class
+   * derives from callback::ReceiveReport\<Foo\>. callback::ReceiveReport
+   * inherits \c Foo and provides two additional virtual methods:
+   *
+   * \code
+   *   virtual void reportbegin() {}
+   *   virtual void reportend() {}
+   * \endcode
+   *
+   * These two are automatically invoked, whenever the sender
+   * creates a callback::SendReport instance, and when it gets
+   * destructed. So even if the sending task is aborted without
+   * sending an explicit notification, the reciever may notice it,
+   * by overloading \c reportend.
+   *
+   * Overload the methods you're interested in.
+   *
+   * \note In case you must return some value and don't know which,
+   * return the task structures default. The author of the task
+   * structure had to provide this value, so it's probabely better
+   * than anything you \e invent.
+   * \code
+   *   int somefunction()
+   *   {
+   *     ...// don't know what to return?
+   *     return Foo::somefunction();
+   *   }
+   * \endcode
+   *
+   * \par Connecting the Receiver
+   *
+   * For this callback::ReceiveReport provides 4 methods:
+   * \code
+   *  void connect();
+   *  void disconnect();
+   *  bool connected() const;
+   *  ReceiveReport * whoIsConnected() const;
+   * \endcode
+   *
+   * \li \c connect Connect this ReceiveReport (in case some other
+   * ReceiveReport is connected, it get disconnected. Remember its
+   * a Callback light).
+   * \li \c disconnect Disconnect this ReceiveReport in case it is
+   * connected. If not connected nothing happens.
+   * \li \c connected Test whether this ReceiveReport is currently
+   * connected.
+   * \li \c whoIsConnected Return a 'ReceiveReport*' to the currently
+   * connected ReceiveReport, or \c NULL if none is connected.
+   *
+   * \par Passing Userdata via Callbacks
+   *
+   * For typesafe passing of user data via callbacks \see \ref UserData.
+   *
+   * ReportBase provides a generic \ref callback::ReportBase:report method
+   * which can be used to communicate by encoding everything in its \a UserData
+   * argument.
+   *
+   * Convenient sending can be achieved by installing non-virtual methods
+   * in the TReport class, which encode the arguments in UserData and send
+   * them via ReportBase::report().
+   *
+   * Convenient receiving can be achieved by installing virtual methods in
+   * the TReport class, which can be simply overloaded by the receiver. Downside
+   * of this is that adding virtual methods breaks binary compatibility.
+   */
   namespace callback
   { /////////////////////////////////////////////////////////////////
 
     /**  */
     struct ReportBase
     {
+      typedef callback::UserData UserData;
+      typedef UserData::ContentType ContentType;
+
+      /** The most generic way of sending/receiving data. */
+      virtual void report( const UserData & userData_r = UserData() )
+      {}
+
       virtual ~ReportBase()
       {}
     };
 
     /**  */
-    template<class _Report>
-      class DistributeReport;
+    template<class TReport>
+      struct DistributeReport;
 
     /**  */
-    template<class _Report>
-      struct ReceiveReport : public _Report
+    template<class TReport>
+      struct ReceiveReport : public TReport
       {
-        typedef DistributeReport<_Report> Distributor;
+       typedef TReport                   ReportType;
+       typedef ReceiveReport<TReport>    Receiver;
+        typedef DistributeReport<TReport> Distributor;
+
+        virtual ~ReceiveReport()
+        { disconnect(); }
+
+        ReceiveReport * whoIsConnected() const
+        { return Distributor::instance().getReceiver(); }
+
+        bool connected() const
+        { return whoIsConnected() == this; }
+
+        void connect()
+        { Distributor::instance().setReceiver( *this ); }
+
+        void disconnect()
+        { Distributor::instance().unsetReceiver( *this ); }
 
         virtual void reportbegin()
         {}
@@ -101,11 +193,13 @@ namespace zypp
       };
 
     /**  */
-    template<class _Report>
+    template<class TReport>
       struct DistributeReport
       {
        public:
-         typedef ReceiveReport<_Report> Receiver;
+       typedef TReport                   ReportType;
+       typedef ReceiveReport<TReport>    Receiver;
+       typedef DistributeReport<TReport> Distributor;
 
          static DistributeReport & instance()
          {
@@ -113,8 +207,14 @@ namespace zypp
            return _self;
          }
 
-         void setReceiver( Receiver & _rec )
-         { _receiver = &_rec; }
+         Receiver * getReceiver() const
+         { return _receiver == &_noReceiver ? 0 : _receiver; }
+
+         void setReceiver( Receiver & rec_r )
+         { _receiver = &rec_r; }
+
+         void unsetReceiver( Receiver & rec_r )
+         { if ( _receiver == &rec_r ) noReceiver(); }
 
          void noReceiver()
          { _receiver = &_noReceiver; }
@@ -132,10 +232,12 @@ namespace zypp
       };
 
     /**  */
-    template<class _Report>
+    template<class TReport>
       struct SendReport : private zypp::base::NonCopyable
       {
-        typedef DistributeReport<_Report> Distributor;
+       typedef TReport                   ReportType;
+        typedef ReceiveReport<TReport>    Receiver;
+        typedef DistributeReport<TReport> Distributor;
 
         SendReport()
         { Distributor::instance()->reportbegin(); }
@@ -143,10 +245,71 @@ namespace zypp
         ~SendReport()
         { Distributor::instance()->reportend(); }
 
+        static Receiver * whoIsConnected()
+        { return Distributor::instance().getReceiver(); }
+
+        static bool connected()
+        { return whoIsConnected(); }
+
         Distributor & operator->()
         { return Distributor::instance(); }
       };
 
+    /** Temporarily connect a ReceiveReport then restore the previous one.
+     *
+     * Pass the ReceiveReport you want to connect temporarily
+     * to the ctor. The ReceiveReport is connected, a previously
+     * connected ReceiveReport is remembered and re-connected in
+     * the dtor.
+     * Use the default ctpr to temporarily disconnect any connected report.
+     * \code
+     *  struct FooReceive : public callback::ReceiveReport<Foo>
+     *  {..};
+     *  struct FooReceive2 : public callback::ReceiveReport<Foo>
+     *  {..};
+     *
+     *  FooReceive  r;
+     *  FooReceive2 r2;
+     *
+     *  r.connect();
+     *  ... // r receiving the report
+     *  {
+     *    callback::TempConnect<Foo> temp( r2 );
+     *    ...// r2 receiving the report
+     *  }
+     *  ...// r receiving the report
+     * \endcode
+    */
+    template<class TReport>
+      struct TempConnect
+      {
+       typedef TReport                   ReportType;
+        typedef ReceiveReport<TReport>    Receiver;
+        typedef DistributeReport<TReport> Distributor;
+
+        TempConnect()
+        : _oldRec( Distributor::instance().getReceiver() )
+        {
+          Distributor::instance().noReceiver();
+        }
+
+        TempConnect( Receiver & rec_r )
+        : _oldRec( Distributor::instance().getReceiver() )
+        {
+          rec_r.connect();
+        }
+
+        ~TempConnect()
+        {
+          if ( _oldRec )
+            Distributor::instance().setReceiver( *_oldRec );
+          else
+            Distributor::instance().noReceiver();
+        }
+      private:
+        Receiver * _oldRec;
+      };
+
     /////////////////////////////////////////////////////////////////
   } // namespace callback
   ///////////////////////////////////////////////////////////////////