<commit_before>
#include <Engine/Assets/AssimpHandleDataLoader.hpp>

#include <set>
#include <Core/Log/Log.hpp>
#include <Core/Containers/AlignedStdVector.hpp>
#include <Engine/Assets/AssimpWrapper.hpp>
#include <Engine/Assets/HandleData.hpp>

namespace Ra {
namespace Asset {

/// CONSTRUCTOR
AssimpHandleDataLoader::AssimpHandleDataLoader( const bool VERBOSE_MODE ) : DataLoader< HandleData > ( VERBOSE_MODE ) { }

/// DESTRUCTOR
AssimpHandleDataLoader::~AssimpHandleDataLoader() { }

/// LOAD
void AssimpHandleDataLoader::loadData( const aiScene* scene, std::vector< std::unique_ptr< HandleData > >& data ) {
    data.clear();

    if( scene == nullptr ) {
        LOG( logDEBUG ) << "AssimpHandleDataLoader : scene is nullptr.";
        return;
    }

    if( !sceneHasHandle( scene ) ) {
        LOG( logDEBUG ) << "AssimpHandleDataLoader : scene has no handle.";
        return;
    }

    if( m_verbose ) {
        LOG( logDEBUG ) << "File contains handle.";
        LOG( logDEBUG ) << "Handle Loading begin...";
    }

    loadHandleData( scene, data );

    if( m_verbose ) {
        LOG( logDEBUG ) << "Handle Loading end.\n";
    }
}

/// QUERY
bool AssimpHandleDataLoader::sceneHasHandle( const aiScene* scene ) const {
    return ( sceneHandleSize( scene ) != 0 );
}

uint AssimpHandleDataLoader::sceneHandleSize( const aiScene* scene ) const {
    const uint size = scene->mNumMeshes;
    uint handle_size = 0;
    if( scene != 0 ) {
        for( uint i = 0; i < size; ++i ) {
            aiMesh* mesh = scene->mMeshes[i];
            if( mesh->HasBones() ) {
                ++handle_size;
            }
        }
    }
    return handle_size;
}

/// LOAD
void AssimpHandleDataLoader::loadHandleData( const aiScene* scene, std::vector< std::unique_ptr< HandleData > >& data ) const {
    const uint meshSize = scene->mNumMeshes;
    std::map< uint, uint > indexTable;
    for( uint n = 0; n < meshSize; ++n ) {
        aiMesh* mesh = scene->mMeshes[n];
        if( mesh->HasBones() ) {
            HandleData* handle = new HandleData();
            fetchName( *mesh, *handle );
            fetchType( *mesh, *handle );
            loadHandleComponentData( scene, mesh, handle );
            loadHandleTopologyData( scene, handle );
            fetchVertexSize( *handle );
            data.push_back( std::unique_ptr< HandleData >( handle ) );
            indexTable[n] = data.size() - 1;
            if( m_verbose ) {
                handle->displayInfo();
            }
        }
    }
    loadHandleFrame( scene->mRootNode, Core::Transform::Identity(), indexTable, data );
}

void AssimpHandleDataLoader::loadHandleComponentData( const aiScene* scene, const aiMesh* mesh, HandleData* data ) const {
    const uint size = mesh->mNumBones;
    Core::AlignedStdVector< HandleComponentData > component( size, HandleComponentData() );
    std::set< std::string > name;
    std::map< std::string , uint > nameTable;
    // Load the meaningful handles

    for( uint i = 0; i < size; ++i ) {
        aiBone* bone = mesh->mBones[i];
        loadHandleComponentData( scene, bone, component[i] );
        nameTable[component[i].m_name] = i;
        name.insert( component[i].m_name );
    }
    // Load the meaningless handles (if any)
    for( uint i = 0; i < size; ++i ) {
        aiNode* node = scene->mRootNode->FindNode( mesh->mBones[i]->mName );
        const uint children_size = node->mNumChildren;
        if( ( children_size == 0 ) && ( component[i].m_weight.size() != 0 ) ) {
            data->m_endNode = true;
        } else {
            if( children_size == 1 ) {
                aiNode* child = node->mChildren[0];
                auto it = name.find( assimpToCore( child->mName ) );
                if( it == name.end() ) {
                    component.push_back( HandleComponentData() );
                    const uint j = component.size() - 1;
                    loadHandleComponentData( child, component[j] );
                    nameTable[component[j].m_name] = j;
                }
            }
        }
    }
    data->setComponents( component );
    data->setNameTable( nameTable );
}

void AssimpHandleDataLoader::loadHandleComponentData( const aiScene* scene, const aiBone* bone, HandleComponentData& data ) const {
    data.m_name  = assimpToCore( bone->mName );
    //data.m_frame = assimpToCore( bone->mOffsetMatrix );
    data.m_frame.setIdentity();
    const uint size = bone->mNumWeights;
    for( uint j = 0; j < size; ++j ) {
        std::pair< uint, Scalar > weight( bone->mWeights[j].mVertexId, bone->mWeights[j].mWeight );
        data.m_weight.push_back( weight );
    }
    aiNode* node = scene->mRootNode->FindNode( bone->mName );
    while( node != nullptr ) {
        data.m_frame = assimpToCore( node->mTransformation ) * data.m_frame;
        node = node->mParent;
    }
}

void AssimpHandleDataLoader::loadHandleComponentData( const aiNode* node, HandleComponentData& data ) const {
    data.m_name  = assimpToCore( node->mName );
    //data.m_frame = assimpToCore( node->mTransformation );
    data.m_frame.setIdentity();
    const aiNode* tmpNode = node;
    while( tmpNode != nullptr ) {
        data.m_frame = assimpToCore( tmpNode->mTransformation ) * data.m_frame;
        tmpNode = tmpNode->mParent;
    }
}

void AssimpHandleDataLoader::loadHandleTopologyData( const aiScene* scene, HandleData* data ) const {
    const uint size = data->getComponentDataSize();
    std::vector< std::pair< std::string, std::string > > edgeList;
    for( uint i = 0; i < size; ++i ) {
        HandleComponentData& component = data->m_component[i];
        aiNode* node = scene->mRootNode->FindNode( aiString( component.m_name ) );
        const uint children_size = node->mNumChildren;
        for( uint j = 0; j < children_size; ++j ) {
            std::string childName = assimpToCore( node->mChildren[j]->mName );
            if( data->getIndexOf( childName ) != -1 ) {
                edgeList.push_back( std::pair< std::string, std::string >( component.m_name, childName ) );
            }
        }
    }
    Core::AlignedStdVector< Core::Vector2i > edge;
    edge.reserve( edgeList.size() );
    for( const auto& it : edgeList ) {
        Core::Vector2i e;
        e[0] = data->getIndexOf( it.first  );
        e[1] = data->getIndexOf( it.second );
        edge.push_back( e );
    }
    data->setEdges( edge );
}

void AssimpHandleDataLoader::loadHandleFrame( const aiNode*                                 node,
                                              const Core::Transform&                        parentFrame,
                                              const std::map< uint, uint >&                 indexTable,
                                              std::vector< std::unique_ptr< HandleData > >& data ) const {
    const uint child_size = node->mNumChildren;
    const uint mesh_size  = node->mNumMeshes;
    if( ( child_size == 0 ) && ( mesh_size == 0 ) ) {
        return;
    }
    Core::Transform frame = parentFrame * assimpToCore( node->mTransformation );
    for( uint i = 0; i < mesh_size; ++i ) {
        const uint ID = node->mMeshes[i];
        auto it = indexTable.find( ID );
        if( it != indexTable.end() ) {
            data[it->second]->setFrame( frame );
        }
    }
    for( uint i = 0; i < child_size; ++i ) {
        loadHandleFrame( node->mChildren[i], frame, indexTable, data );
    }
}

/// NAME
void AssimpHandleDataLoader::fetchName( const aiMesh& mesh, HandleData& data ) const {
    data.setName( assimpToCore( mesh.mName ) );
}

/// TYPE
void AssimpHandleDataLoader::fetchType( const aiMesh& mesh, HandleData& data ) const {
    data.setType( HandleData::SKELETON );
    // TO DO: is there a way to know the right type of handle?
}

/// VERTEX SIZE
void AssimpHandleDataLoader::fetchVertexSize( HandleData& data ) const {
    const uint componentSize = data.getComponentDataSize();
    data.m_vertexSize = 0;
    for( uint i = 0; i < componentSize; ++i ) {
        for( const auto& item : data.m_component[i].m_weight ) {
            data.m_vertexSize = std::max( data.m_vertexSize, ( item.first + 1 ) );
        }
    }
}

} // namespace Asset
} // namespace Ra
<commit_msg>Scaling factor taken out from the bones during loading<commit_after>
#include <Engine/Assets/AssimpHandleDataLoader.hpp>

#include <set>
#include <Core/Log/Log.hpp>
#include <Core/Containers/AlignedStdVector.hpp>
#include <Engine/Assets/AssimpWrapper.hpp>
#include <Engine/Assets/HandleData.hpp>

namespace Ra {
namespace Asset {

/// CONSTRUCTOR
AssimpHandleDataLoader::AssimpHandleDataLoader( const bool VERBOSE_MODE ) : DataLoader< HandleData > ( VERBOSE_MODE ) { }

/// DESTRUCTOR
AssimpHandleDataLoader::~AssimpHandleDataLoader() { }

/// LOAD
void AssimpHandleDataLoader::loadData( const aiScene* scene, std::vector< std::unique_ptr< HandleData > >& data ) {
    data.clear();

    if( scene == nullptr ) {
        LOG( logDEBUG ) << "AssimpHandleDataLoader : scene is nullptr.";
        return;
    }

    if( !sceneHasHandle( scene ) ) {
        LOG( logDEBUG ) << "AssimpHandleDataLoader : scene has no handle.";
        return;
    }

    if( m_verbose ) {
        LOG( logDEBUG ) << "File contains handle.";
        LOG( logDEBUG ) << "Handle Loading begin...";
    }

    loadHandleData( scene, data );

    if( m_verbose ) {
        LOG( logDEBUG ) << "Handle Loading end.\n";
    }
}

/// QUERY
bool AssimpHandleDataLoader::sceneHasHandle( const aiScene* scene ) const {
    return ( sceneHandleSize( scene ) != 0 );
}

uint AssimpHandleDataLoader::sceneHandleSize( const aiScene* scene ) const {
    const uint size = scene->mNumMeshes;
    uint handle_size = 0;
    if( scene != 0 ) {
        for( uint i = 0; i < size; ++i ) {
            aiMesh* mesh = scene->mMeshes[i];
            if( mesh->HasBones() ) {
                ++handle_size;
            }
        }
    }
    return handle_size;
}

/// LOAD
void AssimpHandleDataLoader::loadHandleData( const aiScene* scene, std::vector< std::unique_ptr< HandleData > >& data ) const {
    const uint meshSize = scene->mNumMeshes;
    std::map< uint, uint > indexTable;
    for( uint n = 0; n < meshSize; ++n ) {
        aiMesh* mesh = scene->mMeshes[n];
        if( mesh->HasBones() ) {
            HandleData* handle = new HandleData();
            fetchName( *mesh, *handle );
            fetchType( *mesh, *handle );
            loadHandleComponentData( scene, mesh, handle );
            loadHandleTopologyData( scene, handle );
            fetchVertexSize( *handle );

            for( auto& component : handle->m_component ) {
                Core::Transform& frame = component.m_frame;
                Core::Vector3 t = frame.translation();
                Core::Matrix3 R = frame.rotation();
                frame.setIdentity();
                frame.translation() = t;
                frame.linear() = R;
            }

            data.push_back( std::unique_ptr< HandleData >( handle ) );
            indexTable[n] = data.size() - 1;
            if( m_verbose ) {
                handle->displayInfo();
            }
        }
    }
    loadHandleFrame( scene->mRootNode, Core::Transform::Identity(), indexTable, data );
}

void AssimpHandleDataLoader::loadHandleComponentData( const aiScene* scene, const aiMesh* mesh, HandleData* data ) const {
    const uint size = mesh->mNumBones;
    Core::AlignedStdVector< HandleComponentData > component( size, HandleComponentData() );
    std::set< std::string > name;
    std::map< std::string , uint > nameTable;
    // Load the meaningful handles

    for( uint i = 0; i < size; ++i ) {
        aiBone* bone = mesh->mBones[i];
        loadHandleComponentData( scene, bone, component[i] );
        nameTable[component[i].m_name] = i;
        name.insert( component[i].m_name );
    }
    // Load the meaningless handles (if any)
    for( uint i = 0; i < size; ++i ) {
        aiNode* node = scene->mRootNode->FindNode( mesh->mBones[i]->mName );
        const uint children_size = node->mNumChildren;
        if( ( children_size == 0 ) && ( component[i].m_weight.size() != 0 ) ) {
            data->m_endNode = true;
        } else {
            if( children_size == 1 ) {
                aiNode* child = node->mChildren[0];
                auto it = name.find( assimpToCore( child->mName ) );
                if( it == name.end() ) {
                    component.push_back( HandleComponentData() );
                    const uint j = component.size() - 1;
                    loadHandleComponentData( child, component[j] );
                    nameTable[component[j].m_name] = j;
                }
            }
        }
    }
    data->setComponents( component );
    data->setNameTable( nameTable );
}

void AssimpHandleDataLoader::loadHandleComponentData( const aiScene* scene, const aiBone* bone, HandleComponentData& data ) const {
    data.m_name  = assimpToCore( bone->mName );
    //data.m_frame = assimpToCore( bone->mOffsetMatrix );
    data.m_frame.setIdentity();
    const uint size = bone->mNumWeights;
    for( uint j = 0; j < size; ++j ) {
        std::pair< uint, Scalar > weight( bone->mWeights[j].mVertexId, bone->mWeights[j].mWeight );
        data.m_weight.push_back( weight );
    }
    aiNode* node = scene->mRootNode->FindNode( bone->mName );
    while( node != nullptr ) {
        data.m_frame = assimpToCore( node->mTransformation ) * data.m_frame;
        node = node->mParent;
    }
}

void AssimpHandleDataLoader::loadHandleComponentData( const aiNode* node, HandleComponentData& data ) const {
    data.m_name  = assimpToCore( node->mName );
    //data.m_frame = assimpToCore( node->mTransformation );
    data.m_frame.setIdentity();
    const aiNode* tmpNode = node;
    while( tmpNode != nullptr ) {
        data.m_frame = assimpToCore( tmpNode->mTransformation ) * data.m_frame;
        tmpNode = tmpNode->mParent;
    }
}

void AssimpHandleDataLoader::loadHandleTopologyData( const aiScene* scene, HandleData* data ) const {
    const uint size = data->getComponentDataSize();
    std::vector< std::pair< std::string, std::string > > edgeList;
    for( uint i = 0; i < size; ++i ) {
        HandleComponentData& component = data->m_component[i];
        aiNode* node = scene->mRootNode->FindNode( aiString( component.m_name ) );
        const uint children_size = node->mNumChildren;
        for( uint j = 0; j < children_size; ++j ) {
            std::string childName = assimpToCore( node->mChildren[j]->mName );
            if( data->getIndexOf( childName ) != -1 ) {
                edgeList.push_back( std::pair< std::string, std::string >( component.m_name, childName ) );
            }
        }
    }
    Core::AlignedStdVector< Core::Vector2i > edge;
    edge.reserve( edgeList.size() );
    for( const auto& it : edgeList ) {
        Core::Vector2i e;
        e[0] = data->getIndexOf( it.first  );
        e[1] = data->getIndexOf( it.second );
        edge.push_back( e );
    }
    data->setEdges( edge );
}

void AssimpHandleDataLoader::loadHandleFrame( const aiNode*                                 node,
                                              const Core::Transform&                        parentFrame,
                                              const std::map< uint, uint >&                 indexTable,
                                              std::vector< std::unique_ptr< HandleData > >& data ) const {
    const uint child_size = node->mNumChildren;
    const uint mesh_size  = node->mNumMeshes;
    if( ( child_size == 0 ) && ( mesh_size == 0 ) ) {
        return;
    }
    Core::Transform frame = parentFrame * assimpToCore( node->mTransformation );
    for( uint i = 0; i < mesh_size; ++i ) {
        const uint ID = node->mMeshes[i];
        auto it = indexTable.find( ID );
        if( it != indexTable.end() ) {
            data[it->second]->setFrame( frame );
        }
    }
    for( uint i = 0; i < child_size; ++i ) {
        loadHandleFrame( node->mChildren[i], frame, indexTable, data );
    }
}

/// NAME
void AssimpHandleDataLoader::fetchName( const aiMesh& mesh, HandleData& data ) const {
    data.setName( assimpToCore( mesh.mName ) );
}

/// TYPE
void AssimpHandleDataLoader::fetchType( const aiMesh& mesh, HandleData& data ) const {
    data.setType( HandleData::SKELETON );
    // TO DO: is there a way to know the right type of handle?
}

/// VERTEX SIZE
void AssimpHandleDataLoader::fetchVertexSize( HandleData& data ) const {
    const uint componentSize = data.getComponentDataSize();
    data.m_vertexSize = 0;
    for( uint i = 0; i < componentSize; ++i ) {
        for( const auto& item : data.m_component[i].m_weight ) {
            data.m_vertexSize = std::max( data.m_vertexSize, ( item.first + 1 ) );
        }
    }
}

} // namespace Asset
} // namespace Ra
<|endoftext|>


<commit_before>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2011,2012 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "linear_mixer.hpp"

#include <utility>
#include <string>
#include <vector>

#include <glog/logging.h>
#include <pficommon/concurrent/lock.h>
#include <pficommon/lang/bind.h>
#include <pficommon/lang/shared_ptr.h>
#include <pficommon/system/time_util.h>
#include "jubatus/core/common/exception.hpp"
#include "jubatus/core/framework/mixable.hpp"
#include "../../common/membership.hpp"
#include "../../common/mprpc/rpc_mclient.hpp"

using std::vector;
using std::string;
using std::pair;
using jubatus::core::common::byte_buffer;
using pfi::concurrent::scoped_lock;
using pfi::concurrent::scoped_rlock;
using pfi::concurrent::scoped_wlock;

namespace jubatus {
namespace server {
namespace framework {
namespace mixer {

namespace {
class linear_communication_impl : public linear_communication {
 public:
  linear_communication_impl(
      const pfi::lang::shared_ptr<common::lock_service>& zk,
      const string& type,
      const string& name,
      int timeout_sec);

  size_t update_members();
  pfi::lang::shared_ptr<common::try_lockable> create_lock();
  void get_diff(common::mprpc::rpc_result_object& a) const;
  void put_diff(const vector<byte_buffer>& a) const;

 private:
  pfi::lang::shared_ptr<server::common::lock_service> zk_;
  string type_;
  string name_;
  int timeout_sec_;
  vector<pair<string, int> > servers_;
};

linear_communication_impl::linear_communication_impl(
    const pfi::lang::shared_ptr<server::common::lock_service>& zk,
    const string& type, const string& name, int timeout_sec)
    : zk_(zk),
      type_(type),
      name_(name),
      timeout_sec_(timeout_sec) {
}

pfi::lang::shared_ptr<common::try_lockable>
linear_communication_impl::create_lock() {
  string path;
  common::build_actor_path(path, type_, name_);
  return pfi::lang::shared_ptr<common::try_lockable>(
      new common::lock_service_mutex(*zk_, path + "/master_lock"));
}

size_t linear_communication_impl::update_members() {
  common::get_all_actors(*zk_, type_, name_, servers_);
  return servers_.size();
}

void linear_communication_impl::get_diff(
    common::mprpc::rpc_result_object& result) const {
  // TODO(beam2d): to be replaced to new client with socket connection pooling
  common::mprpc::rpc_mclient client(servers_, timeout_sec_);
#ifndef NDEBUG
  for (size_t i = 0; i < servers_.size(); i++) {
    DLOG(INFO) << "get diff from " << servers_[i].first << ":"
        << servers_[i].second;
  }
#endif
  result = client.call("get_diff", 0);
}

void linear_communication_impl::put_diff(
    const vector<byte_buffer>& mixed) const {
  // TODO(beam2d): to be replaced to new client with socket connection pooling
  server::common::mprpc::rpc_mclient client(servers_, timeout_sec_);
#ifndef NDEBUG
  for (size_t i = 0; i < servers_.size(); i++) {
    DLOG(INFO) << "put diff to " << servers_[i].first << ":"
        << servers_[i].second;
  }
#endif
  client.call("put_diff", mixed);
}

}  // namespace

pfi::lang::shared_ptr<linear_communication> linear_communication::create(
    const pfi::lang::shared_ptr<server::common::lock_service>& zk,
    const string& type, const string& name, int timeout_sec) {
  return pfi::lang::shared_ptr<linear_communication_impl>(
      new linear_communication_impl(zk, type, name, timeout_sec));
}

linear_mixer::linear_mixer(
    pfi::lang::shared_ptr<linear_communication> communication,
    unsigned int count_threshold, unsigned int tick_threshold)
    : communication_(communication),
      count_threshold_(count_threshold),
      tick_threshold_(tick_threshold),
      counter_(0),
      ticktime_(0),
      mix_count_(0),
      is_running_(false),
      t_(pfi::lang::bind(&linear_mixer::mixer_loop, this)) {
}

void linear_mixer::register_api(rpc_server_t& server) {
  server.add<vector<byte_buffer>(int)>(  // NOLINT
      "get_diff",
      pfi::lang::bind(&linear_mixer::get_diff, this, pfi::lang::_1));
  server.add<int(vector<byte_buffer>)>(
      "put_diff",
      pfi::lang::bind(&linear_mixer::put_diff, this, pfi::lang::_1));
}

void linear_mixer::set_mixable_holder(
    pfi::lang::shared_ptr<core::framework::mixable_holder> m) {
  mixable_holder_ = m;
}

void linear_mixer::start() {
  scoped_lock lk(m_);
  if (!is_running_) {
    is_running_ = true;
    t_.start();
  }
}

void linear_mixer::stop() {
  scoped_lock lk(m_);
  if (is_running_) {
    is_running_ = false;
    t_.join();
  }
}

void linear_mixer::updated() {
  scoped_lock lk(m_);
  unsigned int new_ticktime = time(NULL);
  ++counter_;
  if (counter_ > count_threshold_
      || new_ticktime - ticktime_ > tick_threshold_) {
    c_.notify();  // TODO(beam2d): need sync here?
  }
}

void linear_mixer::get_status(server_base::status_t& status) const {
  scoped_lock lk(m_);
  status["linear_mixer.count"] =
    pfi::lang::lexical_cast<string>(counter_);
  status["linear_mixer.ticktime"] =
    pfi::lang::lexical_cast<string>(ticktime_);  // since last mix
}

void linear_mixer::mixer_loop() {
  while (is_running_) {
    pfi::lang::shared_ptr<common::try_lockable> zklock = communication_
        ->create_lock();
    try {
      {
        scoped_lock lk(m_);

        c_.wait(m_, 1);
        unsigned int new_ticktime = time(NULL);
        if (counter_ > count_threshold_
            || new_ticktime - ticktime_ > tick_threshold_) {
          if (zklock->try_lock()) {
            LOG(INFO) << "starting mix:";
            counter_ = 0;
            ticktime_ = new_ticktime;
          } else {
            continue;
          }
        } else {
          continue;
        }
      }  // unlock
      mix();
      LOG(INFO) << ".... " << mix_count_ << "th mix done.";
    } catch (const jubatus::core::common::exception::jubatus_exception& e) {
      LOG(ERROR) << e.diagnostic_information(true);
    }
  }
}

void linear_mixer::mix() {
  using pfi::system::time::clock_time;
  using pfi::system::time::get_clock_time;

  // vector<string> serialized_diffs;
  clock_time start = get_clock_time();
  size_t s = 0;

  size_t servers_size = communication_->update_members();
  if (servers_size == 0) {
    LOG(WARNING) << "no other server. ";
    return;
  } else {
    try {
      core::framework::mixable_holder::mixable_list mixables =
          mixable_holder_->get_mixables();

      common::mprpc::rpc_result_object result;
      communication_->get_diff(result);

      vector<byte_buffer> mixed = result.response.front()
        .as<vector<byte_buffer> >();
      for (size_t i = 1; i < result.response.size(); ++i) {
        vector<byte_buffer> tmp =
          result.response[i].as<vector<byte_buffer> >();
        for (size_t j = 0; j < tmp.size(); ++j) {
          mixables[j]->mix(tmp[j], mixed[j], mixed[j]);
        }
      }

      communication_->put_diff(mixed);
      // TODO(beam2d): output log when result has error

      for (size_t i = 0; i < mixed.size(); ++i) {
        s += mixed[i].size();
      }
    } catch (const std::exception& e) {
      LOG(WARNING) << e.what() << " : mix failed";
      return;
    }
  }

  clock_time end = get_clock_time();
  LOG(INFO) << "mixed with " << servers_size << " servers in "
      << static_cast<double>(end - start) << " secs, " << s
      << " bytes (serialized data) has been put.";
  mix_count_++;
}

vector<byte_buffer> linear_mixer::get_diff(int a) {
  scoped_rlock lk_read(mixable_holder_->rw_mutex());
  scoped_lock lk(m_);

  core::framework::mixable_holder::mixable_list mixables =
      mixable_holder_->get_mixables();
  if (mixables.empty()) {
    throw JUBATUS_EXCEPTION(core::common::config_not_set());  // nothing to mix
  }

  vector<byte_buffer> o;
  for (size_t i = 0; i < mixables.size(); ++i) {
    o.push_back(mixables[i]->get_diff());
  }
  return o;
}

int linear_mixer::put_diff(
    const vector<byte_buffer>& unpacked) {
  scoped_wlock lk_write(mixable_holder_->rw_mutex());
  scoped_lock lk(m_);

  core::framework::mixable_holder::mixable_list mixables =
      mixable_holder_->get_mixables();
  if (unpacked.size() != mixables.size()) {
    // deserialization error
    return -1;
  }
  for (size_t i = 0; i < mixables.size(); ++i) {
    mixables[i]->put_diff(unpacked[i]);
  }
  counter_ = 0;
  ticktime_ = time(NULL);
  return 0;
}

}  // namespace mixer
}  // namespace framework
}  // namespace server
}  // namespace jubatus
<commit_msg>Fix deadlock on linear_mixer::stop()<commit_after>// Jubatus: Online machine learning framework for distributed environment
// Copyright (C) 2011,2012 Preferred Infrastructure and Nippon Telegraph and Telephone Corporation.
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License version 2.1 as published by the Free Software Foundation.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

#include "linear_mixer.hpp"

#include <utility>
#include <string>
#include <vector>

#include <glog/logging.h>
#include <pficommon/concurrent/lock.h>
#include <pficommon/lang/bind.h>
#include <pficommon/lang/shared_ptr.h>
#include <pficommon/system/time_util.h>
#include "jubatus/core/common/exception.hpp"
#include "jubatus/core/framework/mixable.hpp"
#include "../../common/membership.hpp"
#include "../../common/mprpc/rpc_mclient.hpp"
#include "../../common/unique_lock.hpp"

using std::vector;
using std::string;
using std::pair;
using jubatus::core::common::byte_buffer;
using pfi::concurrent::scoped_lock;
using pfi::concurrent::scoped_rlock;
using pfi::concurrent::scoped_wlock;

namespace jubatus {
namespace server {
namespace framework {
namespace mixer {

namespace {
class linear_communication_impl : public linear_communication {
 public:
  linear_communication_impl(
      const pfi::lang::shared_ptr<common::lock_service>& zk,
      const string& type,
      const string& name,
      int timeout_sec);

  size_t update_members();
  pfi::lang::shared_ptr<common::try_lockable> create_lock();
  void get_diff(common::mprpc::rpc_result_object& a) const;
  void put_diff(const vector<byte_buffer>& a) const;

 private:
  pfi::lang::shared_ptr<server::common::lock_service> zk_;
  string type_;
  string name_;
  int timeout_sec_;
  vector<pair<string, int> > servers_;
};

linear_communication_impl::linear_communication_impl(
    const pfi::lang::shared_ptr<server::common::lock_service>& zk,
    const string& type, const string& name, int timeout_sec)
    : zk_(zk),
      type_(type),
      name_(name),
      timeout_sec_(timeout_sec) {
}

pfi::lang::shared_ptr<common::try_lockable>
linear_communication_impl::create_lock() {
  string path;
  common::build_actor_path(path, type_, name_);
  return pfi::lang::shared_ptr<common::try_lockable>(
      new common::lock_service_mutex(*zk_, path + "/master_lock"));
}

size_t linear_communication_impl::update_members() {
  common::get_all_actors(*zk_, type_, name_, servers_);
  return servers_.size();
}

void linear_communication_impl::get_diff(
    common::mprpc::rpc_result_object& result) const {
  // TODO(beam2d): to be replaced to new client with socket connection pooling
  common::mprpc::rpc_mclient client(servers_, timeout_sec_);
#ifndef NDEBUG
  for (size_t i = 0; i < servers_.size(); i++) {
    DLOG(INFO) << "get diff from " << servers_[i].first << ":"
        << servers_[i].second;
  }
#endif
  result = client.call("get_diff", 0);
}

void linear_communication_impl::put_diff(
    const vector<byte_buffer>& mixed) const {
  // TODO(beam2d): to be replaced to new client with socket connection pooling
  server::common::mprpc::rpc_mclient client(servers_, timeout_sec_);
#ifndef NDEBUG
  for (size_t i = 0; i < servers_.size(); i++) {
    DLOG(INFO) << "put diff to " << servers_[i].first << ":"
        << servers_[i].second;
  }
#endif
  client.call("put_diff", mixed);
}

}  // namespace

pfi::lang::shared_ptr<linear_communication> linear_communication::create(
    const pfi::lang::shared_ptr<server::common::lock_service>& zk,
    const string& type, const string& name, int timeout_sec) {
  return pfi::lang::shared_ptr<linear_communication_impl>(
      new linear_communication_impl(zk, type, name, timeout_sec));
}

linear_mixer::linear_mixer(
    pfi::lang::shared_ptr<linear_communication> communication,
    unsigned int count_threshold, unsigned int tick_threshold)
    : communication_(communication),
      count_threshold_(count_threshold),
      tick_threshold_(tick_threshold),
      counter_(0),
      ticktime_(0),
      mix_count_(0),
      is_running_(false),
      t_(pfi::lang::bind(&linear_mixer::mixer_loop, this)) {
}

void linear_mixer::register_api(rpc_server_t& server) {
  server.add<vector<byte_buffer>(int)>(  // NOLINT
      "get_diff",
      pfi::lang::bind(&linear_mixer::get_diff, this, pfi::lang::_1));
  server.add<int(vector<byte_buffer>)>(
      "put_diff",
      pfi::lang::bind(&linear_mixer::put_diff, this, pfi::lang::_1));
}

void linear_mixer::set_mixable_holder(
    pfi::lang::shared_ptr<core::framework::mixable_holder> m) {
  mixable_holder_ = m;
}

void linear_mixer::start() {
  scoped_lock lk(m_);
  if (!is_running_) {
    is_running_ = true;
    t_.start();
  }
}

void linear_mixer::stop() {
  common::unique_lock lk(m_);
  if (is_running_) {
    is_running_ = false;
    lk.unlock();
    t_.join();
  }
}

void linear_mixer::updated() {
  scoped_lock lk(m_);
  unsigned int new_ticktime = time(NULL);
  ++counter_;
  if (counter_ > count_threshold_
      || new_ticktime - ticktime_ > tick_threshold_) {
    c_.notify();  // TODO(beam2d): need sync here?
  }
}

void linear_mixer::get_status(server_base::status_t& status) const {
  scoped_lock lk(m_);
  status["linear_mixer.count"] =
    pfi::lang::lexical_cast<string>(counter_);
  status["linear_mixer.ticktime"] =
    pfi::lang::lexical_cast<string>(ticktime_);  // since last mix
}

void linear_mixer::mixer_loop() {
  while (is_running_) {
    pfi::lang::shared_ptr<common::try_lockable> zklock = communication_
        ->create_lock();
    try {
      {
        scoped_lock lk(m_);

        c_.wait(m_, 1);
        unsigned int new_ticktime = time(NULL);
        if (counter_ > count_threshold_
            || new_ticktime - ticktime_ > tick_threshold_) {
          if (zklock->try_lock()) {
            LOG(INFO) << "starting mix:";
            counter_ = 0;
            ticktime_ = new_ticktime;
          } else {
            continue;
          }
        } else {
          continue;
        }
      }  // unlock
      mix();
      LOG(INFO) << ".... " << mix_count_ << "th mix done.";
    } catch (const jubatus::core::common::exception::jubatus_exception& e) {
      LOG(ERROR) << e.diagnostic_information(true);
    }
  }
}

void linear_mixer::mix() {
  using pfi::system::time::clock_time;
  using pfi::system::time::get_clock_time;

  // vector<string> serialized_diffs;
  clock_time start = get_clock_time();
  size_t s = 0;

  size_t servers_size = communication_->update_members();
  if (servers_size == 0) {
    LOG(WARNING) << "no other server. ";
    return;
  } else {
    try {
      core::framework::mixable_holder::mixable_list mixables =
          mixable_holder_->get_mixables();

      common::mprpc::rpc_result_object result;
      communication_->get_diff(result);

      vector<byte_buffer> mixed = result.response.front()
        .as<vector<byte_buffer> >();
      for (size_t i = 1; i < result.response.size(); ++i) {
        vector<byte_buffer> tmp =
          result.response[i].as<vector<byte_buffer> >();
        for (size_t j = 0; j < tmp.size(); ++j) {
          mixables[j]->mix(tmp[j], mixed[j], mixed[j]);
        }
      }

      communication_->put_diff(mixed);
      // TODO(beam2d): output log when result has error

      for (size_t i = 0; i < mixed.size(); ++i) {
        s += mixed[i].size();
      }
    } catch (const std::exception& e) {
      LOG(WARNING) << e.what() << " : mix failed";
      return;
    }
  }

  clock_time end = get_clock_time();
  LOG(INFO) << "mixed with " << servers_size << " servers in "
      << static_cast<double>(end - start) << " secs, " << s
      << " bytes (serialized data) has been put.";
  mix_count_++;
}

vector<byte_buffer> linear_mixer::get_diff(int a) {
  scoped_rlock lk_read(mixable_holder_->rw_mutex());
  scoped_lock lk(m_);

  core::framework::mixable_holder::mixable_list mixables =
      mixable_holder_->get_mixables();
  if (mixables.empty()) {
    throw JUBATUS_EXCEPTION(core::common::config_not_set());  // nothing to mix
  }

  vector<byte_buffer> o;
  for (size_t i = 0; i < mixables.size(); ++i) {
    o.push_back(mixables[i]->get_diff());
  }
  return o;
}

int linear_mixer::put_diff(
    const vector<byte_buffer>& unpacked) {
  scoped_wlock lk_write(mixable_holder_->rw_mutex());
  scoped_lock lk(m_);

  core::framework::mixable_holder::mixable_list mixables =
      mixable_holder_->get_mixables();
  if (unpacked.size() != mixables.size()) {
    // deserialization error
    return -1;
  }
  for (size_t i = 0; i < mixables.size(); ++i) {
    mixables[i]->put_diff(unpacked[i]);
  }
  counter_ = 0;
  ticktime_ = time(NULL);
  return 0;
}

}  // namespace mixer
}  // namespace framework
}  // namespace server
}  // namespace jubatus
<|endoftext|>


<commit_before>/*
Copyright (c) 2014 Ableton AG, Berlin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// clang-format off

#pragma once

#if defined(__clang__)

  #if __has_warning("-Wcomma")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_COMMA \
    _Pragma("clang diagnostic ignored \"-Wcomma\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_COMMA
  #endif

  #if __has_warning("-Wdouble-promotion")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_DOUBLE_PROMOTION \
    _Pragma("clang diagnostic ignored \"-Wdouble-promotion\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_DOUBLE_PROMOTION
  #endif

  #if __has_warning("-Wexpansion-to-defined")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_EXPANSION_TO_DEFINED \
    _Pragma("clang diagnostic ignored \"-Wexpansion-to-defined\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_EXPANSION_TO_DEFINED
  #endif

  #if __has_warning("-Winconsistent-missing-destructor-override")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE \
    _Pragma("clang diagnostic ignored \"-Winconsistent-missing-destructor-override\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE
  #endif

  #if __has_warning("-Wredundant-parens")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_REDUNDANT_PARENS \
    _Pragma("clang diagnostic ignored \"-Wredundant-parens\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_REDUNDANT_PARENS
  #endif

  #if __has_warning("-Wreserved-id-macro")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_RESERVED_ID_MACRO \
    _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_RESERVED_ID_MACRO
  #endif

  #if __has_warning("-Wundefined-func-template")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNDEFINED_FUNC_TEMPLATE \
    _Pragma("clang diagnostic ignored \"-Wundefined-func-template\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNDEFINED_FUNC_TEMPLATE
  #endif

  #if __has_warning("-Wunused-local-typedef")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNUSED_LOCAL_TYPEDEF \
    _Pragma("clang diagnostic ignored \"-Wunused-local-typedef\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNUSED_LOCAL_TYPEDEF
  #endif

  #if __has_warning("-Wzero-as-null-pointer-constant")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_ZERO_AS_NULL_POINTER_CONSTANT \
    _Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_ZERO_AS_NULL_POINTER_CONSTANT
  #endif

  #define SUPPRESS_WARNINGS \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wconditional-uninitialized\"") \
    _Pragma("clang diagnostic ignored \"-Wconversion\"") \
    _Pragma("clang diagnostic ignored \"-Wcovered-switch-default\"") \
    _Pragma("clang diagnostic ignored \"-Wdeprecated\"") \
    _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \
    _Pragma("clang diagnostic ignored \"-Wdocumentation-unknown-command\"") \
    _Pragma("clang diagnostic ignored \"-Wdocumentation\"") \
    _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \
    _Pragma("clang diagnostic ignored \"-Wextra-semi\"") \
    _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \
    _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \
    _Pragma("clang diagnostic ignored \"-Wheader-hygiene\"") \
    _Pragma("clang diagnostic ignored \"-Wmissing-noreturn\"") \
    _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"") \
    _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
    _Pragma("clang diagnostic ignored \"-Wpadded\"") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    _Pragma("clang diagnostic ignored \"-Wshift-sign-overflow\"") \
    _Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"") \
    _Pragma("clang diagnostic ignored \"-Wsign-compare\"") \
    _Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \
    _Pragma("clang diagnostic ignored \"-Wswitch-enum\"") \
    _Pragma("clang diagnostic ignored \"-Wundef\"") \
    _Pragma("clang diagnostic ignored \"-Wundefined-reinterpret-cast\"") \
    _Pragma("clang diagnostic ignored \"-Wunreachable-code\"") \
    _Pragma("clang diagnostic ignored \"-Wunused-parameter\"") \
    _Pragma("clang diagnostic ignored \"-Wused-but-marked-unused\"") \
    _Pragma("clang diagnostic ignored \"-Wweak-vtables\"") \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_COMMA \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_DOUBLE_PROMOTION \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_EXPANSION_TO_DEFINED \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_REDUNDANT_PARENS \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_RESERVED_ID_MACRO \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNDEFINED_FUNC_TEMPLATE \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNUSED_LOCAL_TYPEDEF \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_ZERO_AS_NULL_POINTER_CONSTANT \
    /**/

  #define RESTORE_WARNINGS \
    _Pragma("clang diagnostic pop")

#elif defined(_MSC_VER)

  #if _MSC_VER < 1900
    #define ABL_PRAGMA_DISABLE_MSVC_1900_WARNINGS
  #else
    /**
    * C4459: declaration of 'parameter' hides global declaration
    * C5031: #pragma warning(pop): likely mismatch, popping warning state pushed in different file (compiling source file <file>)
    */
    #define ABL_PRAGMA_DISABLE_MSVC_1900_WARNINGS \
    __pragma(warning(disable: 4459)) \
    __pragma(warning(disable: 5031))
  #endif

  /**
  * C4100: 'identifier' : unreferenced formal parameter
  * C4127: conditional expression is constant
  * C4191: 'type cast' : unsafe conversion from 'function pointer type' to 'function pointer type'
  *        Calling this function through the result pointer may cause your program to fail
  * C4242: 'identifier' : conversion from 'type1' to 'type2', possible loss of data
  * C4244: 'conversion' conversion from 'type1' to 'type2', possible loss of data
  * C4251: 'identifier' : class 'type' needs to have dll-interface to be used by clients of class 'type2'
  * C4365: 'action' : conversion from 'type_1' to 'type_2', signed/unsigned mismatch
  * C4388: signed/unsigned mismatch
  * C4459: declaration of 'identifier' hides global declaration
  * C4555: expression has no effect; expected expression with side-effect
  * C4619: #pragma warning : there is no warning number 'number'
  * C4628: digraphs not supported with -Ze. Character sequence 'digraph' not interpreted as alternate token for 'char'
  * C4640: 'instance' : construction of local static object is not thread-safe
  * C4668: 'symbol' is not defined as a preprocessor macro, replacing with '0' for 'directives'
  * C4714: function 'function' marked as __forceinline not inlined
  * C4800: 'type' : forcing value to bool 'true' or 'false' (performance warning)
  * C4826: Conversion from 'type1 ' to 'type_2' is sign-extended. This may cause unexpected runtime behavior.
  */
  #define SUPPRESS_WARNINGS \
    __pragma(warning(push)) \
    __pragma(warning(disable: 4100)) \
    __pragma(warning(disable: 4127)) \
    __pragma(warning(disable: 4191)) \
    __pragma(warning(disable: 4242)) \
    __pragma(warning(disable: 4244)) \
    __pragma(warning(disable: 4251)) \
    __pragma(warning(disable: 4267)) \
    __pragma(warning(disable: 4365)) \
    __pragma(warning(disable: 4388)) \
    __pragma(warning(disable: 4510)) \
    __pragma(warning(disable: 4555)) \
    __pragma(warning(disable: 4610)) \
    __pragma(warning(disable: 4619)) \
    __pragma(warning(disable: 4628)) \
    __pragma(warning(disable: 4640)) \
    __pragma(warning(disable: 4668)) \
    __pragma(warning(disable: 4714)) \
    __pragma(warning(disable: 4800)) \
    __pragma(warning(disable: 4826)) \
    ABL_PRAGMA_DISABLE_MSVC_1900_WARNINGS

  #define RESTORE_WARNINGS \
    __pragma(warning(pop))

#elif defined(__GNUC__)

  #define SUPPRESS_WARNINGS _Pragma("GCC diagnostic push") \
    _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
    _Pragma("GCC diagnostic ignored \"-Wsign-compare\"")

  #define RESTORE_WARNINGS _Pragma("GCC diagnostic pop")

#else

  #define SUPPRESS_WARNINGS
  #define RESTORE_WARNINGS

#endif
<commit_msg>Simplify disabling MSVC warnings in third-party headers<commit_after>/*
Copyright (c) 2014 Ableton AG, Berlin

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
*/

// clang-format off

#pragma once

#if defined(__clang__)

  #if __has_warning("-Wcomma")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_COMMA \
    _Pragma("clang diagnostic ignored \"-Wcomma\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_COMMA
  #endif

  #if __has_warning("-Wdouble-promotion")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_DOUBLE_PROMOTION \
    _Pragma("clang diagnostic ignored \"-Wdouble-promotion\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_DOUBLE_PROMOTION
  #endif

  #if __has_warning("-Wexpansion-to-defined")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_EXPANSION_TO_DEFINED \
    _Pragma("clang diagnostic ignored \"-Wexpansion-to-defined\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_EXPANSION_TO_DEFINED
  #endif

  #if __has_warning("-Winconsistent-missing-destructor-override")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE \
    _Pragma("clang diagnostic ignored \"-Winconsistent-missing-destructor-override\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE
  #endif

  #if __has_warning("-Wredundant-parens")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_REDUNDANT_PARENS \
    _Pragma("clang diagnostic ignored \"-Wredundant-parens\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_REDUNDANT_PARENS
  #endif

  #if __has_warning("-Wreserved-id-macro")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_RESERVED_ID_MACRO \
    _Pragma("clang diagnostic ignored \"-Wreserved-id-macro\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_RESERVED_ID_MACRO
  #endif

  #if __has_warning("-Wundefined-func-template")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNDEFINED_FUNC_TEMPLATE \
    _Pragma("clang diagnostic ignored \"-Wundefined-func-template\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNDEFINED_FUNC_TEMPLATE
  #endif

  #if __has_warning("-Wunused-local-typedef")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNUSED_LOCAL_TYPEDEF \
    _Pragma("clang diagnostic ignored \"-Wunused-local-typedef\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNUSED_LOCAL_TYPEDEF
  #endif

  #if __has_warning("-Wzero-as-null-pointer-constant")
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_ZERO_AS_NULL_POINTER_CONSTANT \
    _Pragma("clang diagnostic ignored \"-Wzero-as-null-pointer-constant\"")
  #else
    #define ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_ZERO_AS_NULL_POINTER_CONSTANT
  #endif

  #define SUPPRESS_WARNINGS \
    _Pragma("clang diagnostic push") \
    _Pragma("clang diagnostic ignored \"-Wconditional-uninitialized\"") \
    _Pragma("clang diagnostic ignored \"-Wconversion\"") \
    _Pragma("clang diagnostic ignored \"-Wcovered-switch-default\"") \
    _Pragma("clang diagnostic ignored \"-Wdeprecated\"") \
    _Pragma("clang diagnostic ignored \"-Wdisabled-macro-expansion\"") \
    _Pragma("clang diagnostic ignored \"-Wdocumentation-unknown-command\"") \
    _Pragma("clang diagnostic ignored \"-Wdocumentation\"") \
    _Pragma("clang diagnostic ignored \"-Wexit-time-destructors\"") \
    _Pragma("clang diagnostic ignored \"-Wextra-semi\"") \
    _Pragma("clang diagnostic ignored \"-Wfloat-equal\"") \
    _Pragma("clang diagnostic ignored \"-Wglobal-constructors\"") \
    _Pragma("clang diagnostic ignored \"-Wheader-hygiene\"") \
    _Pragma("clang diagnostic ignored \"-Wmissing-noreturn\"") \
    _Pragma("clang diagnostic ignored \"-Wmissing-prototypes\"") \
    _Pragma("clang diagnostic ignored \"-Wold-style-cast\"") \
    _Pragma("clang diagnostic ignored \"-Wpadded\"") \
    _Pragma("clang diagnostic ignored \"-Wshadow\"") \
    _Pragma("clang diagnostic ignored \"-Wshift-sign-overflow\"") \
    _Pragma("clang diagnostic ignored \"-Wshorten-64-to-32\"") \
    _Pragma("clang diagnostic ignored \"-Wsign-compare\"") \
    _Pragma("clang diagnostic ignored \"-Wsign-conversion\"") \
    _Pragma("clang diagnostic ignored \"-Wswitch-enum\"") \
    _Pragma("clang diagnostic ignored \"-Wundef\"") \
    _Pragma("clang diagnostic ignored \"-Wundefined-reinterpret-cast\"") \
    _Pragma("clang diagnostic ignored \"-Wunreachable-code\"") \
    _Pragma("clang diagnostic ignored \"-Wunused-parameter\"") \
    _Pragma("clang diagnostic ignored \"-Wused-but-marked-unused\"") \
    _Pragma("clang diagnostic ignored \"-Wweak-vtables\"") \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_COMMA \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_DOUBLE_PROMOTION \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_EXPANSION_TO_DEFINED \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_INCONSISTENT_MISSING_DESTRUCTOR_OVERRIDE \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_REDUNDANT_PARENS \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_RESERVED_ID_MACRO \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNDEFINED_FUNC_TEMPLATE \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_UNUSED_LOCAL_TYPEDEF \
    ABL_PRAGMA_CLANG_DIAGNOSTIC_IGNORED_ZERO_AS_NULL_POINTER_CONSTANT \
    /**/

  #define RESTORE_WARNINGS \
    _Pragma("clang diagnostic pop")

#elif defined(_MSC_VER)

  #define SUPPRESS_WARNINGS \
    __pragma(warning(push, 0)) \
    __pragma(warning(disable: 4244))

  #define RESTORE_WARNINGS \
    __pragma(warning(pop))

#elif defined(__GNUC__)

  #define SUPPRESS_WARNINGS _Pragma("GCC diagnostic push") \
    _Pragma("GCC diagnostic ignored \"-Wdeprecated-declarations\"") \
    _Pragma("GCC diagnostic ignored \"-Wsign-compare\"")

  #define RESTORE_WARNINGS _Pragma("GCC diagnostic pop")

#else

  #define SUPPRESS_WARNINGS
  #define RESTORE_WARNINGS

#endif
<|endoftext|>


<commit_before>// WinClient.hh for Fluxbox - an X11 Window manager
// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef WINCLIENT_HH
#define WINCLIENT_HH

#include "Window.hh"
#include "WindowState.hh"

#include "FbTk/FbWindow.hh"
#include "FbTk/FbString.hh"

class BScreen;
class Strut;

/// Holds client window info
class WinClient: public Focusable, public FbTk::FbWindow {
public:
    typedef std::list<WinClient *> TransientList;
    // this structure only contains 3 elements... the Motif 2.0 structure contains
    // 5... we only need the first 3... so that is all we will define
    typedef struct MwmHints {
        unsigned long flags;       // Motif wm flags
        unsigned long functions;   // Motif wm functions
        unsigned long decorations; // Motif wm decorations
    } MwmHints;

    WinClient(Window win, BScreen &screen, FluxboxWindow *fbwin = 0);

    ~WinClient();

    bool sendFocus(); // returns whether we sent a message or not
                      // i.e. whether we assume the focus will get taken
    bool acceptsFocus() const; // will this window accept focus (according to hints)
    void sendClose(bool forceful = false);
    // not aware of anything that makes this false at present
    bool isClosable() const { return true; }

    /// updates from wm class hints
    void updateWMClassHint();
    void updateWMProtocols();

    // override the title with this
    void setTitle(const FbTk::FbString &title);
    void updateTitle();
    /// updates transient window information
    void updateTransientInfo();

    // override the icon with this
    void setIcon(const FbTk::PixmapWithMask& pm);

    // update some thints
    void updateMWMHints();
    void updateWMHints();
    void updateWMNormalHints();

    void setStrut(Strut *strut);
    void clearStrut();

    bool focus(); // calls Window->setCurrentClient to give focus to this client
    bool isFocused() const;
    void setAttentionState(bool value);
    const std::string &title() const { return m_title; }

    void setGroupLeftWindow(Window win);

    void setFluxboxWindow(FluxboxWindow *win);

    // does this client have a pending unmap or destroy event?
    bool validateClient() const;

    //
    // accessors
    //

    bool getAttrib(XWindowAttributes &attr) const;
    bool getWMName(XTextProperty &textprop) const;
    bool getWMIconName(XTextProperty &textprop) const;
    std::string getWMRole() const;
    WindowState::WindowType getWindowType() const { return m_window_type; }
    void setWindowType(WindowState::WindowType type) { m_window_type = type; }

    WinClient *transientFor() { return transient_for; }
    const WinClient *transientFor() const { return transient_for; }
    TransientList &transientList() { return transients; }
    const TransientList &transientList() const { return transients; }
    bool isTransient() const { return transient_for != 0; }

    bool isModal() const { return m_modal_count > 0; }
    bool isStateModal() const { return m_modal; }
    void setStateModal(bool state);

    int gravity() const { return m_size_hints.win_gravity; }

    bool hasGroupLeftWindow() const;
    // grouping is tracked by remembering the window to the left in the group
    Window getGroupLeftWindow() const;

    const MwmHints *getMwmHint() const { return m_mwm_hint; }
    const SizeHints &sizeHints() const { return m_size_hints; }

    unsigned int minWidth() const { return m_size_hints.min_width; }
    unsigned int minHeight() const { return m_size_hints.min_height; }
    unsigned int maxWidth() const { return m_size_hints.max_width; }
    unsigned int maxHeight() const { return m_size_hints.max_height; }
    unsigned int widthInc() const { return m_size_hints.width_inc; }
    unsigned int heightInc() const { return m_size_hints.height_inc; }

    static const int PropMwmHintsElements = 3;

    /**
       !! TODO !!
       remove or move these to private
     */

    WinClient *transient_for; // which window are we a transient for?
    std::list<WinClient *> transients;  // which windows are our transients?
    Window window_group;

    int old_bw;
    unsigned long initial_state, normal_hint_flags, wm_hint_flags;

private:
    /// removes client from any waiting list and clears empty waiting lists
    void removeTransientFromWaitingList();

    // some transient of ours (or us) is modal
    void addModal() { ++m_modal_count; }
    // some transient (or us) is no longer modal
    void removeModal() { --m_modal_count; }

    // number of transients which we are modal for
    int m_modal_count;
    bool m_modal;
    bool accepts_input, send_focus_message, send_close_message;

    bool m_title_override;
    bool m_icon_override;

    WindowState::WindowType m_window_type;
    MwmHints *m_mwm_hint;
    SizeHints m_size_hints;

    Strut *m_strut;
    // map transient_for X window to winclient transient
    // (used if transient_for FbWindow was created after transient)
    // Since a lot of transients can be created before transient_for
    // we need to map transient_for window to a list of transients
    //
    // Stuff to worry about:
    // 1) If transients die before the transient_for is created
    // 2) If transients changes to a new transient_for before old transient_for is created
    // ( 3) Transient_for is never created
    //      This is not a big deal since the key value will be cleared
    //      once the list is empty )
    typedef std::map<Window, TransientList> TransientWaitMap;
    static TransientWaitMap s_transient_wait;

};

#endif // WINCLIENT_HH
<commit_msg>removed unused code<commit_after>// WinClient.hh for Fluxbox - an X11 Window manager
// Copyright (c) 2003 - 2006 Henrik Kinnunen (fluxgen at fluxbox dot org)
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the "Software"),
// to deal in the Software without restriction, including without limitation
// the rights to use, copy, modify, merge, publish, distribute, sublicense,
// and/or sell copies of the Software, and to permit persons to whom the
// Software is furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in
// all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL
// THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
// DEALINGS IN THE SOFTWARE.

#ifndef WINCLIENT_HH
#define WINCLIENT_HH

#include "Window.hh"
#include "WindowState.hh"

#include "FbTk/FbWindow.hh"
#include "FbTk/FbString.hh"

class BScreen;
class Strut;

/// Holds client window info
class WinClient: public Focusable, public FbTk::FbWindow {
public:
    typedef std::list<WinClient *> TransientList;
    // this structure only contains 3 elements... the Motif 2.0 structure contains
    // 5... we only need the first 3... so that is all we will define
    typedef struct MwmHints {
        unsigned long flags;       // Motif wm flags
        unsigned long functions;   // Motif wm functions
        unsigned long decorations; // Motif wm decorations
    } MwmHints;

    WinClient(Window win, BScreen &screen, FluxboxWindow *fbwin = 0);

    ~WinClient();

    bool sendFocus(); // returns whether we sent a message or not
                      // i.e. whether we assume the focus will get taken
    bool acceptsFocus() const; // will this window accept focus (according to hints)
    void sendClose(bool forceful = false);
    // not aware of anything that makes this false at present
    bool isClosable() const { return true; }

    /// updates from wm class hints
    void updateWMClassHint();
    void updateWMProtocols();

    // override the title with this
    void setTitle(const FbTk::FbString &title);
    void updateTitle();
    /// updates transient window information
    void updateTransientInfo();

    // override the icon with this
    void setIcon(const FbTk::PixmapWithMask& pm);

    // update some thints
    void updateMWMHints();
    void updateWMHints();
    void updateWMNormalHints();

    void setStrut(Strut *strut);
    void clearStrut();

    bool focus(); // calls Window->setCurrentClient to give focus to this client
    bool isFocused() const;
    void setAttentionState(bool value);

    void setGroupLeftWindow(Window win);

    void setFluxboxWindow(FluxboxWindow *win);

    // does this client have a pending unmap or destroy event?
    bool validateClient() const;

    //
    // accessors
    //

    bool getAttrib(XWindowAttributes &attr) const;
    bool getWMName(XTextProperty &textprop) const;
    bool getWMIconName(XTextProperty &textprop) const;
    std::string getWMRole() const;
    WindowState::WindowType getWindowType() const { return m_window_type; }
    void setWindowType(WindowState::WindowType type) { m_window_type = type; }

    WinClient *transientFor() { return transient_for; }
    const WinClient *transientFor() const { return transient_for; }
    TransientList &transientList() { return transients; }
    const TransientList &transientList() const { return transients; }
    bool isTransient() const { return transient_for != 0; }

    bool isModal() const { return m_modal_count > 0; }
    bool isStateModal() const { return m_modal; }
    void setStateModal(bool state);

    int gravity() const { return m_size_hints.win_gravity; }

    bool hasGroupLeftWindow() const;
    // grouping is tracked by remembering the window to the left in the group
    Window getGroupLeftWindow() const;

    const MwmHints *getMwmHint() const { return m_mwm_hint; }
    const SizeHints &sizeHints() const { return m_size_hints; }

    unsigned int minWidth() const { return m_size_hints.min_width; }
    unsigned int minHeight() const { return m_size_hints.min_height; }
    unsigned int maxWidth() const { return m_size_hints.max_width; }
    unsigned int maxHeight() const { return m_size_hints.max_height; }
    unsigned int widthInc() const { return m_size_hints.width_inc; }
    unsigned int heightInc() const { return m_size_hints.height_inc; }

    static const int PropMwmHintsElements = 3;

    /**
       !! TODO !!
       remove or move these to private
     */

    WinClient *transient_for; // which window are we a transient for?
    std::list<WinClient *> transients;  // which windows are our transients?
    Window window_group;

    int old_bw;
    unsigned long initial_state, normal_hint_flags, wm_hint_flags;

private:
    /// removes client from any waiting list and clears empty waiting lists
    void removeTransientFromWaitingList();

    // some transient of ours (or us) is modal
    void addModal() { ++m_modal_count; }
    // some transient (or us) is no longer modal
    void removeModal() { --m_modal_count; }

    // number of transients which we are modal for
    int m_modal_count;
    bool m_modal;
    bool accepts_input, send_focus_message, send_close_message;

    bool m_title_override;
    bool m_icon_override;

    WindowState::WindowType m_window_type;
    MwmHints *m_mwm_hint;
    SizeHints m_size_hints;

    Strut *m_strut;
    // map transient_for X window to winclient transient
    // (used if transient_for FbWindow was created after transient)
    // Since a lot of transients can be created before transient_for
    // we need to map transient_for window to a list of transients
    //
    // Stuff to worry about:
    // 1) If transients die before the transient_for is created
    // 2) If transients changes to a new transient_for before old transient_for is created
    // ( 3) Transient_for is never created
    //      This is not a big deal since the key value will be cleared
    //      once the list is empty )
    typedef std::map<Window, TransientList> TransientWaitMap;
    static TransientWaitMap s_transient_wait;

};

#endif // WINCLIENT_HH
<|endoftext|>


<commit_before>//=======================================================================
// Copyright (c) 2013-2014 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>

#include "accounts.hpp"
#include "budget_exception.hpp"
#include "args.hpp"
#include "data.hpp"
#include "guid.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "console.hpp"
#include "earnings.hpp"
#include "expenses.hpp"

using namespace budget;

namespace {

static data_handler<account> accounts;

void show_accounts(){
    std::vector<std::string> columns = {"ID", "Name", "Amount"};
    std::vector<std::vector<std::string>> contents;

    money total;

    for(auto& account : accounts.data){
        if(account.until == budget::date(2099,12,31)){
            contents.push_back({to_string(account.id), account.name, to_string(account.amount)});

            total += account.amount;
        }
    }

    contents.push_back({"", "Total", to_string(total)});

    display_table(columns, contents);
}

void show_all_accounts(){
    std::vector<std::string> columns = {"ID", "Name", "Amount", "Since", "Until"};
    std::vector<std::vector<std::string>> contents;

    for(auto& account : accounts.data){
        contents.push_back({to_string(account.id), account.name, to_string(account.amount), to_string(account.since), to_string(account.until)});
    }

    display_table(columns, contents);
}

budget::date find_new_since(){
    budget::date date(1400,1,1);

    for(auto& account : all_accounts()){
        if(account.until != budget::date(2099,12,31)){
            if(account.until - days(1) > date){
                date = account.until - days(1);
            }
        }
    }

    return date;
}

std::size_t get_account_id(std::string name, budget::year year, budget::month month){
    budget::date date(year, month, 5);

    for(auto& account : accounts.data){
        if(account.since < date && account.until > date && account.name == name){
            return account.id;
        }
    }

    return 0;
}

template<typename Values>
void adapt(Values& values, std::size_t old, std::size_t id){
    for(auto& value : values){
        if(value.account == old){
            value.account = id;
        }
    }
}

} //end of anonymous namespace

void budget::accounts_module::load(){
    load_accounts();
    load_expenses();
    load_earnings();
}

void budget::accounts_module::unload(){
    save_accounts();
    save_expenses();
    save_earnings();
}

void budget::accounts_module::handle(const std::vector<std::string>& args){
    if(args.size() == 1){
        show_accounts();
    } else {
        auto& subcommand = args[1];

        if(subcommand == "show"){
            show_accounts();
        } else if(subcommand == "all"){
            show_all_accounts();
        } else if(subcommand == "add"){
            account account;
            account.guid = generate_guid();
            account.since = find_new_since();
            account.until = budget::date(2099,12,31);

            edit_string(account.name, "Name", not_empty_checker());
            edit_money(account.amount, "Amount", not_negative_checker());

            if(account_exists(account.name)){
                throw budget_exception("An account with this name already exists");
            }

            auto id = add_data(accounts, std::move(account));
            std::cout << "Account " << id << " has been created" << std::endl;
        } else if(subcommand == "delete"){
            enough_args(args, 3);

            std::size_t id = to_number<std::size_t>(args[2]);

            if(!exists(accounts, id)){
                throw budget_exception("There are no account with id " + args[2]);
            }

            for(auto& expense : all_expenses()){
                if(expense.account == id){
                    throw budget_exception("There are still some expenses linked to this account, cannot delete it");
                }
            }

            for(auto& earning : all_earnings()){
                if(earning.account == id){
                    throw budget_exception("There are still some earnings linked to this account, cannot delete it");
                }
            }

            remove(accounts, id);

            std::cout << "Account " << id << " has been deleted" << std::endl;
        } else if(subcommand == "edit"){
            std::size_t id = 0;

            if(args.size() >= 3){
                enough_args(args, 3);

                id = to_number<std::size_t>(args[2]);

                if(!exists(accounts, id)){
                    throw budget_exception("There are no account with id " + args[2]);
                }
            } else {
                std::string name;
                edit_string(name, "Account", not_empty_checker(), account_checker());

                auto today = budget::local_day();
                id = get_account(name, today.year(), today.month()).id;
            }

            auto& account = get(accounts, id);

            edit_string(account.name, "Name", not_empty_checker());
            edit_money(account.amount, "Amount", not_negative_checker());

            //TODO Verify that there are no OTHER account with this name
            //in the current set of accounts (taking archiving into account)

            std::cout << "Account " << id << " has been modified" << std::endl;

            accounts.changed = true;
        } else if(subcommand == "transfer"){
            std::string from_name;
            edit_string(from_name, "Transfer from", not_empty_checker(), account_checker());

            std::string to_name;
            edit_string(to_name, "Transfer to", not_empty_checker(), account_checker());

            if(from_name == to_name){
                throw budget_exception("Cannot transfer to an from the same account");
            }

            std::string name = "Transfer";
            edit_string(name, "Transfer Name", not_empty_checker());

            money amount;
            edit_money(amount, "Amount", not_negative_checker(), not_zero_checker());

            expense expense;
            expense.guid = generate_guid();
            expense.date = budget::local_day();
            expense.name = name;
            expense.amount = amount;
            expense.account = get_account(from_name, expense.date.year(), expense.date.month()).id;

            add_expense(std::move(expense));

            earning earning;
            earning.guid = generate_guid();
            earning.date = budget::local_day();
            earning.name = name;
            earning.amount = amount;
            earning.account = get_account(to_name, expense.date.year(), expense.date.month()).id;

            add_earning(std::move(earning));
        } else if(subcommand == "migrate"){
            std::string source_account_name;
            edit_string(source_account_name, "Source Account", not_empty_checker(), account_checker());

            std::string destination_account_name;
            edit_string(destination_account_name, "Destination Account", not_empty_checker(), account_checker());

            std::cout << "This command will move all expenses and earnings from \"" << source_account_name
                << "\" to \"" << destination_account_name <<"\" and delete \"" << source_account_name
                << "\". Are you sure you want to proceed ? [yes/no] ? ";

            std::string answer;
            std::getline(std::cin, answer);

            if(answer == "yes" || answer == "y"){
                if(source_account_name == destination_account_name){
                    std::cout << "Migrating an account to itself has no effect" << std::endl;
                } else {
                    //Make sure that we find the destination for
                    //each source accounts

                    for(auto& account : all_accounts()){
                        if(account.name == source_account_name){
                            auto destination_id = get_account_id(destination_account_name, account.since.year(), account.since.month());

                            if(!destination_id){
                                std::cout << "Impossible to find the corresponding account for account " << account.id
                                    << ". This may come from a migration issue. Open an issue on Github if you think that this is a bug" << std::endl;

                                return;
                            }
                        }
                    }

                    std::vector<std::size_t> deleted;

                    //Perform the migration

                    for(auto& account : all_accounts()){
                        if(account.name == source_account_name){
                            auto source_id = account.id;
                            auto& destination_account = get_account(destination_account_name, account.since.year(), account.since.month());
                            auto destination_id = destination_account.id;

                            std::cout << "Migrate account " << source_id << " to account " << destination_id << std::endl;

                            destination_account.amount += account.amount;

                            adapt(all_expenses(), source_id, destination_id);
                            adapt(all_earnings(), source_id, destination_id);

                            deleted.push_back(source_id);
                        }
                    }

                    set_expenses_changed();
                    set_earnings_changed();

                    //Delete the source accounts

                    for(auto& id : deleted){
                        std::cout << "Delete account " << id << std::endl;

                        remove(accounts, id);
                    }

                    set_accounts_changed();

                    std::cout << "Migration done" << std::endl;
                }
            }
        } else if(subcommand == "archive"){
            std::cout << "This command will create new accounts that will be used starting from the beginning of the current month. Are you sure you want to proceed ? [yes/no] ? ";

            std::string answer;
            std::cin >> answer;

            if(answer == "yes" || answer == "y"){
                std::vector<std::size_t> sources;
                std::vector<budget::account> copies;

                auto today = budget::local_day();

                auto tmp = budget::local_day() - months(1);
                budget::date until_date(tmp.year(), tmp.month(), tmp.end_of_month().day());

                for(auto& account : all_accounts()){
                    if(account.until == budget::date(2099,12,31)){
                        budget::account copy;
                        copy.guid = generate_guid();
                        copy.name = account.name;
                        copy.amount = account.amount;
                        copy.until = budget::date(2099,12,31);
                        copy.since = until_date + days(1);

                        account.until = until_date;

                        copies.push_back(std::move(copy));

                        sources.push_back(account.id);
                    }
                }

                std::unordered_map<std::size_t, std::size_t> mapping;

                for(std::size_t i = 0; i < copies.size(); ++i){
                    auto& copy = copies[i];

                    auto id = add_data(accounts, std::move(copy));

                    mapping[sources[i]] = id;
                }

                for(auto& expense : all_expenses()){
                    if(expense.date.month() == today.month() && expense.date.year() == today.year()){
                        if(mapping.find(expense.account) != mapping.end()){
                            expense.account = mapping[expense.account];
                            set_expenses_changed();
                        }
                    }
                }

                for(auto& earning : all_earnings()){
                    if(earning.date.month() == today.month() && earning.date.year() == today.year()){
                        if(mapping.find(earning.account) != mapping.end()){
                            earning.account = mapping[earning.account];
                            set_earnings_changed();
                        }
                    }
                }

                accounts.changed = true;
            }
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::load_accounts(){
    load_data(accounts, "accounts.data");
}

void budget::save_accounts(){
    save_data(accounts, "accounts.data");
}

budget::account& budget::get_account(std::size_t id){
    return get(accounts, id);
}

budget::account& budget::get_account(std::string name, budget::year year, budget::month month){
    budget::date date(year, month, 5);

    for(auto& account : accounts.data){
        if(account.since < date && account.until > date && account.name == name){
            return account;
        }
    }

    cpp_unreachable("The account does not exist");
}

std::ostream& budget::operator<<(std::ostream& stream, const account& account){
    return stream << account.id  << ':' << account.guid << ':' << account.name << ':' << account.amount << ':' << to_string(account.since) << ':' << to_string(account.until);
}

void budget::operator>>(const std::vector<std::string>& parts, account& account){
    account.id = to_number<std::size_t>(parts[0]);
    account.guid = parts[1];
    account.name = parts[2];
    account.amount = parse_money(parts[3]);
    account.since = from_string(parts[4]);
    account.until = from_string(parts[5]);
}

bool budget::account_exists(const std::string& name){
    for(auto& account : accounts.data){
        if(account.name == name){
            return true;
        }
    }

    return false;
}

std::vector<account>& budget::all_accounts(){
    return accounts.data;
}

std::vector<account> budget::all_accounts(budget::year year, budget::month month){
    std::vector<account> accounts;

    budget::date date(year, month, 5);

    for(auto& account : all_accounts()){
        if(account.since < date && account.until > date){
            accounts.push_back(account);
        }
    }

    return std::move(accounts);
}

void budget::set_accounts_changed(){
    accounts.changed = true;
}

void budget::set_accounts_next_id(std::size_t next_id){
    accounts.next_id = next_id;
}
<commit_msg>Easy delete<commit_after>//=======================================================================
// Copyright (c) 2013-2014 Baptiste Wicht.
// Distributed under the terms of the MIT License.
// (See accompanying file LICENSE or copy at
//  http://opensource.org/licenses/MIT)
//=======================================================================

#include <iostream>
#include <fstream>
#include <sstream>
#include <unordered_map>

#include "accounts.hpp"
#include "budget_exception.hpp"
#include "args.hpp"
#include "data.hpp"
#include "guid.hpp"
#include "config.hpp"
#include "utils.hpp"
#include "console.hpp"
#include "earnings.hpp"
#include "expenses.hpp"

using namespace budget;

namespace {

static data_handler<account> accounts;

void show_accounts(){
    std::vector<std::string> columns = {"ID", "Name", "Amount"};
    std::vector<std::vector<std::string>> contents;

    money total;

    for(auto& account : accounts.data){
        if(account.until == budget::date(2099,12,31)){
            contents.push_back({to_string(account.id), account.name, to_string(account.amount)});

            total += account.amount;
        }
    }

    contents.push_back({"", "Total", to_string(total)});

    display_table(columns, contents);
}

void show_all_accounts(){
    std::vector<std::string> columns = {"ID", "Name", "Amount", "Since", "Until"};
    std::vector<std::vector<std::string>> contents;

    for(auto& account : accounts.data){
        contents.push_back({to_string(account.id), account.name, to_string(account.amount), to_string(account.since), to_string(account.until)});
    }

    display_table(columns, contents);
}

budget::date find_new_since(){
    budget::date date(1400,1,1);

    for(auto& account : all_accounts()){
        if(account.until != budget::date(2099,12,31)){
            if(account.until - days(1) > date){
                date = account.until - days(1);
            }
        }
    }

    return date;
}

std::size_t get_account_id(std::string name, budget::year year, budget::month month){
    budget::date date(year, month, 5);

    for(auto& account : accounts.data){
        if(account.since < date && account.until > date && account.name == name){
            return account.id;
        }
    }

    return 0;
}

template<typename Values>
void adapt(Values& values, std::size_t old, std::size_t id){
    for(auto& value : values){
        if(value.account == old){
            value.account = id;
        }
    }
}

} //end of anonymous namespace

void budget::accounts_module::load(){
    load_accounts();
    load_expenses();
    load_earnings();
}

void budget::accounts_module::unload(){
    save_accounts();
    save_expenses();
    save_earnings();
}

void budget::accounts_module::handle(const std::vector<std::string>& args){
    if(args.size() == 1){
        show_accounts();
    } else {
        auto& subcommand = args[1];

        if(subcommand == "show"){
            show_accounts();
        } else if(subcommand == "all"){
            show_all_accounts();
        } else if(subcommand == "add"){
            account account;
            account.guid = generate_guid();
            account.since = find_new_since();
            account.until = budget::date(2099,12,31);

            edit_string(account.name, "Name", not_empty_checker());
            edit_money(account.amount, "Amount", not_negative_checker());

            if(account_exists(account.name)){
                throw budget_exception("An account with this name already exists");
            }

            auto id = add_data(accounts, std::move(account));
            std::cout << "Account " << id << " has been created" << std::endl;
        } else if(subcommand == "delete"){
            std::size_t id = 0;

            if(args.size() >= 3){
                enough_args(args, 3);

                id = to_number<std::size_t>(args[2]);

                if(!exists(accounts, id)){
                    throw budget_exception("There are no account with id " + args[2]);
                }
            } else {
                std::string name;
                edit_string(name, "Account", not_empty_checker(), account_checker());

                auto today = budget::local_day();
                id = get_account(name, today.year(), today.month()).id;
            }

            for(auto& expense : all_expenses()){
                if(expense.account == id){
                    throw budget_exception("There are still some expenses linked to this account, cannot delete it");
                }
            }

            for(auto& earning : all_earnings()){
                if(earning.account == id){
                    throw budget_exception("There are still some earnings linked to this account, cannot delete it");
                }
            }

            remove(accounts, id);

            std::cout << "Account " << id << " has been deleted" << std::endl;
        } else if(subcommand == "edit"){
            std::size_t id = 0;

            if(args.size() >= 3){
                enough_args(args, 3);

                id = to_number<std::size_t>(args[2]);

                if(!exists(accounts, id)){
                    throw budget_exception("There are no account with id " + args[2]);
                }
            } else {
                std::string name;
                edit_string(name, "Account", not_empty_checker(), account_checker());

                auto today = budget::local_day();
                id = get_account(name, today.year(), today.month()).id;
            }

            auto& account = get(accounts, id);

            edit_string(account.name, "Name", not_empty_checker());
            edit_money(account.amount, "Amount", not_negative_checker());

            //TODO Verify that there are no OTHER account with this name
            //in the current set of accounts (taking archiving into account)

            std::cout << "Account " << id << " has been modified" << std::endl;

            accounts.changed = true;
        } else if(subcommand == "transfer"){
            std::string from_name;
            edit_string(from_name, "Transfer from", not_empty_checker(), account_checker());

            std::string to_name;
            edit_string(to_name, "Transfer to", not_empty_checker(), account_checker());

            if(from_name == to_name){
                throw budget_exception("Cannot transfer to an from the same account");
            }

            std::string name = "Transfer";
            edit_string(name, "Transfer Name", not_empty_checker());

            money amount;
            edit_money(amount, "Amount", not_negative_checker(), not_zero_checker());

            expense expense;
            expense.guid = generate_guid();
            expense.date = budget::local_day();
            expense.name = name;
            expense.amount = amount;
            expense.account = get_account(from_name, expense.date.year(), expense.date.month()).id;

            add_expense(std::move(expense));

            earning earning;
            earning.guid = generate_guid();
            earning.date = budget::local_day();
            earning.name = name;
            earning.amount = amount;
            earning.account = get_account(to_name, expense.date.year(), expense.date.month()).id;

            add_earning(std::move(earning));
        } else if(subcommand == "migrate"){
            std::string source_account_name;
            edit_string(source_account_name, "Source Account", not_empty_checker(), account_checker());

            std::string destination_account_name;
            edit_string(destination_account_name, "Destination Account", not_empty_checker(), account_checker());

            std::cout << "This command will move all expenses and earnings from \"" << source_account_name
                << "\" to \"" << destination_account_name <<"\" and delete \"" << source_account_name
                << "\". Are you sure you want to proceed ? [yes/no] ? ";

            std::string answer;
            std::getline(std::cin, answer);

            if(answer == "yes" || answer == "y"){
                if(source_account_name == destination_account_name){
                    std::cout << "Migrating an account to itself has no effect" << std::endl;
                } else {
                    //Make sure that we find the destination for
                    //each source accounts

                    for(auto& account : all_accounts()){
                        if(account.name == source_account_name){
                            auto destination_id = get_account_id(destination_account_name, account.since.year(), account.since.month());

                            if(!destination_id){
                                std::cout << "Impossible to find the corresponding account for account " << account.id
                                    << ". This may come from a migration issue. Open an issue on Github if you think that this is a bug" << std::endl;

                                return;
                            }
                        }
                    }

                    std::vector<std::size_t> deleted;

                    //Perform the migration

                    for(auto& account : all_accounts()){
                        if(account.name == source_account_name){
                            auto source_id = account.id;
                            auto& destination_account = get_account(destination_account_name, account.since.year(), account.since.month());
                            auto destination_id = destination_account.id;

                            std::cout << "Migrate account " << source_id << " to account " << destination_id << std::endl;

                            destination_account.amount += account.amount;

                            adapt(all_expenses(), source_id, destination_id);
                            adapt(all_earnings(), source_id, destination_id);

                            deleted.push_back(source_id);
                        }
                    }

                    set_expenses_changed();
                    set_earnings_changed();

                    //Delete the source accounts

                    for(auto& id : deleted){
                        std::cout << "Delete account " << id << std::endl;

                        remove(accounts, id);
                    }

                    set_accounts_changed();

                    std::cout << "Migration done" << std::endl;
                }
            }
        } else if(subcommand == "archive"){
            std::cout << "This command will create new accounts that will be used starting from the beginning of the current month. Are you sure you want to proceed ? [yes/no] ? ";

            std::string answer;
            std::cin >> answer;

            if(answer == "yes" || answer == "y"){
                std::vector<std::size_t> sources;
                std::vector<budget::account> copies;

                auto today = budget::local_day();

                auto tmp = budget::local_day() - months(1);
                budget::date until_date(tmp.year(), tmp.month(), tmp.end_of_month().day());

                for(auto& account : all_accounts()){
                    if(account.until == budget::date(2099,12,31)){
                        budget::account copy;
                        copy.guid = generate_guid();
                        copy.name = account.name;
                        copy.amount = account.amount;
                        copy.until = budget::date(2099,12,31);
                        copy.since = until_date + days(1);

                        account.until = until_date;

                        copies.push_back(std::move(copy));

                        sources.push_back(account.id);
                    }
                }

                std::unordered_map<std::size_t, std::size_t> mapping;

                for(std::size_t i = 0; i < copies.size(); ++i){
                    auto& copy = copies[i];

                    auto id = add_data(accounts, std::move(copy));

                    mapping[sources[i]] = id;
                }

                for(auto& expense : all_expenses()){
                    if(expense.date.month() == today.month() && expense.date.year() == today.year()){
                        if(mapping.find(expense.account) != mapping.end()){
                            expense.account = mapping[expense.account];
                            set_expenses_changed();
                        }
                    }
                }

                for(auto& earning : all_earnings()){
                    if(earning.date.month() == today.month() && earning.date.year() == today.year()){
                        if(mapping.find(earning.account) != mapping.end()){
                            earning.account = mapping[earning.account];
                            set_earnings_changed();
                        }
                    }
                }

                accounts.changed = true;
            }
        } else {
            throw budget_exception("Invalid subcommand \"" + subcommand + "\"");
        }
    }
}

void budget::load_accounts(){
    load_data(accounts, "accounts.data");
}

void budget::save_accounts(){
    save_data(accounts, "accounts.data");
}

budget::account& budget::get_account(std::size_t id){
    return get(accounts, id);
}

budget::account& budget::get_account(std::string name, budget::year year, budget::month month){
    budget::date date(year, month, 5);

    for(auto& account : accounts.data){
        if(account.since < date && account.until > date && account.name == name){
            return account;
        }
    }

    cpp_unreachable("The account does not exist");
}

std::ostream& budget::operator<<(std::ostream& stream, const account& account){
    return stream << account.id  << ':' << account.guid << ':' << account.name << ':' << account.amount << ':' << to_string(account.since) << ':' << to_string(account.until);
}

void budget::operator>>(const std::vector<std::string>& parts, account& account){
    account.id = to_number<std::size_t>(parts[0]);
    account.guid = parts[1];
    account.name = parts[2];
    account.amount = parse_money(parts[3]);
    account.since = from_string(parts[4]);
    account.until = from_string(parts[5]);
}

bool budget::account_exists(const std::string& name){
    for(auto& account : accounts.data){
        if(account.name == name){
            return true;
        }
    }

    return false;
}

std::vector<account>& budget::all_accounts(){
    return accounts.data;
}

std::vector<account> budget::all_accounts(budget::year year, budget::month month){
    std::vector<account> accounts;

    budget::date date(year, month, 5);

    for(auto& account : all_accounts()){
        if(account.since < date && account.until > date){
            accounts.push_back(account);
        }
    }

    return std::move(accounts);
}

void budget::set_accounts_changed(){
    accounts.changed = true;
}

void budget::set_accounts_next_id(std::size_t next_id){
    accounts.next_id = next_id;
}
<|endoftext|>


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

#include "base/testing.h"

#include "replicated_log_impl.h"

using namespace consensus;

class ReplicatedLogTest : public BaseTest {
 public:
  void SetUp() override {
    guard = CreateTestDirGuard();

    options.initial_cluster[1] = "127.0.0.1:12321";
    options.id = 1;
    options.wal_dir = GetTestDir();
    options.heartbeat_interval = 100;
    options.election_timeout = 1000;
    options.taskQueue = new TaskQueue;
    options.timer = new RaftTimer;
    options.flusher = new ReadyFlusher;
  }

  void TearDown() override {
    delete options.timer;
    delete options.flusher;

    delete replicatedLog;
    delete guard;

    delete options.taskQueue;
  }

 protected:
  ReplicatedLog* replicatedLog;
  ReplicatedLogOptions options;
  TestDirectoryGuard* guard;
};

TEST_F(ReplicatedLogTest, WriteToNonLeader) {
  ASSIGN_IF_ASSERT_OK(ReplicatedLog::New(options), replicatedLog);

  Status s = replicatedLog->Write("abc");
  ASSERT_EQ(s.Code(), Error::WalWriteToNonLeader);
}

TEST_F(ReplicatedLogTest, WriteLeader) {
  ASSIGN_IF_ASSERT_OK(ReplicatedLog::New(options), replicatedLog);

  while (replicatedLog->GetInfo().currentTerm != 1) {
    sleep(1);
  }

  for (int i = 0; i < 4; i++) {
    Status s = replicatedLog->Write("abc");
    ASSERT_OK(s);
  }

  ASSERT_EQ(replicatedLog->GetInfo().logIndex, 5);
  ASSERT_EQ(replicatedLog->GetInfo().commitIndex, 5);
  ASSERT_EQ(replicatedLog->GetInfo().currentTerm, 1);
  ASSERT_EQ(replicatedLog->GetInfo().currentLeader, 1);
}<commit_msg>replicated_log: fix unit test<commit_after>// Copyright 2017 Wu Tao
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "base/testing.h"

#include "replicated_log_impl.h"

using namespace consensus;

class ReplicatedLogTest : public BaseTest {
 public:
  void SetUp() override {
    options.initial_cluster[1] = "127.0.0.1:12321";
    options.id = 1;
    options.heartbeat_interval = 100;
    options.election_timeout = 1000;
    options.taskQueue = new TaskQueue;
    options.timer = new RaftTimer;
    options.flusher = new ReadyFlusher;
    options.memstore = new yaraft::MemoryStorage;
    options.wal = wal::TEST_GetWalStore(GetTestDir(), options.memstore);
  }

  void TearDown() override {
    delete options.timer;
    delete options.flusher;

    delete replicatedLog;
    delete options.taskQueue;
  }

 protected:
  ReplicatedLog* replicatedLog;
  ReplicatedLogOptions options;
};

TEST_F(ReplicatedLogTest, WriteToNonLeader) {
  TestDirGuard g(CreateTestDirGuard());
  ASSIGN_IF_ASSERT_OK(ReplicatedLog::New(options), replicatedLog);

  Status s = replicatedLog->Write("abc");
  ASSERT_EQ(s.Code(), Error::WalWriteToNonLeader);
}

TEST_F(ReplicatedLogTest, WriteLeader) {
  TestDirGuard g(CreateTestDirGuard());
  ASSIGN_IF_ASSERT_OK(ReplicatedLog::New(options), replicatedLog);

  while (replicatedLog->GetInfo().currentTerm != 1) {
    sleep(1);
  }

  for (int i = 0; i < 4; i++) {
    Status s = replicatedLog->Write("abc");
    ASSERT_OK(s);
  }

  ASSERT_EQ(replicatedLog->GetInfo().logIndex, 5);
  ASSERT_EQ(replicatedLog->GetInfo().commitIndex, 5);
  ASSERT_EQ(replicatedLog->GetInfo().currentTerm, 1);
  ASSERT_EQ(replicatedLog->GetInfo().currentLeader, 1);
}<|endoftext|>