<commit_before>/*
 * NotebookAlternateEngines.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "SessionExecuteChunkOperation.hpp"
#include "NotebookCache.hpp"
#include "NotebookAlternateEngines.hpp"

#include <boost/algorithm/string.hpp>

#include <core/StringUtils.hpp>
#include <core/Algorithm.hpp>
#include <core/Exec.hpp>
#include <core/json/JsonRpc.hpp>

#include <r/RExec.hpp>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {

namespace {

class ChunkExecCompletedScope : boost::noncopyable
{
public:
   ChunkExecCompletedScope(const std::string& docId,
                           const std::string& chunkId)
      : docId_(docId), chunkId_(chunkId)
   {
   }

   ~ChunkExecCompletedScope()
   {
      events().onChunkExecCompleted(
               docId_,
               chunkId_,
               notebookCtxId());
   }

private:
   std::string docId_;
   std::string chunkId_;
};

Error prepareCacheConsoleOutputFile(const std::string& docId,
                                    const std::string& chunkId,
                                    const std::string& nbCtxId,
                                    FilePath* pChunkOutputFile)
{
   // forward declare error
   Error error;

   // prepare chunk directory
   FilePath cachePath = notebook::chunkOutputPath(
            docId,
            chunkId,
            notebook::ContextExact);

   error = cachePath.resetDirectory();
   if (error)
      return error;

   // prepare cache console output file
   *pChunkOutputFile =
         notebook::chunkOutputFile(docId, chunkId, nbCtxId, ChunkOutputText);

   return Success();
}

void chunkConsoleOutputHandler(module_context::ConsoleOutputType type,
                               const std::string& output,
                               const FilePath& targetPath)
{
   using namespace module_context;

   Error error = appendConsoleOutput(
            type == ConsoleOutputNormal ? kChunkConsoleOutput : kChunkConsoleError,
            output,
            targetPath);
   if (error)
      LOG_ERROR(error);
}

void reportChunkExecutionError(const std::string& docId,
                               const std::string& chunkId,
                               const std::string& nbCtxId,
                               const std::string& message,
                               const FilePath& targetPath)
{
   // emit chunk error
   chunkConsoleOutputHandler(
            module_context::ConsoleOutputError,
            message,
            targetPath);

   // forward failure to chunk
   enqueueChunkOutput(
            docId,
            chunkId,
            nbCtxId,
            0, // no ordinal needed
            ChunkOutputText,
            targetPath);
}

Error executeRcppEngineChunk(const std::string& docId,
                             const std::string& chunkId,
                             const std::string& nbCtxId,
                             const std::string& code,
                             const std::map<std::string, std::string>& options)
{
   // forward declare error
   Error error;

   // always ensure we emit a 'execution complete' event on exit
   ChunkExecCompletedScope execScope(docId, chunkId);

   // prepare cache output file (use tempfile on failure)
   FilePath targetPath = module_context::tempFile("rcpp-cache", "");
   error = prepareCacheConsoleOutputFile(docId, chunkId, nbCtxId, &targetPath);
   if (error)
      LOG_ERROR(error);

   // capture console output, error
   boost::signals::scoped_connection consoleHandler =
         module_context::events().onConsoleOutput.connect(
            boost::bind(chunkConsoleOutputHandler,
                        _1,
                        _2,
                        targetPath));

   // call Rcpp::sourceCpp on code
   std::string escaped = boost::regex_replace(
            code,
            boost::regex("(\\\\|\")"),
            "\\\\$1");

   std::string execCode =
         "Rcpp::sourceCpp(code = \"" + escaped + "\")";

   // write input code to cache
   error = appendConsoleOutput(
            kChunkConsoleInput,
            code,
            targetPath);
   if (error)
      LOG_ERROR(error);

   // execute code (output captured on success; on failure we
   // explicitly forward the error message returned)
   error = r::exec::executeString(execCode);
   if (error)
   {
      chunkConsoleOutputHandler(module_context::ConsoleOutputError,
                                r::endUserErrorMessage(error),
                                targetPath);
   }

   // forward success / failure to chunk
   enqueueChunkOutput(
            docId,
            chunkId,
            nbCtxId,
            0, // no ordinal needed
            ChunkOutputText,
            targetPath);

   return error;
}

void reportStanExecutionError(const std::string& docId,
                              const std::string& chunkId,
                              const std::string& nbCtxId,
                              const FilePath& targetPath)
{
   std::string message =
         "engine.opts$output.var must be a character string providing a "
         "name for the returned `stanmodel` object";
   reportChunkExecutionError(docId, chunkId, nbCtxId, message, targetPath);
}

Error executeStanEngineChunk(const std::string& docId,
                             const std::string& chunkId,
                             const std::string& nbCtxId,
                             const std::string& code,
                             const std::map<std::string, std::string>& options)
{
   // forward-declare error
   Error error;

   // ensure we always emit an execution complete event on exit
   ChunkExecCompletedScope execScope(docId, chunkId);

   // prepare console output file -- use tempfile on failure
   FilePath targetPath = module_context::tempFile("stan-cache-", "");
   error = prepareCacheConsoleOutputFile(docId, chunkId, nbCtxId, &targetPath);
   if (error)
      LOG_ERROR(error);

   // capture console output, error
   boost::signals::scoped_connection consoleHandler =
         module_context::events().onConsoleOutput.connect(
            boost::bind(chunkConsoleOutputHandler,
                        _1,
                        _2,
                        targetPath));

   // write code to file
   FilePath tempFile = module_context::tempFile("stan-", ".stan");
   error = writeStringToFile(tempFile, code);
   if (error)
   {
      reportChunkExecutionError(
               docId,
               chunkId,
               nbCtxId,
               r::endUserErrorMessage(error),
               targetPath);
      LOG_ERROR(error);
      return Success();
   }
   RemoveOnExitScope removeOnExitScope(tempFile, ERROR_LOCATION);

   // ensure existence of 'engine.opts' with 'output.var' parameter
   // ('x' also allowed for backwards compatibility)
   if (!options.count("engine.opts"))
   {
      reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
      return Success();
   }

   // evaluate engine options (so we can pass them through to stan call)
   r::sexp::Protect protect;
   SEXP engineOptsSEXP = R_NilValue;
   error = r::exec::evaluateString(
            options.at("engine.opts"),
            &engineOptsSEXP,
            &protect);

   if (error)
   {
      reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
      return Success();
   }

   // construct call to 'stan_model'
   r::exec::RFunction fStanEngine("rstan:::stan_model");
   std::vector<std::string> engineOptsNames;
   error = r::sexp::getNames(engineOptsSEXP, &engineOptsNames);
   if (error)
   {
      reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
      return Success();
   }

   // build parameters
   std::string modelName;
   for (std::size_t i = 0, n = r::sexp::length(engineOptsSEXP); i < n; ++i)
   {
      // skip 'output.var' engine option (this is the variable we wish to assign to
      // after evaluating the stan model)
      if (engineOptsNames[i] == "output.var" || engineOptsNames[i] == "x")
      {
         modelName = r::sexp::asString(VECTOR_ELT(engineOptsSEXP, i));
         continue;
      }

      fStanEngine.addParam(
               engineOptsNames[i],
               VECTOR_ELT(engineOptsSEXP, i));
   }

   // if no model name was set, return error message
   if (modelName.empty())
   {
      reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
      return Success();
   }

   // if the 'file' option was not set, set it explicitly
   if (!core::algorithm::contains(engineOptsNames, "file"))
      fStanEngine.addParam("file", string_utils::utf8ToSystem(tempFile.absolutePath()));

   // evaluate stan_model call
   SEXP stanModelSEXP = R_NilValue;
   error = fStanEngine.call(&stanModelSEXP, &protect);
   if (error)
   {
      std::string msg = r::endUserErrorMessage(error);
      reportChunkExecutionError(docId, chunkId, nbCtxId, msg, targetPath);
      return Success();
   }

   // assign in global env on success
   if (stanModelSEXP != R_NilValue)
   {
      r::exec::RFunction assign("base:::assign");
      assign.addParam("x", modelName);
      assign.addParam("value", stanModelSEXP);
      error = assign.call();
      if (error)
      {
         std::string msg = r::endUserErrorMessage(error);
         reportChunkExecutionError(docId, chunkId, nbCtxId, msg, targetPath);
         LOG_ERROR(error);
         return Success();
      }
   }

   // forward success / failure to chunk
   enqueueChunkOutput(
            docId,
            chunkId,
            notebookCtxId(),
            0, // no ordinal needed
            ChunkOutputText,
            targetPath);

   return Success();
}

Error executeSqlEngineChunk(const std::string& docId,
                             const std::string& chunkId,
                             const std::string& nbCtxId,
                             const std::string& code,
                             const json::Object& options)
{
   Error error;

   // ensure we always emit an execution complete event on exit
   ChunkExecCompletedScope execScope(docId, chunkId);

      // prepare console output file -- use tempfile on failure
   FilePath consolePath = module_context::tempFile("data-console-", "");
   error = prepareCacheConsoleOutputFile(docId, chunkId, nbCtxId, &consolePath);
   if (error)
      LOG_ERROR(error);

   // capture console output, error
   boost::signals::scoped_connection consoleHandler =
         module_context::events().onConsoleOutput.connect(
            boost::bind(chunkConsoleOutputHandler,
                        _1,
                        _2,
                        consolePath));

   FilePath parentPath = notebook::chunkOutputPath(
       docId, chunkId, nbCtxId, ContextSaved);
   error = parentPath.ensureDirectory();
   if (error)
   {
      std::string message = "Failed to create SQL chunk directory";
      reportChunkExecutionError(docId, chunkId, nbCtxId, message, parentPath);

      return Success();
   }

   FilePath dataPath =
         notebook::chunkOutputFile(docId, chunkId, nbCtxId, ChunkOutputData);

   // check package dependencies
   if (!module_context::isPackageVersionInstalled("DBI", "0.4"))
   {
      std::string message = "Executing SQL chunks requires version 0.4 or "
                            "later of the DBI package";
      reportChunkExecutionError(docId, chunkId, nbCtxId, message, consolePath);
      return Success();
   }

   // run sql and save result
   error = r::exec::RFunction(
               ".rs.runSqlForDataCapture",
               code,
               string_utils::utf8ToSystem(dataPath.absolutePath()),
               options).call();
   if (error)
   {
      std::string message = "Failed to execute SQL chunk";
      reportChunkExecutionError(docId, chunkId, nbCtxId, message, consolePath);

      return Success();
   }

   if (dataPath.exists()) {
      // forward success / failure to chunk
      enqueueChunkOutput(
               docId,
               chunkId,
               notebookCtxId(),
               0, // no ordinal needed
               ChunkOutputData,
               dataPath);
   }

   // forward console output
   enqueueChunkOutput(
            docId,
            chunkId,
            notebookCtxId(),
            0, // no ordinal needed
            ChunkOutputText,
            consolePath);

   return Success();
}

Error interruptEngineChunk(const json::JsonRpcRequest& request,
                           json::JsonRpcResponse* pResponse)
{
   std::string docId, chunkId;
   Error error = json::readParams(request.params,
                                  &docId,
                                  &chunkId);
   if (error)
   {
      LOG_ERROR(error);
      return error;
   }

   interruptChunk(docId, chunkId);
   return Success();
}

} // anonymous namespace

Error executeAlternateEngineChunk(const std::string& docId,
                                  const std::string& chunkId,
                                  const std::string& nbCtxId,
                                  const std::string& engine,
                                  const std::string& code,
                                  const json::Object& jsonChunkOptions)
{
   // read json chunk options
   std::map<std::string, std::string> options;
   for (json::Object::const_iterator it = jsonChunkOptions.begin();
        it != jsonChunkOptions.end();
        ++it)
   {
      if (it->second.type() == json::StringType)
         options[it->first] = it->second.get_str();
   }

   // handle some engines with their own custom routines
   if (engine == "Rcpp")
      return executeRcppEngineChunk(docId, chunkId, nbCtxId, code, options);
   else if (engine == "stan")
      return executeStanEngineChunk(docId, chunkId, nbCtxId, code, options);
   else if (engine == "sql")
      return executeSqlEngineChunk(docId, chunkId, nbCtxId, code, jsonChunkOptions);

   runChunk(docId, chunkId, nbCtxId, engine, code, options);
   return Success();
}

Error initAlternateEngines()
{
   using namespace module_context;

   ExecBlock initBlock;
   initBlock.addFunctions()
      (bind(registerRpcMethod, "interrupt_chunk", interruptEngineChunk));
   return initBlock.execute();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<commit_msg>respect use of 'output.var' in chunk header<commit_after>/*
 * NotebookAlternateEngines.cpp
 *
 * Copyright (C) 2009-16 by RStudio, Inc.
 *
 * Unless you have received this program directly from RStudio pursuant
 * to the terms of a commercial license agreement with RStudio, then
 * this program is licensed to you under the terms of version 3 of the
 * GNU Affero General Public License. This program is distributed WITHOUT
 * ANY EXPRESS OR IMPLIED WARRANTY, INCLUDING THOSE OF NON-INFRINGEMENT,
 * MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Please refer to the
 * AGPL (http://www.gnu.org/licenses/agpl-3.0.txt) for more details.
 *
 */

#include "SessionRmdNotebook.hpp"
#include "SessionExecuteChunkOperation.hpp"
#include "NotebookCache.hpp"
#include "NotebookAlternateEngines.hpp"

#include <boost/algorithm/string.hpp>

#include <core/StringUtils.hpp>
#include <core/Algorithm.hpp>
#include <core/Exec.hpp>
#include <core/json/JsonRpc.hpp>

#include <r/RExec.hpp>

using namespace rstudio::core;

namespace rstudio {
namespace session {
namespace modules {
namespace rmarkdown {
namespace notebook {

namespace {

class ChunkExecCompletedScope : boost::noncopyable
{
public:
   ChunkExecCompletedScope(const std::string& docId,
                           const std::string& chunkId)
      : docId_(docId), chunkId_(chunkId)
   {
   }

   ~ChunkExecCompletedScope()
   {
      events().onChunkExecCompleted(
               docId_,
               chunkId_,
               notebookCtxId());
   }

private:
   std::string docId_;
   std::string chunkId_;
};

Error prepareCacheConsoleOutputFile(const std::string& docId,
                                    const std::string& chunkId,
                                    const std::string& nbCtxId,
                                    FilePath* pChunkOutputFile)
{
   // forward declare error
   Error error;

   // prepare chunk directory
   FilePath cachePath = notebook::chunkOutputPath(
            docId,
            chunkId,
            notebook::ContextExact);

   error = cachePath.resetDirectory();
   if (error)
      return error;

   // prepare cache console output file
   *pChunkOutputFile =
         notebook::chunkOutputFile(docId, chunkId, nbCtxId, ChunkOutputText);

   return Success();
}

void chunkConsoleOutputHandler(module_context::ConsoleOutputType type,
                               const std::string& output,
                               const FilePath& targetPath)
{
   using namespace module_context;

   Error error = appendConsoleOutput(
            type == ConsoleOutputNormal ? kChunkConsoleOutput : kChunkConsoleError,
            output,
            targetPath);
   if (error)
      LOG_ERROR(error);
}

void reportChunkExecutionError(const std::string& docId,
                               const std::string& chunkId,
                               const std::string& nbCtxId,
                               const std::string& message,
                               const FilePath& targetPath)
{
   // emit chunk error
   chunkConsoleOutputHandler(
            module_context::ConsoleOutputError,
            message,
            targetPath);

   // forward failure to chunk
   enqueueChunkOutput(
            docId,
            chunkId,
            nbCtxId,
            0, // no ordinal needed
            ChunkOutputText,
            targetPath);
}

Error executeRcppEngineChunk(const std::string& docId,
                             const std::string& chunkId,
                             const std::string& nbCtxId,
                             const std::string& code,
                             const std::map<std::string, std::string>& options)
{
   // forward declare error
   Error error;

   // always ensure we emit a 'execution complete' event on exit
   ChunkExecCompletedScope execScope(docId, chunkId);

   // prepare cache output file (use tempfile on failure)
   FilePath targetPath = module_context::tempFile("rcpp-cache", "");
   error = prepareCacheConsoleOutputFile(docId, chunkId, nbCtxId, &targetPath);
   if (error)
      LOG_ERROR(error);

   // capture console output, error
   boost::signals::scoped_connection consoleHandler =
         module_context::events().onConsoleOutput.connect(
            boost::bind(chunkConsoleOutputHandler,
                        _1,
                        _2,
                        targetPath));

   // call Rcpp::sourceCpp on code
   std::string escaped = boost::regex_replace(
            code,
            boost::regex("(\\\\|\")"),
            "\\\\$1");

   std::string execCode =
         "Rcpp::sourceCpp(code = \"" + escaped + "\")";

   // write input code to cache
   error = appendConsoleOutput(
            kChunkConsoleInput,
            code,
            targetPath);
   if (error)
      LOG_ERROR(error);

   // execute code (output captured on success; on failure we
   // explicitly forward the error message returned)
   error = r::exec::executeString(execCode);
   if (error)
   {
      chunkConsoleOutputHandler(module_context::ConsoleOutputError,
                                r::endUserErrorMessage(error),
                                targetPath);
   }

   // forward success / failure to chunk
   enqueueChunkOutput(
            docId,
            chunkId,
            nbCtxId,
            0, // no ordinal needed
            ChunkOutputText,
            targetPath);

   return error;
}

void reportStanExecutionError(const std::string& docId,
                              const std::string& chunkId,
                              const std::string& nbCtxId,
                              const FilePath& targetPath)
{
   std::string message =
         "engine.opts$output.var must be a character string providing a "
         "name for the returned `stanmodel` object";
   reportChunkExecutionError(docId, chunkId, nbCtxId, message, targetPath);
}

Error executeStanEngineChunk(const std::string& docId,
                             const std::string& chunkId,
                             const std::string& nbCtxId,
                             const std::string& code,
                             const std::map<std::string, std::string>& options)
{
   // forward-declare error
   Error error;

   // ensure we always emit an execution complete event on exit
   ChunkExecCompletedScope execScope(docId, chunkId);

   // prepare console output file -- use tempfile on failure
   FilePath targetPath = module_context::tempFile("stan-cache-", "");
   error = prepareCacheConsoleOutputFile(docId, chunkId, nbCtxId, &targetPath);
   if (error)
      LOG_ERROR(error);

   // capture console output, error
   boost::signals::scoped_connection consoleHandler =
         module_context::events().onConsoleOutput.connect(
            boost::bind(chunkConsoleOutputHandler,
                        _1,
                        _2,
                        targetPath));

   // write code to file
   FilePath tempFile = module_context::tempFile("stan-", ".stan");
   error = writeStringToFile(tempFile, code + "\n");
   if (error)
   {
      reportChunkExecutionError(
               docId,
               chunkId,
               nbCtxId,
               r::endUserErrorMessage(error),
               targetPath);
      LOG_ERROR(error);
      return Success();
   }
   RemoveOnExitScope removeOnExitScope(tempFile, ERROR_LOCATION);

   // evaluate engine options (so we can pass them through to stan call)
   r::sexp::Protect protect;
   SEXP engineOptsSEXP = R_NilValue;
   if (options.count("engine.opts"))
   {
      error = r::exec::evaluateString(
               options.at("engine.opts"),
               &engineOptsSEXP,
               &protect);

      if (error)
      {
         reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
         return Success();
      }
   }
   else
   {
      // if no engine.opts available, just use a plain empty list
      engineOptsSEXP = r::sexp::createList(std::vector<std::string>(), &protect);
   }

   // construct call to 'stan_model'
   r::exec::RFunction fStanEngine("rstan:::stan_model");
   std::vector<std::string> engineOptsNames;
   error = r::sexp::getNames(engineOptsSEXP, &engineOptsNames);
   if (error)
   {
      reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
      return Success();
   }

   // build parameters
   std::string modelName;
   for (std::size_t i = 0, n = r::sexp::length(engineOptsSEXP); i < n; ++i)
   {
      // skip 'output.var' engine option (this is the variable we wish to assign to
      // after evaluating the stan model)
      if (engineOptsNames[i] == "output.var" || engineOptsNames[i] == "x")
      {
         modelName = r::sexp::asString(VECTOR_ELT(engineOptsSEXP, i));
         continue;
      }

      fStanEngine.addParam(
               engineOptsNames[i],
               VECTOR_ELT(engineOptsSEXP, i));
   }

   // if 'output.var' was provided as part of the chunk parameters
   // (not as part of 'engine.opts') then use that here
   if (options.count("output.var"))
      modelName = options.at("output.var");

   // if no model name was set, return error message
   if (modelName.empty())
   {
      reportStanExecutionError(docId, chunkId, nbCtxId, targetPath);
      return Success();
   }

   // if the 'file' option was not set, set it explicitly
   if (!core::algorithm::contains(engineOptsNames, "file"))
      fStanEngine.addParam("file", string_utils::utf8ToSystem(tempFile.absolutePath()));

   // evaluate stan_model call
   SEXP stanModelSEXP = R_NilValue;
   error = fStanEngine.call(&stanModelSEXP, &protect);
   if (error)
   {
      std::string msg = r::endUserErrorMessage(error);
      reportChunkExecutionError(docId, chunkId, nbCtxId, msg, targetPath);
      return Success();
   }

   // assign in global env on success
   if (stanModelSEXP != R_NilValue)
   {
      r::exec::RFunction assign("base:::assign");
      assign.addParam("x", modelName);
      assign.addParam("value", stanModelSEXP);
      error = assign.call();
      if (error)
      {
         std::string msg = r::endUserErrorMessage(error);
         reportChunkExecutionError(docId, chunkId, nbCtxId, msg, targetPath);
         LOG_ERROR(error);
         return Success();
      }
   }

   // forward success / failure to chunk
   enqueueChunkOutput(
            docId,
            chunkId,
            notebookCtxId(),
            0, // no ordinal needed
            ChunkOutputText,
            targetPath);

   return Success();
}

Error executeSqlEngineChunk(const std::string& docId,
                             const std::string& chunkId,
                             const std::string& nbCtxId,
                             const std::string& code,
                             const json::Object& options)
{
   Error error;

   // ensure we always emit an execution complete event on exit
   ChunkExecCompletedScope execScope(docId, chunkId);

      // prepare console output file -- use tempfile on failure
   FilePath consolePath = module_context::tempFile("data-console-", "");
   error = prepareCacheConsoleOutputFile(docId, chunkId, nbCtxId, &consolePath);
   if (error)
      LOG_ERROR(error);

   // capture console output, error
   boost::signals::scoped_connection consoleHandler =
         module_context::events().onConsoleOutput.connect(
            boost::bind(chunkConsoleOutputHandler,
                        _1,
                        _2,
                        consolePath));

   FilePath parentPath = notebook::chunkOutputPath(
       docId, chunkId, nbCtxId, ContextSaved);
   error = parentPath.ensureDirectory();
   if (error)
   {
      std::string message = "Failed to create SQL chunk directory";
      reportChunkExecutionError(docId, chunkId, nbCtxId, message, parentPath);

      return Success();
   }

   FilePath dataPath =
         notebook::chunkOutputFile(docId, chunkId, nbCtxId, ChunkOutputData);

   // check package dependencies
   if (!module_context::isPackageVersionInstalled("DBI", "0.4"))
   {
      std::string message = "Executing SQL chunks requires version 0.4 or "
                            "later of the DBI package";
      reportChunkExecutionError(docId, chunkId, nbCtxId, message, consolePath);
      return Success();
   }

   // run sql and save result
   error = r::exec::RFunction(
               ".rs.runSqlForDataCapture",
               code,
               string_utils::utf8ToSystem(dataPath.absolutePath()),
               options).call();
   if (error)
   {
      std::string message = "Failed to execute SQL chunk";
      reportChunkExecutionError(docId, chunkId, nbCtxId, message, consolePath);

      return Success();
   }

   if (dataPath.exists()) {
      // forward success / failure to chunk
      enqueueChunkOutput(
               docId,
               chunkId,
               notebookCtxId(),
               0, // no ordinal needed
               ChunkOutputData,
               dataPath);
   }

   // forward console output
   enqueueChunkOutput(
            docId,
            chunkId,
            notebookCtxId(),
            0, // no ordinal needed
            ChunkOutputText,
            consolePath);

   return Success();
}

Error interruptEngineChunk(const json::JsonRpcRequest& request,
                           json::JsonRpcResponse* pResponse)
{
   std::string docId, chunkId;
   Error error = json::readParams(request.params,
                                  &docId,
                                  &chunkId);
   if (error)
   {
      LOG_ERROR(error);
      return error;
   }

   interruptChunk(docId, chunkId);
   return Success();
}

} // anonymous namespace

Error executeAlternateEngineChunk(const std::string& docId,
                                  const std::string& chunkId,
                                  const std::string& nbCtxId,
                                  const std::string& engine,
                                  const std::string& code,
                                  const json::Object& jsonChunkOptions)
{
   // read json chunk options
   std::map<std::string, std::string> options;
   for (json::Object::const_iterator it = jsonChunkOptions.begin();
        it != jsonChunkOptions.end();
        ++it)
   {
      if (it->second.type() == json::StringType)
         options[it->first] = it->second.get_str();
   }

   // handle some engines with their own custom routines
   if (engine == "Rcpp")
      return executeRcppEngineChunk(docId, chunkId, nbCtxId, code, options);
   else if (engine == "stan")
      return executeStanEngineChunk(docId, chunkId, nbCtxId, code, options);
   else if (engine == "sql")
      return executeSqlEngineChunk(docId, chunkId, nbCtxId, code, jsonChunkOptions);

   runChunk(docId, chunkId, nbCtxId, engine, code, options);
   return Success();
}

Error initAlternateEngines()
{
   using namespace module_context;

   ExecBlock initBlock;
   initBlock.addFunctions()
      (bind(registerRpcMethod, "interrupt_chunk", interruptEngineChunk));
   return initBlock.execute();
}

} // namespace notebook
} // namespace rmarkdown
} // namespace modules
} // namespace session
} // namespace rstudio

<|endoftext|>


<commit_before>#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <dynamic_reconfigure/server.h>
#include <geometry_msgs/Point.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/Pose.h>
#include <geometry_msgs/PoseArray.h>
#include <image_transport/image_transport.h>
#include <kinect_tomato_searcher/SearchConfig.h>
#include <sensor_msgs/image_encodings.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>

class ShowImage {
public:
  ShowImage()
    : m_gamma(2048),
      RGB_WINDOW_NAME {"RGB image"},
      DEPTH_WINDOW_NAME {"Depth image"},
      BINARY_WINDOW_NAME {"Binary image"}
  {
    for(int i = 0; i < 2048; i++) {
      float v = i / 2048.0;
      v = std::pow(v, 3) * 6;
      m_gamma[i] = v * 6 * 256;
    }

    cv::namedWindow(RGB_WINDOW_NAME);
    cv::namedWindow(DEPTH_WINDOW_NAME);
    cv::namedWindow(BINARY_WINDOW_NAME);
  }

  ~ShowImage() {
    cv::destroyWindow(RGB_WINDOW_NAME);
    cv::destroyWindow(DEPTH_WINDOW_NAME);
    cv::destroyWindow(BINARY_WINDOW_NAME);
  }

  void showRGB(const cv::Mat& rgb) {
    imshow(RGB_WINDOW_NAME, rgb);
  }

  void showBinary(const cv::Mat& binary) {
    imshow(BINARY_WINDOW_NAME, binary);
  }

  void showDepth(const cv::Mat& depth) {
    cv::Mat buf = cv::Mat::zeros(depth.size(), CV_16UC1);
    cv::Mat output = cv::Mat::zeros(depth.size(), CV_8UC3);
    cv::normalize(depth, buf, 0, 2013, cv::NORM_MINMAX);

    for (int i = 0; i < depth.rows * depth.cols; i++) {
      int y = (int)(i / depth.cols);
      int x = (int)(i % depth.cols);
      int pval = m_gamma[buf.at<uint16_t>(i)];
      int lb = pval & 0xff;

      switch(pval >> 8) {
      case 0:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      case 1:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = lb;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 2:
        output.at<cv::Vec3b>(y,x)[2] = 255-lb;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 3:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = lb;
        break;
      case 4:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255;
        break;
      case 5:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      default:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      }
    }
    imshow(DEPTH_WINDOW_NAME, output);
  }

private:
  std::vector<uint16_t> m_gamma;
  const std::string RGB_WINDOW_NAME;
  const std::string DEPTH_WINDOW_NAME;
  const std::string BINARY_WINDOW_NAME;
};

class SearchTomato
{
public:
  SearchTomato()
    : tomato_contours {},
      siObj {},
      server {},
      f(std::bind(&dynamic_reconfigure_callback, std::placeholders::_1, std::placeholders::_2))
  {
    server.setCallback(f);
  }

  void update(const cv::Mat& capture_rgb) {
    siObj.showRGB(capture_rgb);
    cv::Mat binary_mat = cv::Mat::zeros(capture_rgb.size(), CV_8UC1);
    imageProsessing(capture_rgb, binary_mat);
    siObj.showBinary(binary_mat);
    searchTomatoContours(binary_mat, tomato_contours);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::Point& tomato_point) {
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    cv::drawContours(mask, tomato_contours, -1, cv::Scalar(255), CV_FILLED, 8);
    siObj.showBinary(mask);

    capture_depth.copyTo(maskedDepth, mask);
    return findClosePoint(maskedDepth, tomato_point);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::PoseArray& tomato_poses) {
    tomato_poses.poses.clear();
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    for (std::size_t i {0}; i < tomato_contours.size(); ++i) {
      cv::drawContours(mask, tomato_contours, i, cv::Scalar(255), CV_FILLED, 8);
      siObj.showBinary(mask);

      capture_depth.copyTo(maskedDepth, mask);
      findClosePoint(maskedDepth, tomato_poses);
    }
    return tomato_poses.poses.empty() ? false : true;
  }

private:
  static void dynamic_reconfigure_callback(kinect_tomato_searcher::SearchConfig& config, uint32_t level)
  {
    h_min_ = config.h_min;
    h_max_ = config.h_max;
    s_min_ = config.s_min;
    s_max_ = config.s_max;
    v_min_ = config.v_min;
    v_max_ = config.v_max;
  }

  void imageProsessing(const cv::Mat& rgb, cv::Mat& binary) {
    cv::Mat blur, hsv;
    cv::GaussianBlur(rgb, blur, cv::Size(5, 5), 4.0, 4.0);
    cv::cvtColor(blur, hsv, CV_BGR2HSV);
    redFilter(hsv, binary);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 2);
    cv::erode(binary, binary, cv::Mat(), cv::Point(-1, -1), 4);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 1);
  }

  void searchTomatoContours(const cv::Mat& binary, std::vector<std::vector<cv::Point> >& tomato_contours) {
    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Point> contour;
    cv::Rect bounding_box;
    float ratio_balance;

    cv::findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    tomato_contours.clear();
    while (!contours.empty()) {
      contour = contours.back();
      contours.pop_back();

      bounding_box = cv::boundingRect(contour);
      ratio_balance = (float)bounding_box.width / (float)bounding_box.height;
      if (ratio_balance > 1.0f) ratio_balance = 1.0f / ratio_balance;

      // delete mismach. that is smaller or spier or empty
      if (bounding_box.area() >= 400 && bounding_box.area() < cv::contourArea(contour)*2 && ratio_balance > 0.4f)
        tomato_contours.push_back(contour);
    }
  }

  void redFilter(const cv::Mat& hsv, cv::Mat& binary) {
    int a, x, y;
    for(y = 0; y < hsv.rows; y++) {
      for(x = 0; x < hsv.cols; x++) {
        a = hsv.step*y+(x*3);
        if((hsv.data[a] <= h_min_ || hsv.data[a] >= h_max_) &&
           (hsv.data[a+1] >= s_min_ || hsv.data[a+1] <= s_max_) &&
           (hsv.data[a+2] >= v_min_ || hsv.data[a+2] <= v_max_))
          binary.at<unsigned char>(y,x) = 255;
        else
          binary.at<unsigned char>(y,x) = 0;
      }
    }
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::Point& tomato_point) {
    uint16_t depth;
    constexpr uint16_t OVER_RANGE = 3000;
    uint16_t close_depth = OVER_RANGE;

    for (int y = 0; y < maskedDepth.rows; y++) {
      const uint16_t* line_point = maskedDepth.ptr<uint16_t>(y);
      for (int x = 0; x < maskedDepth.cols; x++) {
        depth = line_point[x];
        if (512 < depth && depth <  close_depth) {
          tomato_point.x = depth; // for ros coordinate system
          tomato_point.y = x - maskedDepth.cols / 2; // for ros coordinate system
          tomato_point.z = -(y - maskedDepth.rows / 2); // for ros coordinate system
          close_depth = depth;
        }
      }
    }
    return (close_depth != OVER_RANGE ? true : false);
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::PoseArray& tomato_poses) {
    geometry_msgs::Point tomato_point {};
    if (findClosePoint(maskedDepth, tomato_point)) {
      geometry_msgs::Pose tomato_pose {};
      tomato_pose.position = tomato_point;
      tomato_pose.orientation.w = 1.0;
      tomato_poses.poses.push_back(tomato_pose);
      return true;
    }
    return false;
  }

  static uint16_t h_min_;
  static uint16_t h_max_;
  static uint16_t s_min_;
  static uint16_t s_max_;
  static uint16_t v_min_;
  static uint16_t v_max_;

  std::vector<std::vector<cv::Point> > tomato_contours;
  ShowImage siObj;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig> server;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig>::CallbackType f;
};
uint16_t SearchTomato::h_min_;
uint16_t SearchTomato::h_max_;
uint16_t SearchTomato::s_min_;
uint16_t SearchTomato::s_max_;
uint16_t SearchTomato::v_min_;
uint16_t SearchTomato::v_max_;

class ImageConverter {
private:
  ros::NodeHandle nh;
  ros::NodeHandle tomapo_nh;
  ros::Publisher poses_pub;
  geometry_msgs::PoseArray pub_msg;
  image_transport::ImageTransport it;
  image_transport::Subscriber rgb_sub;
  image_transport::Subscriber depth_sub;
  cv_bridge::CvImageConstPtr rgb_ptr;
  cv_bridge::CvImageConstPtr depth_ptr;
  SearchTomato stObj;
  const std::size_t height_;
  const double angle_x_per_piccell_;
  const double angle_y_per_piccell_;
  static constexpr double PI {3.141592653589793};

public:
  ImageConverter(const std::size_t width, const std::size_t height, const double angle_of_view_x, const double angle_of_view_y)
    : nh {},
      tomapo_nh {nh, "tomato_point"},
      poses_pub {tomapo_nh.advertise<geometry_msgs::PoseArray>("array", 1)},
      it {nh},
      rgb_sub {it.subscribe("color", 1, &ImageConverter::rgbCb, this)},
      depth_sub {it.subscribe("depth", 1, &ImageConverter::depthCb, this)},
      stObj {},
      height_ {height},
      angle_x_per_piccell_ {(angle_of_view_x * PI / 180.0) / width},
      angle_y_per_piccell_ {(angle_of_view_y * PI / 180.0) / height}
  {
    pub_msg.header.frame_id = "kinect";
  }

  /**
   * search tomato function.
   * get rgb image, and search tomato.
   * will tomato_point have data.
   *
   * @author Yusuke Doi
   */
  void rgbCb(const sensor_msgs::ImageConstPtr& msg) {
    // get rgb image on kinect
    try {
      rgb_ptr = cv_bridge::toCvShare(msg, "bgr8");
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by rgb: %s", e.what());
    }

    stObj.update(rgb_ptr->image);

    cv::waitKey(100);
  }

  void depthCb(const sensor_msgs::ImageConstPtr& msg) {
    try {
      depth_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_16UC1);
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by depth: %s", e.what());
    }

    if (stObj.searchTomato(depth_ptr->image, pub_msg)) {
      pub_msg.header.stamp = ros::Time::now();
      for (auto& tomato_pose : pub_msg.poses) {
        tomato_pose.position.y = tomato_pose.position.x * tan(angle_x_per_piccell_ * tomato_pose.position.y) / 1000.0; // convert to m
        tomato_pose.position.z = tomato_pose.position.x * tan(angle_y_per_piccell_ * tomato_pose.position.z) / 1000.0; // convert to m
        tomato_pose.position.x = tomato_pose.position.x / 1000.0; // convert to m
      }
      poses_pub.publish(pub_msg);
    }

    cv::waitKey(100);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "kinect_tomato_searcher_node");
  ImageConverter ic {512, 424, 70, 60};
  ros::spin();
  return 0;
}
<commit_msg>Fix bug of boolean<commit_after>#include <ros/ros.h>
#include <cv_bridge/cv_bridge.h>
#include <dynamic_reconfigure/server.h>
#include <geometry_msgs/Point.h>
#include <geometry_msgs/PointStamped.h>
#include <geometry_msgs/Pose.h>
#include <geometry_msgs/PoseArray.h>
#include <image_transport/image_transport.h>
#include <kinect_tomato_searcher/SearchConfig.h>
#include <sensor_msgs/image_encodings.h>

#include <opencv2/highgui/highgui.hpp>
#include <opencv2/imgproc/imgproc.hpp>
#include <opencv2/video/video.hpp>

class ShowImage {
public:
  ShowImage()
    : m_gamma(2048),
      RGB_WINDOW_NAME {"RGB image"},
      DEPTH_WINDOW_NAME {"Depth image"},
      BINARY_WINDOW_NAME {"Binary image"}
  {
    for(int i = 0; i < 2048; i++) {
      float v = i / 2048.0;
      v = std::pow(v, 3) * 6;
      m_gamma[i] = v * 6 * 256;
    }

    cv::namedWindow(RGB_WINDOW_NAME);
    cv::namedWindow(DEPTH_WINDOW_NAME);
    cv::namedWindow(BINARY_WINDOW_NAME);
  }

  ~ShowImage() {
    cv::destroyWindow(RGB_WINDOW_NAME);
    cv::destroyWindow(DEPTH_WINDOW_NAME);
    cv::destroyWindow(BINARY_WINDOW_NAME);
  }

  void showRGB(const cv::Mat& rgb) {
    imshow(RGB_WINDOW_NAME, rgb);
  }

  void showBinary(const cv::Mat& binary) {
    imshow(BINARY_WINDOW_NAME, binary);
  }

  void showDepth(const cv::Mat& depth) {
    cv::Mat buf = cv::Mat::zeros(depth.size(), CV_16UC1);
    cv::Mat output = cv::Mat::zeros(depth.size(), CV_8UC3);
    cv::normalize(depth, buf, 0, 2013, cv::NORM_MINMAX);

    for (int i = 0; i < depth.rows * depth.cols; i++) {
      int y = (int)(i / depth.cols);
      int x = (int)(i % depth.cols);
      int pval = m_gamma[buf.at<uint16_t>(i)];
      int lb = pval & 0xff;

      switch(pval >> 8) {
      case 0:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      case 1:
        output.at<cv::Vec3b>(y,x)[2] = 255;
        output.at<cv::Vec3b>(y,x)[1] = lb;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 2:
        output.at<cv::Vec3b>(y,x)[2] = 255-lb;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      case 3:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255;
        output.at<cv::Vec3b>(y,x)[0] = lb;
        break;
      case 4:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 255-lb;
        output.at<cv::Vec3b>(y,x)[0] = 255;
        break;
      case 5:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 255-lb;
        break;
      default:
        output.at<cv::Vec3b>(y,x)[2] = 0;
        output.at<cv::Vec3b>(y,x)[1] = 0;
        output.at<cv::Vec3b>(y,x)[0] = 0;
        break;
      }
    }
    imshow(DEPTH_WINDOW_NAME, output);
  }

private:
  std::vector<uint16_t> m_gamma;
  const std::string RGB_WINDOW_NAME;
  const std::string DEPTH_WINDOW_NAME;
  const std::string BINARY_WINDOW_NAME;
};

class SearchTomato
{
public:
  SearchTomato()
    : tomato_contours {},
      siObj {},
      server {},
      f(std::bind(&dynamic_reconfigure_callback, std::placeholders::_1, std::placeholders::_2))
  {
    server.setCallback(f);
  }

  void update(const cv::Mat& capture_rgb) {
    siObj.showRGB(capture_rgb);
    cv::Mat binary_mat = cv::Mat::zeros(capture_rgb.size(), CV_8UC1);
    imageProsessing(capture_rgb, binary_mat);
    siObj.showBinary(binary_mat);
    searchTomatoContours(binary_mat, tomato_contours);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::Point& tomato_point) {
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    cv::drawContours(mask, tomato_contours, -1, cv::Scalar(255), CV_FILLED, 8);
    siObj.showBinary(mask);

    capture_depth.copyTo(maskedDepth, mask);
    return findClosePoint(maskedDepth, tomato_point);
  }

  bool searchTomato(const cv::Mat& capture_depth, geometry_msgs::PoseArray& tomato_poses) {
    tomato_poses.poses.clear();
    siObj.showDepth(capture_depth);
    cv::Mat maskedDepth = cv::Mat::zeros(capture_depth.size(), CV_16UC1);
    cv::Mat mask = cv::Mat::zeros(capture_depth.size(), CV_8UC1);
    for (std::size_t i {0}; i < tomato_contours.size(); ++i) {
      cv::drawContours(mask, tomato_contours, i, cv::Scalar(255), CV_FILLED, 8);
      siObj.showBinary(mask);

      capture_depth.copyTo(maskedDepth, mask);
      findClosePoint(maskedDepth, tomato_poses);
    }
    return tomato_poses.poses.empty() ? false : true;
  }

private:
  static void dynamic_reconfigure_callback(kinect_tomato_searcher::SearchConfig& config, uint32_t level)
  {
    h_min_ = config.h_min;
    h_max_ = config.h_max;
    s_min_ = config.s_min;
    s_max_ = config.s_max;
    v_min_ = config.v_min;
    v_max_ = config.v_max;
  }

  void imageProsessing(const cv::Mat& rgb, cv::Mat& binary) {
    cv::Mat blur, hsv;
    cv::GaussianBlur(rgb, blur, cv::Size(5, 5), 4.0, 4.0);
    cv::cvtColor(blur, hsv, CV_BGR2HSV);
    redFilter(hsv, binary);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 2);
    cv::erode(binary, binary, cv::Mat(), cv::Point(-1, -1), 4);
    cv::dilate(binary, binary, cv::Mat(), cv::Point(-1, -1), 1);
  }

  void searchTomatoContours(const cv::Mat& binary, std::vector<std::vector<cv::Point> >& tomato_contours) {
    std::vector<std::vector<cv::Point> > contours;
    std::vector<cv::Point> contour;
    cv::Rect bounding_box;
    float ratio_balance;

    cv::findContours(binary, contours, CV_RETR_EXTERNAL, CV_CHAIN_APPROX_NONE);

    tomato_contours.clear();
    while (!contours.empty()) {
      contour = contours.back();
      contours.pop_back();

      bounding_box = cv::boundingRect(contour);
      ratio_balance = (float)bounding_box.width / (float)bounding_box.height;
      if (ratio_balance > 1.0f) ratio_balance = 1.0f / ratio_balance;

      // delete mismach. that is smaller or spier or empty
      if (bounding_box.area() >= 400 && bounding_box.area() < cv::contourArea(contour)*2 && ratio_balance > 0.4f)
        tomato_contours.push_back(contour);
    }
  }

  void redFilter(const cv::Mat& hsv, cv::Mat& binary) {
    int a, x, y;
    for(y = 0; y < hsv.rows; y++) {
      for(x = 0; x < hsv.cols; x++) {
        a = hsv.step*y+(x*3);
        if((hsv.data[a] <= h_min_ || hsv.data[a] >= h_max_) &&
           (hsv.data[a+1] >= s_min_ && hsv.data[a+1] <= s_max_) &&
           (hsv.data[a+2] >= v_min_ && hsv.data[a+2] <= v_max_))
          binary.at<unsigned char>(y,x) = 255;
        else
          binary.at<unsigned char>(y,x) = 0;
      }
    }
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::Point& tomato_point) {
    uint16_t depth;
    constexpr uint16_t OVER_RANGE = 3000;
    uint16_t close_depth = OVER_RANGE;

    for (int y = 0; y < maskedDepth.rows; y++) {
      const uint16_t* line_point = maskedDepth.ptr<uint16_t>(y);
      for (int x = 0; x < maskedDepth.cols; x++) {
        depth = line_point[x];
        if (512 < depth && depth <  close_depth) {
          tomato_point.x = depth; // for ros coordinate system
          tomato_point.y = x - maskedDepth.cols / 2; // for ros coordinate system
          tomato_point.z = -(y - maskedDepth.rows / 2); // for ros coordinate system
          close_depth = depth;
        }
      }
    }
    return (close_depth != OVER_RANGE ? true : false);
  }

  bool findClosePoint(const cv::Mat& maskedDepth, geometry_msgs::PoseArray& tomato_poses) {
    geometry_msgs::Point tomato_point {};
    if (findClosePoint(maskedDepth, tomato_point)) {
      geometry_msgs::Pose tomato_pose {};
      tomato_pose.position = tomato_point;
      tomato_pose.orientation.w = 1.0;
      tomato_poses.poses.push_back(tomato_pose);
      return true;
    }
    return false;
  }

  static uint16_t h_min_;
  static uint16_t h_max_;
  static uint16_t s_min_;
  static uint16_t s_max_;
  static uint16_t v_min_;
  static uint16_t v_max_;

  std::vector<std::vector<cv::Point> > tomato_contours;
  ShowImage siObj;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig> server;
  dynamic_reconfigure::Server<kinect_tomato_searcher::SearchConfig>::CallbackType f;
};
uint16_t SearchTomato::h_min_;
uint16_t SearchTomato::h_max_;
uint16_t SearchTomato::s_min_;
uint16_t SearchTomato::s_max_;
uint16_t SearchTomato::v_min_;
uint16_t SearchTomato::v_max_;

class ImageConverter {
private:
  ros::NodeHandle nh;
  ros::NodeHandle tomapo_nh;
  ros::Publisher poses_pub;
  geometry_msgs::PoseArray pub_msg;
  image_transport::ImageTransport it;
  image_transport::Subscriber rgb_sub;
  image_transport::Subscriber depth_sub;
  cv_bridge::CvImageConstPtr rgb_ptr;
  cv_bridge::CvImageConstPtr depth_ptr;
  SearchTomato stObj;
  const std::size_t height_;
  const double angle_x_per_piccell_;
  const double angle_y_per_piccell_;
  static constexpr double PI {3.141592653589793};

public:
  ImageConverter(const std::size_t width, const std::size_t height, const double angle_of_view_x, const double angle_of_view_y)
    : nh {},
      tomapo_nh {nh, "tomato_point"},
      poses_pub {tomapo_nh.advertise<geometry_msgs::PoseArray>("array", 1)},
      it {nh},
      rgb_sub {it.subscribe("color", 1, &ImageConverter::rgbCb, this)},
      depth_sub {it.subscribe("depth", 1, &ImageConverter::depthCb, this)},
      stObj {},
      height_ {height},
      angle_x_per_piccell_ {(angle_of_view_x * PI / 180.0) / width},
      angle_y_per_piccell_ {(angle_of_view_y * PI / 180.0) / height}
  {
    pub_msg.header.frame_id = "kinect";
  }

  /**
   * search tomato function.
   * get rgb image, and search tomato.
   * will tomato_point have data.
   *
   * @author Yusuke Doi
   */
  void rgbCb(const sensor_msgs::ImageConstPtr& msg) {
    // get rgb image on kinect
    try {
      rgb_ptr = cv_bridge::toCvShare(msg, "bgr8");
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by rgb: %s", e.what());
    }

    stObj.update(rgb_ptr->image);

    cv::waitKey(100);
  }

  void depthCb(const sensor_msgs::ImageConstPtr& msg) {
    try {
      depth_ptr = cv_bridge::toCvShare(msg, sensor_msgs::image_encodings::TYPE_16UC1);
    } catch (cv_bridge::Exception& e) {
      ROS_ERROR("cv_bridge exception by depth: %s", e.what());
    }

    if (stObj.searchTomato(depth_ptr->image, pub_msg)) {
      pub_msg.header.stamp = ros::Time::now();
      for (auto& tomato_pose : pub_msg.poses) {
        tomato_pose.position.y = tomato_pose.position.x * tan(angle_x_per_piccell_ * tomato_pose.position.y) / 1000.0; // convert to m
        tomato_pose.position.z = tomato_pose.position.x * tan(angle_y_per_piccell_ * tomato_pose.position.z) / 1000.0; // convert to m
        tomato_pose.position.x = tomato_pose.position.x / 1000.0; // convert to m
      }
      poses_pub.publish(pub_msg);
    }

    cv::waitKey(100);
  }
};

int main(int argc, char** argv) {
  ros::init(argc, argv, "kinect_tomato_searcher_node");
  ImageConverter ic {512, 424, 70, 60};
  ros::spin();
  return 0;
}
<|endoftext|>


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

   * File Name : 15.cpp

   * Creation Date : 12-08-2020

   * Last Modified : St 12. srpna 2020, 14:28:48

   * Created By : Karel Ha <mathemage@gmail.com>

   * URL : https://www.facebook.com/codingcompetitions/hacker-cup/2018/round-1/problems/A

   * Points Gained (in case of online contest) :

   ==========================================*/

#include <bits/stdc++.h>

using namespace std;

#define REP(I,N)   FOR(I,0,N)
#define FOR(i, begin, end) for (__typeof(end) i = (begin) - ((begin) > (end)); i != (end) - ((begin) > (end)); i += 1 - 2 * ((begin) > (end)))
#define ALL(A)     (A).begin(), (A).end()
#define MSG(a) cout << #a << " == " << (a) << endl;

const int CLEAN = -1;

template <typename T>
string NumberToString ( T Number ) {
  ostringstream ss;
  ss << Number;
  return ss.str();
}

#define ERR(args...) { vector<string> _v = split(#args, ','); err(_v.begin(), args); }
vector<string> split(const string& s, char c) {
  vector<string> v;
  stringstream ss(s);
  string x;
  while (getline(ss, x, c))
    v.emplace_back(x);
  return move(v);
}
void err(vector<string>::iterator it) {}
template<typename T, typename... Args>
void err(vector<string>::iterator it, T a, Args... args) {
  cout << it -> substr((*it)[0] == ' ', it -> length()) << " = " << a << endl;
  err(++it, args...);
}

int main() {
  return 0;
}
<commit_msg>Read input<commit_after>
/* ========================================

   * File Name : 15.cpp

   * Creation Date : 12-08-2020

   * Last Modified : St 12. srpna 2020, 14:36:32

   * Created By : Karel Ha <mathemage@gmail.com>

   * URL : https://www.facebook.com/codingcompetitions/hacker-cup/2018/round-1/problems/A

   * Points Gained (in case of online contest) :

   ==========================================*/

#include <bits/stdc++.h>

using namespace std;

#define REP(I,N)   FOR(I,0,N)
#define FOR(i, begin, end) for (__typeof(end) i = (begin) - ((begin) > (end)); i != (end) - ((begin) > (end)); i += 1 - 2 * ((begin) > (end)))
#define ALL(A)     (A).begin(), (A).end()
#define MSG(a) cout << #a << " == " << (a) << endl;

const int CLEAN = -1;

template <typename T>
string NumberToString ( T Number ) {
  ostringstream ss;
  ss << Number;
  return ss.str();
}

#define ERR(args...) { vector<string> _v = split(#args, ','); err(_v.begin(), args); }
vector<string> split(const string& s, char c) {
  vector<string> v;
  stringstream ss(s);
  string x;
  while (getline(ss, x, c))
    v.emplace_back(x);
  return move(v);
}
void err(vector<string>::iterator it) {}
template<typename T, typename... Args>
void err(vector<string>::iterator it, T a, Args... args) {
  cout << it -> substr((*it)[0] == ' ', it -> length()) << " = " << a << endl;
  err(++it, args...);
}

#define MOD 1000000007

int main() {
  int T;
  cin >> T;
//   MSG(T);

  REP(t,T) {
    int N;
    cin >> N;
//     cout << endl; MSG(N);

    vector<string> G(3);
    REP(l,3) {
      cin >> G[l];
//       MSG(G[l]);
    }
  }

  return 0;
}
<|endoftext|>


<commit_before>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <iostream>
#include <map>
#include <rtl/string.hxx>
#include "po.hxx"

// Translated style names must be unique
static void checkStyleNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedStyleNames;
    std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage + "/sw/source/ui/utlui.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" &&
            aPoEntry.getGroupId().startsWith("STR_POOLCOLL") )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
                aLocalizedStyleNames[aMsgStr] = 1;
            else
                aLocalizedStyleNames[aMsgStr]++;
        }
        if( !aPoEntry.isFuzzy() && aPoEntry.getSourceFile() == "poolfmt.src" &&
            aPoEntry.getGroupId().startsWith("STR_POOLNUMRULE") )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
                aLocalizedNumStyleNames[aMsgStr] = 1;
            else
                aLocalizedNumStyleNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedStyleNames.begin(); it!=aLocalizedStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLCOLL_*\n\n";
        }
    }
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedNumStyleNames.begin(); it!=aLocalizedNumStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLNUMRULE_*\n\n";
        }
    }
}

// Translated spreadsheet function names must be unique
static void checkFunctionNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedFunctionNames;
    std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/formula/source/core/resource.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_STRLIST_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() )
                aLocalizedCoreFunctionNames[aMsgStr] = 1;
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
        "/translations/source/" +
        aLanguage +
        "/scaddins/source/analysis.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_ANALYSIS_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/datefunc.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_DATE_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();

    aPoPath = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/pricing.po";
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_PRICING_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry.getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() )
                aLocalizedFunctionNames[aMsgStr] = 1;
            else
                aLocalizedFunctionNames[aMsgStr]++;
        }
    }
    aPoInput.close();
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedFunctionNames.begin(); it!=aLocalizedFunctionNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Spreadsheet function name translations must be unique.\n" <<
                "Language: " << aLanguage <<
                "\nDuplicated translation is: " << it->first << "\n\n";
        }
    }
}

// In instsetoo_native/inc_openoffice/windows/msi_languages.po
// where an en-US string ends with '|', translation must end
// with '|', too.
static void checkVerticalBar(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/instsetoo_native/inc_openoffice/windows/msi_languages.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") &&
            !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") )
        {
            std::cout << "ERROR: Missing '|' character at the end of translated string.\n" <<
                "It causes runtime error in installer.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
        }
    }
    aPoInput.close();
}

// In starmath/source.po Math symbol names (from symbol.src)
// must not contain spaces
static void checkMathSymbolNames(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/starmath/source.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_UI_SYMBOL_NAMES" &&
            !aPoEntry.getMsgStr().isEmpty() && (aPoEntry.getMsgStr().indexOf(" ") != -1) )
        {
            std::cout << "ERROR: Math symbol names must not contain spaces.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
        }
    }
    aPoInput.close();
}

int main()
{
    OString aLanguages(getenv("ALL_LANGS"));
    if( aLanguages.isEmpty() )
    {
        std::cerr << "Usage: make cmd cmd=solver/*/bin/pocheck\n";
        return 1;
    }
    for(sal_Int32 i = 1;;++i) // skip en-US
    {
         OString aLanguage = aLanguages.getToken(i,' ');
         if( aLanguage.isEmpty() )
             break;
         if( aLanguage == "qtz" )
             continue;
         checkStyleNames(aLanguage);
         checkFunctionNames(aLanguage);
         checkVerticalBar(aLanguage);
         checkMathSymbolNames(aLanguage);
    }
    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<commit_msg>pocheck now removes entries from .po files which are incorrect<commit_after>/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 */

#include <iostream>
#include <map>
#include <list>
#include <vector>
#include <rtl/string.hxx>
#include <rtl/ustring.hxx>
#include <osl/file.hxx>
#include "po.hxx"

// Translated style names must be unique
static void checkStyleNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedStyleNames;
    std::map<OString,sal_uInt16> aLocalizedNumStyleNames;
    std::list<PoEntry*> repeatedEntries;

    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage + "/sw/source/ui/utlui.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;

    for(;;)
    {
        PoEntry* aPoEntry = new PoEntry();
        aPoInput.readEntry(*aPoEntry);
        bool bRepeated = false;
        if( aPoInput.eof() )
            break;
        if( !aPoEntry->isFuzzy() && aPoEntry->getSourceFile() == "poolfmt.src" &&
            aPoEntry->getGroupId().startsWith("STR_POOLCOLL") )
        {
            OString aMsgStr = aPoEntry->getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedStyleNames.find(aMsgStr) == aLocalizedStyleNames.end() )
                aLocalizedStyleNames[aMsgStr] = 1;
            else {
                aLocalizedStyleNames[aMsgStr]++;
                bRepeated = true;
            }
        }
        if( !aPoEntry->isFuzzy() && aPoEntry->getSourceFile() == "poolfmt.src" &&
            aPoEntry->getGroupId().startsWith("STR_POOLNUMRULE") )
        {
            OString aMsgStr = aPoEntry->getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedNumStyleNames.find(aMsgStr) == aLocalizedNumStyleNames.end() )
                aLocalizedNumStyleNames[aMsgStr] = 1;
            else {
                aLocalizedNumStyleNames[aMsgStr]++;
                bRepeated = true;
            }
        }
        if (bRepeated)
            repeatedEntries.push_back(aPoEntry);
         else
            delete aPoEntry;

    }
    aPoInput.close();

    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedStyleNames.begin(); it!=aLocalizedStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLCOLL_*\n\n";
        }
    }
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedNumStyleNames.begin(); it!=aLocalizedNumStyleNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Style name translations must be unique in:\n" <<
                aPoPath << "\nLanguage: " << aLanguage << "\nDuplicated translation is: " << it->first <<
                "\nSee STR_POOLNUMRULE_*\n\n";
        }
    }
    aPoInput.open(aPoPath);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
    PoOfstream aPoOutput;
    aPoOutput.open(aPoPath+".new");
    PoHeader aTmp("sw/source/ui/utlui");
    aPoOutput.writeHeader(aTmp);
    bool bAnyError = false;

    for(;;)
    {
        PoEntry aPoEntry;
        bool bError = false;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        for ( std::list<PoEntry*>::iterator it=repeatedEntries.begin(); it!=repeatedEntries.end(); ++it) {
            if ((*it)->getMsgId() == aPoEntry.getMsgId() && (*it)->getGroupId() == aPoEntry.getGroupId()) {
                bError = true;
                break;
            }
        }
        if (bError) {
            bAnyError = true;
        } else {
            aPoOutput.writeEntry(aPoEntry);
        }
    }
    aPoInput.close();
    aPoOutput.close();
    OUString aPoPathURL;
    osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
    if( bAnyError )
        osl::File::move(aPoPathURL + ".new", aPoPathURL);
    else
        osl::File::remove(aPoPathURL + ".new");

}

// Translated spreadsheet function names must be unique
static void checkFunctionNames(OString aLanguage)
{
    std::map<OString,sal_uInt16> aLocalizedFunctionNames;
    std::map<OString,sal_uInt16> aLocalizedCoreFunctionNames;
    //
    std::list<PoEntry*> repeatedEntries;

    OString aPoPaths[4];
    OUString aPoPathURL;

    aPoPaths[0] = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/formula/source/core/resource.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPaths[0]);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPaths[0] << std::endl;

    for(;;)
    {
        PoEntry* aPoEntry = new PoEntry();
        aPoInput.readEntry(*aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry->isFuzzy() && aPoEntry->getGroupId() == "RID_STRLIST_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry->getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) == aLocalizedCoreFunctionNames.end() )
                aLocalizedCoreFunctionNames[aMsgStr] = 1;
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
                aLocalizedFunctionNames[aMsgStr] = 1;
                delete aPoEntry;
            } else {
                aLocalizedFunctionNames[aMsgStr]++;
                repeatedEntries.push_back(aPoEntry);
            }
        }
    }
    aPoInput.close();

    aPoPaths[1] = OString(getenv("SRC_ROOT")) +
        "/translations/source/" +
        aLanguage +
        "/scaddins/source/analysis.po";
    aPoInput.open(aPoPaths[1]);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPaths[1] << std::endl;

    for(;;)
    {
        PoEntry* aPoEntry = new PoEntry();
        aPoInput.readEntry(*aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry->isFuzzy() && aPoEntry->getGroupId() == "RID_ANALYSIS_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry->getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
                aLocalizedFunctionNames[aMsgStr] = 1;
                delete aPoEntry;
            } else {
                aLocalizedFunctionNames[aMsgStr]++;
                repeatedEntries.push_back(aPoEntry);
            }
        }
    }
    aPoInput.close();

    aPoPaths[2] = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/datefunc.po";
    aPoInput.open(aPoPaths[2]);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPaths[2] << std::endl;

    for(;;)
    {
        PoEntry* aPoEntry = new PoEntry();
        aPoInput.readEntry(*aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry->isFuzzy() && aPoEntry->getGroupId() == "RID_DATE_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry->getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
                aLocalizedFunctionNames[aMsgStr] = 1;
                delete aPoEntry;
            } else {
                aLocalizedFunctionNames[aMsgStr]++;
                repeatedEntries.push_back(aPoEntry);
            }
        }
    }
    aPoInput.close();

    aPoPaths[3] = OString(getenv("SRC_ROOT")) +
              "/translations/source/" +
               aLanguage +
              "/scaddins/source/pricing.po";
    aPoInput.open(aPoPaths[3]);
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPaths[3] << std::endl;

    for(;;)
    {
        PoEntry* aPoEntry = new PoEntry();
        aPoInput.readEntry(*aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry->isFuzzy() && aPoEntry->getGroupId() == "RID_PRICING_FUNCTION_NAMES" )
        {
            OString aMsgStr = aPoEntry->getMsgStr();
            if( aMsgStr.isEmpty() )
                continue;
            if( aLocalizedCoreFunctionNames.find(aMsgStr) != aLocalizedCoreFunctionNames.end() )
                aMsgStr += "_ADD";
            if( aLocalizedFunctionNames.find(aMsgStr) == aLocalizedFunctionNames.end() ) {
                aLocalizedFunctionNames[aMsgStr] = 1;
                delete aPoEntry;
            } else {
                aLocalizedFunctionNames[aMsgStr]++;
                repeatedEntries.push_back(aPoEntry);
            }
        }
    }
    aPoInput.close();
    for( std::map<OString,sal_uInt16>::iterator it=aLocalizedFunctionNames.begin(); it!=aLocalizedFunctionNames.end(); ++it)
    {
        if( it->second > 1 )
        {
            std::cout << "ERROR: Spreadsheet function name translations must be unique.\n" <<
                "Language: " << aLanguage <<
                "\nDuplicated translation is: " << it->first << "\n\n";
        }
    }
    //
    for (int i=0;i<4;i++) {
        aPoInput.open(aPoPaths[i]);
        if( !aPoInput.isOpen() )
            std::cerr << "Warning: Cannot open " << aPoPaths[i] << std::endl;
        PoOfstream aPoOutput;
        aPoOutput.open(aPoPaths[i]+".new");

        switch (i) {
            case 0:
                aPoOutput.writeHeader(PoHeader("formula/source/core/resource"));
                break;
            case 1:
                aPoOutput.writeHeader(PoHeader("scaddins/source/analysis"));
                break;
            case 2:
                aPoOutput.writeHeader(PoHeader("scaddins/source/datefunc"));
                break;
            case 3:
                aPoOutput.writeHeader(PoHeader("scaddins/source/pricing"));
        }
        bool bAnyError = false;

        for(;;)
        {
            PoEntry aPoEntry;
            bool bError = false;
            aPoInput.readEntry(aPoEntry);
            if( aPoInput.eof() )
                break;
            for ( std::list<PoEntry*>::iterator it=repeatedEntries.begin(); it!=repeatedEntries.end(); ++it) {
                if ((*it)->getMsgId() == aPoEntry.getMsgId() && (*it)->getGroupId() == aPoEntry.getGroupId()) {
                    bError = true;
                    break;
                }
            }
            if (bError) {
                bAnyError = true;
            } else {
                aPoOutput.writeEntry(aPoEntry);
            }
        }
        aPoInput.close();
        aPoOutput.close();
        osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPaths[i], RTL_TEXTENCODING_UTF8), aPoPathURL);
        if( bAnyError )
            osl::File::move(aPoPathURL + ".new", aPoPathURL);
        else
            osl::File::remove(aPoPathURL + ".new");
    }
}

// In instsetoo_native/inc_openoffice/windows/msi_languages.po
// where an en-US string ends with '|', translation must end
// with '|', too.
static void checkVerticalBar(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/instsetoo_native/inc_openoffice/windows/msi_languages.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    PoOfstream aPoOutput;
    aPoOutput.open(aPoPath+".new");
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
    PoHeader aTmp("instsetoo_native/inc_openoffice/windows/msi_languages");
    aPoOutput.writeHeader(aTmp);
    bool bError = false;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getMsgId().endsWith("|") &&
            !aPoEntry.getMsgStr().isEmpty() && !aPoEntry.getMsgStr().endsWith("|") )
        {
            std::cout << "ERROR: Missing '|' character at the end of translated string.\n" <<
                "It causes runtime error in installer.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
            bError = true;
        }
        else
            aPoOutput.writeEntry(aPoEntry);
    }
    aPoInput.close();
    aPoOutput.close();
    OUString aPoPathURL;
    osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
    if( bError )
        osl::File::move(aPoPathURL + ".new", aPoPathURL);
    else
        osl::File::remove(aPoPathURL + ".new");
}

// In starmath/source.po Math symbol names (from symbol.src)
// must not contain spaces
static void checkMathSymbolNames(OString aLanguage)
{
    OString aPoPath = OString(getenv("SRC_ROOT")) +
                      "/translations/source/" +
                      aLanguage +
                      "/starmath/source.po";
    PoIfstream aPoInput;
    aPoInput.open(aPoPath);
    PoOfstream aPoOutput;
    aPoOutput.open(aPoPath+".new");
    if( !aPoInput.isOpen() )
        std::cerr << "Warning: Cannot open " << aPoPath << std::endl;
    PoHeader aTmp("starmath/source");
    aPoOutput.writeHeader(aTmp);
    bool bError = false;

    for(;;)
    {
        PoEntry aPoEntry;
        aPoInput.readEntry(aPoEntry);
        if( aPoInput.eof() )
            break;
        if( !aPoEntry.isFuzzy() && aPoEntry.getGroupId() == "RID_UI_SYMBOL_NAMES" &&
            !aPoEntry.getMsgStr().isEmpty() && (aPoEntry.getMsgStr().indexOf(" ") != -1) )
        {
            std::cout << "ERROR: Math symbol names must not contain spaces.\n" <<
                "File: " << aPoPath << std::endl <<
                "Language: " << aLanguage << std::endl <<
                "English:   " << aPoEntry.getMsgId() << std::endl <<
                "Localized: " << aPoEntry.getMsgStr() << std::endl << std::endl;
            bError = true;
        }
        else
            aPoOutput.writeEntry(aPoEntry);
    }
    aPoInput.close();
    aPoOutput.close();
    OUString aPoPathURL;
    osl::FileBase::getFileURLFromSystemPath(OStringToOUString(aPoPath, RTL_TEXTENCODING_UTF8), aPoPathURL);
    if( bError )
        osl::File::move(aPoPathURL + ".new", aPoPathURL);
    else
        osl::File::remove(aPoPathURL + ".new");
}

int main()
{
    OString aLanguages(getenv("ALL_LANGS"));
    if( aLanguages.isEmpty() )
    {
        std::cerr << "Usage: make cmd cmd=solver/*/bin/pocheck\n";
        return 1;
    }
    for(sal_Int32 i = 1;;++i) // skip en-US
    {
         OString aLanguage = aLanguages.getToken(i,' ');
         if( aLanguage.isEmpty() )
             break;
         if( aLanguage == "qtz" )
             continue;
         checkStyleNames(aLanguage);
         checkFunctionNames(aLanguage);
         checkVerticalBar(aLanguage);
         checkMathSymbolNames(aLanguage);
    }
    return 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
<|endoftext|>