<commit_before>#include "MacauPrior.h"

#include <SmurffCpp/IO/MatrixIO.h>
#include <SmurffCpp/IO/GenericIO.h>

#include <SmurffCpp/Utils/Distribution.h>
#include <SmurffCpp/Utils/Error.h>
#include <SmurffCpp/Utils/counters.h>

#include <SmurffCpp/Utils/linop.h>

#include <ios>

using namespace smurff;

MacauPrior::MacauPrior()
   : NormalPrior()
{
}

MacauPrior::MacauPrior(std::shared_ptr<BaseSession> session, uint32_t mode)
   : NormalPrior(session, mode, "MacauPrior")
{
   beta_precision = SideInfoConfig::BETA_PRECISION_DEFAULT_VALUE;
   tol = SideInfoConfig::TOL_DEFAULT_VALUE;

   enable_beta_precision_sampling = Config::ENABLE_BETA_PRECISION_SAMPLING_DEFAULT_VALUE;
}

MacauPrior::~MacauPrior()
{
}

void MacauPrior::init()
{
   NormalPrior::init();

   THROWERROR_ASSERT_MSG(Features->rows() == num_cols(), "Number of rows in train must be equal to number of rows in features");

   if (use_FtF)
   {
      std::uint64_t dim = Features->cols();
      FtF_plus_beta.resize(dim, dim);
      Features->At_mul_A(FtF_plus_beta);
      FtF_plus_beta.diagonal().array() += beta_precision;
   }

   Uhat.resize(this->num_latent(), Features->rows());
   Uhat.setZero();

   m_beta = std::make_shared<Eigen::MatrixXd>(this->num_latent(), Features->cols());
   m_beta->setZero();

   m_session->model().setLinkMatrix(m_mode, m_beta);
}

void MacauPrior::update_prior()
{
    COUNTER("update_prior");
    // residual (Uhat is later overwritten):
    Uhat.noalias() = U() - Uhat;
    Eigen::MatrixXd BBt = smurff::linop::A_mul_At_combo(*m_beta);

    // sampling Gaussian
    std::tie(this->mu, this->Lambda) = CondNormalWishart(Uhat, this->mu0, this->b0, this->WI + beta_precision * BBt, this->df + m_beta->cols());
    sample_beta();
    Features->compute_uhat(Uhat, *m_beta);

    if (enable_beta_precision_sampling)
    {
        double old_beta = beta_precision;
        beta_precision = sample_beta_precision(*m_beta, this->Lambda, beta_precision_nu0, beta_precision_mu0);
        FtF_plus_beta.diagonal().array() += beta_precision - old_beta;
   }
}

const Eigen::VectorXd MacauPrior::getMu(int n) const
{
   return this->mu + Uhat.col(n);
}

void MacauPrior::compute_Ft_y_omp(Eigen::MatrixXd& Ft_y)
{
   const int num_feat = m_beta->cols();

   // Ft_y = (U .- mu + Normal(0, Lambda^-1)) * F + std::sqrt(beta_precision) * Normal(0, Lambda^-1)
   // Ft_y is [ D x F ] matrix
   HyperU = (U() + MvNormal_prec(Lambda, num_cols())).colwise() - mu;

   Ft_y = Features->A_mul_B(HyperU);
   HyperU2 = MvNormal_prec(Lambda, num_feat);

   #pragma omp parallel for schedule(static)
   for (int f = 0; f < num_feat; f++)
   {
      for (int d = 0; d < num_latent(); d++)
      {
         Ft_y(d, f) += std::sqrt(beta_precision) * HyperU2(d, f);
      }
   }
}

void MacauPrior::sample_beta()
{
   COUNTER("sample_beta");
   if (use_FtF)
      sample_beta_direct();
   else
      sample_beta_cg();
}

void MacauPrior::addSideInfo(const std::shared_ptr<ISideInfo>& side_info_a, double beta_precision_a, double tolerance_a, bool direct_a, bool enable_beta_precision_sampling_a, bool throw_on_cholesky_error_a)
{
   //FIXME: remove old code

   // old code

   // side information
   Features = side_info_a;
   beta_precision = beta_precision_a;
   tol = tolerance_a;
   use_FtF = direct_a;
   enable_beta_precision_sampling = enable_beta_precision_sampling_a;
   throw_on_cholesky_error = throw_on_cholesky_error_a;

   // new code

   // side information
   side_info_values.push_back(side_info_a);
   beta_precision_values.push_back(beta_precision_a);
   tol_values.push_back(tolerance_a);
   direct_values.push_back(direct_a);
   enable_beta_precision_sampling_values.push_back(enable_beta_precision_sampling_a);
   throw_on_cholesky_error_values.push_back(throw_on_cholesky_error_a);

   // other code

   // Hyper-prior for beta_precision (mean 1.0, var of 1e+3):
   beta_precision_mu0 = 1.0;
   beta_precision_nu0 = 1e-3;
}

bool MacauPrior::save(std::shared_ptr<const StepFile> sf) const
{
   NormalPrior::save(sf);

   std::string path = sf->getLinkMatrixFileName(m_mode);
   smurff::matrix_io::eigen::write_matrix(path, *m_beta);

   return true;
}

void MacauPrior::restore(std::shared_ptr<const StepFile> sf)
{
   NormalPrior::restore(sf);

   std::string path = sf->getLinkMatrixFileName(m_mode);

   THROWERROR_FILE_NOT_EXIST(path);

   smurff::matrix_io::eigen::read_matrix(path, *m_beta);
}

std::ostream& MacauPrior::info(std::ostream &os, std::string indent)
{
   NormalPrior::info(os, indent);
   os << indent << " SideInfo: ";
   Features->print(os);
   os << indent << " Method: ";
   if (use_FtF)
   {
      os << "Cholesky Decomposition";
      double needs_gb = (double)Features->cols() / 1024. * (double)Features->cols() / 1024. / 1024.;
      if (needs_gb > 1.0) os << " (needing " << needs_gb << " GB of memory)";
      os << std::endl;
   } else {
      os << "CG Solver" << std::endl;
      os << indent << "  with tolerance: " << std::scientific << tol << std::fixed << std::endl;
   }
   os << indent << " BetaPrecision: " << beta_precision << std::endl;
   return os;
}

std::ostream& MacauPrior::status(std::ostream &os, std::string indent) const
{
   os << indent << m_name << ": " << std::endl;
   indent += "  ";
   os << indent << "blockcg iter = " << blockcg_iter << std::endl;
   os << indent << "FtF_plus_beta= " << FtF_plus_beta.norm() << std::endl;
   os << indent << "HyperU       = " << HyperU.norm() << std::endl;
   os << indent << "HyperU2      = " << HyperU2.norm() << std::endl;
   os << indent << "Beta         = " << m_beta->norm() << std::endl;
   os << indent << "beta_precision  = " << beta_precision << std::endl;
   os << indent << "Ft_y         = " << Ft_y.norm() << std::endl;
   return os;
}

// direct method
void MacauPrior::sample_beta_direct()
{
    this->compute_Ft_y_omp(Ft_y);
    *m_beta = FtF_plus_beta.llt().solve(Ft_y.transpose()).transpose();
}

std::pair<double, double> MacauPrior::posterior_beta_precision(Eigen::MatrixXd & beta, Eigen::MatrixXd & Lambda_u, double nu, double mu)
{
   const int D = beta.rows();
   Eigen::MatrixXd BB(D, D);
   smurff::linop::A_mul_At_combo(BB, beta);
   double nux = nu + beta.rows() * beta.cols();
   double mux = mu * nux / (nu + mu * (BB.selfadjointView<Eigen::Lower>() * Lambda_u).trace());
   double b = nux / 2;
   double c = 2 * mux / nux;
   return std::make_pair(b, c);
}

double MacauPrior::sample_beta_precision(Eigen::MatrixXd & beta, Eigen::MatrixXd & Lambda_u, double nu, double mu)
{
   auto gamma_post = posterior_beta_precision(beta, Lambda_u, nu, mu);
   return rgamma(gamma_post.first, gamma_post.second);
}

void MacauPrior::sample_beta_cg()
{
    Eigen::MatrixXd Ft_y;
    this->compute_Ft_y_omp(Ft_y);

    blockcg_iter = Features->solve_blockcg(*m_beta, beta_precision, Ft_y, tol, 32, 8, throw_on_cholesky_error);
}
<commit_msg>ENH: print CG info on one line<commit_after>#include "MacauPrior.h"

#include <SmurffCpp/IO/MatrixIO.h>
#include <SmurffCpp/IO/GenericIO.h>

#include <SmurffCpp/Utils/Distribution.h>
#include <SmurffCpp/Utils/Error.h>
#include <SmurffCpp/Utils/counters.h>

#include <SmurffCpp/Utils/linop.h>

#include <ios>

using namespace smurff;

MacauPrior::MacauPrior()
   : NormalPrior()
{
}

MacauPrior::MacauPrior(std::shared_ptr<BaseSession> session, uint32_t mode)
   : NormalPrior(session, mode, "MacauPrior")
{
   beta_precision = SideInfoConfig::BETA_PRECISION_DEFAULT_VALUE;
   tol = SideInfoConfig::TOL_DEFAULT_VALUE;

   enable_beta_precision_sampling = Config::ENABLE_BETA_PRECISION_SAMPLING_DEFAULT_VALUE;
}

MacauPrior::~MacauPrior()
{
}

void MacauPrior::init()
{
   NormalPrior::init();

   THROWERROR_ASSERT_MSG(Features->rows() == num_cols(), "Number of rows in train must be equal to number of rows in features");

   if (use_FtF)
   {
      std::uint64_t dim = Features->cols();
      FtF_plus_beta.resize(dim, dim);
      Features->At_mul_A(FtF_plus_beta);
      FtF_plus_beta.diagonal().array() += beta_precision;
   }

   Uhat.resize(this->num_latent(), Features->rows());
   Uhat.setZero();

   m_beta = std::make_shared<Eigen::MatrixXd>(this->num_latent(), Features->cols());
   m_beta->setZero();

   m_session->model().setLinkMatrix(m_mode, m_beta);
}

void MacauPrior::update_prior()
{
    COUNTER("update_prior");
    // residual (Uhat is later overwritten):
    Uhat.noalias() = U() - Uhat;
    Eigen::MatrixXd BBt = smurff::linop::A_mul_At_combo(*m_beta);

    // sampling Gaussian
    std::tie(this->mu, this->Lambda) = CondNormalWishart(Uhat, this->mu0, this->b0, this->WI + beta_precision * BBt, this->df + m_beta->cols());
    sample_beta();
    Features->compute_uhat(Uhat, *m_beta);

    if (enable_beta_precision_sampling)
    {
        double old_beta = beta_precision;
        beta_precision = sample_beta_precision(*m_beta, this->Lambda, beta_precision_nu0, beta_precision_mu0);
        FtF_plus_beta.diagonal().array() += beta_precision - old_beta;
   }
}

const Eigen::VectorXd MacauPrior::getMu(int n) const
{
   return this->mu + Uhat.col(n);
}

void MacauPrior::compute_Ft_y_omp(Eigen::MatrixXd& Ft_y)
{
   const int num_feat = m_beta->cols();

   // Ft_y = (U .- mu + Normal(0, Lambda^-1)) * F + std::sqrt(beta_precision) * Normal(0, Lambda^-1)
   // Ft_y is [ D x F ] matrix
   HyperU = (U() + MvNormal_prec(Lambda, num_cols())).colwise() - mu;

   Ft_y = Features->A_mul_B(HyperU);
   HyperU2 = MvNormal_prec(Lambda, num_feat);

   #pragma omp parallel for schedule(static)
   for (int f = 0; f < num_feat; f++)
   {
      for (int d = 0; d < num_latent(); d++)
      {
         Ft_y(d, f) += std::sqrt(beta_precision) * HyperU2(d, f);
      }
   }
}

void MacauPrior::sample_beta()
{
   COUNTER("sample_beta");
   if (use_FtF)
      sample_beta_direct();
   else
      sample_beta_cg();
}

void MacauPrior::addSideInfo(const std::shared_ptr<ISideInfo>& side_info_a, double beta_precision_a, double tolerance_a, bool direct_a, bool enable_beta_precision_sampling_a, bool throw_on_cholesky_error_a)
{
   //FIXME: remove old code

   // old code

   // side information
   Features = side_info_a;
   beta_precision = beta_precision_a;
   tol = tolerance_a;
   use_FtF = direct_a;
   enable_beta_precision_sampling = enable_beta_precision_sampling_a;
   throw_on_cholesky_error = throw_on_cholesky_error_a;

   // new code

   // side information
   side_info_values.push_back(side_info_a);
   beta_precision_values.push_back(beta_precision_a);
   tol_values.push_back(tolerance_a);
   direct_values.push_back(direct_a);
   enable_beta_precision_sampling_values.push_back(enable_beta_precision_sampling_a);
   throw_on_cholesky_error_values.push_back(throw_on_cholesky_error_a);

   // other code

   // Hyper-prior for beta_precision (mean 1.0, var of 1e+3):
   beta_precision_mu0 = 1.0;
   beta_precision_nu0 = 1e-3;
}

bool MacauPrior::save(std::shared_ptr<const StepFile> sf) const
{
   NormalPrior::save(sf);

   std::string path = sf->getLinkMatrixFileName(m_mode);
   smurff::matrix_io::eigen::write_matrix(path, *m_beta);

   return true;
}

void MacauPrior::restore(std::shared_ptr<const StepFile> sf)
{
   NormalPrior::restore(sf);

   std::string path = sf->getLinkMatrixFileName(m_mode);

   THROWERROR_FILE_NOT_EXIST(path);

   smurff::matrix_io::eigen::read_matrix(path, *m_beta);
}

std::ostream& MacauPrior::info(std::ostream &os, std::string indent)
{
   NormalPrior::info(os, indent);
   os << indent << " SideInfo: ";
   Features->print(os);
   os << indent << " Method: ";
   if (use_FtF)
   {
      os << "Cholesky Decomposition";
      double needs_gb = (double)Features->cols() / 1024. * (double)Features->cols() / 1024. / 1024.;
      if (needs_gb > 1.0) os << " (needing " << needs_gb << " GB of memory)";
      os << std::endl;
   } else {
      os << "CG Solver with tolerance: " << std::scientific << tol << std::fixed << std::endl;
   }
   os << indent << " BetaPrecision: " << beta_precision << std::endl;
   return os;
}

std::ostream& MacauPrior::status(std::ostream &os, std::string indent) const
{
   os << indent << m_name << ": " << std::endl;
   indent += "  ";
   os << indent << "blockcg iter = " << blockcg_iter << std::endl;
   os << indent << "FtF_plus_beta= " << FtF_plus_beta.norm() << std::endl;
   os << indent << "HyperU       = " << HyperU.norm() << std::endl;
   os << indent << "HyperU2      = " << HyperU2.norm() << std::endl;
   os << indent << "Beta         = " << m_beta->norm() << std::endl;
   os << indent << "beta_precision  = " << beta_precision << std::endl;
   os << indent << "Ft_y         = " << Ft_y.norm() << std::endl;
   return os;
}

// direct method
void MacauPrior::sample_beta_direct()
{
    this->compute_Ft_y_omp(Ft_y);
    *m_beta = FtF_plus_beta.llt().solve(Ft_y.transpose()).transpose();
}

std::pair<double, double> MacauPrior::posterior_beta_precision(Eigen::MatrixXd & beta, Eigen::MatrixXd & Lambda_u, double nu, double mu)
{
   const int D = beta.rows();
   Eigen::MatrixXd BB(D, D);
   smurff::linop::A_mul_At_combo(BB, beta);
   double nux = nu + beta.rows() * beta.cols();
   double mux = mu * nux / (nu + mu * (BB.selfadjointView<Eigen::Lower>() * Lambda_u).trace());
   double b = nux / 2;
   double c = 2 * mux / nux;
   return std::make_pair(b, c);
}

double MacauPrior::sample_beta_precision(Eigen::MatrixXd & beta, Eigen::MatrixXd & Lambda_u, double nu, double mu)
{
   auto gamma_post = posterior_beta_precision(beta, Lambda_u, nu, mu);
   return rgamma(gamma_post.first, gamma_post.second);
}

void MacauPrior::sample_beta_cg()
{
    Eigen::MatrixXd Ft_y;
    this->compute_Ft_y_omp(Ft_y);

    blockcg_iter = Features->solve_blockcg(*m_beta, beta_precision, Ft_y, tol, 32, 8, throw_on_cholesky_error);
}
<|endoftext|>


<commit_before>#include "TensorUtils.h"

#include <SmurffCpp/Utils/Error.h>

Eigen::MatrixXd smurff::tensor_utils::dense_to_eigen(const smurff::TensorConfig& tensorConfig)
{
   if(!tensorConfig.isDense())
      THROWERROR("tensor config should be dense");

   if(tensorConfig.getNModes() != 2)
      THROWERROR("Invalid number of dimensions. Tensor can not be converted to matrix.");

   std::vector<double> Yvalues = tensorConfig.getValues(); //eigen map can not take const values pointer. have to make copy
   return Eigen::Map<Eigen::MatrixXd>(Yvalues.data(), tensorConfig.getDims()[0], tensorConfig.getDims()[1]);
}

Eigen::MatrixXd smurff::tensor_utils::dense_to_eigen(smurff::TensorConfig& tensorConfig)
{
   const smurff::TensorConfig& tc = tensorConfig;
   return smurff::tensor_utils::dense_to_eigen(tc);
}

template<>
Eigen::SparseMatrix<double> smurff::tensor_utils::sparse_to_eigen<const smurff::TensorConfig>(const smurff::TensorConfig& tensorConfig)
{
   if(tensorConfig.isDense())
      THROWERROR("tensor config should be sparse");

   if(tensorConfig.getNModes() != 2)
      THROWERROR("Invalid number of dimensions. Tensor can not be converted to matrix.");

   std::shared_ptr<std::vector<std::uint32_t> > columnsPtr = tensorConfig.getColumnsPtr();
   std::shared_ptr<std::vector<double> > valuesPtr = tensorConfig.getValuesPtr();

   Eigen::SparseMatrix<double> out(tensorConfig.getDims()[0], tensorConfig.getDims()[1]);

   std::vector<Eigen::Triplet<double> > triplets;
   for(std::uint64_t i = 0; i < tensorConfig.getNNZ(); i++)
   {
      double val = valuesPtr->operator[](i);
      std::uint32_t row = columnsPtr->operator[](i);
      std::uint32_t col = columnsPtr->operator[](i + tensorConfig.getNNZ());
      triplets.push_back(Eigen::Triplet<double>(row, col, val));
   }

   out.setFromTriplets(triplets.begin(), triplets.end());

   return out;
}

template<>
Eigen::SparseMatrix<double> smurff::tensor_utils::sparse_to_eigen<smurff::TensorConfig>(smurff::TensorConfig& tensorConfig)
{
   return smurff::tensor_utils::sparse_to_eigen<const smurff::TensorConfig>(tensorConfig);
}

smurff::MatrixConfig smurff::tensor_utils::tensor_to_matrix(const smurff::TensorConfig& tensorConfig)
{
   if(tensorConfig.getNModes() != 2)
      THROWERROR("Invalid number of dimentions. Tensor can not be converted to matrix.");

   if(tensorConfig.isDense())
   {
      return smurff::MatrixConfig(tensorConfig.getDims()[0], tensorConfig.getDims()[1],
         tensorConfig.getValues(),
         tensorConfig.getNoiseConfig());
   }
   else if(tensorConfig.isBinary())
   {
      return smurff::MatrixConfig(tensorConfig.getDims()[0], tensorConfig.getDims()[1],
         tensorConfig.getColumns(),
         tensorConfig.getNoiseConfig(),
         tensorConfig.isScarce());
   }
   else
   {
      return smurff::MatrixConfig(tensorConfig.getDims()[0], tensorConfig.getDims()[1],
         tensorConfig.getColumns(), tensorConfig.getValues(),
         tensorConfig.getNoiseConfig(),
         tensorConfig.isScarce());
   }
}

std::ostream& smurff::tensor_utils::operator << (std::ostream& os, const TensorConfig& tc)
{
   const std::vector<double>& values = tc.getValues();
   const std::vector<std::uint32_t>& columns = tc.getColumns();

   os << "columns: " << std::endl;
   for(std::uint64_t i = 0; i < columns.size(); i++)
      os << columns[i] << ", ";
   os << std::endl;

   os << "values: " << std::endl;
   for(std::uint64_t i = 0; i < values.size(); i++)
      os << values[i] << ", ";
   os << std::endl;

   if(tc.getNModes() == 2)
   {
      os << "dims: " << tc.getDims()[0] << " " << tc.getDims()[1] << std::endl;

      Eigen::SparseMatrix<double> X(tc.getDims()[0], tc.getDims()[1]);

      std::vector<Eigen::Triplet<double> > triplets;
      for(std::uint64_t i = 0; i < tc.getNNZ(); i++)
         triplets.push_back(Eigen::Triplet<double>(columns[i], columns[i + tc.getNNZ()], values[i]));

      os << "NTriplets: " << triplets.size() << std::endl;

      X.setFromTriplets(triplets.begin(), triplets.end());

      os << X << std::endl;
   }

   return os;
}

Eigen::MatrixXd smurff::tensor_utils::slice( const TensorConfig& tensorConfig
                                           , const std::array<std::uint64_t, 2>& fixedDims
                                           , const std::unordered_map<std::uint64_t, std::uint32_t>& dimCoords)
{
   if (fixedDims[0] == fixedDims[1])
      THROWERROR("fixedDims should contain 2 unique dimension numbers");

   for (const std::uint64_t& fd : fixedDims)
      if (fd > tensorConfig.getNModes() - 1)
         THROWERROR("fixedDims should contain only valid for tensorConfig dimension numbers");

   if (dimCoords.size() != (tensorConfig.getNModes() -  2))
      THROWERROR("dimsCoords.size() should be the same as tensorConfig.getNModes() - 2");

   for (const std::unordered_map<std::uint64_t, std::uint32_t>::value_type& dc : dimCoords)
   {
      if (dc.first == fixedDims[0] || dc.first == fixedDims[1])
         THROWERROR("dimCoords and fixedDims should not intersect");

      if (dc.first >= tensorConfig.getNModes())
         THROWERROR("dimCoords should contain only valid for tensorConfig dimension numbers");

      if (dc.second >= tensorConfig.getDims()[dc.first])
         THROWERROR("dimCoords should contain valid coord values for corresponding dimensions");
   }

   std::unordered_map<std::uint64_t, std::vector<std::uint32_t>::const_iterator> dimColumns;
   for (const std::unordered_map<std::uint64_t, std::uint32_t>::value_type& dc : dimCoords)
   {
      std::size_t dimOffset = dc.first * tensorConfig.getValues().size();
      dimColumns[dc.first] = tensorConfig.getColumns().begin() + dimOffset;
   }

   Eigen::MatrixXd sliceMatrix(tensorConfig.getDims()[fixedDims[0]], tensorConfig.getDims()[fixedDims[1]]);
   for (std::size_t i = 0; i < tensorConfig.getValues().size(); i++)
   {
      bool dimCoordsMatchColumns =
         std::accumulate( dimCoords.begin()
                        , dimCoords.end()
                        , true
                        , [&](bool acc, const std::unordered_map<std::uint64_t, std::uint32_t>::value_type& dc)
                          {
                             return acc & (*(dimColumns[dc.first] + i) == dc.second);
                          }
                        );

      if (dimCoordsMatchColumns)
      {
         std::uint32_t d0_coord =
            tensorConfig.getColumns()[fixedDims[0] * tensorConfig.getValues().size() + i];
         std::uint32_t d1_coord =
            tensorConfig.getColumns()[fixedDims[1] * tensorConfig.getValues().size() + i];
         sliceMatrix(d0_coord, d1_coord) = tensorConfig.getValues()[i];
      }
   }
   return sliceMatrix;
}
<commit_msg>add missing include<commit_after>#include <numeric>

#include "TensorUtils.h"

#include <SmurffCpp/Utils/Error.h>

Eigen::MatrixXd smurff::tensor_utils::dense_to_eigen(const smurff::TensorConfig& tensorConfig)
{
   if(!tensorConfig.isDense())
      THROWERROR("tensor config should be dense");

   if(tensorConfig.getNModes() != 2)
      THROWERROR("Invalid number of dimensions. Tensor can not be converted to matrix.");

   std::vector<double> Yvalues = tensorConfig.getValues(); //eigen map can not take const values pointer. have to make copy
   return Eigen::Map<Eigen::MatrixXd>(Yvalues.data(), tensorConfig.getDims()[0], tensorConfig.getDims()[1]);
}

Eigen::MatrixXd smurff::tensor_utils::dense_to_eigen(smurff::TensorConfig& tensorConfig)
{
   const smurff::TensorConfig& tc = tensorConfig;
   return smurff::tensor_utils::dense_to_eigen(tc);
}

template<>
Eigen::SparseMatrix<double> smurff::tensor_utils::sparse_to_eigen<const smurff::TensorConfig>(const smurff::TensorConfig& tensorConfig)
{
   if(tensorConfig.isDense())
      THROWERROR("tensor config should be sparse");

   if(tensorConfig.getNModes() != 2)
      THROWERROR("Invalid number of dimensions. Tensor can not be converted to matrix.");

   std::shared_ptr<std::vector<std::uint32_t> > columnsPtr = tensorConfig.getColumnsPtr();
   std::shared_ptr<std::vector<double> > valuesPtr = tensorConfig.getValuesPtr();

   Eigen::SparseMatrix<double> out(tensorConfig.getDims()[0], tensorConfig.getDims()[1]);

   std::vector<Eigen::Triplet<double> > triplets;
   for(std::uint64_t i = 0; i < tensorConfig.getNNZ(); i++)
   {
      double val = valuesPtr->operator[](i);
      std::uint32_t row = columnsPtr->operator[](i);
      std::uint32_t col = columnsPtr->operator[](i + tensorConfig.getNNZ());
      triplets.push_back(Eigen::Triplet<double>(row, col, val));
   }

   out.setFromTriplets(triplets.begin(), triplets.end());

   return out;
}

template<>
Eigen::SparseMatrix<double> smurff::tensor_utils::sparse_to_eigen<smurff::TensorConfig>(smurff::TensorConfig& tensorConfig)
{
   return smurff::tensor_utils::sparse_to_eigen<const smurff::TensorConfig>(tensorConfig);
}

smurff::MatrixConfig smurff::tensor_utils::tensor_to_matrix(const smurff::TensorConfig& tensorConfig)
{
   if(tensorConfig.getNModes() != 2)
      THROWERROR("Invalid number of dimentions. Tensor can not be converted to matrix.");

   if(tensorConfig.isDense())
   {
      return smurff::MatrixConfig(tensorConfig.getDims()[0], tensorConfig.getDims()[1],
         tensorConfig.getValues(),
         tensorConfig.getNoiseConfig());
   }
   else if(tensorConfig.isBinary())
   {
      return smurff::MatrixConfig(tensorConfig.getDims()[0], tensorConfig.getDims()[1],
         tensorConfig.getColumns(),
         tensorConfig.getNoiseConfig(),
         tensorConfig.isScarce());
   }
   else
   {
      return smurff::MatrixConfig(tensorConfig.getDims()[0], tensorConfig.getDims()[1],
         tensorConfig.getColumns(), tensorConfig.getValues(),
         tensorConfig.getNoiseConfig(),
         tensorConfig.isScarce());
   }
}

std::ostream& smurff::tensor_utils::operator << (std::ostream& os, const TensorConfig& tc)
{
   const std::vector<double>& values = tc.getValues();
   const std::vector<std::uint32_t>& columns = tc.getColumns();

   os << "columns: " << std::endl;
   for(std::uint64_t i = 0; i < columns.size(); i++)
      os << columns[i] << ", ";
   os << std::endl;

   os << "values: " << std::endl;
   for(std::uint64_t i = 0; i < values.size(); i++)
      os << values[i] << ", ";
   os << std::endl;

   if(tc.getNModes() == 2)
   {
      os << "dims: " << tc.getDims()[0] << " " << tc.getDims()[1] << std::endl;

      Eigen::SparseMatrix<double> X(tc.getDims()[0], tc.getDims()[1]);

      std::vector<Eigen::Triplet<double> > triplets;
      for(std::uint64_t i = 0; i < tc.getNNZ(); i++)
         triplets.push_back(Eigen::Triplet<double>(columns[i], columns[i + tc.getNNZ()], values[i]));

      os << "NTriplets: " << triplets.size() << std::endl;

      X.setFromTriplets(triplets.begin(), triplets.end());

      os << X << std::endl;
   }

   return os;
}

Eigen::MatrixXd smurff::tensor_utils::slice( const TensorConfig& tensorConfig
                                           , const std::array<std::uint64_t, 2>& fixedDims
                                           , const std::unordered_map<std::uint64_t, std::uint32_t>& dimCoords)
{
   if (fixedDims[0] == fixedDims[1])
      THROWERROR("fixedDims should contain 2 unique dimension numbers");

   for (const std::uint64_t& fd : fixedDims)
      if (fd > tensorConfig.getNModes() - 1)
         THROWERROR("fixedDims should contain only valid for tensorConfig dimension numbers");

   if (dimCoords.size() != (tensorConfig.getNModes() -  2))
      THROWERROR("dimsCoords.size() should be the same as tensorConfig.getNModes() - 2");

   for (const std::unordered_map<std::uint64_t, std::uint32_t>::value_type& dc : dimCoords)
   {
      if (dc.first == fixedDims[0] || dc.first == fixedDims[1])
         THROWERROR("dimCoords and fixedDims should not intersect");

      if (dc.first >= tensorConfig.getNModes())
         THROWERROR("dimCoords should contain only valid for tensorConfig dimension numbers");

      if (dc.second >= tensorConfig.getDims()[dc.first])
         THROWERROR("dimCoords should contain valid coord values for corresponding dimensions");
   }

   std::unordered_map<std::uint64_t, std::vector<std::uint32_t>::const_iterator> dimColumns;
   for (const std::unordered_map<std::uint64_t, std::uint32_t>::value_type& dc : dimCoords)
   {
      std::size_t dimOffset = dc.first * tensorConfig.getValues().size();
      dimColumns[dc.first] = tensorConfig.getColumns().begin() + dimOffset;
   }

   Eigen::MatrixXd sliceMatrix(tensorConfig.getDims()[fixedDims[0]], tensorConfig.getDims()[fixedDims[1]]);
   for (std::size_t i = 0; i < tensorConfig.getValues().size(); i++)
   {
      bool dimCoordsMatchColumns =
         std::accumulate( dimCoords.begin()
                        , dimCoords.end()
                        , true
                        , [&](bool acc, const std::unordered_map<std::uint64_t, std::uint32_t>::value_type& dc)
                          {
                             return acc & (*(dimColumns[dc.first] + i) == dc.second);
                          }
                        );

      if (dimCoordsMatchColumns)
      {
         std::uint32_t d0_coord =
            tensorConfig.getColumns()[fixedDims[0] * tensorConfig.getValues().size() + i];
         std::uint32_t d1_coord =
            tensorConfig.getColumns()[fixedDims[1] * tensorConfig.getValues().size() + i];
         sliceMatrix(d0_coord, d1_coord) = tensorConfig.getValues()[i];
      }
   }
   return sliceMatrix;
}
<|endoftext|>


<commit_before>/*  Sirikata Utilities -- Sirikata Logging Utility
 *  Logging.hpp
 *
 *  Copyright (c) 2009, Daniel Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _SIRIKATA_LOGGING_HPP_
#define _SIRIKATA_LOGGING_HPP_

extern "C" SIRIKATA_EXPORT void* Sirikata_Logging_OptionValue_defaultLevel;
extern "C" SIRIKATA_EXPORT void* Sirikata_Logging_OptionValue_atLeastLevel;
extern "C" SIRIKATA_EXPORT void* Sirikata_Logging_OptionValue_moduleLevel;
namespace Sirikata {
class OptionValue;
namespace Logging {
enum LOGGING_LEVEL {
    fatal=1,
    error=8,
    warning=64,
    warn=warning,
    info=512,
    debug=4096,
    detailed=8192,
    insane=32768
};

SIRIKATA_FUNCTION_EXPORT const String& LogModuleString(const char* base);
SIRIKATA_FUNCTION_EXPORT const char* LogLevelString(LOGGING_LEVEL lvl, const char* lvl_as_string);

// Public so the macros work efficiently instead of another call
extern "C" SIRIKATA_EXPORT std::ostream* SirikataLogStream;

SIRIKATA_FUNCTION_EXPORT void setLogStream(std::ostream* logfs);
SIRIKATA_FUNCTION_EXPORT void finishLog();

} }
#if 1
# ifdef DEBUG_ALL
#  define SILOGP(module,lvl) true
# else
//needs to use unsafeAs because the LOGGING_LEVEL typeinfos are not preserved across dll lines
#  define SILOGP(module,lvl) \
    ( \
     std::max( reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_atLeastLevel)->unsafeAs<Sirikata::Logging::LOGGING_LEVEL>(), \
	       reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_defaultLevel)->unsafeAs<Sirikata::Logging::LOGGING_LEVEL>()) \
     >=Sirikata::Logging::lvl &&					\
        ( (reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().find(#module)==reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().end() && \
           reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_defaultLevel)->unsafeAs<Sirikata::Logging::LOGGING_LEVEL>()>=(Sirikata::Logging::lvl)) \
		   || (reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().find(#module)!=reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().end() && \
              reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >()[#module]>=Sirikata::Logging::lvl)))
# endif
# define SILOGNOCR(module,lvl,value)                                    \
    do {                                                                \
        if (SILOGP(module,lvl)) {                                       \
            std::ostringstream __log_stream;                            \
            __log_stream << value;                                      \
            (*Sirikata::Logging::SirikataLogStream) << __log_stream.str(); \
        }                                                               \
    } while (0)
# define SILOGBARE(module,lvl,value) SILOGNOCR(module,lvl,value << std::endl)
#else
# define SILOGP(module,lvl) false
# define SILOGNOCR(module,lvl,value)
# define SILOGBARE(module,lvl,value)
#endif

#define SILOG(module,lvl,value) SILOGBARE(module,lvl, "[" << Sirikata::Logging::LogModuleString(#module) << "] " << Sirikata::Logging::LogLevelString(Sirikata::Logging::lvl, #lvl) << ": " << value)

#if SIRIKATA_PLATFORM == PLATFORM_LINUX
// FIXME only works on GCC
#define NOT_IMPLEMENTED_MSG (Sirikata::String("Not implemented reached in ") + Sirikata::String(__PRETTY_FUNCTION__))
#else
#define NOT_IMPLEMENTED_MSG (Sirikata::String("NOT IMPLEMENTED"))
#endif

#define NOT_IMPLEMENTED(module) SILOG(module,error,NOT_IMPLEMENTED_MSG)

#if SIRIKATA_PLATFORM == PLATFORM_LINUX
// FIXME only works on GCC
#define DEPRECATED_MSG (Sirikata::String("DEPRECATED reached in ") + Sirikata::String(__PRETTY_FUNCTION__))
#else
#define DEPRECATED_MSG (Sirikata::String("DEPRECATED"))
#endif

#define DEPRECATED(module) SILOG(module,warning,DEPRECATED_MSG)

#endif
<commit_msg>Put logging endl with the output stream instead of the one used to collect output into a single stream. Fixes #419.<commit_after>/*  Sirikata Utilities -- Sirikata Logging Utility
 *  Logging.hpp
 *
 *  Copyright (c) 2009, Daniel Reiter Horn
 *  All rights reserved.
 *
 *  Redistribution and use in source and binary forms, with or without
 *  modification, are permitted provided that the following conditions are
 *  met:
 *  * Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 *  * Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in
 *    the documentation and/or other materials provided with the
 *    distribution.
 *  * Neither the name of Sirikata nor the names of its contributors may
 *    be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
 * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
 * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
 * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
 * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
 * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
 * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#ifndef _SIRIKATA_LOGGING_HPP_
#define _SIRIKATA_LOGGING_HPP_

extern "C" SIRIKATA_EXPORT void* Sirikata_Logging_OptionValue_defaultLevel;
extern "C" SIRIKATA_EXPORT void* Sirikata_Logging_OptionValue_atLeastLevel;
extern "C" SIRIKATA_EXPORT void* Sirikata_Logging_OptionValue_moduleLevel;
namespace Sirikata {
class OptionValue;
namespace Logging {
enum LOGGING_LEVEL {
    fatal=1,
    error=8,
    warning=64,
    warn=warning,
    info=512,
    debug=4096,
    detailed=8192,
    insane=32768
};

SIRIKATA_FUNCTION_EXPORT const String& LogModuleString(const char* base);
SIRIKATA_FUNCTION_EXPORT const char* LogLevelString(LOGGING_LEVEL lvl, const char* lvl_as_string);

// Public so the macros work efficiently instead of another call
extern "C" SIRIKATA_EXPORT std::ostream* SirikataLogStream;

SIRIKATA_FUNCTION_EXPORT void setLogStream(std::ostream* logfs);
SIRIKATA_FUNCTION_EXPORT void finishLog();

} }
#if 1
# ifdef DEBUG_ALL
#  define SILOGP(module,lvl) true
# else
//needs to use unsafeAs because the LOGGING_LEVEL typeinfos are not preserved across dll lines
#  define SILOGP(module,lvl) \
    ( \
     std::max( reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_atLeastLevel)->unsafeAs<Sirikata::Logging::LOGGING_LEVEL>(), \
	       reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_defaultLevel)->unsafeAs<Sirikata::Logging::LOGGING_LEVEL>()) \
     >=Sirikata::Logging::lvl &&					\
        ( (reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().find(#module)==reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().end() && \
           reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_defaultLevel)->unsafeAs<Sirikata::Logging::LOGGING_LEVEL>()>=(Sirikata::Logging::lvl)) \
		   || (reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().find(#module)!=reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >().end() && \
              reinterpret_cast<Sirikata::OptionValue*>(Sirikata_Logging_OptionValue_moduleLevel)->unsafeAs<std::tr1::unordered_map<std::string,Sirikata::Logging::LOGGING_LEVEL> >()[#module]>=Sirikata::Logging::lvl)))
# endif
# define SILOGNOCR(module,lvl,value)                                    \
    do {                                                                \
        if (SILOGP(module,lvl)) {                                       \
            std::ostringstream __log_stream;                            \
            __log_stream << value;                                      \
            (*Sirikata::Logging::SirikataLogStream) << __log_stream.str() << std::endl; \
        }                                                               \
    } while (0)
# define SILOGBARE(module,lvl,value) SILOGNOCR(module,lvl,value)
#else
# define SILOGP(module,lvl) false
# define SILOGNOCR(module,lvl,value)
# define SILOGBARE(module,lvl,value)
#endif

#define SILOG(module,lvl,value) SILOGBARE(module,lvl, "[" << Sirikata::Logging::LogModuleString(#module) << "] " << Sirikata::Logging::LogLevelString(Sirikata::Logging::lvl, #lvl) << ": " << value)

#if SIRIKATA_PLATFORM == PLATFORM_LINUX
// FIXME only works on GCC
#define NOT_IMPLEMENTED_MSG (Sirikata::String("Not implemented reached in ") + Sirikata::String(__PRETTY_FUNCTION__))
#else
#define NOT_IMPLEMENTED_MSG (Sirikata::String("NOT IMPLEMENTED"))
#endif

#define NOT_IMPLEMENTED(module) SILOG(module,error,NOT_IMPLEMENTED_MSG)

#if SIRIKATA_PLATFORM == PLATFORM_LINUX
// FIXME only works on GCC
#define DEPRECATED_MSG (Sirikata::String("DEPRECATED reached in ") + Sirikata::String(__PRETTY_FUNCTION__))
#else
#define DEPRECATED_MSG (Sirikata::String("DEPRECATED"))
#endif

#define DEPRECATED(module) SILOG(module,warning,DEPRECATED_MSG)

#endif
<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#pragma once

#include <caf/actor.hpp>
#include <caf/actor_system.hpp>
#include <caf/event_based_actor.hpp>

namespace vast::detail {

template <class Container>
caf::actor spawn_container_source(caf::actor_system& system, caf::actor dst,
                                  Container elements) {
  using namespace caf;
  struct outer_state {
    /// Name of this actor in log events.
    const char* name = "container-source";
  };
  auto f = [](stateful_actor<outer_state>* self, actor dest, Container xs) {
    using iterator = typename Container::iterator;
    using value_type = typename Container::value_type;
    struct state {
      Container xs;
      iterator i;
      iterator e;
    };
    self->make_source(
      dest,
      [&](state& st) {
        st.xs = std::move(xs);
        st.i = st.xs.begin();
        st.e = st.xs.end();
      },
      [](state& st, downstream<value_type>& out, size_t num) {
        size_t pushed = 0;
        while (pushed < num && st.i != st.e) {
          out.push(std::move(*st.i++));
          ++pushed;
        }
      },
      [](const state& st) {
        return st.i == st.e;
      }
    );
  };
  return system.spawn(f, std::move(dst), std::move(elements));
}

} // namespace vast::detail

<commit_msg>Support typed actors<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#pragma once

#include <caf/actor.hpp>
#include <caf/actor_system.hpp>
#include <caf/event_based_actor.hpp>

namespace vast::detail {

template <class Handle, class Container>
caf::actor spawn_container_source(caf::actor_system& system, Handle dst,
                                  Container elements) {
  using namespace caf;
  struct outer_state {
    /// Name of this actor in log events.
    const char* name = "container-source";
  };
  auto f = [](stateful_actor<outer_state>* self, Handle dest, Container xs) {
    using iterator = typename Container::iterator;
    using value_type = typename Container::value_type;
    struct state {
      Container xs;
      iterator i;
      iterator e;
    };
    self->make_source(
      dest,
      [&](state& st) {
        st.xs = std::move(xs);
        st.i = st.xs.begin();
        st.e = st.xs.end();
      },
      [](state& st, downstream<value_type>& out, size_t num) {
        size_t pushed = 0;
        while (pushed < num && st.i != st.e) {
          out.push(std::move(*st.i++));
          ++pushed;
        }
      },
      [](const state& st) {
        return st.i == st.e;
      }
    );
  };
  return system.spawn(f, std::move(dst), std::move(elements));
}

} // namespace vast::detail

<|endoftext|>


<commit_before>#include "Player.hpp"

PCWSTR szTitle = L"BasicPlayback";
PCWSTR szWindowClass = L"MFBASICPLAYBACK";

HINSTANCE   g_hInstance;                        // current instance
BOOL        g_bRepaintClient = TRUE;            // Repaint the application client area?
CPlayer     *g_pPlayer = NULL;                  // Global player object.

// Note: After WM_CREATE is processed, g_pPlayer remains valid until the
// window is destroyed.

// Forward declarations of functions included in this code module:
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    OpenUrlDialogProc(HWND, UINT, WPARAM, LPARAM);
void                NotifyError(HWND hwnd, const WCHAR *sErrorMessage, HRESULT hr);
void                UpdateUI(HWND hwnd, PlayerState state);
HRESULT             AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen);

// Message handlers
LRESULT             OnCreateWindow(HWND hwnd);
void                OnFileOpen(HWND hwnd);
void                OnOpenURL(HWND hwnd);
void                OnPlayerEvent(HWND hwnd, WPARAM pUnkPtr);
void                OnPaint(HWND hwnd);
void                OnResize(WORD width, WORD height);
void                OnKeyPress(WPARAM key);

// OpenUrlDialogInfo: Contains data passed to the "Open URL" dialog proc.
struct OpenUrlDialogInfo
{
    WCHAR *pszURL;
    DWORD cch;
};

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    MSG msg;

    ZeroMemory(&msg, sizeof(msg));

    // Perform application initialization.
    if (!InitInstance(hInstance, nCmdShow))
    {
        NotifyError(NULL, L"Could not initialize the application.",
            HRESULT_FROM_WIN32(GetLastError()));
        return FALSE;
    }

    // Main message loop.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Clean up.
    if (g_pPlayer)
    {
        g_pPlayer->Shutdown();
        SafeRelease(&g_pPlayer);
    }
    return 0;
}

//  Create the application window.
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hwnd;
    WNDCLASSEX wcex;

    g_hInstance = hInst; // Store the instance handle.

    // Register the window class.
    ZeroMemory(&wcex, sizeof(WNDCLASSEX));
    wcex.cbSize         = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.hInstance      = hInst;
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_MFPLAYBACK);
    wcex.lpszClassName  = szWindowClass;

    if (RegisterClassEx(&wcex) == 0)
    {
        return FALSE;
    }

    // Create the application window.
    hwnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);

    if (hwnd == 0)
    {
        return FALSE;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    return TRUE;
}

//  Message handler for the main window.

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
        return OnCreateWindow(hwnd);

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_EXIT:
            DestroyWindow(hwnd);
            break;
        case ID_FILE_OPENFILE:
            OnFileOpen(hwnd);
            break;
        case ID_FILE_OPENURL:
            OnOpenURL(hwnd);
            break;

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;

    case WM_PAINT:
        OnPaint(hwnd);
        break;

    case WM_SIZE:
        OnResize(LOWORD(lParam), HIWORD(lParam));
        break;

    case WM_ERASEBKGND:
        // Suppress window erasing, to reduce flickering while the video is playing.
        return 1;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_CHAR:
        OnKeyPress(wParam);
        break;

    case WM_APP_PLAYER_EVENT:
        OnPlayerEvent(hwnd, wParam);
        break;

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

//  Open an audio/video file.
void OnFileOpen(HWND hwnd)
{
    IFileOpenDialog *pFileOpen = NULL;
    IShellItem *pItem = NULL;
    PWSTR pszFilePath = NULL;

    // Create the FileOpenDialog object.
    HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL,
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
    if (FAILED(hr))
    {
        goto done;
    }

    // Show the Open dialog box.
    hr = pFileOpen->Show(NULL);
    if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
    {
        // The user canceled the dialog. Do not treat as an error.
        hr = S_OK;
        goto done;
    }
    else if (FAILED(hr))
    {
        goto done;
    }

    // Get the file name from the dialog box.
    hr = pFileOpen->GetResult(&pItem);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
    if (FAILED(hr))
    {
        goto done;
    }

    // Display the file name to the user.
    hr = g_pPlayer->OpenURL(pszFilePath);
    if (SUCCEEDED(hr))
    {
        UpdateUI(hwnd, OpenPending);
    }

done:
    if (FAILED(hr))
    {
        NotifyError(hwnd, L"Could not open the file.", hr);
        UpdateUI(hwnd, Closed);
    }
    CoTaskMemFree(pszFilePath);
    SafeRelease(&pItem);
    SafeRelease(&pFileOpen);
}

//  Open a media file from a URL.
void OnOpenURL(HWND hwnd)
{
    HRESULT hr = S_OK;

    // Pass in an OpenUrlDialogInfo structure to the dialog. The dialog
    // fills in this structure with the URL. The dialog proc allocates
    // the memory for the string.

    OpenUrlDialogInfo url;
    ZeroMemory(&url, sizeof(&url));

    // Show the Open URL dialog.
    if (IDOK == DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_OPENURL), hwnd,
        OpenUrlDialogProc, (LPARAM)&url))
    {
        // Open the file with the playback object.
        hr = g_pPlayer->OpenURL(url.pszURL);
        if (SUCCEEDED(hr))
        {
            UpdateUI(hwnd, OpenPending);
        }
        else
        {
            NotifyError(hwnd, L"Could not open this URL.", hr);
            UpdateUI(hwnd, Closed);
        }
    }

    // The caller must free the URL string.
    CoTaskMemFree(url.pszURL);
}

//  Handler for WM_CREATE message.
LRESULT OnCreateWindow(HWND hwnd)
{
    // Initialize the player object.
    HRESULT hr = CPlayer::CreateInstance(hwnd, hwnd, &g_pPlayer);
    if (SUCCEEDED(hr))
    {
        UpdateUI(hwnd, Closed);
        return 0;   // Success.
    }
    else
    {
        NotifyError(NULL, L"Could not initialize the player object.", hr);
        return -1;  // Destroy the window
    }
}

//  Handler for WM_PAINT messages.
void OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    if (g_pPlayer && g_pPlayer->HasVideo())
    {
        // Video is playing. Ask the player to repaint.
        g_pPlayer->Repaint();
    }
    else
    {
        // The video is not playing, so we must paint the application window.
        RECT rc;
        GetClientRect(hwnd, &rc);
        FillRect(hdc, &rc, (HBRUSH) COLOR_WINDOW);
    }
    EndPaint(hwnd, &ps);
}

//  Handler for WM_SIZE messages.
void OnResize(WORD width, WORD height)
{
    if (g_pPlayer)
    {
        g_pPlayer->ResizeVideo(width, height);
    }
}

// Handler for WM_CHAR messages.
void OnKeyPress(WPARAM key)
{
    switch (key)
    {
    // Space key toggles between running and paused
    case VK_SPACE:
        if (g_pPlayer->GetState() == Started)
        {
            g_pPlayer->Pause();
        }
        else if (g_pPlayer->GetState() == Paused)
        {
            g_pPlayer->Play();
        }
        break;
    }
}

// Handler for Media Session events.
void OnPlayerEvent(HWND hwnd, WPARAM pUnkPtr)
{
    HRESULT hr = g_pPlayer->HandleEvent(pUnkPtr);
    if (FAILED(hr))
    {
        NotifyError(hwnd, L"An error occurred.", hr);
    }
    UpdateUI(hwnd, g_pPlayer->GetState());
}

// Update the application UI to reflect the current state.

void UpdateUI(HWND hwnd, PlayerState state)
{
    BOOL bWaiting = FALSE;
    BOOL bPlayback = FALSE;

    assert(g_pPlayer != NULL);

    switch (state)
    {
    case OpenPending:
        bWaiting = TRUE;
        break;

    case Started:
        bPlayback = TRUE;
        break;

    case Paused:
        bPlayback = TRUE;
        break;
    }

    HMENU hMenu = GetMenu(hwnd);
    UINT  uEnable = MF_BYCOMMAND | (bWaiting ? MF_GRAYED : MF_ENABLED);

    EnableMenuItem(hMenu, ID_FILE_OPENFILE, uEnable);
    EnableMenuItem(hMenu, ID_FILE_OPENURL, uEnable);

    if (bPlayback && g_pPlayer->HasVideo())
    {
        g_bRepaintClient = FALSE;
    }
    else
    {
        g_bRepaintClient = TRUE;
    }
}

//  Show a message box with an error message.
void NotifyError(HWND hwnd, PCWSTR pszErrorMessage, HRESULT hrErr)
{
    const size_t MESSAGE_LEN = 512;
    WCHAR message[MESSAGE_LEN];

    if (SUCCEEDED(StringCchPrintf(message, MESSAGE_LEN, L"%s (HRESULT = 0x%X)",
        pszErrorMessage, hrErr)))
    {
        MessageBox(hwnd, message, NULL, MB_OK | MB_ICONERROR);
    }
}

//  Dialog proc for the "Open URL" dialog.
INT_PTR CALLBACK OpenUrlDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static OpenUrlDialogInfo *pUrl = NULL;

    BOOL result = FALSE;

    switch (message)
    {
    case WM_INITDIALOG:
        // The caller sends a pointer to an OpenUrlDialogInfo structure as the
        // lParam. This structure stores the URL.
        pUrl = (OpenUrlDialogInfo*)lParam;
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            if (pUrl)
            {
                // Get the URL from the edit box in the dialog. This function
                // allocates memory. The caller must call CoTaskMemAlloc.
                if (SUCCEEDED(AllocGetWindowText(GetDlgItem(hDlg, IDC_EDIT_URL),
                    &pUrl->pszURL, &pUrl->cch)))
                {
                    result = TRUE;
                }
            }
            EndDialog(hDlg, result ? IDOK : IDABORT);
            break;

        case IDCANCEL:
            EndDialog(hDlg, LOWORD(IDCANCEL));
            break;
        }
        return (INT_PTR)FALSE;
    }
    return (INT_PTR)FALSE;
}

// Helper function to get text from a window.
//
// This function allocates a buffer and returns it in pszText. The caller must
// call CoTaskMemFree on the buffer.
//
// hwnd:     Handle to the window.
// pszText:  Receives a pointer to the string.
// pcchLen:  Receives the length of the string, in characters, not including
//           the terminating NULL character.

HRESULT AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen)
{
    if (pszText == NULL || pcchLen == NULL)
    {
        return E_POINTER;
    }

    *pszText = NULL;

    int cch = GetWindowTextLength(hwnd);
    if (cch < 0)
    {
        return E_UNEXPECTED;
    }

    PWSTR pszTmp = (PWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (cch + 1));
    // Includes room for terminating NULL character

    if (!pszTmp)
    {
        return E_OUTOFMEMORY;
    }

    if (cch == 0)
    {
        pszTmp[0] = L'\0';  // No text.
    }
    else
    {
        int res = GetWindowText(hwnd, pszTmp, (cch + 1));
        // Size includes terminating null character.

        // GetWindowText returns 0 if (a) there is no text or (b) it failed.
        // We checked for (a) already, so 0 means failure here.
        if (res == 0)
        {
            CoTaskMemFree(pszTmp);
            return __HRESULT_FROM_WIN32(GetLastError());
        }
    }

    // If we got here, szTmp is valid, so return it to the caller.
    *pszText = pszTmp;

    // Return the length NOT including the '\0'.
    *pcchLen = static_cast<DWORD>(cch);

     return S_OK;
}
<commit_msg>mfplay: remove trailing whitespace in winmain<commit_after>#include "gtest/gtest.h"
#include "Player.hpp"

PCWSTR szTitle = L"BasicPlayback";
PCWSTR szWindowClass = L"MFBASICPLAYBACK";

HINSTANCE   g_hInstance;                        // current instance
BOOL        g_bRepaintClient = TRUE;            // Repaint the application client area?
CPlayer     *g_pPlayer = NULL;                  // Global player object.

// Note: After WM_CREATE is processed, g_pPlayer remains valid until the
// window is destroyed.

// Forward declarations of functions included in this code module:
BOOL                InitInstance(HINSTANCE, int);
LRESULT CALLBACK    WndProc(HWND, UINT, WPARAM, LPARAM);
INT_PTR CALLBACK    OpenUrlDialogProc(HWND, UINT, WPARAM, LPARAM);
void                NotifyError(HWND hwnd, const WCHAR *sErrorMessage, HRESULT hr);
void                UpdateUI(HWND hwnd, PlayerState state);
HRESULT             AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen);

// Message handlers
LRESULT             OnCreateWindow(HWND hwnd);
void                OnFileOpen(HWND hwnd);
void                OnOpenURL(HWND hwnd);
void                OnPlayerEvent(HWND hwnd, WPARAM pUnkPtr);
void                OnPaint(HWND hwnd);
void                OnResize(WORD width, WORD height);
void                OnKeyPress(WPARAM key);

// OpenUrlDialogInfo: Contains data passed to the "Open URL" dialog proc.
struct OpenUrlDialogInfo
{
    WCHAR *pszURL;
    DWORD cch;
};

int WINAPI wWinMain(HINSTANCE hInstance, HINSTANCE, PWSTR, int nCmdShow)
{
    HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);

    MSG msg;

    ZeroMemory(&msg, sizeof(msg));

    // Perform application initialization.
    if (!InitInstance(hInstance, nCmdShow))
    {
        NotifyError(NULL, L"Could not initialize the application.",
            HRESULT_FROM_WIN32(GetLastError()));
        return FALSE;
    }

    // Main message loop.
    while (GetMessage(&msg, NULL, 0, 0))
    {
        TranslateMessage(&msg);
        DispatchMessage(&msg);
    }

    // Clean up.
    if (g_pPlayer)
    {
        g_pPlayer->Shutdown();
        SafeRelease(&g_pPlayer);
    }
    return 0;
}

//  Create the application window.
BOOL InitInstance(HINSTANCE hInst, int nCmdShow)
{
    HWND hwnd;
    WNDCLASSEX wcex;

    g_hInstance = hInst; // Store the instance handle.

    // Register the window class.
    ZeroMemory(&wcex, sizeof(WNDCLASSEX));
    wcex.cbSize         = sizeof(WNDCLASSEX);
    wcex.style          = CS_HREDRAW | CS_VREDRAW;
    wcex.lpfnWndProc    = WndProc;
    wcex.hInstance      = hInst;
    wcex.hbrBackground  = (HBRUSH)(COLOR_WINDOW+1);
    wcex.lpszMenuName   = MAKEINTRESOURCE(IDC_MFPLAYBACK);
    wcex.lpszClassName  = szWindowClass;

    if (RegisterClassEx(&wcex) == 0)
    {
        return FALSE;
    }

    // Create the application window.
    hwnd = CreateWindow(szWindowClass, szTitle, WS_OVERLAPPEDWINDOW,
        CW_USEDEFAULT, 0, CW_USEDEFAULT, 0, NULL, NULL, hInst, NULL);

    if (hwnd == 0)
    {
        return FALSE;
    }

    ShowWindow(hwnd, nCmdShow);
    UpdateWindow(hwnd);

    return TRUE;
}

//  Message handler for the main window.

LRESULT CALLBACK WndProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch (message)
    {
    case WM_CREATE:
        return OnCreateWindow(hwnd);

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDM_EXIT:
            DestroyWindow(hwnd);
            break;
        case ID_FILE_OPENFILE:
            OnFileOpen(hwnd);
            break;
        case ID_FILE_OPENURL:
            OnOpenURL(hwnd);
            break;

        default:
            return DefWindowProc(hwnd, message, wParam, lParam);
        }
        break;

    case WM_PAINT:
        OnPaint(hwnd);
        break;

    case WM_SIZE:
        OnResize(LOWORD(lParam), HIWORD(lParam));
        break;

    case WM_ERASEBKGND:
        // Suppress window erasing, to reduce flickering while the video is playing.
        return 1;

    case WM_DESTROY:
        PostQuitMessage(0);
        break;

    case WM_CHAR:
        OnKeyPress(wParam);
        break;

    case WM_APP_PLAYER_EVENT:
        OnPlayerEvent(hwnd, wParam);
        break;

    default:
        return DefWindowProc(hwnd, message, wParam, lParam);
    }
    return 0;
}

//  Open an audio/video file.
void OnFileOpen(HWND hwnd)
{
    IFileOpenDialog *pFileOpen = NULL;
    IShellItem *pItem = NULL;
    PWSTR pszFilePath = NULL;

    // Create the FileOpenDialog object.
    HRESULT hr = CoCreateInstance(__uuidof(FileOpenDialog), NULL,
        CLSCTX_INPROC_SERVER, IID_PPV_ARGS(&pFileOpen));
    if (FAILED(hr))
    {
        goto done;
    }

    // Show the Open dialog box.
    hr = pFileOpen->Show(NULL);
    if (hr == HRESULT_FROM_WIN32(ERROR_CANCELLED))
    {
        // The user canceled the dialog. Do not treat as an error.
        hr = S_OK;
        goto done;
    }
    else if (FAILED(hr))
    {
        goto done;
    }

    // Get the file name from the dialog box.
    hr = pFileOpen->GetResult(&pItem);
    if (FAILED(hr))
    {
        goto done;
    }

    hr = pItem->GetDisplayName(SIGDN_FILESYSPATH, &pszFilePath);
    if (FAILED(hr))
    {
        goto done;
    }

    // Display the file name to the user.
    hr = g_pPlayer->OpenURL(pszFilePath);
    if (SUCCEEDED(hr))
    {
        UpdateUI(hwnd, OpenPending);
    }

done:
    if (FAILED(hr))
    {
        NotifyError(hwnd, L"Could not open the file.", hr);
        UpdateUI(hwnd, Closed);
    }
    CoTaskMemFree(pszFilePath);
    SafeRelease(&pItem);
    SafeRelease(&pFileOpen);
}

//  Open a media file from a URL.
void OnOpenURL(HWND hwnd)
{
    HRESULT hr = S_OK;

    // Pass in an OpenUrlDialogInfo structure to the dialog. The dialog
    // fills in this structure with the URL. The dialog proc allocates
    // the memory for the string.

    OpenUrlDialogInfo url;
    ZeroMemory(&url, sizeof(&url));

    // Show the Open URL dialog.
    if (IDOK == DialogBoxParam(g_hInstance, MAKEINTRESOURCE(IDD_OPENURL), hwnd,
        OpenUrlDialogProc, (LPARAM)&url))
    {
        // Open the file with the playback object.
        hr = g_pPlayer->OpenURL(url.pszURL);
        if (SUCCEEDED(hr))
        {
            UpdateUI(hwnd, OpenPending);
        }
        else
        {
            NotifyError(hwnd, L"Could not open this URL.", hr);
            UpdateUI(hwnd, Closed);
        }
    }

    // The caller must free the URL string.
    CoTaskMemFree(url.pszURL);
}

//  Handler for WM_CREATE message.
LRESULT OnCreateWindow(HWND hwnd)
{
    // Initialize the player object.
    HRESULT hr = CPlayer::CreateInstance(hwnd, hwnd, &g_pPlayer);
    if (SUCCEEDED(hr))
    {
        UpdateUI(hwnd, Closed);
        return 0;   // Success.
    }
    else
    {
        NotifyError(NULL, L"Could not initialize the player object.", hr);
        return -1;  // Destroy the window
    }
}

//  Handler for WM_PAINT messages.
void OnPaint(HWND hwnd)
{
    PAINTSTRUCT ps;
    HDC hdc = BeginPaint(hwnd, &ps);

    if (g_pPlayer && g_pPlayer->HasVideo())
    {
        // Video is playing. Ask the player to repaint.
        g_pPlayer->Repaint();
    }
    else
    {
        // The video is not playing, so we must paint the application window.
        RECT rc;
        GetClientRect(hwnd, &rc);
        FillRect(hdc, &rc, (HBRUSH) COLOR_WINDOW);
    }
    EndPaint(hwnd, &ps);
}

//  Handler for WM_SIZE messages.
void OnResize(WORD width, WORD height)
{
    if (g_pPlayer)
    {
        g_pPlayer->ResizeVideo(width, height);
    }
}

// Handler for WM_CHAR messages.
void OnKeyPress(WPARAM key)
{
    switch (key)
    {
    // Space key toggles between running and paused
    case VK_SPACE:
        if (g_pPlayer->GetState() == Started)
        {
            g_pPlayer->Pause();
        }
        else if (g_pPlayer->GetState() == Paused)
        {
            g_pPlayer->Play();
        }
        break;
    }
}

// Handler for Media Session events.
void OnPlayerEvent(HWND hwnd, WPARAM pUnkPtr)
{
    HRESULT hr = g_pPlayer->HandleEvent(pUnkPtr);
    if (FAILED(hr))
    {
        NotifyError(hwnd, L"An error occurred.", hr);
    }
    UpdateUI(hwnd, g_pPlayer->GetState());
}

// Update the application UI to reflect the current state.

void UpdateUI(HWND hwnd, PlayerState state)
{
    BOOL bWaiting = FALSE;
    BOOL bPlayback = FALSE;

    assert(g_pPlayer != NULL);

    switch (state)
    {
    case OpenPending:
        bWaiting = TRUE;
        break;

    case Started:
        bPlayback = TRUE;
        break;

    case Paused:
        bPlayback = TRUE;
        break;
    }

    HMENU hMenu = GetMenu(hwnd);
    UINT  uEnable = MF_BYCOMMAND | (bWaiting ? MF_GRAYED : MF_ENABLED);

    EnableMenuItem(hMenu, ID_FILE_OPENFILE, uEnable);
    EnableMenuItem(hMenu, ID_FILE_OPENURL, uEnable);

    if (bPlayback && g_pPlayer->HasVideo())
    {
        g_bRepaintClient = FALSE;
    }
    else
    {
        g_bRepaintClient = TRUE;
    }
}

//  Show a message box with an error message.
void NotifyError(HWND hwnd, PCWSTR pszErrorMessage, HRESULT hrErr)
{
    const size_t MESSAGE_LEN = 512;
    WCHAR message[MESSAGE_LEN];

    if (SUCCEEDED(StringCchPrintf(message, MESSAGE_LEN, L"%s (HRESULT = 0x%X)",
        pszErrorMessage, hrErr)))
    {
        MessageBox(hwnd, message, NULL, MB_OK | MB_ICONERROR);
    }
}

//  Dialog proc for the "Open URL" dialog.
INT_PTR CALLBACK OpenUrlDialogProc(HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam)
{
    static OpenUrlDialogInfo *pUrl = NULL;

    BOOL result = FALSE;

    switch (message)
    {
    case WM_INITDIALOG:
        // The caller sends a pointer to an OpenUrlDialogInfo structure as the
        // lParam. This structure stores the URL.
        pUrl = (OpenUrlDialogInfo*)lParam;
        return (INT_PTR)TRUE;

    case WM_COMMAND:
        switch (LOWORD(wParam))
        {
        case IDOK:
            if (pUrl)
            {
                // Get the URL from the edit box in the dialog. This function
                // allocates memory. The caller must call CoTaskMemAlloc.
                if (SUCCEEDED(AllocGetWindowText(GetDlgItem(hDlg, IDC_EDIT_URL),
                    &pUrl->pszURL, &pUrl->cch)))
                {
                    result = TRUE;
                }
            }
            EndDialog(hDlg, result ? IDOK : IDABORT);
            break;

        case IDCANCEL:
            EndDialog(hDlg, LOWORD(IDCANCEL));
            break;
        }
        return (INT_PTR)FALSE;
    }
    return (INT_PTR)FALSE;
}

// Helper function to get text from a window.
//
// This function allocates a buffer and returns it in pszText. The caller must
// call CoTaskMemFree on the buffer.
//
// hwnd:     Handle to the window.
// pszText:  Receives a pointer to the string.
// pcchLen:  Receives the length of the string, in characters, not including
//           the terminating NULL character.

HRESULT AllocGetWindowText(HWND hwnd, WCHAR **pszText, DWORD *pcchLen)
{
    if (pszText == NULL || pcchLen == NULL)
    {
        return E_POINTER;
    }

    *pszText = NULL;

    int cch = GetWindowTextLength(hwnd);
    if (cch < 0)
    {
        return E_UNEXPECTED;
    }

    PWSTR pszTmp = (PWSTR)CoTaskMemAlloc(sizeof(WCHAR) * (cch + 1));
    // Includes room for terminating NULL character

    if (!pszTmp)
    {
        return E_OUTOFMEMORY;
    }

    if (cch == 0)
    {
        pszTmp[0] = L'\0';  // No text.
    }
    else
    {
        int res = GetWindowText(hwnd, pszTmp, (cch + 1));
        // Size includes terminating null character.

        // GetWindowText returns 0 if (a) there is no text or (b) it failed.
        // We checked for (a) already, so 0 means failure here.
        if (res == 0)
        {
            CoTaskMemFree(pszTmp);
            return __HRESULT_FROM_WIN32(GetLastError());
        }
    }

    // If we got here, szTmp is valid, so return it to the caller.
    *pszText = pszTmp;

    // Return the length NOT including the '\0'.
    *pcchLen = static_cast<DWORD>(cch);

     return S_OK;
}
<|endoftext|>


<commit_before>/*
 * FILE          $Id: frpcmethodregistry.cc,v 1.2 2006-02-09 16:00:26 vasek Exp $
 *
 * DESCRIPTION
 *
 * AUTHOR
 *              Miroslav Talasek <miroslav.talasek@firma.seznam.cz>
 *
 * Copyright (C) Seznam.cz a.s. 2002
 * All Rights Reserved
 *
 * HISTORY
 *
 */

#include "frpcmethodregistry.h"

#include <frpcmethod.h>
#include <frpcdefaultmethod.h>
#include <frpcheadmethod.h>
#include <frpctreebuilder.h>
#include <frpctreefeeder.h>
#include <frpcunmarshaller.h>
#include <frpcmarshaller.h>
#include <frpcstreamerror.h>
#include <frpcfault.h>
#include <frpclenerror.h>
#include <frpckeyerror.h>
#include <frpcindexerror.h>
#include <frpc.h>
#include <frpcinternals.h>
#include <memory>

#ifdef WIN32
#include <windows.h>
#endif //WIN32

namespace FRPC
{

MethodRegistry_t::TimeDiff_t::TimeDiff_t()
{
    // get current time
#ifdef WIN32
	FILETIME ft;
	GetSystemTimeAsFileTime(&ft);
	time(&second);
	usecond = (ft.dwLowDateTime / 10) % 1000000;

#else //WIN32

    struct timeval now;
    gettimeofday(&now, 0);
    second = now.tv_sec;
    usecond = now.tv_usec;
#endif //WIN32
}

MethodRegistry_t::TimeDiff_t MethodRegistry_t::TimeDiff_t::diff()
{

    TimeDiff_t now;

    // check for time skew
    if ((now.second < second)
            || ((now.second == second) && (now.usecond < usecond)))
        return TimeDiff_t(0,0);

    // compute difference
    if (now.usecond >= usecond)
        return TimeDiff_t(now.second - second,
                          now.usecond - usecond);

    return TimeDiff_t
           (now.second - second - 1L,
            1000000L - usecond + now.usecond);
}

MethodRegistry_t::MethodRegistry_t(Callbacks_t *callbacks, bool introspectionEnabled)
        :callbacks(callbacks), introspectionEnabled(introspectionEnabled),
        defaultMethod(0),headMethod(0)
{
    if(introspectionEnabled)
    {
        registerMethod("system.listMethods",boundMethod(&MethodRegistry_t::listMethods, *this),
                       "A:", "List all registered methods");

        registerMethod("system.methodHelp",boundMethod(&MethodRegistry_t::methodHelp, *this),
                       "s:s", "Return given method help");

        registerMethod("system.methodSignature",boundMethod(&MethodRegistry_t::methodSignature,
                       *this), "A:s", "Return given method signature");

        registerMethod("system.multicall",boundMethod(&MethodRegistry_t::muticall,
                       *this), "A:A", "Call given methods");
    }

}

void MethodRegistry_t::registerMethod(const std::string &methodName, Method_t *method,
                                      const std::string signature , const std::string help )
{
    methodMap.insert(std::make_pair(methodName, RegistryEntry_t(method, signature, help)));
}

void MethodRegistry_t::registerDefaultMethod(DefaultMethod_t *defaultMethod)
{
    if(this->defaultMethod != 0)
        delete  this->defaultMethod;

    this->defaultMethod = defaultMethod;
}

void MethodRegistry_t::registerHeadMethod(HeadMethod_t *headMethod)
{
    if(this->headMethod != 0)
        delete  this->headMethod;

    this->headMethod = headMethod;
}
MethodRegistry_t::~MethodRegistry_t()
{
    for(std::map<std::string, RegistryEntry_t>::iterator i = methodMap.begin();
            i != methodMap.end(); ++i)
    {
        delete i->second.method;
    }
}
/*
namespace
{
const long BUFFER_SIZE = 1<<16;
}
*/
long MethodRegistry_t::processCall(const std::string &clientIP, const std::string &methodName,
                                   Array_t &params,
                                   Writer_t &writer, long typeOut)
{
    Pool_t pool;
    std::auto_ptr<Marshaller_t> marshaller(Marshaller_t::create(typeOut, writer));
    TimeDiff_t timeD;

    TreeFeeder_t feeder(*marshaller);

    try
    {

        Value_t &retValue = processCall(clientIP, methodName, params, pool);

        marshaller->packMethodResponse();
        feeder.feedValue(retValue);
        marshaller->flush();

    }

    catch(const StreamError_t &streamError)
    {
        if(callbacks)
            callbacks->postProcess(methodName, clientIP, params,Fault_t(FRPC_PARSE_ERROR,
                                   streamError.message()),timeD.diff());
        marshaller->packFault(FRPC_PARSE_ERROR,streamError.message().c_str());
        marshaller->flush();
    }
    catch(const Fault_t &fault)
    {
        if(callbacks)
            callbacks->postProcess(methodName, clientIP, params, fault,timeD.diff());
        marshaller->packFault(fault.errorNum(),fault.message().c_str());
        marshaller->flush();
    }

    return 0;
}

Value_t& MethodRegistry_t::processCall(const std::string &clientIP, const std::string &methodName,
                                       Array_t &params,
                                       Pool_t &pool)
{
    TimeDiff_t timeD;
    Value_t *result;
    try
    {
        std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                    methodName);

        if(pos == methodMap.end())
        {

            //if default method registered call it
            if(!defaultMethod)
            {
                throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                              methodName.c_str());
            }
            else
            {
                if(callbacks)
                    callbacks->preProcess(methodName, clientIP, params);

                result = &(defaultMethod->call(pool, methodName,params));
                if(callbacks)
                    callbacks->postProcess(methodName, clientIP, params, *result,timeD.diff());

            }

        }
        else
        {
            if(callbacks)
                callbacks->preProcess(methodName, clientIP, params);

            result = &(pos->second.method->call(pool, params));
            if(callbacks)
                callbacks->postProcess(methodName, clientIP, params, *result,timeD.diff());

        }
    }
    catch(const TypeError_t &typeError)
    {
        throw Fault_t(FRPC_TYPE_ERROR,typeError.message());
    }
    catch(const LenError_t &lenError)
    {
        throw Fault_t(FRPC_TYPE_ERROR,lenError.message());
    }
    catch(const KeyError_t &keyError)
    {
        throw Fault_t(FRPC_INDEX_ERROR,keyError.message());
    }
    catch(const IndexError_t &indexError)
    {
        throw Fault_t(FRPC_INDEX_ERROR,indexError.message());
    }
    catch(const StreamError_t &streamError)
    {
        throw Fault_t(FRPC_PARSE_ERROR,streamError.message());
    }

    return *result;

}

Value_t& MethodRegistry_t::processCall(const std::string &clientIP, Reader_t &reader,
                                       long typeIn, Pool_t &pool)
{
    TreeBuilder_t builder(pool);
    std::auto_ptr<UnMarshaller_t> unmarshaller(UnMarshaller_t::create(typeIn, builder));
    char buffer[BUFFER_SIZE];
    long readed;
    Value_t *result;

    try
    {
        while((readed = reader.read(buffer,BUFFER_SIZE)))
        {
            unmarshaller->unMarshall(buffer, readed,
                                     UnMarshaller_t::TYPE_METHOD_CALL);
        }

        result = &(processCall(builder.getUnMarshaledMethodName(), clientIP,
                               Array(builder.getUnMarshaledData()),pool));
    }
    catch(const StreamError_t &streamError)
    {
        throw Fault_t(FRPC_PARSE_ERROR,streamError.message());
    }

    return *result;
}

long MethodRegistry_t::processCall(const std::string &clientIP, Reader_t &reader,
                                   long typeIn, Writer_t &writer, long typeOut)
{
    Pool_t pool;
    TreeBuilder_t builder(pool);
    std::auto_ptr<UnMarshaller_t> unmarshaller(UnMarshaller_t::create(typeIn, builder));
    std::auto_ptr<Marshaller_t> marshaller(Marshaller_t::create(typeOut, writer));
    Value_t *retValue;
    TreeFeeder_t feeder(*marshaller);

    char buffer[BUFFER_SIZE];
    long readed;

    try
    {
        while((readed = reader.read(buffer,BUFFER_SIZE)))
        {
            unmarshaller->unMarshall(buffer, readed,
                                     UnMarshaller_t::TYPE_METHOD_CALL);
        }

        retValue = &(processCall(builder.getUnMarshaledMethodName(), clientIP,
                                 Array(builder.getUnMarshaledData()),pool));

        marshaller->packMethodResponse();

        feeder.feedValue(*retValue);
        marshaller->flush();
    }
    catch(const StreamError_t &streamError)
    {
        marshaller->packFault(FRPC_PARSE_ERROR,streamError.message().c_str());
        marshaller->flush();
    }
    catch(const Fault_t &fault)
    {
        marshaller->packFault(fault.errorNum(),fault.message().c_str());
        marshaller->flush();
    }

    return 0;
}

long MethodRegistry_t::headCall()
{

    //if head method registered call it
    if(!headMethod)
    {
        return -1;
    }
    else
    {
        if(headMethod->call())
            return 0;
        else
            return 1;
    }

}

//*******************system methods************************************
Value_t& MethodRegistry_t::listMethods(Pool_t &pool, Array_t &params)
{
    if(params.size() != 0)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 0 arguments but %d argumet(s) given",
                      params.size());

    Array_t &retArray = pool.Array();

    for(std::map<std::string, RegistryEntry_t>::iterator i = methodMap.begin();
            i != methodMap.end(); ++i)
    {
        retArray.append(pool.String(i->first));
    }

    return retArray;
}

Value_t& MethodRegistry_t::methodHelp(Pool_t &pool, Array_t &params)
{
    if(params.size() != 1)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 1 argument but %d argumet(s) given",
                      params.size());

    params.checkItems("s");

    std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                String(params[0]).getString());

    if(pos == methodMap.end())
        throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                      String(params[0]).getString().c_str());

    return pool.String(pos->second.help);
}

Value_t& MethodRegistry_t::methodSignature(Pool_t &pool, Array_t &params)
{
    if(params.size() != 1)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 1 argument but %d argumet(s) given",
                      params.size());

    params.checkItems("s");

    std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                String(params[0]).getString());

    if(pos == methodMap.end())
        throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                      String(params[0]).getString().c_str());

    //build array signature
    Array_t &array = pool.Array();
    Array_t *actual = &(pool.Array());

    array.append(*actual);

    for(unsigned long i = 0; i <= pos->second.signature.size(); i++)
    {
        switch(pos->second.signature[i])
        {
        case 'A':
            actual->append(pool.String("array"));
            break;
        case 'S':
            actual->append(pool.String("struct"));
            break;
        case 'B':
            actual->append(pool.String("binary"));
            break;
        case 'D':
            actual->append(pool.String("dateTime"));
            break;
        case 'b':
            actual->append(pool.String("bool"));
            break;
        case 'd':
            actual->append(pool.String("double"));
            break;
        case 'i':
            actual->append(pool.String("int"));
            break;
        case 's':
            actual->append(pool.String("string"));
            break;
        case ':':
            break;
        case ',':
            actual = &(pool.Array());
            array.append(*actual);
            break;
        default:
            break;

        }

    }
    return array;
}
Value_t& MethodRegistry_t::muticall(Pool_t &pool, Array_t &params)
{
    if(params.size() != 1)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 1 argument but %d argumet(s) given",
                      params.size());

    params.checkItems("A");

    Array_t &array = pool.Array();

    for(Array_t::const_iterator pos = Array(params[0]).begin(); pos != Array(params[0]).end();
            ++pos)
    {
        if((*pos)->getType() != Struct_t::TYPE)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_TYPE_ERROR),
                                     "faultString",pool.String("Parameter must be struct")));
            continue;
        }

        try
        {

            Struct_t &strct = Struct(**pos);

            std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                        String(strct["methodName"]).getString());

            if(pos == methodMap.end())
            {

                //if default method registered call it
                if(!defaultMethod)
                {
                    throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                                  String(strct["methodName"]).getString().c_str());

                }
                else
                {
                    array.append(pool.Array(defaultMethod->call(pool,
                                            String(strct["methodName"]).getString(),
                                            Array(strct["params"]))));
                }

            }
            else
            {
                array.append(pool.Array(pos->second.method->call(pool,
                                        Array(strct["params"]))));

            }

        }
        catch(const TypeError_t &typeError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_TYPE_ERROR),
                                     "faultString",pool.String(typeError.message())));
        }
        catch(const LenError_t &lenError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_TYPE_ERROR),
                                     "faultString",pool.String(lenError.message())));
        }
        catch(const KeyError_t &keyError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_INDEX_ERROR),
                                     "faultString",pool.String(keyError.message())));
        }
        catch(const IndexError_t &indexError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_INDEX_ERROR),
                                     "faultString",pool.String(indexError.message())));

        }
        catch(const Fault_t &fault)
        {

            array.append(pool.Struct("faultCode",pool.Int(fault.errorNum()),
                                     "faultString",pool.String(fault.message())));
        }

    }

    return array;
}

}
<commit_msg>preprocess is called even if method is not known and no default method is registered<commit_after>/*
 * FILE          $Id: frpcmethodregistry.cc,v 1.3 2006-03-02 13:55:39 vasek Exp $
 *
 * DESCRIPTION
 *
 * AUTHOR
 *              Miroslav Talasek <miroslav.talasek@firma.seznam.cz>
 *
 * Copyright (C) Seznam.cz a.s. 2002
 * All Rights Reserved
 *
 * HISTORY
 *
 */

#include "frpcmethodregistry.h"

#include <frpcmethod.h>
#include <frpcdefaultmethod.h>
#include <frpcheadmethod.h>
#include <frpctreebuilder.h>
#include <frpctreefeeder.h>
#include <frpcunmarshaller.h>
#include <frpcmarshaller.h>
#include <frpcstreamerror.h>
#include <frpcfault.h>
#include <frpclenerror.h>
#include <frpckeyerror.h>
#include <frpcindexerror.h>
#include <frpc.h>
#include <frpcinternals.h>
#include <memory>

#ifdef WIN32
#include <windows.h>
#endif //WIN32

namespace FRPC
{

MethodRegistry_t::TimeDiff_t::TimeDiff_t()
{
    // get current time
#ifdef WIN32
	FILETIME ft;
	GetSystemTimeAsFileTime(&ft);
	time(&second);
	usecond = (ft.dwLowDateTime / 10) % 1000000;

#else //WIN32

    struct timeval now;
    gettimeofday(&now, 0);
    second = now.tv_sec;
    usecond = now.tv_usec;
#endif //WIN32
}

MethodRegistry_t::TimeDiff_t MethodRegistry_t::TimeDiff_t::diff()
{

    TimeDiff_t now;

    // check for time skew
    if ((now.second < second)
            || ((now.second == second) && (now.usecond < usecond)))
        return TimeDiff_t(0,0);

    // compute difference
    if (now.usecond >= usecond)
        return TimeDiff_t(now.second - second,
                          now.usecond - usecond);

    return TimeDiff_t
           (now.second - second - 1L,
            1000000L - usecond + now.usecond);
}

MethodRegistry_t::MethodRegistry_t(Callbacks_t *callbacks, bool introspectionEnabled)
        :callbacks(callbacks), introspectionEnabled(introspectionEnabled),
        defaultMethod(0),headMethod(0)
{
    if(introspectionEnabled)
    {
        registerMethod("system.listMethods",boundMethod(&MethodRegistry_t::listMethods, *this),
                       "A:", "List all registered methods");

        registerMethod("system.methodHelp",boundMethod(&MethodRegistry_t::methodHelp, *this),
                       "s:s", "Return given method help");

        registerMethod("system.methodSignature",boundMethod(&MethodRegistry_t::methodSignature,
                       *this), "A:s", "Return given method signature");

        registerMethod("system.multicall",boundMethod(&MethodRegistry_t::muticall,
                       *this), "A:A", "Call given methods");
    }

}

void MethodRegistry_t::registerMethod(const std::string &methodName, Method_t *method,
                                      const std::string signature , const std::string help )
{
    methodMap.insert(std::make_pair(methodName, RegistryEntry_t(method, signature, help)));
}

void MethodRegistry_t::registerDefaultMethod(DefaultMethod_t *defaultMethod)
{
    if(this->defaultMethod != 0)
        delete  this->defaultMethod;

    this->defaultMethod = defaultMethod;
}

void MethodRegistry_t::registerHeadMethod(HeadMethod_t *headMethod)
{
    if(this->headMethod != 0)
        delete  this->headMethod;

    this->headMethod = headMethod;
}
MethodRegistry_t::~MethodRegistry_t()
{
    for(std::map<std::string, RegistryEntry_t>::iterator i = methodMap.begin();
            i != methodMap.end(); ++i)
    {
        delete i->second.method;
    }
}
/*
namespace
{
const long BUFFER_SIZE = 1<<16;
}
*/
long MethodRegistry_t::processCall(const std::string &clientIP, const std::string &methodName,
                                   Array_t &params,
                                   Writer_t &writer, long typeOut)
{
    Pool_t pool;
    std::auto_ptr<Marshaller_t> marshaller(Marshaller_t::create(typeOut, writer));
    TimeDiff_t timeD;

    TreeFeeder_t feeder(*marshaller);

    try
    {

        Value_t &retValue = processCall(clientIP, methodName, params, pool);

        marshaller->packMethodResponse();
        feeder.feedValue(retValue);
        marshaller->flush();

    }

    catch(const StreamError_t &streamError)
    {
        if(callbacks)
            callbacks->postProcess(methodName, clientIP, params,Fault_t(FRPC_PARSE_ERROR,
                                   streamError.message()),timeD.diff());
        marshaller->packFault(FRPC_PARSE_ERROR,streamError.message().c_str());
        marshaller->flush();
    }
    catch(const Fault_t &fault)
    {
        if(callbacks)
            callbacks->postProcess(methodName, clientIP, params, fault,timeD.diff());
        marshaller->packFault(fault.errorNum(),fault.message().c_str());
        marshaller->flush();
    }

    return 0;
}

Value_t& MethodRegistry_t::processCall(const std::string &clientIP, const std::string &methodName,
                                       Array_t &params,
                                       Pool_t &pool)
{
    TimeDiff_t timeD;
    Value_t *result;
    try
    {
        std::map<std::string, RegistryEntry_t>::const_iterator
            pos = methodMap.find(methodName);

        if (pos == methodMap.end()){
            if (callbacks)
                callbacks->preProcess(methodName, clientIP, params);

            // if not default method generate fault
            if (!defaultMethod)
                throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR, "Method %s not found",
                              methodName.c_str());

            result = &(defaultMethod->call(pool, methodName,params));
            if (callbacks)
                callbacks->postProcess(methodName, clientIP, params, *result,
                                       timeD.diff());
        } else {
            if(callbacks)
                callbacks->preProcess(methodName, clientIP, params);

            result = &(pos->second.method->call(pool, params));
            if(callbacks)
                callbacks->postProcess(methodName, clientIP, params, *result,
                                       timeD.diff());
        }
    }
    catch(const TypeError_t &typeError)
    {
        throw Fault_t(FRPC_TYPE_ERROR,typeError.message());
    }
    catch(const LenError_t &lenError)
    {
        throw Fault_t(FRPC_TYPE_ERROR,lenError.message());
    }
    catch(const KeyError_t &keyError)
    {
        throw Fault_t(FRPC_INDEX_ERROR,keyError.message());
    }
    catch(const IndexError_t &indexError)
    {
        throw Fault_t(FRPC_INDEX_ERROR,indexError.message());
    }
    catch(const StreamError_t &streamError)
    {
        throw Fault_t(FRPC_PARSE_ERROR,streamError.message());
    }

    return *result;

}

Value_t& MethodRegistry_t::processCall(const std::string &clientIP, Reader_t &reader,
                                       long typeIn, Pool_t &pool)
{
    TreeBuilder_t builder(pool);
    std::auto_ptr<UnMarshaller_t> unmarshaller(UnMarshaller_t::create(typeIn, builder));
    char buffer[BUFFER_SIZE];
    long readed;
    Value_t *result;

    try
    {
        while((readed = reader.read(buffer,BUFFER_SIZE)))
        {
            unmarshaller->unMarshall(buffer, readed,
                                     UnMarshaller_t::TYPE_METHOD_CALL);
        }

        result = &(processCall(builder.getUnMarshaledMethodName(), clientIP,
                               Array(builder.getUnMarshaledData()),pool));
    }
    catch(const StreamError_t &streamError)
    {
        throw Fault_t(FRPC_PARSE_ERROR,streamError.message());
    }

    return *result;
}

long MethodRegistry_t::processCall(const std::string &clientIP, Reader_t &reader,
                                   long typeIn, Writer_t &writer, long typeOut)
{
    Pool_t pool;
    TreeBuilder_t builder(pool);
    std::auto_ptr<UnMarshaller_t> unmarshaller(UnMarshaller_t::create(typeIn, builder));
    std::auto_ptr<Marshaller_t> marshaller(Marshaller_t::create(typeOut, writer));
    Value_t *retValue;
    TreeFeeder_t feeder(*marshaller);

    char buffer[BUFFER_SIZE];
    long readed;

    try
    {
        while((readed = reader.read(buffer,BUFFER_SIZE)))
        {
            unmarshaller->unMarshall(buffer, readed,
                                     UnMarshaller_t::TYPE_METHOD_CALL);
        }

        retValue = &(processCall(builder.getUnMarshaledMethodName(), clientIP,
                                 Array(builder.getUnMarshaledData()),pool));

        marshaller->packMethodResponse();

        feeder.feedValue(*retValue);
        marshaller->flush();
    }
    catch(const StreamError_t &streamError)
    {
        marshaller->packFault(FRPC_PARSE_ERROR,streamError.message().c_str());
        marshaller->flush();
    }
    catch(const Fault_t &fault)
    {
        marshaller->packFault(fault.errorNum(),fault.message().c_str());
        marshaller->flush();
    }

    return 0;
}

long MethodRegistry_t::headCall()
{

    //if head method registered call it
    if(!headMethod)
    {
        return -1;
    }
    else
    {
        if(headMethod->call())
            return 0;
        else
            return 1;
    }

}

//*******************system methods************************************
Value_t& MethodRegistry_t::listMethods(Pool_t &pool, Array_t &params)
{
    if(params.size() != 0)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 0 arguments but %d argumet(s) given",
                      params.size());

    Array_t &retArray = pool.Array();

    for(std::map<std::string, RegistryEntry_t>::iterator i = methodMap.begin();
            i != methodMap.end(); ++i)
    {
        retArray.append(pool.String(i->first));
    }

    return retArray;
}

Value_t& MethodRegistry_t::methodHelp(Pool_t &pool, Array_t &params)
{
    if(params.size() != 1)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 1 argument but %d argumet(s) given",
                      params.size());

    params.checkItems("s");

    std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                String(params[0]).getString());

    if(pos == methodMap.end())
        throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                      String(params[0]).getString().c_str());

    return pool.String(pos->second.help);
}

Value_t& MethodRegistry_t::methodSignature(Pool_t &pool, Array_t &params)
{
    if(params.size() != 1)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 1 argument but %d argumet(s) given",
                      params.size());

    params.checkItems("s");

    std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                String(params[0]).getString());

    if(pos == methodMap.end())
        throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                      String(params[0]).getString().c_str());

    //build array signature
    Array_t &array = pool.Array();
    Array_t *actual = &(pool.Array());

    array.append(*actual);

    for(unsigned long i = 0; i <= pos->second.signature.size(); i++)
    {
        switch(pos->second.signature[i])
        {
        case 'A':
            actual->append(pool.String("array"));
            break;
        case 'S':
            actual->append(pool.String("struct"));
            break;
        case 'B':
            actual->append(pool.String("binary"));
            break;
        case 'D':
            actual->append(pool.String("dateTime"));
            break;
        case 'b':
            actual->append(pool.String("bool"));
            break;
        case 'd':
            actual->append(pool.String("double"));
            break;
        case 'i':
            actual->append(pool.String("int"));
            break;
        case 's':
            actual->append(pool.String("string"));
            break;
        case ':':
            break;
        case ',':
            actual = &(pool.Array());
            array.append(*actual);
            break;
        default:
            break;

        }

    }
    return array;
}
Value_t& MethodRegistry_t::muticall(Pool_t &pool, Array_t &params)
{
    if(params.size() != 1)
        throw Fault_t(FRPC_TYPE_ERROR,"Method required 1 argument but %d argumet(s) given",
                      params.size());

    params.checkItems("A");

    Array_t &array = pool.Array();

    for(Array_t::const_iterator pos = Array(params[0]).begin(); pos != Array(params[0]).end();
            ++pos)
    {
        if((*pos)->getType() != Struct_t::TYPE)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_TYPE_ERROR),
                                     "faultString",pool.String("Parameter must be struct")));
            continue;
        }

        try
        {

            Struct_t &strct = Struct(**pos);

            std::map<std::string, RegistryEntry_t>::const_iterator pos = methodMap.find(
                        String(strct["methodName"]).getString());

            if(pos == methodMap.end())
            {

                //if default method registered call it
                if(!defaultMethod)
                {
                    throw Fault_t(FRPC_NO_SUCH_METHOD_ERROR,"Method %s not found",
                                  String(strct["methodName"]).getString().c_str());

                }
                else
                {
                    array.append(pool.Array(defaultMethod->call(pool,
                                            String(strct["methodName"]).getString(),
                                            Array(strct["params"]))));
                }

            }
            else
            {
                array.append(pool.Array(pos->second.method->call(pool,
                                        Array(strct["params"]))));

            }

        }
        catch(const TypeError_t &typeError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_TYPE_ERROR),
                                     "faultString",pool.String(typeError.message())));
        }
        catch(const LenError_t &lenError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_TYPE_ERROR),
                                     "faultString",pool.String(lenError.message())));
        }
        catch(const KeyError_t &keyError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_INDEX_ERROR),
                                     "faultString",pool.String(keyError.message())));
        }
        catch(const IndexError_t &indexError)
        {
            array.append(pool.Struct("faultCode",pool.Int(FRPC_INDEX_ERROR),
                                     "faultString",pool.String(indexError.message())));

        }
        catch(const Fault_t &fault)
        {

            array.append(pool.Struct("faultCode",pool.Int(fault.errorNum()),
                                     "faultString",pool.String(fault.message())));
        }

    }

    return array;
}

}
<|endoftext|>