<commit_before>#ifndef K3_RUNTIME_LISTENER_H
#define K3_RUNTIME_LISTENER_H

#include <atomic>
#include <memory>
#include <unordered_set>
#include <boost/array.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/lock_types.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <runtime/cpp/Common.hpp>
#include <runtime/cpp/Queue.hpp>
#include <runtime/cpp/IOHandle.hpp>
#include <runtime/cpp/Endpoint.hpp>

namespace K3 {

  using namespace std;

  using std::atomic_uint;

  using boost::condition_variable;
  using boost::lock_guard;
  using boost::mutex;
  using boost::unique_lock;

  //--------------------------------------------
  // A reference counter for listener instances.

  class ListenerCounter : public atomic_uint {
  public:
    ListenerCounter() : atomic_uint(0) {}

    void registerListener() { this->fetch_add(1); }
    void deregisterListener() { this->fetch_sub(1); }
    unsigned int operator()() { return this->load(); }
  };

  //---------------------------------------------------------------
  // Control data structures for multi-threaded listener execution.

  class ListenerControl {
  public:

    ListenerControl(shared_ptr<mutex> m, shared_ptr<condition_variable> c,
                    shared_ptr<ListenerCounter> i)
      : listenerCounter(i), msgAvailable(false), msgAvailMutex(m), msgAvailCondition(c)
    {}

    // Waits on the message available condition variable.
    void waitForMessage()
    {
      unique_lock<mutex> lock(*msgAvailMutex);
      while ( !msgAvailable ) { msgAvailCondition->wait(lock); }
    }

    // Notifies one waiter on the message available condition variable.
    void messageAvailable()
    {
      {
        lock_guard<mutex> lock(*msgAvailMutex);
        msgAvailable = true;
      }
      msgAvailCondition->notify_one();
    }

    shared_ptr<ListenerCounter> counter() { return listenerCounter; }

    shared_ptr<mutex> msgMutex() { return msgAvailMutex; }
    shared_ptr<condition_variable> msgCondition() { return msgAvailCondition; }

  protected:
    shared_ptr<ListenerCounter> listenerCounter;

    bool msgAvailable;
    shared_ptr<mutex> msgAvailMutex;
    shared_ptr<condition_variable> msgAvailCondition;
  };

  // Listener Processors
  // -------------------
  //
  // These are callbacks which listeners register with their endpoints, to handle asynchronous
  // notification.

  class ListenerProcessor: public virtual LogMT {
    public:
      ListenerProcessor(shared_ptr<ListenerControl> c,
        shared_ptr<Endpoint> e
      ): control(c), endpoint(e), LogMT("ListenerProcessor") {}

      virtual void operator()() = 0;

    protected:
      shared_ptr<ListenerControl> control;
      shared_ptr<Endpoint> endpoint;
  };

  // Internal Listener Processors are intended for internal endpoints, namely those whose messages
  // themselves contain dispatch information.
  class InternalListenerProcessor: ListenerProcessor {
    public:
      InternalListenerProcessor(
        shared_ptr<ListenerControl> c,
        shared_ptr<Endpoint> e,
        shared_ptr<MessageQueues> q,
        shared_ptr<InternalCodec> d
      ): ListenerProcessor(c, e), engine_queues(q), codec(d), LogMT("InternalListenerProcessor") {}

      void operator()(shared_ptr<Value> payload) {
        this->endpoint->subscribers()->notifyEvent(EndpointNotification::SocketData, payload);
        engine_queues->enqueue(codec->read_message(*payload));
      }

    private:
      shared_ptr<MessageQueues> engine_queues;
      shared_ptr<InternalCodec> codec;
  };

  // ExternalListenerPrc
  class ExternalListenerProcessor: public ListenerProcessor {
    public:
      void operator()(shared_ptr<Value> payload) {
        if (this->control && this->endpoint) {
          this->endpoint->subscribers()->notifyEvent(EndpointNotification::SocketData, payload);
          this->control->messageAvailable();
        }
      }
  };

  //------------
  // Listeners

  // Abstract base class for listeners.
  template<typename NContext, typename NEndpoint>
  class Listener : public virtual LogMT {
    public:
      Listener(Identifier n,
               shared_ptr<NContext> ctxt,
               shared_ptr<Endpoint> ep,
               shared_ptr<ListenerControl> ctrl,
               shared_ptr<ListenerProcessor> p)
        : LogMT("Listener_"+n), name(n), ctxt_(ctxt), endpoint_(ep), control_(ctrl), processor_(p)
      {
        if ( endpoint_ ) {
          typename IOHandle::SourceDetails source = ep->handle()->networkSource();
          nEndpoint_ = get<1>(source);
          codec_ = get<0>(source);
        }
      }

    protected:
      Identifier name;

      shared_ptr<NContext> ctxt_;
      shared_ptr<Endpoint> endpoint_;
      shared_ptr<NEndpoint> nEndpoint_;
      shared_ptr<Codec> codec_;
        // We assume this wire description performs the framing necessary
        // for partial network messages.

      shared_ptr<ListenerControl> control_;
      shared_ptr<ListenerProcessor> processor_;
  };

  namespace Asio
  {
    using namespace boost::asio;
    using namespace boost::log;
    using namespace boost::system;

    using boost::system::error_code;
    using boost::thread;

    template<typename NContext, typename NEndpoint>
    using BaseListener = ::K3::Listener<NContext, NEndpoint>;

    // TODO: close method, terminating all incoming connections to this acceptor.
    class Listener : public BaseListener<NContext, NEndpoint>, public basic_lockable_adapter<mutex> {
    public:
      typedef basic_lockable_adapter<mutex> llockable;
      typedef list<shared_ptr<NConnection> > ConnectionList;
      typedef externally_locked<shared_ptr<ConnectionList>, Listener> ConcurrentConnectionList;

      Listener(Identifier n,
               shared_ptr<NContext> ctxt,
               shared_ptr<Endpoint> ep,
               shared_ptr<ListenerControl> ctrl,
               shared_ptr<ListenerProcessor> p)
        : BaseListener<NContext, NEndpoint>(n, ctxt, ep, ctrl, p),
          llockable(), connections_(emptyConnections()), LogMT("AsioListener")
      {
        if ( this->nEndpoint_ && this->codec_
                && this->ctxt_ && this->ctxt_->service_threads )
        {
          acceptConnection();
          thread_ = shared_ptr<thread>(this->ctxt_->service_threads->create_thread(*(this->ctxt_)));
        } else {
          this->logAt(trivial::error, "Invalid listener arguments.");
        }
      }

    protected:
      shared_ptr<thread> thread_;
      shared_ptr<externally_locked<shared_ptr<ConnectionList>, Listener> > connections_;

      //---------
      // Helpers.

      shared_ptr<ConcurrentConnectionList> emptyConnections()
      {
        shared_ptr<ConnectionList> l = shared_ptr<ConnectionList>(new ConnectionList());
        return shared_ptr<ConcurrentConnectionList>(new ConcurrentConnectionList(*this, l));
      }

      //---------------------
      // Endpoint execution.

      void acceptConnection()
      {
        if ( this->endpoint_ && this->codec_ ) {
          shared_ptr<NConnection> nextConnection = shared_ptr<NConnection>(new NConnection(this->ctxt_));

          this->nEndpoint_->acceptor()->async_accept(nextConnection,
            [=] (const error_code& ec) {
              if ( !ec ) { registerConnection(nextConnection); }
              else { this->logAt(trivial::error, string("Failed to accept a connection: ")+ec.message()); }
            });

          acceptConnection();
        }
        else { this->logAt(trivial::error, "Invalid listener endpoint or wire description"); }
      }

      void registerConnection(shared_ptr<NConnection> c)
      {
        if ( connections_ ) {
          {
            strict_lock<Listener> guard(*this);
            connections_->get(guard)->push_back(c);
          }

          // Notify subscribers of socket accept event.
          if ( this->endpoint_ ) {
            this->endpoint_->subscribers()->notifyEvent(EndpointNotification::SocketAccept, nullptr);
          }

          // Start connection.
          receiveMessages(c);
        }
      }

      void deregisterConnection(shared_ptr<NConnection> c)
      {
        if ( connections_ ) {
          strict_lock<Listener> guard(*this);
          connections_->get(guard)->remove(c);
        }
      }

      void receiveMessages(shared_ptr<NConnection> c)
      {
        if ( c && c->socket() && this->processor_ )
        {
          // TODO: extensible buffer size.
          // We use a local variable for the socket buffer since multiple threads
          // may invoke this handler simultaneously (i.e. for different connections).
          typedef boost::array<char, 8192> SocketBuffer;
          shared_ptr<SocketBuffer> buffer_(new SocketBuffer());

          async_read(c->socket(), buffer(buffer_->c_array(), buffer_->size()),
            [=](const error_code& ec, std::size_t bytes_transferred)
            {
              if ( !ec )
              {
                // Unpack buffer, check if it returns a valid message, and pass that to the processor.
                // We assume the processor notifies subscribers regarding socket data events.
                //shared_ptr<Value> s = make_shared<Value>();
                shared_ptr<Value> v = this->codec_->decode(string(buffer_->c_array(), buffer_->size()));
                if ( v ) {
                  // Add the value to the endpoint's buffer, and invoke the listener processor.
                  //this->endpoint_->buffer()->append(v);
                  this->endpoint_->enqueueToEndpoint(v);

                  // TODO: deprecated with the revised endpoint design.
                  //(*(this->processor_))();
                }

                // Recursive invocation for the next message.
                receiveMessages(c);
              }
              else
              {
                deregisterConnection(c);
                this->logAt(trivial::error, string("Connection error: ")+ec.message());
              }
            });
        }
        else { this->logAt(trivial::error, "Invalid listener connection"); }
      }
    };
  }

  namespace Nanomsg
  {
    using std::atomic_bool;

    template<typename NContext, typename NEndpoint>
    using BaseListener = ::K3::Listener<NContext, NEndpoint>;

    class Listener : public BaseListener<NContext, NEndpoint>
    {
    public:
      Listener(Identifier n,
               shared_ptr<NContext> ctxt,
               shared_ptr<Endpoint> ep,
               shared_ptr<ListenerControl> ctrl,
               shared_ptr<ListenerProcessor> p)
        : BaseListener<NContext, NEndpoint>(n, ctxt, ep, ctrl, p), LogMT("NanomsgListener")
      {
        if ( this->nEndpoint_ && this->codec_ && this->ctxt_ && this->ctxt_->listenerThreads ) {
          // Instantiate a new thread to listen for messages on the nanomsg
          // socket, tracking it in the network context.
          terminated_ = false;
          thread_ = shared_ptr<thread>(this->ctxt_->listenerThreads->create_thread(*this));
        } else {
          this->logAt(trivial::error, "Invalid listener arguments.");
        }
      }

      void operator()()
      {
        typedef boost::array<char, 8192> SocketBuffer;
        SocketBuffer buffer_;

        while ( !terminated_ ) {
          // Receive a message.
          int bytes = nn_recv(this->nEndpoint_->acceptor(), buffer_.c_array(), buffer_.static_size, 0);
          if ( bytes >= 0 ) {
            // Decode, process.
            shared_ptr<Value> v = this->codec_->decode(string(buffer_.c_array(), buffer_.static_size));
            if ( v ) {
              // Simulate accept events for nanomsg.
              refreshSenders(v);

              // Add the value to the endpoint's buffer, and invoke the listener processor.
              this->endpoint_->buffer()->push_back(v);
              (*(this->processor_))();
            }
          }
          else {
            this->logAt(trivial::error, string("Error receiving message: ") + nn_strerror(nn_errno()));
            terminate();
          }
        }
      }

      void terminate() { terminated_ = true; }

    protected:
      shared_ptr<thread> thread_;
      atomic_bool terminated_;
      unordered_set<string> senders;

      // TODO: simulate socket accept notifications. Since nanomsg is not connection-oriented,
      // we simulate connections based on the arrival of messages from unseen addresses.
      // TODO: break assumption that value is a Message.
      void refreshSenders(shared_ptr<Value> v) {
        /*
        if ( senders.find(v->address()) == senders.end() ) {
          senders.insert(v->address());
          endpoint_->subscribers()->notifyEvent(EndpointNotification::SocketAccept);
        }

        // TODO: remove addresses from the recipients pool based on a timeout.
        // TODO: time out stale senders.
        */
      }
    };
  }
}

#endif
// vim:set sw=2 ts=2 sts=2:
<commit_msg>Add multiple callbacks to ListenerProcessors, transfer/notification.<commit_after>#ifndef K3_RUNTIME_LISTENER_H
#define K3_RUNTIME_LISTENER_H

#include <atomic>
#include <functional>
#include <memory>
#include <unordered_set>
#include <boost/array.hpp>
#include <boost/thread/condition_variable.hpp>
#include <boost/thread/locks.hpp>
#include <boost/thread/lock_types.hpp>
#include <boost/thread/mutex.hpp>
#include <boost/thread/thread.hpp>
#include <runtime/cpp/Common.hpp>
#include <runtime/cpp/Queue.hpp>
#include <runtime/cpp/IOHandle.hpp>
#include <runtime/cpp/Endpoint.hpp>

namespace K3 {

  using namespace std;

  using std::atomic_uint;

  using boost::condition_variable;
  using boost::lock_guard;
  using boost::mutex;
  using boost::unique_lock;

  //--------------------------------------------
  // A reference counter for listener instances.

  class ListenerCounter : public atomic_uint {
  public:
    ListenerCounter() : atomic_uint(0) {}

    void registerListener() { this->fetch_add(1); }
    void deregisterListener() { this->fetch_sub(1); }
    unsigned int operator()() { return this->load(); }
  };

  //---------------------------------------------------------------
  // Control data structures for multi-threaded listener execution.

  class ListenerControl {
  public:

    ListenerControl(shared_ptr<mutex> m, shared_ptr<condition_variable> c,
                    shared_ptr<ListenerCounter> i)
      : listenerCounter(i), msgAvailable(false), msgAvailMutex(m), msgAvailCondition(c)
    {}

    // Waits on the message available condition variable.
    void waitForMessage()
    {
      unique_lock<mutex> lock(*msgAvailMutex);
      while ( !msgAvailable ) { msgAvailCondition->wait(lock); }
    }

    // Notifies one waiter on the message available condition variable.
    void messageAvailable()
    {
      {
        lock_guard<mutex> lock(*msgAvailMutex);
        msgAvailable = true;
      }
      msgAvailCondition->notify_one();
    }

    shared_ptr<ListenerCounter> counter() { return listenerCounter; }

    shared_ptr<mutex> msgMutex() { return msgAvailMutex; }
    shared_ptr<condition_variable> msgCondition() { return msgAvailCondition; }

  protected:
    shared_ptr<ListenerCounter> listenerCounter;

    bool msgAvailable;
    shared_ptr<mutex> msgAvailMutex;
    shared_ptr<condition_variable> msgAvailCondition;
  };

  // Listener Processors
  // -------------------
  //
  // These are callbacks which listeners register with their endpoints, to handle asynchronous
  // notification.

  class ListenerProcessor: public virtual LogMT {
    public:
      ListenerProcessor(shared_ptr<ListenerControl> c,
        shared_ptr<Endpoint> e
      ): control(c), endpoint(e), LogMT("ListenerProcessor") {}

      virtual void process_transfer() = 0;

      virtual void process_notification(shared_ptr<Value> payload) {
        this->endpoint->subscribers()->notifyEvent(EndpointNotification::SocketData, nullptr);
        this->endpoint->subscribers()->notifyEvent(EndpointNotification::SocketData, payload);
      }

    protected:
      shared_ptr<ListenerControl> control;
      shared_ptr<Endpoint> endpoint;
  };

  // Internal Listener Processors are intended for internal endpoints, namely those whose messages
  // themselves contain dispatch information.
  class InternalListenerProcessor: ListenerProcessor {
    public:
      InternalListenerProcessor(
        shared_ptr<ListenerControl> c,
        shared_ptr<Endpoint> e,
        shared_ptr<MessageQueues> q,
        shared_ptr<InternalCodec> d
      ): ListenerProcessor(c, e), engine_queues(q), codec(d), LogMT("InternalListenerProcessor") {}

      void process_transfer() {
        this->endpoint->buffer()->transfer(
          engine_queues,
          codec,
          bind(&ListenerProcessor::process_notification, this, placeholders::_1)
        );
      }

      void process_notification(shared_ptr<Value> payload) {
        ListenerProcessor::process_notification(payload);
        this->control->messageAvailable();
      }

    private:
      shared_ptr<MessageQueues> engine_queues;
      shared_ptr<InternalCodec> codec;
  };

  // ExternalListenerPrc
  class ExternalListenerProcessor: public ListenerProcessor {
    public:
      void process_transfer() {
        this->endpoint->buffer()->transfer(nullptr, nullptr, bind(&ListenerProcessor, this, placeholders::_1);
      }
  };

  //------------
  // Listeners

  // Abstract base class for listeners.
  template<typename NContext, typename NEndpoint>
  class Listener : public virtual LogMT {
    public:
      Listener(Identifier n,
               shared_ptr<NContext> ctxt,
               shared_ptr<Endpoint> ep,
               shared_ptr<ListenerControl> ctrl,
               shared_ptr<ListenerProcessor> p)
        : LogMT("Listener_"+n), name(n), ctxt_(ctxt), endpoint_(ep), control_(ctrl), processor_(p)
      {
        if ( endpoint_ ) {
          typename IOHandle::SourceDetails source = ep->handle()->networkSource();
          nEndpoint_ = get<1>(source);
          codec_ = get<0>(source);
        }
      }

    protected:
      Identifier name;

      shared_ptr<NContext> ctxt_;
      shared_ptr<Endpoint> endpoint_;
      shared_ptr<NEndpoint> nEndpoint_;
      shared_ptr<Codec> codec_;
        // We assume this wire description performs the framing necessary
        // for partial network messages.

      shared_ptr<ListenerControl> control_;
      shared_ptr<ListenerProcessor> processor_;
  };

  namespace Asio
  {
    using namespace boost::asio;
    using namespace boost::log;
    using namespace boost::system;

    using boost::system::error_code;
    using boost::thread;

    template<typename NContext, typename NEndpoint>
    using BaseListener = ::K3::Listener<NContext, NEndpoint>;

    // TODO: close method, terminating all incoming connections to this acceptor.
    class Listener : public BaseListener<NContext, NEndpoint>, public basic_lockable_adapter<mutex> {
    public:
      typedef basic_lockable_adapter<mutex> llockable;
      typedef list<shared_ptr<NConnection> > ConnectionList;
      typedef externally_locked<shared_ptr<ConnectionList>, Listener> ConcurrentConnectionList;

      Listener(Identifier n,
               shared_ptr<NContext> ctxt,
               shared_ptr<Endpoint> ep,
               shared_ptr<ListenerControl> ctrl,
               shared_ptr<ListenerProcessor> p)
        : BaseListener<NContext, NEndpoint>(n, ctxt, ep, ctrl, p),
          llockable(), connections_(emptyConnections()), LogMT("AsioListener")
      {
        if ( this->nEndpoint_ && this->codec_
                && this->ctxt_ && this->ctxt_->service_threads )
        {
          acceptConnection();
          thread_ = shared_ptr<thread>(this->ctxt_->service_threads->create_thread(*(this->ctxt_)));
        } else {
          this->logAt(trivial::error, "Invalid listener arguments.");
        }
      }

    protected:
      shared_ptr<thread> thread_;
      shared_ptr<externally_locked<shared_ptr<ConnectionList>, Listener> > connections_;

      //---------
      // Helpers.

      shared_ptr<ConcurrentConnectionList> emptyConnections()
      {
        shared_ptr<ConnectionList> l = shared_ptr<ConnectionList>(new ConnectionList());
        return shared_ptr<ConcurrentConnectionList>(new ConcurrentConnectionList(*this, l));
      }

      //---------------------
      // Endpoint execution.

      void acceptConnection()
      {
        if ( this->endpoint_ && this->codec_ ) {
          shared_ptr<NConnection> nextConnection = shared_ptr<NConnection>(new NConnection(this->ctxt_));

          this->nEndpoint_->acceptor()->async_accept(nextConnection,
            [=] (const error_code& ec) {
              if ( !ec ) { registerConnection(nextConnection); }
              else { this->logAt(trivial::error, string("Failed to accept a connection: ")+ec.message()); }
            });

          acceptConnection();
        }
        else { this->logAt(trivial::error, "Invalid listener endpoint or wire description"); }
      }

      void registerConnection(shared_ptr<NConnection> c)
      {
        if ( connections_ ) {
          {
            strict_lock<Listener> guard(*this);
            connections_->get(guard)->push_back(c);
          }

          // Notify subscribers of socket accept event.
          if ( this->endpoint_ ) {
            this->endpoint_->subscribers()->notifyEvent(EndpointNotification::SocketAccept, nullptr);
          }

          // Start connection.
          receiveMessages(c);
        }
      }

      void deregisterConnection(shared_ptr<NConnection> c)
      {
        if ( connections_ ) {
          strict_lock<Listener> guard(*this);
          connections_->get(guard)->remove(c);
        }
      }

      void receiveMessages(shared_ptr<NConnection> c)
      {
        if ( c && c->socket() && this->processor_ )
        {
          // TODO: extensible buffer size.
          // We use a local variable for the socket buffer since multiple threads
          // may invoke this handler simultaneously (i.e. for different connections).
          typedef boost::array<char, 8192> SocketBuffer;
          shared_ptr<SocketBuffer> buffer_(new SocketBuffer());

          async_read(c->socket(), buffer(buffer_->c_array(), buffer_->size()),
            [=](const error_code& ec, std::size_t bytes_transferred)
            {
              if ( !ec )
              {
                // Unpack buffer, check if it returns a valid message, and pass that to the processor.
                // We assume the processor notifies subscribers regarding socket data events.
                //shared_ptr<Value> s = make_shared<Value>();
                shared_ptr<Value> v = this->codec_->decode(string(buffer_->c_array(), buffer_->size()));
                if ( v ) {
                  // Add the value to the endpoint's buffer, and invoke the listener processor.
                  //this->endpoint_->buffer()->append(v);
                  this->endpoint_->enqueueToEndpoint(v);

                  // TODO: deprecated with the revised endpoint design.
                  //(*(this->processor_))();
                }

                // Recursive invocation for the next message.
                receiveMessages(c);
              }
              else
              {
                deregisterConnection(c);
                this->logAt(trivial::error, string("Connection error: ")+ec.message());
              }
            });
        }
        else { this->logAt(trivial::error, "Invalid listener connection"); }
      }
    };
  }

  namespace Nanomsg
  {
    using std::atomic_bool;

    template<typename NContext, typename NEndpoint>
    using BaseListener = ::K3::Listener<NContext, NEndpoint>;

    class Listener : public BaseListener<NContext, NEndpoint>
    {
    public:
      Listener(Identifier n,
               shared_ptr<NContext> ctxt,
               shared_ptr<Endpoint> ep,
               shared_ptr<ListenerControl> ctrl,
               shared_ptr<ListenerProcessor> p)
        : BaseListener<NContext, NEndpoint>(n, ctxt, ep, ctrl, p), LogMT("NanomsgListener")
      {
        if ( this->nEndpoint_ && this->codec_ && this->ctxt_ && this->ctxt_->listenerThreads ) {
          // Instantiate a new thread to listen for messages on the nanomsg
          // socket, tracking it in the network context.
          terminated_ = false;
          thread_ = shared_ptr<thread>(this->ctxt_->listenerThreads->create_thread(*this));
        } else {
          this->logAt(trivial::error, "Invalid listener arguments.");
        }
      }

      void operator()()
      {
        typedef boost::array<char, 8192> SocketBuffer;
        SocketBuffer buffer_;

        while ( !terminated_ ) {
          // Receive a message.
          int bytes = nn_recv(this->nEndpoint_->acceptor(), buffer_.c_array(), buffer_.static_size, 0);
          if ( bytes >= 0 ) {
            // Decode, process.
            shared_ptr<Value> v = this->codec_->decode(string(buffer_.c_array(), buffer_.static_size));
            if ( v ) {
              // Simulate accept events for nanomsg.
              refreshSenders(v);

              // Add the value to the endpoint's buffer, and invoke the listener processor.
              this->endpoint_->buffer()->push_back(v);
              (*(this->processor_))();
            }
          }
          else {
            this->logAt(trivial::error, string("Error receiving message: ") + nn_strerror(nn_errno()));
            terminate();
          }
        }
      }

      void terminate() { terminated_ = true; }

    protected:
      shared_ptr<thread> thread_;
      atomic_bool terminated_;
      unordered_set<string> senders;

      // TODO: simulate socket accept notifications. Since nanomsg is not connection-oriented,
      // we simulate connections based on the arrival of messages from unseen addresses.
      // TODO: break assumption that value is a Message.
      void refreshSenders(shared_ptr<Value> v) {
        /*
        if ( senders.find(v->address()) == senders.end() ) {
          senders.insert(v->address());
          endpoint_->subscribers()->notifyEvent(EndpointNotification::SocketAccept);
        }

        // TODO: remove addresses from the recipients pool based on a timeout.
        // TODO: time out stale senders.
        */
      }
    };
  }
}

#endif
// vim:set sw=2 ts=2 sts=2:
<|endoftext|>


<commit_before>#ifndef K3_RUNTIME_LITERALS_H
#define K3_RUNTIME_LITERALS_H

#include <map>
#include <string>

#include "boost/fusion/include/std_pair.hpp"
#include "boost/spirit/include/qi.hpp"

namespace K3 {
  namespace qi = boost::spirit::qi;

  using std::map;
  using std::pair;
  using std::string;

  template <class iterator>
  class literal: public qi::grammar<iterator, map<string, string>()> {
   public:
    literal(): literal::base_type(start) {
      key = +qi::char_("a-zA-Z0-9_");
      value = +qi::char_("a-zA-Z0-9_");
      assignment = key >> '=' >> value;
      start = assignment % ';';
    }

   private:
    qi::rule<iterator, string()> key;
    qi::rule<iterator, string()> value;
    qi::rule<iterator, pair<string, string>()> assignment;
    qi::rule<iterator, map<string, string>()> start;
  };
}

#endif
<commit_msg>Allow for space-skipping in assignment list.<commit_after>#ifndef K3_RUNTIME_LITERALS_H
#define K3_RUNTIME_LITERALS_H

#include <map>
#include <string>

#include "boost/fusion/include/std_pair.hpp"
#include "boost/spirit/include/qi.hpp"

namespace K3 {
  namespace qi = boost::spirit::qi;

  using std::map;
  using std::pair;
  using std::string;

  template <class iterator>
  class literal: public qi::grammar<iterator, map<string, string>(), qi::space_type> {
   public:
    literal(): literal::base_type(start) {
      key = +qi::char_("a-zA-Z0-9_");
      value = +qi::char_("a-zA-Z0-9_");
      assignment = key >> '=' >> value;
      start = assignment % ';';
    }

   private:
    qi::rule<iterator, string()> key;
    qi::rule<iterator, string()> value;
    qi::rule<iterator, pair<string, string>(), qi::space_type> assignment;
    qi::rule<iterator, map<string, string>(), qi::space_type> start;
  };
}

#endif
<|endoftext|>


<commit_before>#ifndef K3_RUNTIME_LITERALS_H
#define K3_RUNTIME_LITERALS_H

#include <list>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

#include "boost/fusion/include/std_pair.hpp"
#include "boost/spirit/include/qi.hpp"

namespace K3 {
  namespace qi = boost::spirit::qi;

  using std::list;
  using std::make_shared;
  using std::map;
  using std::pair;
  using std::shared_ptr;
  using std::string;
  using std::tuple;
  using std::vector;

  template <class iterator>
  class shallow: public qi::grammar<iterator, qi::space_type, string()> {
   public:
    shallow(): shallow::base_type(start) {
      start = qi::raw[indirection | option | angles | braces | parens | quotes | other];

      indirection = qi::lit("ind") >> start;
      option = qi::lit("none") | qi::lit("some") >> start;

      angles = '<' >> *(qi::char_ - '>') >> '>';
      braces = '{' >> *(qi::char_ - '}') >> '}';
      parens = '(' >> *(qi::char_ - ')') >> ')';
      quotes = '"' >> *(escape - '"') >> '"';
      other = *(qi::char_ - ',');

      escape = '\\' >> qi::char_ | qi::char_;
    }

   private:
    qi::rule<iterator, qi::space_type, string()> start;

    qi::rule<iterator, qi::space_type> indirection;
    qi::rule<iterator, qi::space_type> option;

    qi::rule<iterator, qi::space_type> angles;
    qi::rule<iterator, qi::space_type> braces;
    qi::rule<iterator, qi::space_type> parens;
    qi::rule<iterator, qi::space_type> quotes;
    qi::rule<iterator, qi::space_type> other;

    qi::rule<iterator, qi::space_type> escape;
  };

  template <class iterator>
  class literal: public qi::grammar<iterator, map<string, string>(), qi::space_type> {
   public:
    literal(): literal::base_type(start) {

      start = binding % ';';
      binding = key >> '=' >> value;

      key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_");
      value = shallow<iterator>();
    }

   private:
    qi::rule<iterator, map<string, string>(), qi::space_type> start;
    qi::rule<iterator, pair<string, string>(), qi::space_type> binding;
    qi::rule<iterator, string(), qi::space_type> key;
    qi::rule<iterator, string(), qi::space_type> value;
  };

  // Built-in type refresh specializations.

  template <class T> void refresh(string, T&);
  template <class T, size_t i, size_t n> struct refresh_many;

  template <> void refresh<bool>(string s, bool& b) {
    qi::parse(begin(s), end(s), qi::bool_[([&b] (bool b_) { b = b_; })]);
  }

  template <> void refresh<int>(string s, int& i) {
    qi::parse(begin(s), end(s), qi::int_[([&i] (int i_) { i = i_; })]);
  }

  template <> void refresh<double>(string s, double& d) {
    qi::parse(begin(s), end(s), qi::double_[([&d] (double d_) { d = d_; })]);
  }

  template <> void refresh<string>(string s, string& t) {
    string t_;
    bool result = qi::parse(begin(s), end(s), ('"' >> *(('\\' >> qi::char_) | (qi::char_ - '"')) >> '"'), t_);

    if (result) {
      t = t_;
    }
  }

  template <class T> void refresh(string s, shared_ptr<T>& p) {
    shallow<string::iterator> _shallow;
    qi::rule<string::iterator, qi::space_type> raw_rule = _shallow[([&p] (string s_) {
      if (!p) {
        p = make_shared<T>();
      }

      refresh(s_, *p);
    })];
    qi::rule<string::iterator, qi::space_type> nullptr_rule = qi::lit("none")[([&p] () { p = nullptr; })];
    qi::rule<string::iterator, qi::space_type> someptr_rule = (qi::lit("some") | qi::lit("ind")) >> raw_rule;
    qi::phrase_parse(begin(s), end(s), nullptr_rule | someptr_rule, qi::space);
  }

  template <class ... Ts> void refresh(string s, tuple<Ts...>& t) {
    list<string> v;
    shallow<string::iterator> _shallow;
    qi::rule<string::iterator, qi::space_type> tuple_rule = _shallow[([&v] (string s_) { v.push_back(s_); })];
    qi::phrase_parse(begin(s), end(s), '(' >> (tuple_rule % ',') >> ')', qi::space);
    refresh_many<tuple<Ts...>, 0, sizeof...(Ts)>()(v, t);
  }

  template <class T, size_t i> struct refresh_many<T, i, i> {
    void operator()(list<string>, T&) {
      return;
    }
  };

  template <class T, size_t i, size_t n> struct refresh_many {
    void operator()(list<string> v, T& t) {
      if (v.empty()) {
        return;
      }

      refresh<typename std::tuple_element<i, T>::type>(v.front(), std::get<i>(t));

      v.pop_front();
      refresh_many<T, i + 1, n>()(v, t);
    }
  };
}

#endif
<commit_msg>Make top-level binding syntax record-like.<commit_after>#ifndef K3_RUNTIME_LITERALS_H
#define K3_RUNTIME_LITERALS_H

#include <list>
#include <map>
#include <memory>
#include <string>
#include <tuple>
#include <vector>

#include "boost/fusion/include/std_pair.hpp"
#include "boost/spirit/include/qi.hpp"

namespace K3 {
  namespace qi = boost::spirit::qi;

  using std::list;
  using std::make_shared;
  using std::map;
  using std::pair;
  using std::shared_ptr;
  using std::string;
  using std::tuple;
  using std::vector;

  template <class iterator>
  class shallow: public qi::grammar<iterator, qi::space_type, string()> {
   public:
    shallow(): shallow::base_type(start) {
      start = qi::raw[indirection | option | angles | braces | parens | quotes | other];

      indirection = qi::lit("ind") >> start;
      option = qi::lit("none") | qi::lit("some") >> start;

      angles = '<' >> *(qi::char_ - '>') >> '>';
      braces = '{' >> *(qi::char_ - '}') >> '}';
      parens = '(' >> *(qi::char_ - ')') >> ')';
      quotes = '"' >> *(escape - '"') >> '"';
      other = *(qi::char_ - ',');

      escape = '\\' >> qi::char_ | qi::char_;
    }

   private:
    qi::rule<iterator, qi::space_type, string()> start;

    qi::rule<iterator, qi::space_type> indirection;
    qi::rule<iterator, qi::space_type> option;

    qi::rule<iterator, qi::space_type> angles;
    qi::rule<iterator, qi::space_type> braces;
    qi::rule<iterator, qi::space_type> parens;
    qi::rule<iterator, qi::space_type> quotes;
    qi::rule<iterator, qi::space_type> other;

    qi::rule<iterator, qi::space_type> escape;
  };

  template <class iterator>
  class literal: public qi::grammar<iterator, map<string, string>(), qi::space_type> {
   public:
    literal(): literal::base_type(start) {

      start = binding % ',';
      binding = key >> ':' >> value;

      key = qi::char_("a-zA-Z_") >> *qi::char_("a-zA-Z0-9_");
    }

   private:
    qi::rule<iterator, map<string, string>(), qi::space_type> start;
    qi::rule<iterator, pair<string, string>(), qi::space_type> binding;
    qi::rule<iterator, string(), qi::space_type> key;
    shallow<iterator> value;
  };

  // Built-in type refresh specializations.

  template <class T> void refresh(string, T&);
  template <class T, size_t i, size_t n> struct refresh_many;

  template <> void refresh<bool>(string s, bool& b) {
    qi::parse(begin(s), end(s), qi::bool_[([&b] (bool b_) { b = b_; })]);
  }

  template <> void refresh<int>(string s, int& i) {
    qi::parse(begin(s), end(s), qi::int_[([&i] (int i_) { i = i_; })]);
  }

  template <> void refresh<double>(string s, double& d) {
    qi::parse(begin(s), end(s), qi::double_[([&d] (double d_) { d = d_; })]);
  }

  template <> void refresh<string>(string s, string& t) {
    string t_;
    bool result = qi::parse(begin(s), end(s), ('"' >> *(('\\' >> qi::char_) | (qi::char_ - '"')) >> '"'), t_);

    if (result) {
      t = t_;
    }
  }

  template <class T> void refresh(string s, shared_ptr<T>& p) {
    shallow<string::iterator> _shallow;
    qi::rule<string::iterator, qi::space_type> raw_rule = _shallow[([&p] (string s_) {
      if (!p) {
        p = make_shared<T>();
      }

      refresh(s_, *p);
    })];
    qi::rule<string::iterator, qi::space_type> nullptr_rule = qi::lit("none")[([&p] () { p = nullptr; })];
    qi::rule<string::iterator, qi::space_type> someptr_rule = (qi::lit("some") | qi::lit("ind")) >> raw_rule;
    qi::phrase_parse(begin(s), end(s), nullptr_rule | someptr_rule, qi::space);
  }

  template <class ... Ts> void refresh(string s, tuple<Ts...>& t) {
    list<string> v;
    shallow<string::iterator> _shallow;
    qi::rule<string::iterator, qi::space_type> tuple_rule = _shallow[([&v] (string s_) { v.push_back(s_); })];
    qi::phrase_parse(begin(s), end(s), '(' >> (tuple_rule % ',') >> ')', qi::space);
    refresh_many<tuple<Ts...>, 0, sizeof...(Ts)>()(v, t);
  }

  template <class T, size_t i> struct refresh_many<T, i, i> {
    void operator()(list<string>, T&) {
      return;
    }
  };

  template <class T, size_t i, size_t n> struct refresh_many {
    void operator()(list<string> v, T& t) {
      if (v.empty()) {
        return;
      }

      refresh<typename std::tuple_element<i, T>::type>(v.front(), std::get<i>(t));

      v.pop_front();
      refresh_many<T, i + 1, n>()(v, t);
    }
  };
}

#endif
<|endoftext|>


<commit_before>/* Copyright 2015 Stanford University, NVIDIA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Realm logging infrastructure

#include "logging.h"

#ifdef SHARED_LOWLEVEL
#define gasnet_mynode() 0
#define gasnet_nodes() 1
#else
#include "activemsg.h"
#endif

#include "cmdline.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include <set>
#include <map>

namespace Realm {

  // abstract class for an output stream
  class LoggerOutputStream {
  public:
    LoggerOutputStream(void) {}
    virtual ~LoggerOutputStream(void) {}

    virtual void write(const char *buffer, size_t len) = 0;
    virtual void flush(void) = 0;
  };

  class LoggerFileStream : public LoggerOutputStream {
  public:
    LoggerFileStream(FILE *_f, bool _close_file)
      : f(_f), close_file(_close_file)
    {}

    virtual ~LoggerFileStream(void)
    {
      if(close_file)
	fclose(f);
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      size_t amt =
#endif
	fwrite(buffer, 1, len, f);
      assert(amt == len);
    }

    virtual void flush(void)
    {
      fflush(f);
    }

  protected:
    FILE *f;
    bool close_file;
  };

  // use a pthread mutex to prevent simultaneous writes to a stream
  template <typename T>
  class LoggerStreamSerialized : public LoggerOutputStream {
  public:
    LoggerStreamSerialized(T *_stream, bool _delete_inner)
      : stream(_stream), delete_inner(_delete_inner)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_init(&mutex, 0);
      assert(ret == 0);
    }

    virtual ~LoggerStreamSerialized(void)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_destroy(&mutex);
      assert(ret == 0);
      if(delete_inner)
	delete stream;
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->write(buffer, len);
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

    virtual void flush(void)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->flush();
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

  protected:
    LoggerOutputStream *stream;
    bool delete_inner;
    pthread_mutex_t mutex;
  };

  class LoggerConfig {
  protected:
    LoggerConfig(void);
    ~LoggerConfig(void);

  public:
    static LoggerConfig *get_config(void);

    static void flush_all_streams(void);

    void read_command_line(std::vector<std::string>& cmdline);

    // either configures a logger right away or remembers it to config once
    //   we know the desired settings
    void configure(Logger *logger);

  protected:
    bool parse_level_argument(const std::string& s);

    bool cmdline_read;
    Logger::LoggingLevel default_level;
    std::map<std::string, Logger::LoggingLevel> category_levels;
    std::string cats_enabled;
    std::set<Logger *> pending_configs;
    LoggerOutputStream *stream;
  };

  LoggerConfig::LoggerConfig(void)
    : cmdline_read(false), default_level(Logger::LEVEL_PRINT), stream(0)
  {}

  LoggerConfig::~LoggerConfig(void)
  {
    delete stream;
  }

  /*static*/ LoggerConfig *LoggerConfig::get_config(void)
  {
    static LoggerConfig cfg;
    return &cfg;
  }

  /*static*/ void LoggerConfig::flush_all_streams(void)
  {
    LoggerConfig *cfg = get_config();
    if(cfg->stream)
      cfg->stream->flush();
  }

  bool LoggerConfig::parse_level_argument(const std::string& s)
  {
    const char *p1 = s.c_str();

    while(true) {
      // skip commas
      while(*p1 == ',') p1++;
      if(!*p1) break;

      // numbers may be preceeded by name= to specify a per-category level
      std::string catname;
      if(!isdigit(*p1)) {
	const char *p2 = p1;
	while(*p2 != '=') {
	  if(!*p2) {
	    fprintf(stderr, "ERROR: category name in -level must be followed by =\n");
	    return false;
	  }
	  p2++;
	}
	catname.assign(p1, p2 - p1);
	p1 = p2 + 1;
      }

      // levels are small integers
      if(isdigit(*p1)) {
	char *p2;
	errno = 0; // no leftover errors from elsewhere
	long v = strtol(p1, &p2, 10);

	if((errno == 0) && ((*p2 == 0) || (*p2) == ',') &&
	   (v >= 0) && (v <= Logger::LEVEL_NONE)) {
	  if(catname.empty())
	    default_level = (Logger::LoggingLevel)v;
	  else
	    category_levels[catname] = (Logger::LoggingLevel)v;

	  p1 = p2;
	  continue;
	}
      }

      fprintf(stderr, "ERROR: logger level malformed or out of range: '%s'\n", p1);
      return false;
    }

    return true;
  }

  void LoggerConfig::read_command_line(std::vector<std::string>& cmdline)
  {
    std::string logname;

    bool ok = CommandLineParser()
      .add_option_string("-cat", cats_enabled)
      .add_option_string("-logfile", logname)
      .add_option_method("-level", this, &LoggerConfig::parse_level_argument)
      .parse_command_line(cmdline);

    if(!ok) {
      fprintf(stderr, "couldn't parse logger config options\n");
      exit(1);
    }

    // lots of choices for log output
    if(logname.empty() || (logname == "stdout")) {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
    } else if(logname == "stderr") {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
    } else {
      // we're going to open a file, but key off a + for appending and
      //  look for a % for node number insertion
      bool append = false;
      size_t start = 0;

      if(logname[0] == '+') {
	append = true;
	start++;
      }

      FILE *f = 0;
      size_t pct = logname.find_first_of('%', start);
      if(pct == std::string::npos) {
	// no node number - everybody uses the same file
	if(gasnet_nodes() > 1) {
	  if(!append) {
	    if(gasnet_mynode() == 1)
	      fprintf(stderr, "WARNING: all ranks are logging to the same output file - appending is forced and output may be jumbled\n");
	    append = true;
	  }
	}
	const char *fn = logname.c_str() + start;
	f = fopen(fn, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", fn, strerror(errno));
	  exit(1);
	}
      } else {
	// replace % with node number
	char filename[256];
	sprintf(filename, "%.*s%d%s",
		(int)(pct - start), logname.c_str() + start, gasnet_mynode(), logname.c_str() + pct + 1);

	f = fopen(filename, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", filename, strerror(errno));
	  exit(1);
	}
      }
      // TODO: consider buffering in some cases?
      setbuf(f, 0); // disable output buffering
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(f, true),
							    true);
    }

    atexit(LoggerConfig::flush_all_streams);

    cmdline_read = true;
    if(!pending_configs.empty()) {
      for(std::set<Logger *>::iterator it = pending_configs.begin();
	  it != pending_configs.end();
	  it++)
	configure(*it);
      pending_configs.clear();
    }
  }

  void LoggerConfig::configure(Logger *logger)
  {
    // if we haven't read the command line yet, remember this for later
    if(!cmdline_read) {
      pending_configs.insert(logger);
      return;
    }

    // see if this logger is one of the categories we want
    if(!cats_enabled.empty()) {
      bool found = false;
      const char *p = cats_enabled.c_str();
      int l = logger->get_name().length();
      const char *n = logger->get_name().c_str();
      while(*p) {
	if(((p[l] == '\0') || (p[l] == ',')) && !strncmp(p, n, l)) {
	  found = true;
	  break;
	}
	// skip to after next comma
	while(*p && (*p != ',')) p++;
	while(*p && (*p == ',')) p++;
      }
      if(!found) {
	//printf("'%s' not in '%s'\n", n, cats_enabled);
	return;
      }
    }

    // see if the level for this category has been customized
    Logger::LoggingLevel level = default_level;
    std::map<std::string, Logger::LoggingLevel>::const_iterator it = category_levels.find(logger->get_name());
    if(it != category_levels.end())
      level = it->second;

    // give this logger a copy of the global stream
    logger->add_stream(stream, level,
		       false,  /* don't delete */
		       false); /* don't flush each write */
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class Logger

  Logger::Logger(const std::string& _name)
    : name(_name), log_level(LEVEL_NONE)
  {
    LoggerConfig::get_config()->configure(this);
  }

  Logger::~Logger(void)
  {
    // go through our streams and delete any we're supposed to
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++)
      if(it->delete_when_done)
	delete it->s;

    streams.clear();
  }

  /*static*/ void Logger::configure_from_cmdline(std::vector<std::string>& cmdline)
  {
    LoggerConfig::get_config()->read_command_line(cmdline);
  }

  void Logger::log_msg(LoggingLevel level, const std::string& msg)
  {
    // no logging of empty messages
    if(msg.length() == 0)
      return;

    // build message string, including prefix
    static const int MAXLEN = 4096;
    char buffer[MAXLEN];
    int len = snprintf(buffer, MAXLEN - 2, "[%d - %lx] {%d}{%s}: ",
		       gasnet_mynode(), (unsigned long)pthread_self(),
		       level, name.c_str());
    int amt = msg.length();
    if((len + amt) >= MAXLEN)
      amt = MAXLEN - 2 - len;
    memcpy(buffer + len, msg.data(), amt);
    len += amt;
    buffer[len++] = '\n';
    buffer[len] = 0;

    // go through all the streams
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++) {
      if(level < it->min_level)
	continue;

      it->s->write(buffer, len);

      if(it->flush_each_write)
	it->s->flush();
    }
  }

  void Logger::add_stream(LoggerOutputStream *s, LoggingLevel min_level,
			  bool delete_when_done, bool flush_each_write)
  {
    LogStream ls;
    ls.s = s;
    ls.min_level = min_level;
    ls.delete_when_done = delete_when_done;
    ls.flush_each_write = flush_each_write;
    streams.push_back(ls);

    // update our logging level if needed
    if(log_level > min_level)
      log_level = min_level;
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class LoggerMessage

  LoggerMessage& LoggerMessage::vprintf(const char *fmt, va_list args)
  {
    if(active) {
      char msg[256];
      vsnprintf(msg, 256, fmt, args);
      oss << msg;
    }
    return *this;
  }

}; // namespace Realm
<commit_msg>logging: make default stderr with UDP conduit so you can actually see the messages<commit_after>/* Copyright 2015 Stanford University, NVIDIA Corporation
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

// Realm logging infrastructure

#include "logging.h"

#ifdef SHARED_LOWLEVEL
#define gasnet_mynode() 0
#define gasnet_nodes() 1
#else
#include "activemsg.h"
#endif

#include "cmdline.h"

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <assert.h>
#include <errno.h>

#include <set>
#include <map>

namespace Realm {

  // abstract class for an output stream
  class LoggerOutputStream {
  public:
    LoggerOutputStream(void) {}
    virtual ~LoggerOutputStream(void) {}

    virtual void write(const char *buffer, size_t len) = 0;
    virtual void flush(void) = 0;
  };

  class LoggerFileStream : public LoggerOutputStream {
  public:
    LoggerFileStream(FILE *_f, bool _close_file)
      : f(_f), close_file(_close_file)
    {}

    virtual ~LoggerFileStream(void)
    {
      if(close_file)
	fclose(f);
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      size_t amt =
#endif
	fwrite(buffer, 1, len, f);
      assert(amt == len);
    }

    virtual void flush(void)
    {
      fflush(f);
    }

  protected:
    FILE *f;
    bool close_file;
  };

  // use a pthread mutex to prevent simultaneous writes to a stream
  template <typename T>
  class LoggerStreamSerialized : public LoggerOutputStream {
  public:
    LoggerStreamSerialized(T *_stream, bool _delete_inner)
      : stream(_stream), delete_inner(_delete_inner)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_init(&mutex, 0);
      assert(ret == 0);
    }

    virtual ~LoggerStreamSerialized(void)
    {
#ifndef NDEBUG
      int ret =
#endif
	pthread_mutex_destroy(&mutex);
      assert(ret == 0);
      if(delete_inner)
	delete stream;
    }

    virtual void write(const char *buffer, size_t len)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->write(buffer, len);
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

    virtual void flush(void)
    {
#ifndef NDEBUG
      int ret;
      ret =
#endif
	pthread_mutex_lock(&mutex);
      assert(ret == 0);
      stream->flush();
#ifndef NDEBUG
      ret =
#endif
	pthread_mutex_unlock(&mutex);
      assert(ret == 0);
    }

  protected:
    LoggerOutputStream *stream;
    bool delete_inner;
    pthread_mutex_t mutex;
  };

  class LoggerConfig {
  protected:
    LoggerConfig(void);
    ~LoggerConfig(void);

  public:
    static LoggerConfig *get_config(void);

    static void flush_all_streams(void);

    void read_command_line(std::vector<std::string>& cmdline);

    // either configures a logger right away or remembers it to config once
    //   we know the desired settings
    void configure(Logger *logger);

  protected:
    bool parse_level_argument(const std::string& s);

    bool cmdline_read;
    Logger::LoggingLevel default_level;
    std::map<std::string, Logger::LoggingLevel> category_levels;
    std::string cats_enabled;
    std::set<Logger *> pending_configs;
    LoggerOutputStream *stream;
  };

  LoggerConfig::LoggerConfig(void)
    : cmdline_read(false), default_level(Logger::LEVEL_PRINT), stream(0)
  {}

  LoggerConfig::~LoggerConfig(void)
  {
    delete stream;
  }

  /*static*/ LoggerConfig *LoggerConfig::get_config(void)
  {
    static LoggerConfig cfg;
    return &cfg;
  }

  /*static*/ void LoggerConfig::flush_all_streams(void)
  {
    LoggerConfig *cfg = get_config();
    if(cfg->stream)
      cfg->stream->flush();
  }

  bool LoggerConfig::parse_level_argument(const std::string& s)
  {
    const char *p1 = s.c_str();

    while(true) {
      // skip commas
      while(*p1 == ',') p1++;
      if(!*p1) break;

      // numbers may be preceeded by name= to specify a per-category level
      std::string catname;
      if(!isdigit(*p1)) {
	const char *p2 = p1;
	while(*p2 != '=') {
	  if(!*p2) {
	    fprintf(stderr, "ERROR: category name in -level must be followed by =\n");
	    return false;
	  }
	  p2++;
	}
	catname.assign(p1, p2 - p1);
	p1 = p2 + 1;
      }

      // levels are small integers
      if(isdigit(*p1)) {
	char *p2;
	errno = 0; // no leftover errors from elsewhere
	long v = strtol(p1, &p2, 10);

	if((errno == 0) && ((*p2 == 0) || (*p2) == ',') &&
	   (v >= 0) && (v <= Logger::LEVEL_NONE)) {
	  if(catname.empty())
	    default_level = (Logger::LoggingLevel)v;
	  else
	    category_levels[catname] = (Logger::LoggingLevel)v;

	  p1 = p2;
	  continue;
	}
      }

      fprintf(stderr, "ERROR: logger level malformed or out of range: '%s'\n", p1);
      return false;
    }

    return true;
  }

  void LoggerConfig::read_command_line(std::vector<std::string>& cmdline)
  {
    std::string logname;

    bool ok = CommandLineParser()
      .add_option_string("-cat", cats_enabled)
      .add_option_string("-logfile", logname)
      .add_option_method("-level", this, &LoggerConfig::parse_level_argument)
      .parse_command_line(cmdline);

    if(!ok) {
      fprintf(stderr, "couldn't parse logger config options\n");
      exit(1);
    }

    // lots of choices for log output
    if(logname.empty()) {
      // the gasnet UDP job spawner (amudprun) seems to buffer stdout, so make stderr the default
#ifdef GASNET_CONDUIT_UDP
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
#else
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
#endif
    } else if(logname == "stdout") {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stdout, false),
							    true);
    } else if(logname == "stderr") {
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(stderr, false),
							    true);
    } else {
      // we're going to open a file, but key off a + for appending and
      //  look for a % for node number insertion
      bool append = false;
      size_t start = 0;

      if(logname[0] == '+') {
	append = true;
	start++;
      }

      FILE *f = 0;
      size_t pct = logname.find_first_of('%', start);
      if(pct == std::string::npos) {
	// no node number - everybody uses the same file
	if(gasnet_nodes() > 1) {
	  if(!append) {
	    if(gasnet_mynode() == 1)
	      fprintf(stderr, "WARNING: all ranks are logging to the same output file - appending is forced and output may be jumbled\n");
	    append = true;
	  }
	}
	const char *fn = logname.c_str() + start;
	f = fopen(fn, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", fn, strerror(errno));
	  exit(1);
	}
      } else {
	// replace % with node number
	char filename[256];
	sprintf(filename, "%.*s%d%s",
		(int)(pct - start), logname.c_str() + start, gasnet_mynode(), logname.c_str() + pct + 1);

	f = fopen(filename, append ? "a" : "w");
	if(!f) {
	  fprintf(stderr, "could not open log file '%s': %s\n", filename, strerror(errno));
	  exit(1);
	}
      }
      // TODO: consider buffering in some cases?
      setbuf(f, 0); // disable output buffering
      stream = new LoggerStreamSerialized<LoggerFileStream>(new LoggerFileStream(f, true),
							    true);
    }

    atexit(LoggerConfig::flush_all_streams);

    cmdline_read = true;
    if(!pending_configs.empty()) {
      for(std::set<Logger *>::iterator it = pending_configs.begin();
	  it != pending_configs.end();
	  it++)
	configure(*it);
      pending_configs.clear();
    }
  }

  void LoggerConfig::configure(Logger *logger)
  {
    // if we haven't read the command line yet, remember this for later
    if(!cmdline_read) {
      pending_configs.insert(logger);
      return;
    }

    // see if this logger is one of the categories we want
    if(!cats_enabled.empty()) {
      bool found = false;
      const char *p = cats_enabled.c_str();
      int l = logger->get_name().length();
      const char *n = logger->get_name().c_str();
      while(*p) {
	if(((p[l] == '\0') || (p[l] == ',')) && !strncmp(p, n, l)) {
	  found = true;
	  break;
	}
	// skip to after next comma
	while(*p && (*p != ',')) p++;
	while(*p && (*p == ',')) p++;
      }
      if(!found) {
	//printf("'%s' not in '%s'\n", n, cats_enabled);
	return;
      }
    }

    // see if the level for this category has been customized
    Logger::LoggingLevel level = default_level;
    std::map<std::string, Logger::LoggingLevel>::const_iterator it = category_levels.find(logger->get_name());
    if(it != category_levels.end())
      level = it->second;

    // give this logger a copy of the global stream
    logger->add_stream(stream, level,
		       false,  /* don't delete */
		       false); /* don't flush each write */
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class Logger

  Logger::Logger(const std::string& _name)
    : name(_name), log_level(LEVEL_NONE)
  {
    LoggerConfig::get_config()->configure(this);
  }

  Logger::~Logger(void)
  {
    // go through our streams and delete any we're supposed to
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++)
      if(it->delete_when_done)
	delete it->s;

    streams.clear();
  }

  /*static*/ void Logger::configure_from_cmdline(std::vector<std::string>& cmdline)
  {
    LoggerConfig::get_config()->read_command_line(cmdline);
  }

  void Logger::log_msg(LoggingLevel level, const std::string& msg)
  {
    // no logging of empty messages
    if(msg.length() == 0)
      return;

    // build message string, including prefix
    static const int MAXLEN = 4096;
    char buffer[MAXLEN];
    int len = snprintf(buffer, MAXLEN - 2, "[%d - %lx] {%d}{%s}: ",
		       gasnet_mynode(), (unsigned long)pthread_self(),
		       level, name.c_str());
    int amt = msg.length();
    if((len + amt) >= MAXLEN)
      amt = MAXLEN - 2 - len;
    memcpy(buffer + len, msg.data(), amt);
    len += amt;
    buffer[len++] = '\n';
    buffer[len] = 0;

    // go through all the streams
    for(std::vector<LogStream>::iterator it = streams.begin();
	it != streams.end();
	it++) {
      if(level < it->min_level)
	continue;

      it->s->write(buffer, len);

      if(it->flush_each_write)
	it->s->flush();
    }
  }

  void Logger::add_stream(LoggerOutputStream *s, LoggingLevel min_level,
			  bool delete_when_done, bool flush_each_write)
  {
    LogStream ls;
    ls.s = s;
    ls.min_level = min_level;
    ls.delete_when_done = delete_when_done;
    ls.flush_each_write = flush_each_write;
    streams.push_back(ls);

    // update our logging level if needed
    if(log_level > min_level)
      log_level = min_level;
  }

  ////////////////////////////////////////////////////////////////////////
  //
  // class LoggerMessage

  LoggerMessage& LoggerMessage::vprintf(const char *fmt, va_list args)
  {
    if(active) {
      char msg[256];
      vsnprintf(msg, 256, fmt, args);
      oss << msg;
    }
    return *this;
  }

}; // namespace Realm
<|endoftext|>


<commit_before>// TCP receiver
// Author: Max Schwarz <max.schwarz@uni-bonn.de>

#include "tcp_receiver.h"

#include <sys/socket.h>
#include <arpa/inet.h>

#include "tcp_packet.h"
#include "../topic_info.h"
#include <topic_tools/shape_shifter.h>

#include <bzlib.h>

#include <nimbro_topic_transport/CompressedMsg.h>

namespace nimbro_topic_transport
{

static bool sureRead(int fd, void* dest, ssize_t size)
{
	uint8_t* destWPtr = (uint8_t*)dest;
	while(size != 0)
	{
		ssize_t ret = read(fd, destWPtr, size);

		if(ret < 0)
		{
			ROS_ERROR("Could not read(): %s", strerror(errno));
			return false;
		}

		if(ret == 0)
		{
			// Client has closed connection (ignore silently)
			return false;
		}

		size -= ret;
		destWPtr += ret;
	}

	return true;
}

TCPReceiver::TCPReceiver()
 : m_nh("~")
{
	m_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(m_fd < 0)
	{
		ROS_FATAL("Could not create socket: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	int port;
	m_nh.param("port", port, 5050);

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);

	int on = 1;
	if(setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
	{
		ROS_FATAL("Could not enable SO_REUSEADDR: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	ROS_DEBUG("Binding to :%d", port);

	if(bind(m_fd, (sockaddr*)&addr, sizeof(addr)) != 0)
	{
		ROS_FATAL("Could not bind socket: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	if(listen(m_fd, 10) != 0)
	{
		ROS_FATAL("Could not listen: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	m_nh.param("keep_compressed", m_keepCompressed, false);
}

TCPReceiver::~TCPReceiver()
{
}

void TCPReceiver::run()
{
	fd_set fds;

	while(ros::ok())
	{
		ros::spinOnce();

		// Clean up any exited threads
		std::list<ClientHandler*>::iterator it = m_handlers.begin();
		while(it != m_handlers.end())
		{
			if(!(*it)->isRunning())
			{
				delete *it;
				it = m_handlers.erase(it);
			}
			else
				it++;
		}

		FD_ZERO(&fds);
		FD_SET(m_fd, &fds);

		timeval timeout;
		timeout.tv_usec = 0;
		timeout.tv_sec = 1;

		int ret = select(m_fd+1, &fds, 0, 0, &timeout);
		if(ret < 0)
		{
			if(errno == EINTR || errno == EAGAIN)
				continue;

			ROS_ERROR("Could not select(): %s", strerror(errno));
			throw std::runtime_error("Could not select");
		}
		if(ret == 0)
			continue;

		int client_fd = accept(m_fd, 0, 0);
		m_handlers.push_back(
			new ClientHandler(client_fd)
		);
	}
}

TCPReceiver::ClientHandler::ClientHandler(int fd)
 : m_fd(fd)
 , m_uncompressBuf(1024)
 , m_running(true)
 , m_keepCompressed(false)
{
	m_thread = boost::thread(boost::bind(&ClientHandler::start, this));
}

TCPReceiver::ClientHandler::~ClientHandler()
{
	close(m_fd);
}

class VectorStream
{
public:
	VectorStream(std::vector<uint8_t>* vector)
	 : m_vector(vector)
	{}

	inline const uint8_t* getData()
	{ return m_vector->data(); }

	inline size_t getLength()
	{ return m_vector->size(); }
private:
	std::vector<uint8_t>* m_vector;
};

void TCPReceiver::ClientHandler::start()
{
	run();
	m_running = false;
}

void TCPReceiver::ClientHandler::run()
{
	while(1)
	{
		TCPHeader header;

		if(!sureRead(m_fd, &header, sizeof(header)))
			return;

		std::vector<char> buf(header.topic_len + 1);
		if(!sureRead(m_fd, buf.data(), header.topic_len))
			return;
		buf[buf.size()-1] = 0;

		std::string topic(buf.data());

		buf.resize(header.type_len+1);
		if(!sureRead(m_fd, buf.data(), header.type_len))
			return;
		buf[buf.size()-1] = 0;

		std::string type(buf.data());

		std::string md5;
		topic_info::unpackMD5(header.topic_md5sum, &md5);

		std::vector<uint8_t> data(header.data_len);
		if(!sureRead(m_fd, data.data(), header.data_len))
			return;

		ROS_DEBUG("Got msg with flags: %d", header.flags());

		if(m_keepCompressed && (header.flags() & TCP_FLAG_COMPRESSED))
		{
			CompressedMsg compressed;
			compressed.type = type;
			memcpy(compressed.md5.data(), header.topic_md5sum, sizeof(header.topic_md5sum));
			compressed.data.swap(data);

			std::map<std::string, ros::Publisher>::iterator it = m_pub.find(topic);
			if(it == m_pub.end())
			{
				ros::NodeHandle nh;
				ros::Publisher pub = nh.advertise<CompressedMsg>(topic, 2);
				m_pub[topic] = pub;

				pub.publish(compressed);
			}
			else
				it->second.publish(compressed);
		}
		else
		{
			topic_tools::ShapeShifter shifter;

			if(header.flags() & TCP_FLAG_COMPRESSED)
			{
				int ret = 0;
				unsigned int len = m_uncompressBuf.size();

				while(1)
				{
					ret = BZ2_bzBuffToBuffDecompress((char*)m_uncompressBuf.data(), &len, (char*)data.data(), data.size(), 0, 0);

					if(ret == BZ_OUTBUFF_FULL)
					{
						len = 4 * m_uncompressBuf.size();
						ROS_INFO("Increasing buffer size to %d KiB", (int)len / 1024);
						m_uncompressBuf.resize(len);
						continue;
					}
					else
						break;
				}

				if(ret != BZ_OK)
				{
					ROS_ERROR("Could not decompress bz2 data (reason %d), dropping msg", ret);
					continue;
				}

				ROS_INFO("decompress %d KiB (%d) to %d KiB (%d)", (int)data.size() / 1024, (int)data.size(), (int)len / 1024, (int)len);
				m_uncompressBuf.resize(len);

				VectorStream stream(&m_uncompressBuf);
				shifter.read(stream);
			}
			else
			{
				VectorStream stream(&data);
				shifter.read(stream);
			}

			ROS_DEBUG("Got message from topic '%s' (type '%s', md5 '%s')", topic.c_str(), type.c_str(), md5.c_str());

			shifter.morph(md5, type, "", "");

			std::map<std::string, ros::Publisher>::iterator it = m_pub.find(topic);
			if(it == m_pub.end())
			{
				ROS_DEBUG("Advertising new topic '%s'", topic.c_str());
				std::string msgDef = topic_info::getMsgDef(type);

				ros::NodeHandle nh;

				ros::AdvertiseOptions options(
					topic,
					2,
					md5,
					type,
					topic_info::getMsgDef(type)
				);

				// It will take subscribers some time to connect to our publisher.
				// Therefore, latch messages so they will not be lost.
				// No, this is often unexpected. Instead, wait before publishing.
	// 			options.latch = true;

				m_pub[topic] = nh.advertise(options);
				it = m_pub.find(topic);

				sleep(1);
			}

			it->second.publish(shifter);
		}

		uint8_t ack = 1;
		if(write(m_fd, &ack, 1) != 1)
		{
			ROS_ERROR("Could not write(): %s", strerror(errno));
			return;
		}
	}
}

bool TCPReceiver::ClientHandler::isRunning() const
{
	return m_running;
}

}

int main(int argc, char** argv)
{
	ros::init(argc, argv, "tcp_receiver");

	nimbro_topic_transport::TCPReceiver recv;

	recv.run();

	return 0;
}
<commit_msg>nimbro_topic_transport: tcp_receiver: fix keep_compressed flag<commit_after>// TCP receiver
// Author: Max Schwarz <max.schwarz@uni-bonn.de>

#include "tcp_receiver.h"

#include <sys/socket.h>
#include <arpa/inet.h>

#include "tcp_packet.h"
#include "../topic_info.h"
#include <topic_tools/shape_shifter.h>

#include <bzlib.h>

#include <nimbro_topic_transport/CompressedMsg.h>

namespace nimbro_topic_transport
{

static bool sureRead(int fd, void* dest, ssize_t size)
{
	uint8_t* destWPtr = (uint8_t*)dest;
	while(size != 0)
	{
		ssize_t ret = read(fd, destWPtr, size);

		if(ret < 0)
		{
			ROS_ERROR("Could not read(): %s", strerror(errno));
			return false;
		}

		if(ret == 0)
		{
			// Client has closed connection (ignore silently)
			return false;
		}

		size -= ret;
		destWPtr += ret;
	}

	return true;
}

TCPReceiver::TCPReceiver()
 : m_nh("~")
{
	m_fd = socket(AF_INET, SOCK_STREAM, 0);
	if(m_fd < 0)
	{
		ROS_FATAL("Could not create socket: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	int port;
	m_nh.param("port", port, 5050);

	sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = INADDR_ANY;
	addr.sin_port = htons(port);

	int on = 1;
	if(setsockopt(m_fd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on)) != 0)
	{
		ROS_FATAL("Could not enable SO_REUSEADDR: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	ROS_DEBUG("Binding to :%d", port);

	if(bind(m_fd, (sockaddr*)&addr, sizeof(addr)) != 0)
	{
		ROS_FATAL("Could not bind socket: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	if(listen(m_fd, 10) != 0)
	{
		ROS_FATAL("Could not listen: %s", strerror(errno));
		throw std::runtime_error(strerror(errno));
	}

	m_nh.param("keep_compressed", m_keepCompressed, false);
}

TCPReceiver::~TCPReceiver()
{
}

void TCPReceiver::run()
{
	fd_set fds;

	while(ros::ok())
	{
		ros::spinOnce();

		// Clean up any exited threads
		std::list<ClientHandler*>::iterator it = m_handlers.begin();
		while(it != m_handlers.end())
		{
			if(!(*it)->isRunning())
			{
				delete *it;
				it = m_handlers.erase(it);
			}
			else
				it++;
		}

		FD_ZERO(&fds);
		FD_SET(m_fd, &fds);

		timeval timeout;
		timeout.tv_usec = 0;
		timeout.tv_sec = 1;

		int ret = select(m_fd+1, &fds, 0, 0, &timeout);
		if(ret < 0)
		{
			if(errno == EINTR || errno == EAGAIN)
				continue;

			ROS_ERROR("Could not select(): %s", strerror(errno));
			throw std::runtime_error("Could not select");
		}
		if(ret == 0)
			continue;

		int client_fd = accept(m_fd, 0, 0);

		ClientHandler* handler = new ClientHandler(client_fd);
		handler->setKeepCompressed(m_keepCompressed);

		m_handlers.push_back(handler);
	}
}

TCPReceiver::ClientHandler::ClientHandler(int fd)
 : m_fd(fd)
 , m_uncompressBuf(1024)
 , m_running(true)
 , m_keepCompressed(false)
{
	m_thread = boost::thread(boost::bind(&ClientHandler::start, this));
}

TCPReceiver::ClientHandler::~ClientHandler()
{
	close(m_fd);
}

class VectorStream
{
public:
	VectorStream(std::vector<uint8_t>* vector)
	 : m_vector(vector)
	{}

	inline const uint8_t* getData()
	{ return m_vector->data(); }

	inline size_t getLength()
	{ return m_vector->size(); }
private:
	std::vector<uint8_t>* m_vector;
};

void TCPReceiver::ClientHandler::start()
{
	run();
	m_running = false;
}

void TCPReceiver::ClientHandler::run()
{
	while(1)
	{
		TCPHeader header;

		if(!sureRead(m_fd, &header, sizeof(header)))
			return;

		std::vector<char> buf(header.topic_len + 1);
		if(!sureRead(m_fd, buf.data(), header.topic_len))
			return;
		buf[buf.size()-1] = 0;

		std::string topic(buf.data());

		buf.resize(header.type_len+1);
		if(!sureRead(m_fd, buf.data(), header.type_len))
			return;
		buf[buf.size()-1] = 0;

		std::string type(buf.data());

		std::string md5;
		topic_info::unpackMD5(header.topic_md5sum, &md5);

		std::vector<uint8_t> data(header.data_len);
		if(!sureRead(m_fd, data.data(), header.data_len))
			return;

		ROS_DEBUG("Got msg with flags: %d", header.flags());

		if(m_keepCompressed && (header.flags() & TCP_FLAG_COMPRESSED))
		{
			CompressedMsg compressed;
			compressed.type = type;
			memcpy(compressed.md5.data(), header.topic_md5sum, sizeof(header.topic_md5sum));
			compressed.data.swap(data);

			std::map<std::string, ros::Publisher>::iterator it = m_pub.find(topic);
			if(it == m_pub.end())
			{
				ros::NodeHandle nh;
				ros::Publisher pub = nh.advertise<CompressedMsg>(topic, 2);
				m_pub[topic] = pub;

				pub.publish(compressed);
			}
			else
				it->second.publish(compressed);
		}
		else
		{
			topic_tools::ShapeShifter shifter;

			if(header.flags() & TCP_FLAG_COMPRESSED)
			{
				int ret = 0;
				unsigned int len = m_uncompressBuf.size();

				while(1)
				{
					ret = BZ2_bzBuffToBuffDecompress((char*)m_uncompressBuf.data(), &len, (char*)data.data(), data.size(), 0, 0);

					if(ret == BZ_OUTBUFF_FULL)
					{
						len = 4 * m_uncompressBuf.size();
						ROS_INFO("Increasing buffer size to %d KiB", (int)len / 1024);
						m_uncompressBuf.resize(len);
						continue;
					}
					else
						break;
				}

				if(ret != BZ_OK)
				{
					ROS_ERROR("Could not decompress bz2 data (reason %d), dropping msg", ret);
					continue;
				}

				ROS_INFO("decompress %d KiB (%d) to %d KiB (%d)", (int)data.size() / 1024, (int)data.size(), (int)len / 1024, (int)len);
				m_uncompressBuf.resize(len);

				VectorStream stream(&m_uncompressBuf);
				shifter.read(stream);
			}
			else
			{
				VectorStream stream(&data);
				shifter.read(stream);
			}

			ROS_DEBUG("Got message from topic '%s' (type '%s', md5 '%s')", topic.c_str(), type.c_str(), md5.c_str());

			shifter.morph(md5, type, "", "");

			std::map<std::string, ros::Publisher>::iterator it = m_pub.find(topic);
			if(it == m_pub.end())
			{
				ROS_DEBUG("Advertising new topic '%s'", topic.c_str());
				std::string msgDef = topic_info::getMsgDef(type);

				ros::NodeHandle nh;

				ros::AdvertiseOptions options(
					topic,
					2,
					md5,
					type,
					topic_info::getMsgDef(type)
				);

				// It will take subscribers some time to connect to our publisher.
				// Therefore, latch messages so they will not be lost.
				// No, this is often unexpected. Instead, wait before publishing.
	// 			options.latch = true;

				m_pub[topic] = nh.advertise(options);
				it = m_pub.find(topic);

				sleep(1);
			}

			it->second.publish(shifter);
		}

		uint8_t ack = 1;
		if(write(m_fd, &ack, 1) != 1)
		{
			ROS_ERROR("Could not write(): %s", strerror(errno));
			return;
		}
	}
}

bool TCPReceiver::ClientHandler::isRunning() const
{
	return m_running;
}

}

int main(int argc, char** argv)
{
	ros::init(argc, argv, "tcp_receiver");

	nimbro_topic_transport::TCPReceiver recv;

	recv.run();

	return 0;
}
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ximpshow.cxx,v $
 *
 *  $Revision: 1.9 $
 *
 *  last change: $Author: hr $ $Date: 2006-06-19 18:15:25 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#include <tools/debug.hxx>

#ifndef _COM_SUN_STAR_UTIL_DATETIME_HPP_
#include <com/sun/star/util/DateTime.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_XATTRIBUTELIST_HPP_
#include <com/sun/star/xml/sax/XAttributeList.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_PRESENTATION_XCUSTOMPRESENTATIONSUPPLIER_HPP_
#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp>
#endif
#ifndef _COM_SUN_STAR_PRESENTATION_XPRESENTATIONSUPPLIER_HPP_
#include <com/sun/star/presentation/XPresentationSupplier.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XINDEXCONTAINER_HPP_
#include <com/sun/star/container/XIndexContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_DRAWING_XDRAWPAGESSUPPLIER_HPP_
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#endif

#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif

#ifndef _COMPHELPER_EXTRACT_HXX_
#include <comphelper/extract.hxx>
#endif

#ifndef _XMLOFF_XMLNMSPE_HXX
#include "xmlnmspe.hxx"
#endif
#ifndef _XMLOFF_NMSPMAP_HXX
#include "nmspmap.hxx"
#endif

#ifndef _XMLOFF_XMLUCONV_HXX
#include "xmluconv.hxx"
#endif

#ifndef _XMLOFF_XIMPSHOW_HXX
#include "ximpshow.hxx"
#endif

using namespace ::rtl;
using namespace ::std;
using namespace ::cppu;
using namespace ::com::sun::star;
using namespace ::com::sun::star::xml;
using namespace ::com::sun::star::xml::sax;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::presentation;
using namespace ::xmloff::token;

///////////////////////////////////////////////////////////////////////

class ShowsImpImpl
{
public:
    Reference< XSingleServiceFactory > mxShowFactory;
    Reference< XNameContainer > mxShows;
    Reference< XPropertySet > mxPresProps;
    Reference< XNameAccess > mxPages;
    OUString maCustomShowName;
    SdXMLImport& mrImport;

    ShowsImpImpl( SdXMLImport& rImport )
    :   mrImport( rImport )
    {}
};

///////////////////////////////////////////////////////////////////////

TYPEINIT1( SdXMLShowsContext, SvXMLImportContext );

SdXMLShowsContext::SdXMLShowsContext( SdXMLImport& rImport,  sal_uInt16 nPrfx, const OUString& rLocalName,  const Reference< XAttributeList >& xAttrList )
:   SvXMLImportContext(rImport, nPrfx, rLocalName)
{
    mpImpl = new ShowsImpImpl( rImport );

    Reference< XCustomPresentationSupplier > xShowsSupplier( rImport.GetModel(), UNO_QUERY );
    if( xShowsSupplier.is() )
    {
        mpImpl->mxShows = xShowsSupplier->getCustomPresentations();
        mpImpl->mxShowFactory = Reference< XSingleServiceFactory >::query( mpImpl->mxShows );
    }

    Reference< XDrawPagesSupplier > xDrawPagesSupplier( rImport.GetModel(), UNO_QUERY );
    if( xDrawPagesSupplier.is() )
        mpImpl->mxPages = Reference< XNameAccess >::query( xDrawPagesSupplier->getDrawPages() );

    Reference< XPresentationSupplier > xPresentationSupplier( rImport.GetModel(), UNO_QUERY );
    if( xPresentationSupplier.is() )
        mpImpl->mxPresProps = Reference< XPropertySet >::query( xPresentationSupplier->getPresentation() );

    if( mpImpl->mxPresProps.is() )
    {
        sal_Bool bAll = sal_True;
        uno::Any aAny;

        // read attributes
        const sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
        for(sal_Int16 i=0; i < nAttrCount; i++)
        {
            OUString sAttrName = xAttrList->getNameByIndex( i );
            OUString aLocalName;
            sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );
            OUString sValue = xAttrList->getValueByIndex( i );

            switch( nPrefix )
            {
            case XML_NAMESPACE_PRESENTATION:
                if( IsXMLToken( aLocalName, XML_START_PAGE ) )
                {
                    aAny <<= sValue;
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "FirstPage" ) ), aAny );
                    bAll = sal_False;
                }
                else if( IsXMLToken( aLocalName, XML_SHOW ) )
                {
                    mpImpl->maCustomShowName = sValue;
                    bAll = sal_False;
                }
                else if( IsXMLToken( aLocalName, XML_PAUSE ) )
                {
                    DateTime aTime;
                    if( !SvXMLUnitConverter::convertTime( aTime,  sValue ) )
                        continue;

                    const sal_Int32 nMS = ( aTime.Hours * 60 + aTime.Minutes ) * 60 + aTime.Seconds;
                    aAny <<= nMS;
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Pause" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_ANIMATIONS ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_ENABLED ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "AllowAnimations" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_STAY_ON_TOP ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsAlwaysOnTop" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_FORCE_MANUAL ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsAutomatic" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_ENDLESS ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsEndless" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_FULL_SCREEN ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFullScreen" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_MOUSE_VISIBLE ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsMouseVisible" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_START_WITH_NAVIGATOR ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "StartWithNavigator" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_MOUSE_AS_PEN ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "UsePen" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_TRANSITION_ON_CLICK ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_ENABLED ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsTransitionOnClick" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_SHOW_LOGO ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsShowLogo" ) ), aAny );
                }
            }
        }
        aAny = bool2any( bAll );
        mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsShowAll" ) ), aAny );
    }
}

SdXMLShowsContext::~SdXMLShowsContext()
{
    if( mpImpl && mpImpl->maCustomShowName.getLength() )
    {
        uno::Any aAny;
        aAny <<= mpImpl->maCustomShowName;
        mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CustomShow" ) ), aAny );
    }

    delete mpImpl;
}

SvXMLImportContext * SdXMLShowsContext::CreateChildContext( USHORT p_nPrefix, const OUString& rLocalName, const Reference< XAttributeList>& xAttrList )
{
    if( mpImpl && p_nPrefix == XML_NAMESPACE_PRESENTATION && IsXMLToken( rLocalName, XML_SHOW ) )
    {
        OUString aName;
        OUString aPages;

        // read attributes
        const sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
        for(sal_Int16 i=0; i < nAttrCount; i++)
        {
            OUString sAttrName = xAttrList->getNameByIndex( i );
            OUString aLocalName;
            sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );
            OUString sValue = xAttrList->getValueByIndex( i );

            switch( nPrefix )
            {
            case XML_NAMESPACE_PRESENTATION:
                if( IsXMLToken( aLocalName, XML_NAME ) )
                {
                    aName = sValue;
                }
                else if( IsXMLToken( aLocalName, XML_PAGES ) )
                {
                    aPages = sValue;
                }
            }
        }

        if( aName.getLength() != 0 && aPages.getLength() != 0 )
        {
            Reference< XIndexContainer > xShow( mpImpl->mxShowFactory->createInstance(), UNO_QUERY );
            if( xShow.is() )
            {
                SvXMLTokenEnumerator aPageNames( aPages, sal_Unicode(',') );
                OUString sPageName;
                Any aAny;

                while( aPageNames.getNextToken( sPageName ) )
                {
                    if( !mpImpl->mxPages->hasByName( sPageName ) )
                        continue;

                    Reference< XDrawPage > xPage;
                    mpImpl->mxPages->getByName( sPageName ) >>= xPage;
                    if( xPage.is() )
                    {
                        aAny <<= xPage;
                        xShow->insertByIndex( xShow->getCount(), aAny );
                    }
                }

                aAny <<= xShow;

                if( mpImpl->mxShows->hasByName( aName ) )
                {
                    mpImpl->mxShows->replaceByName( aName, aAny );
                }
                else
                {
                    mpImpl->mxShows->insertByName( aName, aAny );
                }
            }
        }
    }

    return new SvXMLImportContext( GetImport(), p_nPrefix, rLocalName );
}

<commit_msg>INTEGRATION: CWS pchfix02 (1.9.34); FILE MERGED 2006/09/01 17:59:37 kaib 1.9.34.1: #i68856# Added header markers and pch files<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: ximpshow.cxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: obo $ $Date: 2006-09-17 10:32:28 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#include <tools/debug.hxx>

#ifndef _COM_SUN_STAR_UTIL_DATETIME_HPP_
#include <com/sun/star/util/DateTime.hpp>
#endif
#ifndef _COM_SUN_STAR_XML_SAX_XATTRIBUTELIST_HPP_
#include <com/sun/star/xml/sax/XAttributeList.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSINGLESERVICEFACTORY_HPP_
#include <com/sun/star/lang/XSingleServiceFactory.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XNAMECONTAINER_HPP_
#include <com/sun/star/container/XNameContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_PRESENTATION_XCUSTOMPRESENTATIONSUPPLIER_HPP_
#include <com/sun/star/presentation/XCustomPresentationSupplier.hpp>
#endif
#ifndef _COM_SUN_STAR_PRESENTATION_XPRESENTATIONSUPPLIER_HPP_
#include <com/sun/star/presentation/XPresentationSupplier.hpp>
#endif
#ifndef _COM_SUN_STAR_CONTAINER_XINDEXCONTAINER_HPP_
#include <com/sun/star/container/XIndexContainer.hpp>
#endif
#ifndef _COM_SUN_STAR_DRAWING_XDRAWPAGESSUPPLIER_HPP_
#include <com/sun/star/drawing/XDrawPagesSupplier.hpp>
#endif

#ifndef _XMLOFF_XMLTOKEN_HXX
#include "xmltoken.hxx"
#endif

#ifndef _COMPHELPER_EXTRACT_HXX_
#include <comphelper/extract.hxx>
#endif

#ifndef _XMLOFF_XMLNMSPE_HXX
#include "xmlnmspe.hxx"
#endif
#ifndef _XMLOFF_NMSPMAP_HXX
#include "nmspmap.hxx"
#endif

#ifndef _XMLOFF_XMLUCONV_HXX
#include "xmluconv.hxx"
#endif

#ifndef _XMLOFF_XIMPSHOW_HXX
#include "ximpshow.hxx"
#endif

using namespace ::rtl;
using namespace ::std;
using namespace ::cppu;
using namespace ::com::sun::star;
using namespace ::com::sun::star::xml;
using namespace ::com::sun::star::xml::sax;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::drawing;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::util;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::presentation;
using namespace ::xmloff::token;

///////////////////////////////////////////////////////////////////////

class ShowsImpImpl
{
public:
    Reference< XSingleServiceFactory > mxShowFactory;
    Reference< XNameContainer > mxShows;
    Reference< XPropertySet > mxPresProps;
    Reference< XNameAccess > mxPages;
    OUString maCustomShowName;
    SdXMLImport& mrImport;

    ShowsImpImpl( SdXMLImport& rImport )
    :   mrImport( rImport )
    {}
};

///////////////////////////////////////////////////////////////////////

TYPEINIT1( SdXMLShowsContext, SvXMLImportContext );

SdXMLShowsContext::SdXMLShowsContext( SdXMLImport& rImport,  sal_uInt16 nPrfx, const OUString& rLocalName,  const Reference< XAttributeList >& xAttrList )
:   SvXMLImportContext(rImport, nPrfx, rLocalName)
{
    mpImpl = new ShowsImpImpl( rImport );

    Reference< XCustomPresentationSupplier > xShowsSupplier( rImport.GetModel(), UNO_QUERY );
    if( xShowsSupplier.is() )
    {
        mpImpl->mxShows = xShowsSupplier->getCustomPresentations();
        mpImpl->mxShowFactory = Reference< XSingleServiceFactory >::query( mpImpl->mxShows );
    }

    Reference< XDrawPagesSupplier > xDrawPagesSupplier( rImport.GetModel(), UNO_QUERY );
    if( xDrawPagesSupplier.is() )
        mpImpl->mxPages = Reference< XNameAccess >::query( xDrawPagesSupplier->getDrawPages() );

    Reference< XPresentationSupplier > xPresentationSupplier( rImport.GetModel(), UNO_QUERY );
    if( xPresentationSupplier.is() )
        mpImpl->mxPresProps = Reference< XPropertySet >::query( xPresentationSupplier->getPresentation() );

    if( mpImpl->mxPresProps.is() )
    {
        sal_Bool bAll = sal_True;
        uno::Any aAny;

        // read attributes
        const sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
        for(sal_Int16 i=0; i < nAttrCount; i++)
        {
            OUString sAttrName = xAttrList->getNameByIndex( i );
            OUString aLocalName;
            sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );
            OUString sValue = xAttrList->getValueByIndex( i );

            switch( nPrefix )
            {
            case XML_NAMESPACE_PRESENTATION:
                if( IsXMLToken( aLocalName, XML_START_PAGE ) )
                {
                    aAny <<= sValue;
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "FirstPage" ) ), aAny );
                    bAll = sal_False;
                }
                else if( IsXMLToken( aLocalName, XML_SHOW ) )
                {
                    mpImpl->maCustomShowName = sValue;
                    bAll = sal_False;
                }
                else if( IsXMLToken( aLocalName, XML_PAUSE ) )
                {
                    DateTime aTime;
                    if( !SvXMLUnitConverter::convertTime( aTime,  sValue ) )
                        continue;

                    const sal_Int32 nMS = ( aTime.Hours * 60 + aTime.Minutes ) * 60 + aTime.Seconds;
                    aAny <<= nMS;
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "Pause" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_ANIMATIONS ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_ENABLED ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "AllowAnimations" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_STAY_ON_TOP ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsAlwaysOnTop" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_FORCE_MANUAL ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsAutomatic" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_ENDLESS ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsEndless" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_FULL_SCREEN ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsFullScreen" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_MOUSE_VISIBLE ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsMouseVisible" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_START_WITH_NAVIGATOR ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "StartWithNavigator" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_MOUSE_AS_PEN ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "UsePen" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_TRANSITION_ON_CLICK ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_ENABLED ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsTransitionOnClick" ) ), aAny );
                }
                else if( IsXMLToken( aLocalName, XML_SHOW_LOGO ) )
                {
                    aAny = bool2any( IsXMLToken( sValue, XML_TRUE ) );
                    mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsShowLogo" ) ), aAny );
                }
            }
        }
        aAny = bool2any( bAll );
        mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "IsShowAll" ) ), aAny );
    }
}

SdXMLShowsContext::~SdXMLShowsContext()
{
    if( mpImpl && mpImpl->maCustomShowName.getLength() )
    {
        uno::Any aAny;
        aAny <<= mpImpl->maCustomShowName;
        mpImpl->mxPresProps->setPropertyValue( OUString( RTL_CONSTASCII_USTRINGPARAM( "CustomShow" ) ), aAny );
    }

    delete mpImpl;
}

SvXMLImportContext * SdXMLShowsContext::CreateChildContext( USHORT p_nPrefix, const OUString& rLocalName, const Reference< XAttributeList>& xAttrList )
{
    if( mpImpl && p_nPrefix == XML_NAMESPACE_PRESENTATION && IsXMLToken( rLocalName, XML_SHOW ) )
    {
        OUString aName;
        OUString aPages;

        // read attributes
        const sal_Int16 nAttrCount = xAttrList.is() ? xAttrList->getLength() : 0;
        for(sal_Int16 i=0; i < nAttrCount; i++)
        {
            OUString sAttrName = xAttrList->getNameByIndex( i );
            OUString aLocalName;
            sal_uInt16 nPrefix = GetImport().GetNamespaceMap().GetKeyByAttrName( sAttrName, &aLocalName );
            OUString sValue = xAttrList->getValueByIndex( i );

            switch( nPrefix )
            {
            case XML_NAMESPACE_PRESENTATION:
                if( IsXMLToken( aLocalName, XML_NAME ) )
                {
                    aName = sValue;
                }
                else if( IsXMLToken( aLocalName, XML_PAGES ) )
                {
                    aPages = sValue;
                }
            }
        }

        if( aName.getLength() != 0 && aPages.getLength() != 0 )
        {
            Reference< XIndexContainer > xShow( mpImpl->mxShowFactory->createInstance(), UNO_QUERY );
            if( xShow.is() )
            {
                SvXMLTokenEnumerator aPageNames( aPages, sal_Unicode(',') );
                OUString sPageName;
                Any aAny;

                while( aPageNames.getNextToken( sPageName ) )
                {
                    if( !mpImpl->mxPages->hasByName( sPageName ) )
                        continue;

                    Reference< XDrawPage > xPage;
                    mpImpl->mxPages->getByName( sPageName ) >>= xPage;
                    if( xPage.is() )
                    {
                        aAny <<= xPage;
                        xShow->insertByIndex( xShow->getCount(), aAny );
                    }
                }

                aAny <<= xShow;

                if( mpImpl->mxShows->hasByName( aName ) )
                {
                    mpImpl->mxShows->replaceByName( aName, aAny );
                }
                else
                {
                    mpImpl->mxShows->insertByName( aName, aAny );
                }
            }
        }
    }

    return new SvXMLImportContext( GetImport(), p_nPrefix, rLocalName );
}

<|endoftext|>