<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <algorithm>
#include <sstream>

#include <boost/algorithm/string.hpp>
#include <boost/range/algorithm/find_if.hpp>

#include <qi/log.hpp>
#include <qi/numeric.hpp>

#include "messagesocket.hpp"
#include "transportsocketcache.hpp"

qiLogCategory("qimessaging.transportsocketcache");

namespace qi
{
TransportSocketCache::TransportSocketCache()
  : _dying(false)
{
}

TransportSocketCache::~TransportSocketCache()
{
  _dying = true;
  destroy();
  try
  {
    close();
  }
  catch (const std::exception& ex)
  {
    qiLogError() << "Exception caught during destruction: " << ex.what();
  }
  catch (...)
  {
    qiLogError() << "Unknown exception caught during destruction";
  }
}

void TransportSocketCache::init()
{
  _dying = false;
}

/// Container<DisconnectInfo> C
template<typename C>
static void releaseDisconnectInfoPromises(C& disconnectInfos)
{
  for (auto& d: disconnectInfos)
  {
    d.promiseSocketRemoved.setValue(0);
  }
}

void TransportSocketCache::close()
{
  qiLogDebug() << "TransportSocketCache is closing";
  {
    ConnectionMap map;
    std::list<MessageSocketPtr> pending;
    {
      boost::mutex::scoped_lock lock(_socketMutex);
      _dying = true;
      std::swap(map, _connections);
      std::swap(pending, _allPendingConnections);
    }
    for (auto& pairMachineIdConnection: map)
    {
      auto& mapUrlConnection = pairMachineIdConnection.second;
      for (auto& pairUrlConnection: mapUrlConnection)
      {
        auto& connectionAttempt = *pairUrlConnection.second;
        auto endpoint = connectionAttempt.endpoint;

        // Disconnect any valid socket we were holding.
        if (endpoint)
        {
          endpoint->disconnect();
          endpoint->disconnected.disconnect(connectionAttempt.disconnectionTracking);
        }
        else
        {
          connectionAttempt.state = State_Error;
          connectionAttempt.promise.setError("TransportSocketCache is closing.");
        }
      }
    }
    for (auto& socket: pending)
    {
      socket->disconnect();
    }
  }

  /// Release all disconnect promises to avoid deadlocks.
  /// A deadlock could happen if the user calls `disconnect(socket)` and the cache is
  /// destroyed before the disconnected socket signal has arrived.
  auto sync = _disconnectInfos.synchronize();
  releaseDisconnectInfoPromises(*sync);
}

bool isLocalHost(const std::string& host)
{
  return boost::algorithm::starts_with(host, "127.") || host == "localhost";
}

static UrlVector localhost_only(const UrlVector& input)
{
  UrlVector result;
  result.reserve(input.size());
  for (const auto& url: input)
  {
    if (isLocalHost(url.host()))
      result.push_back(url);
  }
  return result;
}

Future<MessageSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo, const std::string& url)
{
  const std::string& machineId = servInfo.machineId();
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->relatedUrls = servInfo.endpoints();
  bool local = machineId == os::getMachineId();
  UrlVector connectionCandidates;

  // If the connection is local, we're mainly interested in localhost endpoint
  if (local)
    connectionCandidates = localhost_only(servInfo.endpoints());

  // If the connection isn't local or if the service doesn't expose local endpoints,
  // try and connect to whatever is available.
  if (connectionCandidates.size() == 0)
    connectionCandidates = servInfo.endpoints();

  couple->endpoint = MessageSocketPtr();
  couple->state = State_Pending;
  {
    // If we already have a pending connection to one of the urls, we return the future in question
    boost::mutex::scoped_lock lock(_socketMutex);

    if (_dying)
      return makeFutureError<MessageSocketPtr>("TransportSocketCache is closed.");

    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt != _connections.end())
    {
      // Check if any connection to the machine matches one of our urls
      UrlVector& vurls = couple->relatedUrls;
      for (std::map<Url, ConnectionAttemptPtr>::iterator b = machineIt->second.begin(), e = machineIt->second.end();
           b != e;
           b++)
      {
        UrlVector::iterator uIt = std::find(vurls.begin(), vurls.end(), b->first);
        // We found a matching machineId and URL : return the connected endpoint.
        if (uIt != vurls.end())
        {
          qiLogDebug() << "Found pending promise.";
          return b->second->promise.future();
        }
      }
    }
    // Otherwise, we keep track of all those URLs and assign them the same promise in our map.
    // They will all track the same connection.
    couple->attemptCount = qi::numericConvert<int>(connectionCandidates.size());
    std::map<Url, ConnectionAttemptPtr>& urlMap = _connections[machineId];
    for (const auto& url: connectionCandidates)
    {
      if (!url.isValid())
        continue; // Do not try to connect to an invalid url!

      if (!local && isLocalHost(url.host()))
        continue; // Do not try to connect on localhost when it is a remote!

      urlMap[url] = couple;
      MessageSocketPtr socket = makeMessageSocket(url.protocol());
      _allPendingConnections.push_back(socket);
      Future<void> sockFuture = socket->connect(url);
      qiLogDebug() << "Inserted [" << machineId << "][" << url.str() << "]";
      sockFuture.then(std::bind(&TransportSocketCache::onSocketParallelConnectionAttempt, this,
                                std::placeholders::_1, socket, url, servInfo));
    }
  }
  return couple->promise.future();
}

FutureSync<void> TransportSocketCache::disconnect(MessageSocketPtr socket)
{
  Promise<void> promiseSocketRemoved;
  {
    auto syncDisconnectInfos = _disconnectInfos.synchronize();
    // TODO: Remove Promise<void>{} when get rid of VS2013.
    syncDisconnectInfos->push_back(DisconnectInfo{socket, Promise<void>{}});
    promiseSocketRemoved = syncDisconnectInfos->back().promiseSocketRemoved;
  }
  // We wait that the socket has been disconnected _and_ the `disconnected`
  // signal has been received by the cache.
  FutureBarrier<void> barrier;
  barrier.addFuture(promiseSocketRemoved.future());
  barrier.addFuture(socket->disconnect());
  Promise<void> promise;
  return barrier.future().andThen([=](const std::vector<Future<void>>& v) mutable {
    const auto isInError = [](const Future<void>& f) {
      return f.hasError();
    };
    if (std::any_of(begin(v), end(v), isInError))
    {
      promise.setError("disconnect error");
      return;
    }
    promise.setValue(0);
  });
}

void TransportSocketCache::insert(const std::string& machineId, const Url& url, MessageSocketPtr socket)
{
  // If a connection is pending for this machine / url, terminate the pendage and set the
  // service socket as this one
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
    return;

  ServiceInfo info;

  info.setMachineId(machineId);
  qi::SignalLink disconnectionTracking = socket->disconnected.connect(
      track([=](const std::string&) { onSocketDisconnected(url, info); }, this));

  ConnectionMap::iterator mIt = _connections.find(machineId);
  if (mIt != _connections.end())
  {
    std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.find(url);
    if (uIt != mIt->second.end())
    {
      auto& connectionAttempt = *uIt->second;
      QI_ASSERT(!connectionAttempt.endpoint);
      // If the attempt is done and the endpoint is null, it means the
      // attempt failed and the promise is set as error.
      // We replace it by a new one.
      // If the attempt is not done we do not replace it, otherwise the future
      // currently in circulation will never finish.
      if (connectionAttempt.state != State_Pending)
        connectionAttempt.promise = Promise<MessageSocketPtr>();
      connectionAttempt.state = State_Connected;
      connectionAttempt.endpoint = socket;
      connectionAttempt.promise.setValue(socket);
      connectionAttempt.disconnectionTracking = disconnectionTracking;
      return;
    }
  }
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->promise = Promise<MessageSocketPtr>();
  couple->endpoint = socket;
  couple->state = State_Connected;
  couple->relatedUrls.push_back(url);
  _connections[machineId][url] = couple;
  couple->promise.setValue(socket);
}

/*
 * Corner case to manage (TODO):
 *
 * You are connecting to machineId foo, you are machineId bar. foo and bar are
 * on different sub-networks with the same netmask. They sadly got the same IP
 * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
 * will try to connect its endpoints, basically:
 *   - tcp://1.2.3.4:1333 (public IP)
 *   - tcp://192.168.1.42:1333 (subnet public IP)
 * If bar is listening on port 1333, we may connect to it instead of foo (our
 * real target).
 */
void TransportSocketCache::onSocketParallelConnectionAttempt(Future<void> fut,
                                                             MessageSocketPtr socket,
                                                             Url url,
                                                             const ServiceInfo& info)
{
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
  {
    qiLogDebug() << "ConnectionAttempt: TransportSocketCache is closed";
    if (!fut.hasError())
    {
      _allPendingConnections.remove(socket);
      socket->disconnect();
    }
    return;
  }

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  std::map<Url, ConnectionAttemptPtr>::iterator urlIt;
  if (machineIt != _connections.end())
    urlIt = machineIt->second.find(url);

  if (machineIt == _connections.end() || urlIt == machineIt->second.end())
  {
    // The socket was disconnected at some point, and we removed it from our map:
    // return early.
    _allPendingConnections.remove(socket);
    socket->disconnect();
    return;
  }

  ConnectionAttemptPtr attempt = urlIt->second;
  attempt->attemptCount--;
  if (attempt->state != State_Pending)
  {
    qiLogDebug() << "Already connected: reject socket " << socket.get() << " endpoint " << url.str();
    _allPendingConnections.remove(socket);
    socket->disconnect();
    checkClear(attempt, info.machineId());
    return;
  }
  if (fut.hasError())
  {
    // Failing to connect to some of the endpoint is expected.
    qiLogDebug() << "Could not connect to service #" << info.serviceId() << " through url " << url.str();
    _allPendingConnections.remove(socket);
    // It's a critical error if we've exhausted all available endpoints.
    if (attempt->attemptCount == 0)
    {
      std::stringstream err;
      err << "Could not connect to service #" << info.serviceId() << ": no endpoint replied.";
      qiLogError() << err.str();
      attempt->promise.setError(err.str());
      attempt->state = State_Error;
      checkClear(attempt, info.machineId());
    }
    return;
  }
  qi::SignalLink disconnectionTracking = socket->disconnected.connect(
      track([=](const std::string&) { onSocketDisconnected(url, info); }, this));
  attempt->state = State_Connected;
  attempt->endpoint = socket;
  attempt->promise.setValue(socket);
  attempt->disconnectionTracking = disconnectionTracking;
  qiLogDebug() << "Connected to service #" << info.serviceId() << " through url " << url.str() << " and socket "
               << socket.get();
}

void TransportSocketCache::checkClear(ConnectionAttemptPtr attempt, const std::string& machineId)
{
  if ((attempt->attemptCount <= 0 && attempt->state != State_Connected) || attempt->state == State_Error)
  {
    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt == _connections.end())
      return;
    for (UrlVector::const_iterator uit = attempt->relatedUrls.begin(), end = attempt->relatedUrls.end(); uit != end;
         ++uit)
      machineIt->second.erase(*uit);
    if (machineIt->second.size() == 0)
      _connections.erase(machineIt);
  }
}

/// Remove infos of the given socket and set the associated promise.
///
/// Container<DisconnectInfo> C
template<typename C>
static void updateDisconnectInfos(C& disconnectInfos, const MessageSocketPtr& socket)
{
  // TODO: Replace `using` by `auto` in lambda when C++14 is available.
  using Value = typename C::value_type;
  const auto it = boost::find_if(disconnectInfos, [&](const Value& d) {
    return d.socket == socket;
  });
  if (it == disconnectInfos.end())
  {
    qiLogWarning() << "Disconnected socket not found in disconnect infos.";
    return;
  }
  auto promise = (*it).promiseSocketRemoved;
  disconnectInfos.erase(it);
  promise.setValue(0);
}

void TransportSocketCache::onSocketDisconnected(Url url, const ServiceInfo& info)
{
  // remove from the available connections
  boost::mutex::scoped_lock lock(_socketMutex);

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  if (machineIt == _connections.end())
    return;
  qiLogDebug() << "onSocketDisconnected: about to erase socket";
  auto attempt = machineIt->second[url];
  attempt->state = State_Error;
  checkClear(attempt, info.machineId());
  auto syncDisconnectInfos = _disconnectInfos.synchronize();
  updateDisconnectInfos(*syncDisconnectInfos, attempt->endpoint);
}

}
<commit_msg>Changes log level for socket disconnection error #42613<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <algorithm>
#include <sstream>

#include <boost/algorithm/string.hpp>
#include <boost/range/algorithm/find_if.hpp>

#include <qi/log.hpp>
#include <qi/numeric.hpp>

#include "messagesocket.hpp"
#include "transportsocketcache.hpp"

qiLogCategory("qimessaging.transportsocketcache");

namespace qi
{
TransportSocketCache::TransportSocketCache()
  : _dying(false)
{
}

TransportSocketCache::~TransportSocketCache()
{
  _dying = true;
  destroy();
  try
  {
    close();
  }
  catch (const std::exception& ex)
  {
    qiLogError() << "Exception caught during destruction: " << ex.what();
  }
  catch (...)
  {
    qiLogError() << "Unknown exception caught during destruction";
  }
}

void TransportSocketCache::init()
{
  _dying = false;
}

/// Container<DisconnectInfo> C
template<typename C>
static void releaseDisconnectInfoPromises(C& disconnectInfos)
{
  for (auto& d: disconnectInfos)
  {
    d.promiseSocketRemoved.setValue(0);
  }
}

void TransportSocketCache::close()
{
  qiLogDebug() << "TransportSocketCache is closing";
  {
    ConnectionMap map;
    std::list<MessageSocketPtr> pending;
    {
      boost::mutex::scoped_lock lock(_socketMutex);
      _dying = true;
      std::swap(map, _connections);
      std::swap(pending, _allPendingConnections);
    }
    for (auto& pairMachineIdConnection: map)
    {
      auto& mapUrlConnection = pairMachineIdConnection.second;
      for (auto& pairUrlConnection: mapUrlConnection)
      {
        auto& connectionAttempt = *pairUrlConnection.second;
        auto endpoint = connectionAttempt.endpoint;

        // Disconnect any valid socket we were holding.
        if (endpoint)
        {
          endpoint->disconnect();
          endpoint->disconnected.disconnect(connectionAttempt.disconnectionTracking);
        }
        else
        {
          connectionAttempt.state = State_Error;
          connectionAttempt.promise.setError("TransportSocketCache is closing.");
        }
      }
    }
    for (auto& socket: pending)
    {
      socket->disconnect();
    }
  }

  /// Release all disconnect promises to avoid deadlocks.
  /// A deadlock could happen if the user calls `disconnect(socket)` and the cache is
  /// destroyed before the disconnected socket signal has arrived.
  auto sync = _disconnectInfos.synchronize();
  releaseDisconnectInfoPromises(*sync);
}

bool isLocalHost(const std::string& host)
{
  return boost::algorithm::starts_with(host, "127.") || host == "localhost";
}

static UrlVector localhost_only(const UrlVector& input)
{
  UrlVector result;
  result.reserve(input.size());
  for (const auto& url: input)
  {
    if (isLocalHost(url.host()))
      result.push_back(url);
  }
  return result;
}

Future<MessageSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo, const std::string& url)
{
  const std::string& machineId = servInfo.machineId();
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->relatedUrls = servInfo.endpoints();
  bool local = machineId == os::getMachineId();
  UrlVector connectionCandidates;

  // If the connection is local, we're mainly interested in localhost endpoint
  if (local)
    connectionCandidates = localhost_only(servInfo.endpoints());

  // If the connection isn't local or if the service doesn't expose local endpoints,
  // try and connect to whatever is available.
  if (connectionCandidates.size() == 0)
    connectionCandidates = servInfo.endpoints();

  couple->endpoint = MessageSocketPtr();
  couple->state = State_Pending;
  {
    // If we already have a pending connection to one of the urls, we return the future in question
    boost::mutex::scoped_lock lock(_socketMutex);

    if (_dying)
      return makeFutureError<MessageSocketPtr>("TransportSocketCache is closed.");

    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt != _connections.end())
    {
      // Check if any connection to the machine matches one of our urls
      UrlVector& vurls = couple->relatedUrls;
      for (std::map<Url, ConnectionAttemptPtr>::iterator b = machineIt->second.begin(), e = machineIt->second.end();
           b != e;
           b++)
      {
        UrlVector::iterator uIt = std::find(vurls.begin(), vurls.end(), b->first);
        // We found a matching machineId and URL : return the connected endpoint.
        if (uIt != vurls.end())
        {
          qiLogDebug() << "Found pending promise.";
          return b->second->promise.future();
        }
      }
    }
    // Otherwise, we keep track of all those URLs and assign them the same promise in our map.
    // They will all track the same connection.
    couple->attemptCount = qi::numericConvert<int>(connectionCandidates.size());
    std::map<Url, ConnectionAttemptPtr>& urlMap = _connections[machineId];
    for (const auto& url: connectionCandidates)
    {
      if (!url.isValid())
        continue; // Do not try to connect to an invalid url!

      if (!local && isLocalHost(url.host()))
        continue; // Do not try to connect on localhost when it is a remote!

      urlMap[url] = couple;
      MessageSocketPtr socket = makeMessageSocket(url.protocol());
      _allPendingConnections.push_back(socket);
      Future<void> sockFuture = socket->connect(url);
      qiLogDebug() << "Inserted [" << machineId << "][" << url.str() << "]";
      sockFuture.then(std::bind(&TransportSocketCache::onSocketParallelConnectionAttempt, this,
                                std::placeholders::_1, socket, url, servInfo));
    }
  }
  return couple->promise.future();
}

FutureSync<void> TransportSocketCache::disconnect(MessageSocketPtr socket)
{
  Promise<void> promiseSocketRemoved;
  {
    auto syncDisconnectInfos = _disconnectInfos.synchronize();
    // TODO: Remove Promise<void>{} when get rid of VS2013.
    syncDisconnectInfos->push_back(DisconnectInfo{socket, Promise<void>{}});
    promiseSocketRemoved = syncDisconnectInfos->back().promiseSocketRemoved;
  }
  // We wait that the socket has been disconnected _and_ the `disconnected`
  // signal has been received by the cache.
  FutureBarrier<void> barrier;
  barrier.addFuture(promiseSocketRemoved.future());
  barrier.addFuture(socket->disconnect());
  Promise<void> promise;
  return barrier.future().andThen([=](const std::vector<Future<void>>& v) mutable {
    const auto isInError = [](const Future<void>& f) {
      return f.hasError();
    };
    if (std::any_of(begin(v), end(v), isInError))
    {
      promise.setError("disconnect error");
      return;
    }
    promise.setValue(0);
  });
}

void TransportSocketCache::insert(const std::string& machineId, const Url& url, MessageSocketPtr socket)
{
  // If a connection is pending for this machine / url, terminate the pendage and set the
  // service socket as this one
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
    return;

  ServiceInfo info;

  info.setMachineId(machineId);
  qi::SignalLink disconnectionTracking = socket->disconnected.connect(
      track([=](const std::string&) { onSocketDisconnected(url, info); }, this));

  ConnectionMap::iterator mIt = _connections.find(machineId);
  if (mIt != _connections.end())
  {
    std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.find(url);
    if (uIt != mIt->second.end())
    {
      auto& connectionAttempt = *uIt->second;
      QI_ASSERT(!connectionAttempt.endpoint);
      // If the attempt is done and the endpoint is null, it means the
      // attempt failed and the promise is set as error.
      // We replace it by a new one.
      // If the attempt is not done we do not replace it, otherwise the future
      // currently in circulation will never finish.
      if (connectionAttempt.state != State_Pending)
        connectionAttempt.promise = Promise<MessageSocketPtr>();
      connectionAttempt.state = State_Connected;
      connectionAttempt.endpoint = socket;
      connectionAttempt.promise.setValue(socket);
      connectionAttempt.disconnectionTracking = disconnectionTracking;
      return;
    }
  }
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->promise = Promise<MessageSocketPtr>();
  couple->endpoint = socket;
  couple->state = State_Connected;
  couple->relatedUrls.push_back(url);
  _connections[machineId][url] = couple;
  couple->promise.setValue(socket);
}

/*
 * Corner case to manage (TODO):
 *
 * You are connecting to machineId foo, you are machineId bar. foo and bar are
 * on different sub-networks with the same netmask. They sadly got the same IP
 * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
 * will try to connect its endpoints, basically:
 *   - tcp://1.2.3.4:1333 (public IP)
 *   - tcp://192.168.1.42:1333 (subnet public IP)
 * If bar is listening on port 1333, we may connect to it instead of foo (our
 * real target).
 */
void TransportSocketCache::onSocketParallelConnectionAttempt(Future<void> fut,
                                                             MessageSocketPtr socket,
                                                             Url url,
                                                             const ServiceInfo& info)
{
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
  {
    qiLogDebug() << "ConnectionAttempt: TransportSocketCache is closed";
    if (!fut.hasError())
    {
      _allPendingConnections.remove(socket);
      socket->disconnect();
    }
    return;
  }

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  std::map<Url, ConnectionAttemptPtr>::iterator urlIt;
  if (machineIt != _connections.end())
    urlIt = machineIt->second.find(url);

  if (machineIt == _connections.end() || urlIt == machineIt->second.end())
  {
    // The socket was disconnected at some point, and we removed it from our map:
    // return early.
    _allPendingConnections.remove(socket);
    socket->disconnect();
    return;
  }

  ConnectionAttemptPtr attempt = urlIt->second;
  attempt->attemptCount--;
  if (attempt->state != State_Pending)
  {
    qiLogDebug() << "Already connected: reject socket " << socket.get() << " endpoint " << url.str();
    _allPendingConnections.remove(socket);
    socket->disconnect();
    checkClear(attempt, info.machineId());
    return;
  }
  if (fut.hasError())
  {
    // Failing to connect to some of the endpoint is expected.
    qiLogDebug() << "Could not connect to service #" << info.serviceId() << " through url " << url.str();
    _allPendingConnections.remove(socket);
    // It's a critical error if we've exhausted all available endpoints.
    if (attempt->attemptCount == 0)
    {
      std::stringstream err;
      err << "Could not connect to service #" << info.serviceId() << ": no endpoint replied.";
      qiLogError() << err.str();
      attempt->promise.setError(err.str());
      attempt->state = State_Error;
      checkClear(attempt, info.machineId());
    }
    return;
  }
  qi::SignalLink disconnectionTracking = socket->disconnected.connect(
      track([=](const std::string&) { onSocketDisconnected(url, info); }, this));
  attempt->state = State_Connected;
  attempt->endpoint = socket;
  attempt->promise.setValue(socket);
  attempt->disconnectionTracking = disconnectionTracking;
  qiLogDebug() << "Connected to service #" << info.serviceId() << " through url " << url.str() << " and socket "
               << socket.get();
}

void TransportSocketCache::checkClear(ConnectionAttemptPtr attempt, const std::string& machineId)
{
  if ((attempt->attemptCount <= 0 && attempt->state != State_Connected) || attempt->state == State_Error)
  {
    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt == _connections.end())
      return;
    for (UrlVector::const_iterator uit = attempt->relatedUrls.begin(), end = attempt->relatedUrls.end(); uit != end;
         ++uit)
      machineIt->second.erase(*uit);
    if (machineIt->second.size() == 0)
      _connections.erase(machineIt);
  }
}

/// Remove infos of the given socket and set the associated promise.
///
/// Container<DisconnectInfo> C
template<typename C>
static void updateDisconnectInfos(C& disconnectInfos, const MessageSocketPtr& socket)
{
  // TODO: Replace `using` by `auto` in lambda when C++14 is available.
  using Value = typename C::value_type;
  const auto it = boost::find_if(disconnectInfos, [&](const Value& d) {
    return d.socket == socket;
  });
  if (it == disconnectInfos.end())
  {
    // We should not fall into this if statement, but due to the racy nature of
    // the disconnection, it does indeed occur. Fixing this would be
    // a significant rearchitecture, and we choose for now to lower the log
    // level because there does not seem to be any other side effect besides the
    // warning.
    qiLogVerbose() << "Disconnected socket not found in disconnect infos.";
    return;
  }
  auto promise = (*it).promiseSocketRemoved;
  disconnectInfos.erase(it);
  promise.setValue(0);
}

void TransportSocketCache::onSocketDisconnected(Url url, const ServiceInfo& info)
{
  // remove from the available connections
  boost::mutex::scoped_lock lock(_socketMutex);

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  if (machineIt == _connections.end())
    return;
  qiLogDebug() << "onSocketDisconnected: about to erase socket";
  auto attempt = machineIt->second[url];
  attempt->state = State_Error;
  checkClear(attempt, info.machineId());
  auto syncDisconnectInfos = _disconnectInfos.synchronize();
  updateDisconnectInfos(*syncDisconnectInfos, attempt->endpoint);
}

}
<|endoftext|>


<commit_before>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <sstream>

#include <boost/algorithm/string.hpp>

#include <qi/log.hpp>

#include "transportsocket.hpp"
#include "transportsocketcache.hpp"

qiLogCategory("qimessaging.transportsocketcache");

namespace qi
{
TransportSocketCache::TransportSocketCache()
  : _dying(false)
{
}

TransportSocketCache::~TransportSocketCache()
{
  _dying = true;
  destroy();
  close();
}

void TransportSocketCache::init()
{
  _dying = false;
}

void TransportSocketCache::close()
{
  qiLogDebug() << "TransportSocketCache is closing";
  ConnectionMap map;
  std::list<TransportSocketPtr> pending;
  {
    boost::mutex::scoped_lock lock(_socketMutex);
    _dying = true;
    std::swap(map, _connections);
    std::swap(pending, _allPendingConnections);
  }
  for (ConnectionMap::iterator mIt = map.begin(), mEnd = map.end(); mIt != mEnd; ++mIt)
  {
    for (std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.begin(), uEnd = mIt->second.end();
         uIt != uEnd;
         ++uIt)
    {
      TransportSocketPtr endpoint = uIt->second->endpoint;

      // Disconnect any valid socket we were holding.
      if (endpoint)
      {
        endpoint->disconnect();
      }
      else
      {
        uIt->second->state = State_Error;
        uIt->second->promise.setError("TransportSocketCache is closing.");
      }
    }
  }
  for (std::list<TransportSocketPtr>::iterator it = pending.begin(), end = pending.end(); it != end; ++it)
    (*it)->disconnect();
}

static UrlVector localhost_only(const UrlVector& input)
{
  UrlVector result;

  result.reserve(input.size());
  for (UrlVector::const_iterator it = input.begin(), end = input.end(); it != end; ++it)
  {
    const std::string& host = it->host();
    if (boost::algorithm::starts_with(host, "127.") || host == "localhost")
      result.push_back(*it);
  }
  return result;
}

Future<TransportSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo, const std::string& protocol)
{
  const std::string& machineId = servInfo.machineId();
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->relatedUrls = servInfo.endpoints();
  bool local = machineId == os::getMachineId();
  UrlVector connectionCandidates;

  // If the connection is local, we're mainly interested in localhost endpoint
  if (local)
    connectionCandidates = localhost_only(servInfo.endpoints());

  // If the connection isn't local or if the service doesn't expose local endpoints,
  // try and connect to whatever is available.
  if (connectionCandidates.size() == 0)
    connectionCandidates = servInfo.endpoints();

  couple->endpoint = TransportSocketPtr();
  couple->state = State_Pending;
  {
    // If we already have a pending connection to one of the urls, we return the future in question
    boost::mutex::scoped_lock lock(_socketMutex);

    if (_dying)
      return makeFutureError<TransportSocketPtr>("TransportSocketCache is closed.");

    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt != _connections.end())
    {
      // Check if any connection to the machine matches one of our urls
      UrlVector& vurls = couple->relatedUrls;
      for (std::map<Url, ConnectionAttemptPtr>::iterator b = machineIt->second.begin(), e = machineIt->second.end();
           b != e;
           b++)
      {
        UrlVector::iterator uIt = std::find(vurls.begin(), vurls.end(), b->first);
        // We found a matching machineId and URL : return the connected endpoint.
        if (uIt != vurls.end())
          return b->second->promise.future();
      }
    }
    // Otherwise, we keep track of all those URLs and assign them the same promise in our map.
    // They will all track the same connection.
    couple->attemptCount = connectionCandidates.size();
    std::map<Url, ConnectionAttemptPtr>& urlMap = _connections[machineId];
    for (UrlVector::iterator it = connectionCandidates.begin(), end = connectionCandidates.end(); it != end; ++it)
    {
      urlMap[*it] = couple;
      TransportSocketPtr socket = makeTransportSocket(it->protocol());
      _allPendingConnections.push_back(socket);
      Future<void> sockFuture = socket->connect(*it);
      qiLogDebug() << "Inserted [" << machineId << "][" << it->str() << "]";
      sockFuture.connect(&TransportSocketCache::onSocketParallelConnectionAttempt, this, _1, socket, *it, servInfo);
    }
  }
  return couple->promise.future();
}

void TransportSocketCache::insert(const std::string& machineId, const Url& url, TransportSocketPtr socket)
{
  // If a connection is pending for this machine / url, terminate the pendage and set the
  // service socket as this one
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
    return;

  ServiceInfo info;

  info.setMachineId(machineId);
  socket->disconnected.connect(&TransportSocketCache::onSocketDisconnected, this, socket, url, _1, info)
      .setCallType(MetaCallType_Direct);
  ConnectionMap::iterator mIt = _connections.find(machineId);
  if (mIt != _connections.end())
  {
    std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.find(url);
    if (uIt != mIt->second.end())
    {
      QI_ASSERT(!uIt->second->endpoint);
      // If the attempt is done and the endpoint is null, it means the
      // attempt failed and the promise is set as error.
      // We replace it by a new one.
      // If the attempt is not done we do not replace it, otherwise the future
      // currently in circulation will never finish.
      if (uIt->second->state != State_Pending)
        uIt->second->promise = Promise<TransportSocketPtr>();
      uIt->second->state = State_Connected;
      uIt->second->endpoint = socket;
      uIt->second->promise.setValue(socket);
      return;
    }
  }
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->promise = Promise<TransportSocketPtr>();
  couple->endpoint = socket;
  couple->state = State_Connected;
  couple->relatedUrls.push_back(url);
  _connections[machineId][url] = couple;
  couple->promise.setValue(socket);
}

/*
 * Corner case to manage (TODO):
 *
 * You are connecting to machineId foo, you are machineId bar. foo and bar are
 * on different sub-networks with the same netmask. They sadly got the same IP
 * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
 * will try to connect its endpoints, basically:
 *   - tcp://1.2.3.4:1333 (public IP)
 *   - tcp://192.168.1.42:1333 (subnet public IP)
 * If bar is listening on port 1333, we may connect to it instead of foo (our
 * real target).
 */
void TransportSocketCache::onSocketParallelConnectionAttempt(Future<void> fut,
                                                             TransportSocketPtr socket,
                                                             Url url,
                                                             const ServiceInfo& info)
{
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
  {
    qiLogDebug() << "ConnectionAttempt: TransportSocketCache is closed";
    if (!fut.hasError())
    {
      _allPendingConnections.remove(socket);
      socket->disconnect();
    }
    return;
  }

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  std::map<Url, ConnectionAttemptPtr>::iterator urlIt;
  if (machineIt != _connections.end())
    urlIt = machineIt->second.find(url);

  if (machineIt == _connections.end() || urlIt == machineIt->second.end())
  {
    // The socket was disconnected at some point, and we removed it from our map:
    // return early.
    _allPendingConnections.remove(socket);
    socket->disconnect();
    return;
  }

  ConnectionAttemptPtr attempt = urlIt->second;
  attempt->attemptCount--;
  if (attempt->state != State_Pending)
  {
    qiLogDebug() << "Already connected: reject socket " << socket.get() << " endpoint " << url.str();
    _allPendingConnections.remove(socket);
    socket->disconnect();
    checkClear(attempt, info.machineId());
    return;
  }
  if (fut.hasError())
  {
    // Failing to connect to some of the endpoint is expected.
    qiLogDebug() << "Could not connect to service #" << info.serviceId() << " through url " << url.str();
    _allPendingConnections.remove(socket);
    // It's a critical error if we've exhausted all available endpoints.
    if (attempt->attemptCount == 0)
    {
      std::stringstream err;
      err << "Could not connect to service #" << info.serviceId() << ": no endpoint answered.";
      qiLogError() << err.str();
      attempt->promise.setError(err.str());
      attempt->state = State_Error;
      checkClear(attempt, info.machineId());
    }
    return;
  }
  socket->disconnected.connect(&TransportSocketCache::onSocketDisconnected, this, socket, url, _1, info)
      .setCallType(MetaCallType_Direct);
  attempt->state = State_Connected;
  attempt->endpoint = socket;
  attempt->promise.setValue(socket);
  qiLogDebug() << "Connected to service #" << info.serviceId() << " through url " << url.str() << " and socket "
               << socket.get();
}

void TransportSocketCache::checkClear(ConnectionAttemptPtr attempt, const std::string& machineId)
{
  if ((attempt->attemptCount <= 0 && attempt->state != State_Connected) || attempt->state == State_Error)
  {
    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt == _connections.end())
      return;
    for (UrlVector::const_iterator uit = attempt->relatedUrls.begin(), end = attempt->relatedUrls.end(); uit != end;
         ++uit)
      machineIt->second.erase(*uit);
    if (machineIt->second.size() == 0)
      _connections.erase(machineIt);
  }
}

void TransportSocketCache::onSocketDisconnected(TransportSocketPtr socket,
                                                Url url,
                                                const std::string& reason,
                                                const ServiceInfo& info)
{
  // remove from the available connections
  boost::mutex::scoped_lock lock(_socketMutex);

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  if (machineIt == _connections.end())
    return;

  machineIt->second[url]->state = State_Error;
  checkClear(machineIt->second[url], info.machineId());
}
}
<commit_msg>Transport Socket Cache: fix error message<commit_after>/*
**  Copyright (C) 2012 Aldebaran Robotics
**  See COPYING for the license
*/
#include <sstream>

#include <boost/algorithm/string.hpp>

#include <qi/log.hpp>

#include "transportsocket.hpp"
#include "transportsocketcache.hpp"

qiLogCategory("qimessaging.transportsocketcache");

namespace qi
{
TransportSocketCache::TransportSocketCache()
  : _dying(false)
{
}

TransportSocketCache::~TransportSocketCache()
{
  _dying = true;
  destroy();
  close();
}

void TransportSocketCache::init()
{
  _dying = false;
}

void TransportSocketCache::close()
{
  qiLogDebug() << "TransportSocketCache is closing";
  ConnectionMap map;
  std::list<TransportSocketPtr> pending;
  {
    boost::mutex::scoped_lock lock(_socketMutex);
    _dying = true;
    std::swap(map, _connections);
    std::swap(pending, _allPendingConnections);
  }
  for (ConnectionMap::iterator mIt = map.begin(), mEnd = map.end(); mIt != mEnd; ++mIt)
  {
    for (std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.begin(), uEnd = mIt->second.end();
         uIt != uEnd;
         ++uIt)
    {
      TransportSocketPtr endpoint = uIt->second->endpoint;

      // Disconnect any valid socket we were holding.
      if (endpoint)
      {
        endpoint->disconnect();
      }
      else
      {
        uIt->second->state = State_Error;
        uIt->second->promise.setError("TransportSocketCache is closing.");
      }
    }
  }
  for (std::list<TransportSocketPtr>::iterator it = pending.begin(), end = pending.end(); it != end; ++it)
    (*it)->disconnect();
}

static UrlVector localhost_only(const UrlVector& input)
{
  UrlVector result;

  result.reserve(input.size());
  for (UrlVector::const_iterator it = input.begin(), end = input.end(); it != end; ++it)
  {
    const std::string& host = it->host();
    if (boost::algorithm::starts_with(host, "127.") || host == "localhost")
      result.push_back(*it);
  }
  return result;
}

Future<TransportSocketPtr> TransportSocketCache::socket(const ServiceInfo& servInfo, const std::string& protocol)
{
  const std::string& machineId = servInfo.machineId();
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->relatedUrls = servInfo.endpoints();
  bool local = machineId == os::getMachineId();
  UrlVector connectionCandidates;

  // If the connection is local, we're mainly interested in localhost endpoint
  if (local)
    connectionCandidates = localhost_only(servInfo.endpoints());

  // If the connection isn't local or if the service doesn't expose local endpoints,
  // try and connect to whatever is available.
  if (connectionCandidates.size() == 0)
    connectionCandidates = servInfo.endpoints();

  couple->endpoint = TransportSocketPtr();
  couple->state = State_Pending;
  {
    // If we already have a pending connection to one of the urls, we return the future in question
    boost::mutex::scoped_lock lock(_socketMutex);

    if (_dying)
      return makeFutureError<TransportSocketPtr>("TransportSocketCache is closed.");

    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt != _connections.end())
    {
      // Check if any connection to the machine matches one of our urls
      UrlVector& vurls = couple->relatedUrls;
      for (std::map<Url, ConnectionAttemptPtr>::iterator b = machineIt->second.begin(), e = machineIt->second.end();
           b != e;
           b++)
      {
        UrlVector::iterator uIt = std::find(vurls.begin(), vurls.end(), b->first);
        // We found a matching machineId and URL : return the connected endpoint.
        if (uIt != vurls.end())
          return b->second->promise.future();
      }
    }
    // Otherwise, we keep track of all those URLs and assign them the same promise in our map.
    // They will all track the same connection.
    couple->attemptCount = connectionCandidates.size();
    std::map<Url, ConnectionAttemptPtr>& urlMap = _connections[machineId];
    for (UrlVector::iterator it = connectionCandidates.begin(), end = connectionCandidates.end(); it != end; ++it)
    {
      urlMap[*it] = couple;
      TransportSocketPtr socket = makeTransportSocket(it->protocol());
      _allPendingConnections.push_back(socket);
      Future<void> sockFuture = socket->connect(*it);
      qiLogDebug() << "Inserted [" << machineId << "][" << it->str() << "]";
      sockFuture.connect(&TransportSocketCache::onSocketParallelConnectionAttempt, this, _1, socket, *it, servInfo);
    }
  }
  return couple->promise.future();
}

void TransportSocketCache::insert(const std::string& machineId, const Url& url, TransportSocketPtr socket)
{
  // If a connection is pending for this machine / url, terminate the pendage and set the
  // service socket as this one
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
    return;

  ServiceInfo info;

  info.setMachineId(machineId);
  socket->disconnected.connect(&TransportSocketCache::onSocketDisconnected, this, socket, url, _1, info)
      .setCallType(MetaCallType_Direct);
  ConnectionMap::iterator mIt = _connections.find(machineId);
  if (mIt != _connections.end())
  {
    std::map<Url, ConnectionAttemptPtr>::iterator uIt = mIt->second.find(url);
    if (uIt != mIt->second.end())
    {
      QI_ASSERT(!uIt->second->endpoint);
      // If the attempt is done and the endpoint is null, it means the
      // attempt failed and the promise is set as error.
      // We replace it by a new one.
      // If the attempt is not done we do not replace it, otherwise the future
      // currently in circulation will never finish.
      if (uIt->second->state != State_Pending)
        uIt->second->promise = Promise<TransportSocketPtr>();
      uIt->second->state = State_Connected;
      uIt->second->endpoint = socket;
      uIt->second->promise.setValue(socket);
      return;
    }
  }
  ConnectionAttemptPtr couple = boost::make_shared<ConnectionAttempt>();
  couple->promise = Promise<TransportSocketPtr>();
  couple->endpoint = socket;
  couple->state = State_Connected;
  couple->relatedUrls.push_back(url);
  _connections[machineId][url] = couple;
  couple->promise.setValue(socket);
}

/*
 * Corner case to manage (TODO):
 *
 * You are connecting to machineId foo, you are machineId bar. foo and bar are
 * on different sub-networks with the same netmask. They sadly got the same IP
 * on their subnet: 192.168.1.42. When trying to connect to foo from bar, we
 * will try to connect its endpoints, basically:
 *   - tcp://1.2.3.4:1333 (public IP)
 *   - tcp://192.168.1.42:1333 (subnet public IP)
 * If bar is listening on port 1333, we may connect to it instead of foo (our
 * real target).
 */
void TransportSocketCache::onSocketParallelConnectionAttempt(Future<void> fut,
                                                             TransportSocketPtr socket,
                                                             Url url,
                                                             const ServiceInfo& info)
{
  boost::mutex::scoped_lock lock(_socketMutex);

  if (_dying)
  {
    qiLogDebug() << "ConnectionAttempt: TransportSocketCache is closed";
    if (!fut.hasError())
    {
      _allPendingConnections.remove(socket);
      socket->disconnect();
    }
    return;
  }

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  std::map<Url, ConnectionAttemptPtr>::iterator urlIt;
  if (machineIt != _connections.end())
    urlIt = machineIt->second.find(url);

  if (machineIt == _connections.end() || urlIt == machineIt->second.end())
  {
    // The socket was disconnected at some point, and we removed it from our map:
    // return early.
    _allPendingConnections.remove(socket);
    socket->disconnect();
    return;
  }

  ConnectionAttemptPtr attempt = urlIt->second;
  attempt->attemptCount--;
  if (attempt->state != State_Pending)
  {
    qiLogDebug() << "Already connected: reject socket " << socket.get() << " endpoint " << url.str();
    _allPendingConnections.remove(socket);
    socket->disconnect();
    checkClear(attempt, info.machineId());
    return;
  }
  if (fut.hasError())
  {
    // Failing to connect to some of the endpoint is expected.
    qiLogDebug() << "Could not connect to service #" << info.serviceId() << " through url " << url.str();
    _allPendingConnections.remove(socket);
    // It's a critical error if we've exhausted all available endpoints.
    if (attempt->attemptCount == 0)
    {
      std::stringstream err;
      err << "Could not connect to service #" << info.serviceId() << ": no endpoint replied.";
      qiLogError() << err.str();
      attempt->promise.setError(err.str());
      attempt->state = State_Error;
      checkClear(attempt, info.machineId());
    }
    return;
  }
  socket->disconnected.connect(&TransportSocketCache::onSocketDisconnected, this, socket, url, _1, info)
      .setCallType(MetaCallType_Direct);
  attempt->state = State_Connected;
  attempt->endpoint = socket;
  attempt->promise.setValue(socket);
  qiLogDebug() << "Connected to service #" << info.serviceId() << " through url " << url.str() << " and socket "
               << socket.get();
}

void TransportSocketCache::checkClear(ConnectionAttemptPtr attempt, const std::string& machineId)
{
  if ((attempt->attemptCount <= 0 && attempt->state != State_Connected) || attempt->state == State_Error)
  {
    ConnectionMap::iterator machineIt = _connections.find(machineId);
    if (machineIt == _connections.end())
      return;
    for (UrlVector::const_iterator uit = attempt->relatedUrls.begin(), end = attempt->relatedUrls.end(); uit != end;
         ++uit)
      machineIt->second.erase(*uit);
    if (machineIt->second.size() == 0)
      _connections.erase(machineIt);
  }
}

void TransportSocketCache::onSocketDisconnected(TransportSocketPtr socket,
                                                Url url,
                                                const std::string& reason,
                                                const ServiceInfo& info)
{
  // remove from the available connections
  boost::mutex::scoped_lock lock(_socketMutex);

  ConnectionMap::iterator machineIt = _connections.find(info.machineId());
  if (machineIt == _connections.end())
    return;

  machineIt->second[url]->state = State_Error;
  checkClear(machineIt->second[url], info.machineId());
}
}
<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Library
  Module:    BArray.hh
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
//
//  Dynamic, self adjusting bit array
//
#ifndef __vlBitArray_h
#define __vlBitArray_h

#include "Object.hh"

class vlBitArray : public vlObject
{
public:
  vlBitArray():Array(NULL),Size(0),MaxId(-1),Extend(1000) {};
  void Initialize();
  int Allocate(const int sz, const int ext);
  vlBitArray(const int sz, const int ext);
  vlBitArray(const vlBitArray& ia);
  ~vlBitArray();
  int GetValue(const int id) {return (this->Array[id/8]&(0x80 >> (id%8)));};
  char *GetPtr(const int id) {return this->Array + id/8;};
  vlBitArray &SetValue(const int id, const int i)
  {
  if (i) this->Array[id/8] != (0x80 >> id%8);
  else this->Array[id/8] &= (~(0x80 >> id%8));
  if ( id > this->MaxId ) this->MaxId = id;
  }
  vlBitArray &InsertValue(const int id, const int i)
  {
  if ( id >= this->Size ) this->Resize(id);
  if (i) this->Array[id/8] != (0x80 >> id%8);
  else this->Array[id/8] &= (~(0x80 >> id%8));
  if ( id > this->MaxId ) this->MaxId = id;
  return *this;
  }
  int InsertNextValue(const int i)
  {this->InsertValue (++this->MaxId,i); return this->MaxId;};
  vlBitArray &operator=(const vlBitArray& ia);
  vlBitArray &operator+=(const vlBitArray& ia);
  void operator+=(const char i) {this->InsertNextValue(i);};
  // operator[] can be used on both left and right side of expression;
  // Note: if used on lh side, user's responsibility to do range checking
  // Note: bit arrays don't handle [] very well
  //  char& operator[](const int i)
  //    {if (i > this->MaxId) this->MaxId = i;
  //    return (this->Array[i/8]&(0x80 >> (id%8)));};
  void Squeeze() {this->Resize (this->MaxId+1);};
  int GetSize() {return this->Size;};
  int GetMaxId() {return this->MaxId;};
  char *GetArray() {return this->Array;};
  void Reset() {this->MaxId = -1;};
  virtual char *GetClassName() {return "vlBitArray";};
  void PrintSelf(ostream& os, vlIndent indent);

private:
  unsigned char *Array;   // pointer to data
  int Size;       // allocated size of data
  int MaxId;     // maximum index inserted thus iar
  int Extend;     // grow array by this point
  char *Resize(const int sz);  // function to resize data
};

#endif

<commit_msg>fixed bug in the Set methods<commit_after>/*=========================================================================

  Program:   Visualization Library
  Module:    BArray.hh
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
//
//  Dynamic, self adjusting bit array
//
#ifndef __vlBitArray_h
#define __vlBitArray_h

#include "Object.hh"

class vlBitArray : public vlObject
{
public:
  vlBitArray():Array(NULL),Size(0),MaxId(-1),Extend(1000) {};
  void Initialize();
  int Allocate(const int sz, const int ext);
  vlBitArray(const int sz, const int ext);
  vlBitArray(const vlBitArray& ia);
  ~vlBitArray();
  int GetValue(const int id) {return (this->Array[id/8]&(0x80 >> (id%8)));};
  char *GetPtr(const int id) {return this->Array + id/8;};
  vlBitArray &SetValue(const int id, const int i)
  {
  if (i) this->Array[id/8] |= (0x80 >> id%8);
  else this->Array[id/8] &= (~(0x80 >> id%8));
  if ( id > this->MaxId ) this->MaxId = id;
  }
  vlBitArray &InsertValue(const int id, const int i)
  {
  if ( id >= this->Size ) this->Resize(id);
  if (i) this->Array[id/8] |= (0x80 >> id%8);
  else this->Array[id/8] &= (~(0x80 >> id%8));
  if ( id > this->MaxId ) this->MaxId = id;
  return *this;
  }
  int InsertNextValue(const int i)
  {this->InsertValue (++this->MaxId,i); return this->MaxId;};
  vlBitArray &operator=(const vlBitArray& ia);
  vlBitArray &operator+=(const vlBitArray& ia);
  void operator+=(const char i) {this->InsertNextValue(i);};
  // operator[] can be used on both left and right side of expression;
  // Note: if used on lh side, user's responsibility to do range checking
  // Note: bit arrays don't handle [] very well
  //  char& operator[](const int i)
  //    {if (i > this->MaxId) this->MaxId = i;
  //    return (this->Array[i/8]&(0x80 >> (id%8)));};
  void Squeeze() {this->Resize (this->MaxId+1);};
  int GetSize() {return this->Size;};
  int GetMaxId() {return this->MaxId;};
  char *GetArray() {return this->Array;};
  void Reset() {this->MaxId = -1;};
  virtual char *GetClassName() {return "vlBitArray";};
  void PrintSelf(ostream& os, vlIndent indent);

private:
  unsigned char *Array;   // pointer to data
  int Size;       // allocated size of data
  int MaxId;     // maximum index inserted thus iar
  int Extend;     // grow array by this point
  char *Resize(const int sz);  // function to resize data
};

#endif

<|endoftext|>


<commit_before>//======================================================================
//-----------------------------------------------------------------------
/**
 * @file		iutest_ostream_formatter_tests.cpp
 * @brief		QuietResultPrinter test
 *
 * @author		t.shirayanagi
 * @par			copyright
 * Copyright (C) 2014, Takazumi Shirayanagi\n
 * This software is released under the new BSD License,
 * see LICENSE
*/
//-----------------------------------------------------------------------
//======================================================================

//======================================================================
// include
#include "../include/iutest.hpp"
#include "iutest_logger_tests.hpp"
#include <iomanip>

TestLogger logger;

IUTEST(Test, Hex)
{
	IUTEST_ASSERT_EQ(1024, 1025);
}

IUTEST(Test, Float)
{
	IUTEST_ASSERT_EQ(0.33f, 1.0f/3.0f);
}

#ifdef UNICODE
int wmain(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
	IUTEST_INIT(&argc, argv);
#if defined(OUTPUTXML)
	// 実行対象テストがないので xml 出力しない
	::iutest::IUTEST_FLAG(output) = NULL;
#endif

	::iutest::detail::iuConsole::SetLogger(&logger);

	::iutest::IUTEST_FLAG(color) = "no";
#if IUTEST_HAS_STRINGSTREAM || IUTEST_HAS_STRSTREAM
	::iutest::IUTEST_FLAG(ostream_formatter) << ::std::hex << ::std::setw(8) << ::std::setfill('0') << std::setprecision(5);
#endif

	{
		if( IUTEST_RUN_ALL_TESTS() == 0 ) return 1;
#if IUTEST_HAS_STRINGSTREAM || IUTEST_HAS_STRSTREAM

#if IUTEST_HAS_ASSERTION_RETURN
		IUTEST_ASSERT_STRIN("  Actual: 00000401", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
		IUTEST_ASSERT_STRIN("Which is: 00000400", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
		IUTEST_ASSERT_STRIN("  Actual: 00.33333", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
		IUTEST_ASSERT_STRIN("Which is: 00000.33", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
#endif

#endif
	}
	printf("*** Successful ***\n");

	return 0;
}
<commit_msg>update r685<commit_after>//======================================================================
//-----------------------------------------------------------------------
/**
 * @file		iutest_ostream_formatter_tests.cpp
 * @brief		QuietResultPrinter test
 *
 * @author		t.shirayanagi
 * @par			copyright
 * Copyright (C) 2014, Takazumi Shirayanagi\n
 * This software is released under the new BSD License,
 * see LICENSE
*/
//-----------------------------------------------------------------------
//======================================================================

//======================================================================
// include
#include "../include/iutest.hpp"
#include "iutest_logger_tests.hpp"
#include <iomanip>

TestLogger logger;

IUTEST(Test, Hex)
{
	IUTEST_ASSERT_EQ(1024, 1025);
}

IUTEST(Test, Float)
{
	IUTEST_ASSERT_EQ(0.33f, 1.0f/3.0f);
}

#ifdef UNICODE
int wmain(int argc, wchar_t* argv[])
#else
int main(int argc, char* argv[])
#endif
{
	IUTEST_INIT(&argc, argv);
#if defined(OUTPUTXML)
	// 実行対象テストがないので xml 出力しない
	::iutest::IUTEST_FLAG(output) = NULL;
#endif

	::iutest::detail::iuConsole::SetLogger(&logger);

	::iutest::IUTEST_FLAG(color) = "no";
#if IUTEST_HAS_STRINGSTREAM || IUTEST_HAS_STRSTREAM
	::iutest::IUTEST_FLAG(ostream_formatter) << ::std::hex << ::std::setw(8) << ::std::setfill('0');
	::iutest::IUTEST_FLAG(ostream_formatter) << ::std::setprecision(5);
#endif

	{
		if( IUTEST_RUN_ALL_TESTS() == 0 ) return 1;
#if IUTEST_HAS_STRINGSTREAM || IUTEST_HAS_STRSTREAM

#if IUTEST_HAS_ASSERTION_RETURN
		IUTEST_ASSERT_STRIN("  Actual: 00000401", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
		IUTEST_ASSERT_STRIN("Which is: 00000400", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
		IUTEST_ASSERT_STRIN("  Actual: 00.33333", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
		IUTEST_ASSERT_STRIN("Which is: 00000.33", logger.c_str()) << ::iutest::AssertionReturn<int>(1);
#endif

#endif
	}
	printf("*** Successful ***\n");

	return 0;
}
<|endoftext|>


<commit_before><commit_msg>added function collison<commit_after>mains
aflkjsd
askljdf
skldfjk
<|endoftext|>


<commit_before>// Test that out-of-scope local variables are ignored by LSan.
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0:use_stacks=1"
// RUN: %clangxx_lsan %s -o %t
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s
//
// x86 passes parameters through stack that may lead to false negatives
// UNSPPORTED: x86

#include <stdio.h>
#include <stdlib.h>
#include "sanitizer_common/print_address.h"

void **pp;

// Put pointer far enough on the stack that LSan has space to run in without
// overwriting it.
// Hopefully the argument p will be passed on a register, saving us from false
// negatives.
__attribute__((noinline))
void *PutPointerOnStaleStack(void *p) {
  void *locals[2048];
  locals[0] = p;
  pp = &locals[0];
  print_address("Test alloc: ", 1, locals[0]);
  return 0;
}

int main() {
  PutPointerOnStaleStack(malloc(1337));
  return 0;
}

// This must run after LSan, to ensure LSan didn't overwrite the pointer before
// it had a chance to see it. If LSan is invoked with atexit(), this works.
// Otherwise, we need a different method.
__attribute__((destructor))
__attribute__((no_sanitize_address))
void ConfirmPointerHasSurvived() {
  print_address("Value after LSan: ", 1, *pp);
}
// CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]]
// CHECK-sanity: Test alloc: [[ADDR:0x[0-9,a-f]+]]
// CHECK: LeakSanitizer: detected memory leaks
// CHECK: [[ADDR]] (1337 bytes)
// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
// CHECK-sanity: Value after LSan: [[ADDR]]
<commit_msg>[lsan] Fix typo in stale_stack_leak.cc testcase<commit_after>// Test that out-of-scope local variables are ignored by LSan.
// RUN: LSAN_BASE="detect_leaks=1:report_objects=1:use_registers=0:use_stacks=1"
// RUN: %clangxx_lsan %s -o %t
// RUN: LSAN_OPTIONS=$LSAN_BASE not %run %t 2>&1 | FileCheck %s
// RUN: LSAN_OPTIONS=$LSAN_BASE":exitcode=0" %run %t 2>&1 | FileCheck --check-prefix=CHECK-sanity %s
//
// x86 passes parameters through stack that may lead to false negatives
// UNSUPPORTED: x86

#include <stdio.h>
#include <stdlib.h>
#include "sanitizer_common/print_address.h"

void **pp;

// Put pointer far enough on the stack that LSan has space to run in without
// overwriting it.
// Hopefully the argument p will be passed on a register, saving us from false
// negatives.
__attribute__((noinline))
void *PutPointerOnStaleStack(void *p) {
  void *locals[2048];
  locals[0] = p;
  pp = &locals[0];
  print_address("Test alloc: ", 1, locals[0]);
  return 0;
}

int main() {
  PutPointerOnStaleStack(malloc(1337));
  return 0;
}

// This must run after LSan, to ensure LSan didn't overwrite the pointer before
// it had a chance to see it. If LSan is invoked with atexit(), this works.
// Otherwise, we need a different method.
__attribute__((destructor))
__attribute__((no_sanitize_address))
void ConfirmPointerHasSurvived() {
  print_address("Value after LSan: ", 1, *pp);
}
// CHECK: Test alloc: [[ADDR:0x[0-9,a-f]+]]
// CHECK-sanity: Test alloc: [[ADDR:0x[0-9,a-f]+]]
// CHECK: LeakSanitizer: detected memory leaks
// CHECK: [[ADDR]] (1337 bytes)
// CHECK: SUMMARY: {{(Leak|Address)}}Sanitizer:
// CHECK-sanity: Value after LSan: [[ADDR]]
<|endoftext|>


<commit_before>// This file is part of UDPipe <http://github.com/ufal/udpipe/>.
//
// Copyright 2015 Institute of Formal and Applied Linguistics, Faculty of
// Mathematics and Physics, Charles University in Prague, Czech Republic.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <algorithm>

#include "common.h"
#include "model_morphodita_parsito.h"
#include "utils/parse_int.h"

namespace ufal {
namespace udpipe {

tokenizer* model_morphodita_parsito::new_tokenizer(const string& /*options*/) const {
  return new tokenizer_morphodita(this);
}

bool model_morphodita_parsito::tag(sentence& s, const string& /*options*/, string& error) const {
  error.clear();

  tagger_cache* c = tagger_caches.pop();
  if (!c) c = new tagger_cache();

  c->forms.clear();
  for (size_t i = 1; i < s.words.size(); i++)
    c->forms.emplace_back(s.words[i].form.c_str(), s.words[i].form.size());

  tagger->tag(c->forms, c->lemmas);

  for (size_t i = 0; i < c->lemmas.size(); i++) {
    // Lemma
    s.words[i + 1].lemma.assign(c->lemmas[i].lemma);

    // UPOSTag
    size_t start = 0, end = min(c->lemmas[i].tag.find('|'), c->lemmas[i].tag.size());
    s.words[i+1].upostag.assign(c->lemmas[i].tag, start, end - start);

    // XPOSTag
    start = min(end + 1, c->lemmas[i].tag.size());
    end = min(c->lemmas[i].tag.find('|', start), c->lemmas[i].tag.size());
    s.words[i+1].xpostag.assign(c->lemmas[i].tag, start, end - start);

    // Features
    start = min(end + 1, c->lemmas[i].tag.size());
    s.words[i+1].feats.assign(c->lemmas[i].tag, start, c->lemmas[i].tag.size() - start);
  }

  tagger_caches.push(c);
  return true;
}

bool model_morphodita_parsito::parse(sentence& s, const string& options, string& error) const {
  error.clear();

  parser_cache* c = parser_caches.pop();
  if (!c) c = new parser_cache();

  int beam_search = 1;
  if (!named_values::parse(options, c->options, error))
    return false;
  if (c->options.count("beam_search"))
    if (!parse_int(c->options["beam_search"], "beam_search", beam_search, error))
      return false;

  c->tree.clear();
  for (size_t i = 1; i < s.words.size(); i++) {
    c->tree.add_node(s.words[i].form);
    c->tree.nodes.back().lemma.assign(s.words[i].lemma);
    c->tree.nodes.back().upostag.assign(s.words[i].upostag);
    c->tree.nodes.back().xpostag.assign(s.words[i].xpostag);
    c->tree.nodes.back().feats.assign(s.words[i].feats);
    c->tree.nodes.back().deps.assign(s.words[i].deps);
    c->tree.nodes.back().misc.assign(s.words[i].misc);
  }

  parser->parse(c->tree, beam_search);
  for (size_t i = 1; i < s.words.size(); i++)
    s.set_head(i, c->tree.nodes[i].head, c->tree.nodes[i].deprel);

  parser_caches.push(c);
  return true;
}

model* model_morphodita_parsito::load(istream& is) {
  char version;
  if (!is.get(version)) return nullptr;

  unique_ptr<model_morphodita_parsito> m(new model_morphodita_parsito());
  if (!m) return nullptr;

  m->tagger.reset(morphodita::tagger::load(is));
  if (!m->tagger) return nullptr;

  m->parser.reset(parsito::parser::load(is));
  if (!m->parser) return nullptr;

  return m.release();
}

model_morphodita_parsito::tokenizer_morphodita::tokenizer_morphodita(const model_morphodita_parsito* m)
  : tokenizer(m->tagger->new_tokenizer()) {}

bool model_morphodita_parsito::tokenizer_morphodita::read_block(istream& is, string& block) const {
  block.clear();

  string line;
  while (getline(is, line)) {
    block.append(line).push_back('\n');
    if (line.empty()) break;
  }

  return !block.empty();
}

void model_morphodita_parsito::tokenizer_morphodita::set_text(string_piece text, bool make_copy) {
  tokenizer->set_text(text, make_copy);
}

bool model_morphodita_parsito::tokenizer_morphodita::next_sentence(sentence& s, string& error) {
  s.clear();
  error.clear();
  if (tokenizer->next_sentence(&forms, nullptr)) {
    for (size_t i = 0; i < forms.size(); i++) {
      s.add_word(string(forms[i].str, forms[i].len));
      if (i+1 < forms.size() && forms[i+1].str == forms[i].str + forms[i].len)
        s.words.back().misc.assign("SpaceAfter=No");
    }
    return true;
  }

  return false;
}

} // namespace udpipe
} // namespace ufal
<commit_msg>Make beam search of size 5 default.<commit_after>// This file is part of UDPipe <http://github.com/ufal/udpipe/>.
//
// Copyright 2015 Institute of Formal and Applied Linguistics, Faculty of
// Mathematics and Physics, Charles University in Prague, Czech Republic.
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.

#include <algorithm>

#include "common.h"
#include "model_morphodita_parsito.h"
#include "utils/parse_int.h"

namespace ufal {
namespace udpipe {

tokenizer* model_morphodita_parsito::new_tokenizer(const string& /*options*/) const {
  return new tokenizer_morphodita(this);
}

bool model_morphodita_parsito::tag(sentence& s, const string& /*options*/, string& error) const {
  error.clear();

  tagger_cache* c = tagger_caches.pop();
  if (!c) c = new tagger_cache();

  c->forms.clear();
  for (size_t i = 1; i < s.words.size(); i++)
    c->forms.emplace_back(s.words[i].form.c_str(), s.words[i].form.size());

  tagger->tag(c->forms, c->lemmas);

  for (size_t i = 0; i < c->lemmas.size(); i++) {
    // Lemma
    s.words[i + 1].lemma.assign(c->lemmas[i].lemma);

    // UPOSTag
    size_t start = 0, end = min(c->lemmas[i].tag.find('|'), c->lemmas[i].tag.size());
    s.words[i+1].upostag.assign(c->lemmas[i].tag, start, end - start);

    // XPOSTag
    start = min(end + 1, c->lemmas[i].tag.size());
    end = min(c->lemmas[i].tag.find('|', start), c->lemmas[i].tag.size());
    s.words[i+1].xpostag.assign(c->lemmas[i].tag, start, end - start);

    // Features
    start = min(end + 1, c->lemmas[i].tag.size());
    s.words[i+1].feats.assign(c->lemmas[i].tag, start, c->lemmas[i].tag.size() - start);
  }

  tagger_caches.push(c);
  return true;
}

bool model_morphodita_parsito::parse(sentence& s, const string& options, string& error) const {
  error.clear();

  parser_cache* c = parser_caches.pop();
  if (!c) c = new parser_cache();

  int beam_search = 5;
  if (!named_values::parse(options, c->options, error))
    return false;
  if (c->options.count("beam_search"))
    if (!parse_int(c->options["beam_search"], "beam_search", beam_search, error))
      return false;

  c->tree.clear();
  for (size_t i = 1; i < s.words.size(); i++) {
    c->tree.add_node(s.words[i].form);
    c->tree.nodes.back().lemma.assign(s.words[i].lemma);
    c->tree.nodes.back().upostag.assign(s.words[i].upostag);
    c->tree.nodes.back().xpostag.assign(s.words[i].xpostag);
    c->tree.nodes.back().feats.assign(s.words[i].feats);
    c->tree.nodes.back().deps.assign(s.words[i].deps);
    c->tree.nodes.back().misc.assign(s.words[i].misc);
  }

  parser->parse(c->tree, beam_search);
  for (size_t i = 1; i < s.words.size(); i++)
    s.set_head(i, c->tree.nodes[i].head, c->tree.nodes[i].deprel);

  parser_caches.push(c);
  return true;
}

model* model_morphodita_parsito::load(istream& is) {
  char version;
  if (!is.get(version)) return nullptr;

  unique_ptr<model_morphodita_parsito> m(new model_morphodita_parsito());
  if (!m) return nullptr;

  m->tagger.reset(morphodita::tagger::load(is));
  if (!m->tagger) return nullptr;

  m->parser.reset(parsito::parser::load(is));
  if (!m->parser) return nullptr;

  return m.release();
}

model_morphodita_parsito::tokenizer_morphodita::tokenizer_morphodita(const model_morphodita_parsito* m)
  : tokenizer(m->tagger->new_tokenizer()) {}

bool model_morphodita_parsito::tokenizer_morphodita::read_block(istream& is, string& block) const {
  block.clear();

  string line;
  while (getline(is, line)) {
    block.append(line).push_back('\n');
    if (line.empty()) break;
  }

  return !block.empty();
}

void model_morphodita_parsito::tokenizer_morphodita::set_text(string_piece text, bool make_copy) {
  tokenizer->set_text(text, make_copy);
}

bool model_morphodita_parsito::tokenizer_morphodita::next_sentence(sentence& s, string& error) {
  s.clear();
  error.clear();
  if (tokenizer->next_sentence(&forms, nullptr)) {
    for (size_t i = 0; i < forms.size(); i++) {
      s.add_word(string(forms[i].str, forms[i].len));
      if (i+1 < forms.size() && forms[i+1].str == forms[i].str + forms[i].len)
        s.words.back().misc.assign("SpaceAfter=No");
    }
    return true;
  }

  return false;
}

} // namespace udpipe
} // namespace ufal
<|endoftext|>


<commit_before>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 Module:       FGSwitch.cpp
 Author:       Jon S. Berndt
 Date started: 4/2000

 ------------- Copyright (C) 2000 -------------

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU Lesser General Public License can also be found on
 the world wide web at http://www.gnu.org.

FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------

HISTORY
--------------------------------------------------------------------------------

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES,  and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

The switch component is defined as follows (see the API documentation for more
information):

@code
<switch name="switch1">
  <default value="{property|value}"/>
  <test logic="{AND|OR}" value="{property|value}">
    {property} {conditional} {property|value}
    <test logic="{AND|OR}">
      {property} {conditional} {property|value}
      ...
    </test>
    ...
  </test>
  <test logic="{AND|OR}" value="{property|value}">
    {property} {conditional} {property|value}
    ...
  </test>
  ...
</switch>
@endcode

Also, see the header file (FGSwitch.h) for further details.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include "FGSwitch.h"
#include <iostream>

using namespace std;

namespace JSBSim {

static const char *IdSrc = "$Id: FGSwitch.cpp,v 1.25 2012/12/02 12:59:19 bcoconni Exp $";
static const char *IdHdr = ID_SWITCH;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

FGSwitch::FGSwitch(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
{
  string value;
  struct test *current_test;
  Element *test_element;

  FGFCSComponent::bind(); // Bind() this component here in case it is used
                          // in its own definition for a sample-and-hold

  int num = element->GetNumElements("test");
  if (element->FindElement("default")) num++;
  tests.reserve(num);

  test_element = element->FindElement("default");
  if (test_element) {
    current_test = new struct test;
    current_test->condition = 0;
    value = test_element->GetAttributeValue("value");
    current_test->setTestValue(value, Name, PropertyManager);
    current_test->Default = true;
    tests.push_back(current_test);
  }

  test_element = element->FindElement("test");
  while (test_element) {
    current_test = new struct test;
    current_test->condition = new FGCondition(test_element, PropertyManager);
    value = test_element->GetAttributeValue("value");
    current_test->setTestValue(value, Name, PropertyManager);
    tests.push_back(current_test);
    test_element = element->FindNextElement("test");
  }

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGSwitch::~FGSwitch()
{
  for (unsigned int i=0; i<tests.size(); i++) {
    delete tests[i]->condition;
    delete tests[i]->OutputProp;
    delete tests[i];
  }

  Debug(1);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool FGSwitch::Run(void )
{
  bool pass = false;
  double default_output=0.0;

  for (unsigned int i=0; i<tests.size(); i++) {
    if (tests[i]->Default) {
      default_output = tests[i]->GetValue();
    } else {
      pass = tests[i]->condition->Evaluate();
    }

    if (pass) {
      Output = tests[i]->GetValue();
      break;
    }
  }

  if (!pass) Output = default_output;

  if (delay != 0) Delay();
  Clip();
  if (IsOutput) SetOutput();

  return true;
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//    The bitmasked value choices are as follows:
//    unset: In this case (the default) JSBSim would only print
//       out the normally expected messages, essentially echoing
//       the config files as they are read. If the environment
//       variable is not set, debug_lvl is set to 1 internally
//    0: This requests JSBSim not to output any messages
//       whatsoever.
//    1: This value explicity requests the normal JSBSim
//       startup messages
//    2: This value asks for a message to be printed out when
//       a class is instantiated
//    4: When this value is set, a message is displayed when a
//       FGModel object executes its Run() method
//    8: When this value is set, various runtime state variables
//       are printed out periodically
//    16: When set various parameters are sanity checked and
//       a message is printed out when they go out of bounds

void FGSwitch::Debug(int from)
{
  string comp, scratch;
  string indent = "        ";
  //bool first = false;

  if (debug_lvl <= 0) return;

  if (debug_lvl & 1) { // Standard console startup message output
    if (from == 0) { // Constructor
      for (unsigned int i=0; i<tests.size(); i++) {
        if (tests[i]->Default) {
          if (tests[i]->OutputProp == 0) {
            cout << "      Switch default value is: " << tests[i]->OutputVal;
          } else {
            cout << "      Switch default value is: " << tests[i]->OutputProp->GetName();
          }
        } else {
          if (tests[i]->OutputProp == 0) {
            cout << "      Switch takes test " << i << " value (" << tests[i]->OutputVal << ")" << endl;
          } else {
            cout << "      Switch takes test " << i << " value (" << tests[i]->OutputProp->GetName() << ")" << endl;
          }
          tests[i]->condition->PrintCondition("      ");
        }
        cout << endl;
      }
      if (IsOutput) {
        for (unsigned int i=0; i<OutputNodes.size(); i++)
          cout << "      OUTPUT: " << OutputNodes[i]->getName() << endl;
      }
    }
  }
  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
    if (from == 0) cout << "Instantiated: FGSwitch" << endl;
    if (from == 1) cout << "Destroyed:    FGSwitch" << endl;
  }
  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
  }
  if (debug_lvl & 8 ) { // Runtime state variables
  }
  if (debug_lvl & 16) { // Sanity checking
  }
  if (debug_lvl & 64) {
    if (from == 0) { // Constructor
      cout << IdSrc << endl;
      cout << IdHdr << endl;
    }
  }
}

} //namespace JSBSim

<commit_msg>Added the delay code to the switch<commit_after>/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

 Module:       FGSwitch.cpp
 Author:       Jon S. Berndt
 Date started: 4/2000

 ------------- Copyright (C) 2000 -------------

 This program is free software; you can redistribute it and/or modify it under
 the terms of the GNU Lesser General Public License as published by the Free Software
 Foundation; either version 2 of the License, or (at your option) any later
 version.

 This program 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 program; if not, write to the Free Software Foundation, Inc., 59 Temple
 Place - Suite 330, Boston, MA  02111-1307, USA.

 Further information about the GNU Lesser General Public License can also be found on
 the world wide web at http://www.gnu.org.

FUNCTIONAL DESCRIPTION
--------------------------------------------------------------------------------

HISTORY
--------------------------------------------------------------------------------

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
COMMENTS, REFERENCES,  and NOTES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

The switch component is defined as follows (see the API documentation for more
information):

@code
<switch name="switch1">
  <default value="{property|value}"/>
  <test logic="{AND|OR}" value="{property|value}">
    {property} {conditional} {property|value}
    <test logic="{AND|OR}">
      {property} {conditional} {property|value}
      ...
    </test>
    ...
  </test>
  <test logic="{AND|OR}" value="{property|value}">
    {property} {conditional} {property|value}
    ...
  </test>
  ...
</switch>
@endcode

Also, see the header file (FGSwitch.h) for further details.

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
INCLUDES
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

#include "FGSwitch.h"
#include <iostream>

using namespace std;

namespace JSBSim {

static const char *IdSrc = "$Id: FGSwitch.cpp,v 1.26 2013/11/17 05:11:03 jberndt Exp $";
static const char *IdHdr = ID_SWITCH;

/*%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
CLASS IMPLEMENTATION
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%*/

FGSwitch::FGSwitch(FGFCS* fcs, Element* element) : FGFCSComponent(fcs, element)
{
  string value;
  struct test *current_test;
  Element *test_element;

  FGFCSComponent::bind(); // Bind() this component here in case it is used
                          // in its own definition for a sample-and-hold

  int num = element->GetNumElements("test");
  if (element->FindElement("default")) num++;
  tests.reserve(num);

  test_element = element->FindElement("default");
  if (test_element) {
    current_test = new struct test;
    current_test->condition = 0;
    value = test_element->GetAttributeValue("value");
    current_test->setTestValue(value, Name, PropertyManager);
    current_test->Default = true;
    if (delay > 0 && is_number(value)) {        // If there is a delay, initialize the
      for (int i=0; i<delay-1; i++) {           // delay buffer to the default value
        output_array[i] = atof(value.c_str());  // for the switch if that value is a number.
      }
    }
    tests.push_back(current_test);
  }

  test_element = element->FindElement("test");
  while (test_element) {
    current_test = new struct test;
    current_test->condition = new FGCondition(test_element, PropertyManager);
    value = test_element->GetAttributeValue("value");
    current_test->setTestValue(value, Name, PropertyManager);
    tests.push_back(current_test);
    test_element = element->FindNextElement("test");
  }

  Debug(0);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

FGSwitch::~FGSwitch()
{
  for (unsigned int i=0; i<tests.size(); i++) {
    delete tests[i]->condition;
    delete tests[i]->OutputProp;
    delete tests[i];
  }

  Debug(1);
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

bool FGSwitch::Run(void )
{
  bool pass = false;
  double default_output=0.0;

  for (unsigned int i=0; i<tests.size(); i++) {
    if (tests[i]->Default) {
      default_output = tests[i]->GetValue();
    } else {
      pass = tests[i]->condition->Evaluate();
    }

    if (pass) {
      Output = tests[i]->GetValue();
      break;
    }
  }

  if (!pass) Output = default_output;

  if (delay != 0) Delay();
  Clip();
  if (IsOutput) SetOutput();

  return true;
}

//%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
//    The bitmasked value choices are as follows:
//    unset: In this case (the default) JSBSim would only print
//       out the normally expected messages, essentially echoing
//       the config files as they are read. If the environment
//       variable is not set, debug_lvl is set to 1 internally
//    0: This requests JSBSim not to output any messages
//       whatsoever.
//    1: This value explicity requests the normal JSBSim
//       startup messages
//    2: This value asks for a message to be printed out when
//       a class is instantiated
//    4: When this value is set, a message is displayed when a
//       FGModel object executes its Run() method
//    8: When this value is set, various runtime state variables
//       are printed out periodically
//    16: When set various parameters are sanity checked and
//       a message is printed out when they go out of bounds

void FGSwitch::Debug(int from)
{
  string comp, scratch;
  string indent = "        ";
  //bool first = false;

  if (debug_lvl <= 0) return;

  if (debug_lvl & 1) { // Standard console startup message output
    if (from == 0) { // Constructor
      for (unsigned int i=0; i<tests.size(); i++) {
        if (tests[i]->Default) {
          if (tests[i]->OutputProp == 0) {
            cout << "      Switch default value is: " << tests[i]->OutputVal;
          } else {
            cout << "      Switch default value is: " << tests[i]->OutputProp->GetName();
          }
        } else {
          if (tests[i]->OutputProp == 0) {
            cout << "      Switch takes test " << i << " value (" << tests[i]->OutputVal << ")" << endl;
          } else {
            cout << "      Switch takes test " << i << " value (" << tests[i]->OutputProp->GetName() << ")" << endl;
          }
          tests[i]->condition->PrintCondition("      ");
        }
        cout << endl;
      }
      if (IsOutput) {
        for (unsigned int i=0; i<OutputNodes.size(); i++)
          cout << "      OUTPUT: " << OutputNodes[i]->getName() << endl;
      }
    }
  }
  if (debug_lvl & 2 ) { // Instantiation/Destruction notification
    if (from == 0) cout << "Instantiated: FGSwitch" << endl;
    if (from == 1) cout << "Destroyed:    FGSwitch" << endl;
  }
  if (debug_lvl & 4 ) { // Run() method entry print for FGModel-derived objects
  }
  if (debug_lvl & 8 ) { // Runtime state variables
  }
  if (debug_lvl & 16) { // Sanity checking
  }
  if (debug_lvl & 64) {
    if (from == 0) { // Constructor
      cout << IdSrc << endl;
      cout << IdHdr << endl;
    }
  }
}

} //namespace JSBSim

<|endoftext|>


<commit_before>/*=========================================================================

  Program:   Visualization Library
  Module:    Object.hh
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
//
// Common abstract class for Visualization Library.  Maintains debug
// flag, reference counting, modified time, and other common
// functions/parameters.
//

#ifndef __vlObject_hh
#define __vlObject_hh

#include <iostream.h>
#include "TimeSt.hh"
#include "SetGet.hh"
#include "Indent.hh"
#include "PrintLst.hh"

//
// Common #defines / parameters
//

//
// Class definition
//

class vlObject
{
public:
  vlObject();
  virtual ~vlObject();
  void Register(void* p);
  void UnRegister(void* p);
  int  GetRefCount() {return this->RefCount;};
  void DebugOn();
  void DebugOff();
  virtual unsigned long int GetMTime() {return this->MTime.GetMTime();};
  int GetDebug();
  void Modified() {MTime.Modified();};
  virtual char *GetClassName() {return "vlObject";};

  void Print(ostream& os);
  virtual void PrintHeader(ostream& os, vlIndent indent);
  virtual void PrintSelf(ostream& os, vlIndent indent);
  int ShouldIPrint(char *a) { return this->PrintList.ShouldIPrint(a);};
  virtual void PrintTrailer(ostream& os, vlIndent indent);
  void PrintWatchOn() {this->PrintList.ActiveOn();};
  void PrintWatchOff() {this->PrintList.ActiveOff();};

protected:
  int Debug;       // Enable debug messages
  vlTimeStamp MTime; // Keep track of modification time

private:
  int RefCount;    // Number of uses of this object by other objects
  vlPrintList PrintList;

friend ostream& operator<<(ostream& os, vlObject& o) {o.Print(os);return os;}
};

#endif

<commit_msg>Made the ostream friend function not inline<commit_after>/*=========================================================================

  Program:   Visualization Library
  Module:    Object.hh
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

This file is part of the Visualization Library. No part of this file or its
contents may be copied, reproduced or altered in any way without the express
written consent of the authors.

Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen 1993, 1994

=========================================================================*/
//
// Common abstract class for Visualization Library.  Maintains debug
// flag, reference counting, modified time, and other common
// functions/parameters.
//

#ifndef __vlObject_hh
#define __vlObject_hh

#include <iostream.h>
#include "TimeSt.hh"
#include "SetGet.hh"
#include "Indent.hh"
#include "PrintLst.hh"

//
// Common #defines / parameters
//

//
// Class definition
//

class vlObject
{
public:
  vlObject();
  virtual ~vlObject();
  void Register(void* p);
  void UnRegister(void* p);
  int  GetRefCount() {return this->RefCount;};
  void DebugOn();
  void DebugOff();
  virtual unsigned long int GetMTime() {return this->MTime.GetMTime();};
  int GetDebug();
  void Modified() {MTime.Modified();};
  virtual char *GetClassName() {return "vlObject";};

  void Print(ostream& os);
  virtual void PrintHeader(ostream& os, vlIndent indent);
  virtual void PrintSelf(ostream& os, vlIndent indent);
  int ShouldIPrint(char *a) { return this->PrintList.ShouldIPrint(a);};
  virtual void PrintTrailer(ostream& os, vlIndent indent);
  void PrintWatchOn() {this->PrintList.ActiveOn();};
  void PrintWatchOff() {this->PrintList.ActiveOff();};

protected:
  int Debug;       // Enable debug messages
  vlTimeStamp MTime; // Keep track of modification time

private:
  int RefCount;    // Number of uses of this object by other objects
  vlPrintList PrintList;

  friend ostream& operator<<(ostream& os, vlObject& o);
};

#endif

<|endoftext|>


<commit_before><commit_msg>imgproc: use target type for calculations<commit_after><|endoftext|>


<commit_before>#pragma once

#include "../widget.hpp"
#include "../container.hpp"

#include "../base/oriented_widget.hpp"

namespace morda{

/**
 * @brief Scrollable list widget.
 * This is a base class for vertical and horizontal lists.
 */
class list_widget :
		// NOTE: order of virtual public and private declarations here matters for clang due to some bug,
		//       see http://stackoverflow.com/questions/42427145/clang-cannot-cast-to-private-base-while-there-is-a-public-virtual-inheritance
		virtual public widget,
		private container,
		protected oriented_widget
{
	// index of the first item added to container as child
	size_t added_index = size_t(-1);

	size_t pos_index = 0; // index of the first visible item
	real pos_offset = real(0); // offset in pixels of the first visible item

	size_t num_tail_items = 0; // Zero means that number of tail items has to be recomputed
	size_t first_tail_item_index = 0;
	real first_tail_item_offset = real(0);

protected:
	list_widget(std::shared_ptr<morda::context> c, const puu::forest& desc, bool vertical);
public:
	list_widget(const list_widget&) = delete;
	list_widget& operator=(const list_widget&) = delete;

	/**
	 * @brief list items provider.
	 * User should subclass this class to provide items to the list.
	 */
	class provider : virtual public utki::shared{
		friend class list_widget;

		list_widget* parent_list = nullptr;
	protected:
		provider(){}
	public:
		/**
		 * @brief Get parent list widget.
		 * @return list widget which owns the provider, in case the provider is set to some list widget.
		 * @return nullptr in case the provider is not set to any list widget.
		 */
		list_widget* get_list()noexcept{
			return this->parent_list;
		}

		/**
		 * @brief Get total number of items in the list.
		 * @return Number of items in the list.
		 */
		virtual size_t count()const noexcept = 0;

		/**
		 * @brief Get widget for item.
		 * @param index - index of item to get widget for.
		 * @return widget for the requested item.
		 */
		virtual std::shared_ptr<widget> get_widget(size_t index) = 0;

		/**
		 * @brief Recycle widget of item.
		 * @param index - index of item to recycle widget of.
		 * @param w - widget to recycle.
		 */
		virtual void recycle(size_t index, std::shared_ptr<widget> w){}

		void notify_data_set_change();
	};

	void set_provider(std::shared_ptr<provider> item_provider = nullptr);

	void lay_out()override;

	morda::vector2 measure(const morda::vector2& quotum) const override;

	/**
	 * @brief Set scroll position as factor from [0:1].
	 * @param factor - factor of the scroll position to set.
	 */
	void set_scroll_factor(real factor);

	/**
	 * @brief Get scroll factor.
	 * @return Current scroll position as factor from [0:1].
	 */
	real get_scroll_factor()const noexcept;

	/**
	 * @brief Scroll the list by given number of pixels.
	 * @param delta - number of pixels to scroll, can be positive or negative.
	 */
	void scroll_by(real delta);

	/**
	 * @brief Data set changed signal.
	 * Emitted when list widget contents have actually been updated due to change in provider's model data set.
	 */
	std::function<void(list_widget&)> data_set_change_handler;

private:
	std::shared_ptr<provider> item_provider;

	void update_children_list();

	// returns true if it was the last visible widget
	bool arrange_widget(
			std::shared_ptr<widget>& w,
			real& pos,
			bool add,
			size_t index,
			widget_list::const_iterator& insertBefore
		);

	void update_tail_items_info();

	void handle_data_set_changed();
};

/**
 * @brief Horizontal list widget.
 * Panorama list widget.
 * From GUI script it can be instantiated as "pan_list".
 */
class pan_list : public list_widget{
public:
	pan_list(std::shared_ptr<morda::context> c, const puu::forest& desc) :
			widget(std::move(c), desc),
			list_widget(this->context, desc, false)
	{}

	pan_list(const pan_list&) = delete;
	pan_list& operator=(const pan_list&) = delete;
};

/**
 * @brief Vertical list widget.
 * From GUI script it can be instantiated as "list".
 */
class list : public list_widget{
public:
	list(std::shared_ptr<morda::context> c, const puu::forest& desc) :
			widget(std::move(c), desc),
			list_widget(this->context, desc, true)
	{}

	list(const list&) = delete;
	list& operator=(const list&) = delete;
};

}
<commit_msg>add list::get_pos_index()/get_pos_offset()<commit_after>#pragma once

#include "../widget.hpp"
#include "../container.hpp"

#include "../base/oriented_widget.hpp"

namespace morda{

/**
 * @brief Scrollable list widget.
 * This is a base class for vertical and horizontal lists.
 */
class list_widget :
		// NOTE: order of virtual public and private declarations here matters for clang due to some bug,
		//       see http://stackoverflow.com/questions/42427145/clang-cannot-cast-to-private-base-while-there-is-a-public-virtual-inheritance
		virtual public widget,
		private container,
		protected oriented_widget
{
	// index of the first item added to container as child
	size_t added_index = size_t(-1);

	size_t pos_index = 0; // index of the first visible item

	// offset in pixels of the first visible item.
	// the value is positive, tough the item is biased towards negative coordinate values.
	real pos_offset = real(0);

	size_t num_tail_items = 0; // Zero means that number of tail items has to be recomputed
	size_t first_tail_item_index = 0;
	real first_tail_item_offset = real(0);

protected:
	list_widget(std::shared_ptr<morda::context> c, const puu::forest& desc, bool vertical);
public:
	list_widget(const list_widget&) = delete;
	list_widget& operator=(const list_widget&) = delete;

	/**
	 * @brief list items provider.
	 * User should subclass this class to provide items to the list.
	 */
	class provider : virtual public utki::shared{
		friend class list_widget;

		list_widget* parent_list = nullptr;
	protected:
		provider(){}
	public:
		/**
		 * @brief Get parent list widget.
		 * @return list widget which owns the provider, in case the provider is set to some list widget.
		 * @return nullptr in case the provider is not set to any list widget.
		 */
		list_widget* get_list()noexcept{
			return this->parent_list;
		}

		/**
		 * @brief Get total number of items in the list.
		 * @return Number of items in the list.
		 */
		virtual size_t count()const noexcept = 0;

		/**
		 * @brief Get widget for item.
		 * @param index - index of item to get widget for.
		 * @return widget for the requested item.
		 */
		virtual std::shared_ptr<widget> get_widget(size_t index) = 0;

		/**
		 * @brief Recycle widget of item.
		 * @param index - index of item to recycle widget of.
		 * @param w - widget to recycle.
		 */
		virtual void recycle(size_t index, std::shared_ptr<widget> w){}

		void notify_data_set_change();
	};

	void set_provider(std::shared_ptr<provider> item_provider = nullptr);

	void lay_out()override;

	morda::vector2 measure(const morda::vector2& quotum) const override;

	/**
	 * @brief Set scroll position as factor from [0:1].
	 * @param factor - factor of the scroll position to set.
	 */
	void set_scroll_factor(real factor);

	/**
	 * @brief Get scroll factor.
	 * @return Current scroll position as factor from [0:1].
	 */
	real get_scroll_factor()const noexcept;

	/**
	 * @brief Get index of the first visible item.
	 * @return index of the first visible item.
	 */
	size_t get_pos_index()const noexcept{
		return this->pos_index;
	}

	/**
	 * @brief Get offset of the first visible item.
	 * The value is positive, though the item coordinate is <= 0.
	 * @return offset in pixels of the first visible item.
	 */
	real get_pos_offset()const noexcept{
		return this->pos_offset;
	}

	/**
	 * @brief Scroll the list by given number of pixels.
	 * @param delta - number of pixels to scroll, can be positive or negative.
	 */
	void scroll_by(real delta);

	/**
	 * @brief Data set changed signal.
	 * Emitted when list widget contents have actually been updated due to change in provider's model data set.
	 */
	std::function<void(list_widget&)> data_set_change_handler;

private:
	std::shared_ptr<provider> item_provider;

	void update_children_list();

	// returns true if it was the last visible widget
	bool arrange_widget(
			std::shared_ptr<widget>& w,
			real& pos,
			bool add,
			size_t index,
			widget_list::const_iterator& insertBefore
		);

	void update_tail_items_info();

	void handle_data_set_changed();
};

/**
 * @brief Horizontal list widget.
 * Panorama list widget.
 * From GUI script it can be instantiated as "pan_list".
 */
class pan_list : public list_widget{
public:
	pan_list(std::shared_ptr<morda::context> c, const puu::forest& desc) :
			widget(std::move(c), desc),
			list_widget(this->context, desc, false)
	{}

	pan_list(const pan_list&) = delete;
	pan_list& operator=(const pan_list&) = delete;
};

/**
 * @brief Vertical list widget.
 * From GUI script it can be instantiated as "list".
 */
class list : public list_widget{
public:
	list(std::shared_ptr<morda::context> c, const puu::forest& desc) :
			widget(std::move(c), desc),
			list_widget(this->context, desc, true)
	{}

	list(const list&) = delete;
	list& operator=(const list&) = delete;
};

}
<|endoftext|>