<commit_before>#pragma once
//=====================================================================//
/*! @file
    @brief  ファイル書き込みクラス@n
			Copyright 2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <cstdio>
#include <cstring>
#include "common/fifo.hpp"
#include "common/format.hpp"

// #define WRITE_FILE_DEBUG

namespace seeda {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  write_file class
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class write_file {

#ifdef WRITE_FILE_DEBUG
		typedef utils::format debug_format;
#else
		typedef utils::null_format debug_format;
#endif

		uint32_t	limit_;
		uint32_t	count_;

		char		path_[128];

		bool		enable_;
		bool		state_;

		static const uint32_t BUF_SIZE = 64;

		typedef utils::fifo<uint16_t, BUF_SIZE, sample_t> FIFO;
		FIFO	fifo_;

		FILE	*fp_;

		time_t	time_ref_;

		enum class task : uint8_t {
			wait_request,
			sync_time,
			make_filename,
			open_file,
			write_header,
			push_data,
			make_data,
			write_body,
			next_data,
			next_file,
		};

		task	task_;

		bool	last_data_;

		uint8_t	second_;

		char	filename_[256];

		char		data_[512];
		uint32_t	data_len_;

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief  コンストラクター
		*/
		//-----------------------------------------------------------------//
		write_file() : limit_(5), count_(0), path_{ "00000" },
			enable_(false), state_(false),
			fp_(nullptr), time_ref_(0), task_(task::wait_request), last_data_(false), second_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み許可
		*/
		//-----------------------------------------------------------------//
		void enable(bool ena = true) {
			enable_ = ena;
			count_ = 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み許可取得
			@return 書き込み許可
		*/
		//-----------------------------------------------------------------//
		bool get_enable() const { return enable_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込みパス設定
			@param[in]	path	ファイルパス
		*/
		//-----------------------------------------------------------------//
		void set_path(const char* path)
		{
			utils::sformat("%s", path_, sizeof(path_)) % path;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込みパス取得
			@param[in]	path	ファイルパス
		*/
		//-----------------------------------------------------------------//
		const char* get_path() const { return path_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み回数の設定
			@param[in]	limit	書き込み回数
		*/
		//-----------------------------------------------------------------//
		void set_limit(uint32_t limit)
		{
			limit_ = limit;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み時間設定
			@return	書き込み時間（秒）
		*/
		//-----------------------------------------------------------------//
		uint32_t get_limit() const { return limit_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  経過時間の取得
			@return 経過時間
		*/
		//-----------------------------------------------------------------//
		uint32_t get_resume() const { return count_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  インジェクション
			@param[in]	data	データ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool injection(const sample_t& data)
		{
			if(fifo_.length() >= (BUF_SIZE -2)) { // スペースが無い場合
				return false;
			}
			fifo_.put(data);
			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  サービス
		*/
		//-----------------------------------------------------------------//
		void service()
		{
			bool back = state_;
			state_ = enable_;
			if(!enable_) {
				if(back && fp_ != nullptr) {
					debug_format("Write file aborted\n");
					fclose(fp_);
					fp_ = nullptr;
					task_ = task::wait_request;
				}
				return;
			}

			switch(task_) {

			case task::wait_request:
				if(enable_ && !back) {
					task_ = task::sync_time;
				}
				break;

			case task::sync_time:
				{
					const sample_data& smd = get_sample_data();
					if(time_ref_ != smd.time_) {
						time_ref_ = smd.time_;
						task_ = task::make_filename;
					}
				}
				break;

			case task::make_filename:
				{
					struct tm *m = localtime(&time_ref_);
					utils::sformat("%s_%04d%02d%02d%02d%02d.csv", filename_, sizeof(filename_))
						% path_
						% static_cast<uint32_t>(m->tm_year + 1900)
						% static_cast<uint32_t>(m->tm_mon + 1)
						% static_cast<uint32_t>(m->tm_mday)
						% static_cast<uint32_t>(m->tm_hour)
						% static_cast<uint32_t>(m->tm_min);
					task_ = task::open_file;
				}
				break;

			case task::open_file:
				fp_ = fopen(filename_, "wb");
				if(fp_ == nullptr) {  // error then disable write.
					debug_format("File open error: '%s'\n") % filename_;
					enable_ = false;
					task_ = task::wait_request;
				} else {
					debug_format("Start write file: '%s'\n") % filename_;
					task_ = task::write_header;
				}
				break;

			case task::write_header:
				{
					char data[1024];
					utils::sformat("DATE,TIME", data, sizeof(data));
					for(int i = 0; i < 8; ++i) {
						utils::sformat(",CH,MAX,MIN,AVE,MEDIAN,COUNTUP", data, sizeof(data), true);
					}
					utils::sformat("\n", data, sizeof(data), true);

					fwrite(data, 1, utils::sformat::chaout().size(), fp_);
					fifo_.clear();
					task_ = task::push_data;
				}
				break;

			case task::push_data:
				{
					const sample_data& smd = get_sample_data();
					for(int i = 0; i < 8; ++i) {
						fifo_.put(smd.smp_[i]);
					}
				}
				task_ = task::make_data;
				break;

			case task::make_data:
				if(fifo_.length() != 0) {
					sample_t smp = fifo_.get();
					if(smp.ch_ == 0) {
						struct tm *m = localtime(&time_ref_);
						utils::sformat("%04d/%02d/%02d,%02d:%02d:%02d,", data_, sizeof(data_))
							% static_cast<uint32_t>(m->tm_year + 1900)
							% static_cast<uint32_t>(m->tm_mon + 1)
							% static_cast<uint32_t>(m->tm_mday)
							% static_cast<uint32_t>(m->tm_hour)
							% static_cast<uint32_t>(m->tm_min)
							% static_cast<uint32_t>(m->tm_sec);
						second_ = m->tm_sec;
					} else {
						utils::sformat(",", data_, sizeof(data_));
					}
					smp.make_csv2(data_, sizeof(data_), true);
					if(smp.ch_ == 7) {
						utils::sformat("\n", data_, sizeof(data_), true);
						last_data_ = true;
					} else {
						last_data_ = false;
					}
					data_len_ = utils::sformat::chaout().size();
					task_ = task::write_body;
				}
				break;

			case task::write_body:
				fwrite(data_, 1, data_len_, fp_);
				if(last_data_) {
					if(second_ == 59) {  // change file.
						task_ = task::next_file;
					} else {
						task_ = task::next_data;
					}
				} else {
					task_ = task::make_data;
				}
				break;

			case task::next_data:
				{
					const sample_data& smd = get_sample_data();
					if(time_ref_ != smd.time_) {
						time_ref_ = smd.time_;
						task_ = task::push_data;
					}
				}
				break;

			case task::next_file:
				fclose(fp_);
				fp_ = nullptr;
				++count_;
				if(count_ >= limit_) {
					fp_ = nullptr;
					debug_format("Fin write file: %d files\n") % count_;
					enable_ = false;
					task_ = task::wait_request;
				} else {
					task_ = task::sync_time;
				}
				break;

			default:
				break;
			}
		}
	};
}
<commit_msg>update fifo template<commit_after>#pragma once
//=====================================================================//
/*! @file
    @brief  ファイル書き込みクラス@n
			Copyright 2017 Kunihito Hiramatsu
    @author 平松邦仁 (hira@rvf-rc45.net)
*/
//=====================================================================//
#include <cstdio>
#include <cstring>
#include "common/fixed_fifo.hpp"
#include "common/format.hpp"

// #define WRITE_FILE_DEBUG

namespace seeda {

	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	/*!
		@brief  write_file class
	*/
	//+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++//
	class write_file {

#ifdef WRITE_FILE_DEBUG
		typedef utils::format debug_format;
#else
		typedef utils::null_format debug_format;
#endif

		uint32_t	limit_;
		uint32_t	count_;

		char		path_[128];

		bool		enable_;
		bool		state_;

		static const uint32_t BUF_SIZE = 64;

		typedef utils::fixed_fifo<sample_t, BUF_SIZE> FIFO;
		FIFO	fifo_;

		FILE	*fp_;

		time_t	time_ref_;

		enum class task : uint8_t {
			wait_request,
			sync_time,
			make_filename,
			open_file,
			write_header,
			push_data,
			make_data,
			write_body,
			next_data,
			next_file,
		};

		task	task_;

		bool	last_data_;

		uint8_t	second_;

		char	filename_[256];

		char		data_[512];
		uint32_t	data_len_;

	public:
		//-----------------------------------------------------------------//
		/*!
			@brief  コンストラクター
		*/
		//-----------------------------------------------------------------//
		write_file() : limit_(5), count_(0), path_{ "00000" },
			enable_(false), state_(false),
			fp_(nullptr), time_ref_(0), task_(task::wait_request), last_data_(false), second_(0) { }

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み許可
		*/
		//-----------------------------------------------------------------//
		void enable(bool ena = true) {
			enable_ = ena;
			count_ = 0;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み許可取得
			@return 書き込み許可
		*/
		//-----------------------------------------------------------------//
		bool get_enable() const { return enable_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込みパス設定
			@param[in]	path	ファイルパス
		*/
		//-----------------------------------------------------------------//
		void set_path(const char* path)
		{
			utils::sformat("%s", path_, sizeof(path_)) % path;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込みパス取得
			@param[in]	path	ファイルパス
		*/
		//-----------------------------------------------------------------//
		const char* get_path() const { return path_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み回数の設定
			@param[in]	limit	書き込み回数
		*/
		//-----------------------------------------------------------------//
		void set_limit(uint32_t limit)
		{
			limit_ = limit;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  書き込み時間設定
			@return	書き込み時間（秒）
		*/
		//-----------------------------------------------------------------//
		uint32_t get_limit() const { return limit_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  経過時間の取得
			@return 経過時間
		*/
		//-----------------------------------------------------------------//
		uint32_t get_resume() const { return count_; }

		//-----------------------------------------------------------------//
		/*!
			@brief  インジェクション
			@param[in]	data	データ
			@return 成功なら「true」
		*/
		//-----------------------------------------------------------------//
		bool injection(const sample_t& data)
		{
			if(fifo_.length() >= (BUF_SIZE - 2)) { // スペースが無い場合
				return false;
			}
			fifo_.put(data);
			return true;
		}

		//-----------------------------------------------------------------//
		/*!
			@brief  サービス
		*/
		//-----------------------------------------------------------------//
		void service()
		{
			bool back = state_;
			state_ = enable_;
			if(!enable_) {
				if(back && fp_ != nullptr) {
					debug_format("Write file aborted\n");
					fclose(fp_);
					fp_ = nullptr;
					task_ = task::wait_request;
				}
				return;
			}

			switch(task_) {

			case task::wait_request:
				if(enable_ && !back) {
					task_ = task::sync_time;
				}
				break;

			case task::sync_time:
				{
					const sample_data& smd = get_sample_data();
					if(time_ref_ != smd.time_) {
						time_ref_ = smd.time_;
						task_ = task::make_filename;
					}
				}
				break;

			case task::make_filename:
				{
					struct tm *m = localtime(&time_ref_);
					utils::sformat("%s_%04d%02d%02d%02d%02d.csv", filename_, sizeof(filename_))
						% path_
						% static_cast<uint32_t>(m->tm_year + 1900)
						% static_cast<uint32_t>(m->tm_mon + 1)
						% static_cast<uint32_t>(m->tm_mday)
						% static_cast<uint32_t>(m->tm_hour)
						% static_cast<uint32_t>(m->tm_min);
					task_ = task::open_file;
				}
				break;

			case task::open_file:
				fp_ = fopen(filename_, "wb");
				if(fp_ == nullptr) {  // error then disable write.
					debug_format("File open error: '%s'\n") % filename_;
					enable_ = false;
					task_ = task::wait_request;
				} else {
					debug_format("Start write file: '%s'\n") % filename_;
					task_ = task::write_header;
				}
				break;

			case task::write_header:
				{
					char data[1024];
					utils::sformat("DATE,TIME", data, sizeof(data));
					for(int i = 0; i < 8; ++i) {
						utils::sformat(",CH,MAX,MIN,AVE,MEDIAN,COUNTUP", data, sizeof(data), true);
					}
					utils::sformat("\n", data, sizeof(data), true);

					fwrite(data, 1, utils::sformat::chaout().size(), fp_);
					fifo_.clear();
					task_ = task::push_data;
				}
				break;

			case task::push_data:
				{
					const sample_data& smd = get_sample_data();
					for(int i = 0; i < 8; ++i) {
						fifo_.put(smd.smp_[i]);
					}
				}
				task_ = task::make_data;
				break;

			case task::make_data:
				if(fifo_.length() != 0) {
					sample_t smp = fifo_.get();
					if(smp.ch_ == 0) {
						struct tm *m = localtime(&time_ref_);
						utils::sformat("%04d/%02d/%02d,%02d:%02d:%02d,", data_, sizeof(data_))
							% static_cast<uint32_t>(m->tm_year + 1900)
							% static_cast<uint32_t>(m->tm_mon + 1)
							% static_cast<uint32_t>(m->tm_mday)
							% static_cast<uint32_t>(m->tm_hour)
							% static_cast<uint32_t>(m->tm_min)
							% static_cast<uint32_t>(m->tm_sec);
						second_ = m->tm_sec;
					} else {
						utils::sformat(",", data_, sizeof(data_));
					}
					smp.make_csv2(data_, sizeof(data_), true);
					if(smp.ch_ == 7) {
						utils::sformat("\n", data_, sizeof(data_), true);
						last_data_ = true;
					} else {
						last_data_ = false;
					}
					data_len_ = utils::sformat::chaout().size();
					task_ = task::write_body;
				}
				break;

			case task::write_body:
				fwrite(data_, 1, data_len_, fp_);
				if(last_data_) {
					if(second_ == 59) {  // change file.
						task_ = task::next_file;
					} else {
						task_ = task::next_data;
					}
				} else {
					task_ = task::make_data;
				}
				break;

			case task::next_data:
				{
					const sample_data& smd = get_sample_data();
					if(time_ref_ != smd.time_) {
						time_ref_ = smd.time_;
						task_ = task::push_data;
					}
				}
				break;

			case task::next_file:
				fclose(fp_);
				fp_ = nullptr;
				++count_;
				if(count_ >= limit_) {
					fp_ = nullptr;
					debug_format("Fin write file: %d files\n") % count_;
					enable_ = false;
					task_ = task::wait_request;
				} else {
					task_ = task::sync_time;
				}
				break;

			default:
				break;
			}
		}
	};
}
<|endoftext|>


<commit_before>/*
 * ServerSessionManager.cpp
 *
 * Copyright (C) 2009-19 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 <core/CrashHandler.hpp>

#include <server/ServerSessionManager.hpp>

#include <boost/format.hpp>

#include <core/SafeConvert.hpp>
#include <core/system/PosixUser.hpp>
#include <core/system/Environment.hpp>
#include <core/json/JsonRpc.hpp>

#include <monitor/MonitorClient.hpp>
#include <session/SessionConstants.hpp>

#include <server/ServerOptions.hpp>

#include <server/ServerErrorCategory.hpp>

#include <server/auth/ServerValidateUser.hpp>

#include "ServerREnvironment.hpp"
#include "server-config.h"

using namespace rstudio::core;

namespace rstudio {
namespace server {

namespace {

static std::string s_launcherToken;

void readRequestArgs(const core::http::Request& request, core::system::Options *pArgs)
{
   // we only do this when establishing new sessions via client_init
   if (!boost::algorithm::ends_with(request.uri(), "client_init"))
      return;

   // parse the request (okay if it fails, none of the below is critical)
   json::JsonRpcRequest clientInit;
   Error error = json::parseJsonRpcRequest(request.body(), &clientInit);
   if (error)
      return;

   // read parameters from the request if present
   int restoreWorkspace = -1;
   json::getOptionalParam<int>(clientInit.kwparams, "restore_workspace", -1, &restoreWorkspace);
   if (restoreWorkspace != -1)
      pArgs->push_back(std::make_pair("--r-restore-workspace",
               safe_convert::numberToString(restoreWorkspace)));
   int runRprofile = -1;
   json::getOptionalParam<int>(clientInit.kwparams, "run_rprofile", -1, &runRprofile);
   if (runRprofile != -1)
      pArgs->push_back(std::make_pair("--r-run-rprofile",
            safe_convert::numberToString(runRprofile)));
}

core::system::ProcessConfig sessionProcessConfig(
         r_util::SessionContext context,
         const core::system::Options& extraArgs = core::system::Options())
{
   // prepare command line arguments
   server::Options& options = server::options();
   core::system::Options args ;

   // check for options-specified config file and add to command
   // line if specified
   std::string rsessionConfigFile(options.rsessionConfigFile());
   if (!rsessionConfigFile.empty())
      args.push_back(std::make_pair("--config-file", rsessionConfigFile));

   // pass the user-identity
   args.push_back(std::make_pair("-" kUserIdentitySessionOptionShort,
                                 context.username));

   // pass the project if specified
   if (!context.scope.project().empty())
   {
      args.push_back(std::make_pair("-" kProjectSessionOptionShort,
                                    context.scope.project()));
   }

   // pass the scope id if specified
   if (!context.scope.id().empty())
   {
      args.push_back(std::make_pair("-" kScopeSessionOptionShort,
                                    context.scope.id()));
   }

   // ensure cookies are marked secure if applicable
   bool useSecureCookies = options.authCookiesForceSecure() ||
                           options.getOverlayOption("ssl-enabled") == "1";
   args.push_back(std::make_pair("--" kUseSecureCookiesSessionOption,
                                 useSecureCookies ? "1" : "0"));

   // create launch token if we haven't already
   if (s_launcherToken.empty())
      s_launcherToken = core::system::generateShortenedUuid();
   args.push_back(std::make_pair("--launcher-token", s_launcherToken));

   // allow session timeout to be overridden via environment variable
   std::string timeout = core::system::getenv("RSTUDIO_SESSION_TIMEOUT");
   if (!timeout.empty())
      args.push_back(std::make_pair("--" kTimeoutSessionOption, timeout));

   // pass our uid to instruct rsession to limit rpc clients to us and itself
   core::system::Options environment;
   uid_t uid = core::system::user::currentUserIdentity().userId;
   environment.push_back(std::make_pair(
                           kRStudioLimitRpcClientUid,
                           safe_convert::numberToString(uid)));

   // set session scope project if we have one
   if (!context.scope.project().empty())
   {
      environment.push_back(std::make_pair(
                              kRStudioSessionScopeProject,
                              context.scope.project()));
   }

   // set session scope id if we have one
   if (!context.scope.id().empty())
   {
      environment.push_back(std::make_pair(
                              kRStudioSessionScopeId,
                              context.scope.id()));
   }

   sessionProcessConfigOverlay(&args, &environment);

   // pass extra params
   std::copy(extraArgs.begin(), extraArgs.end(), std::back_inserter(args));

   // append R environment variables
   r_util::RVersion rVersion = r_environment::rVersion();
   core::system::Options rEnvVars = rVersion.environment();
   environment.insert(environment.end(), rEnvVars.begin(), rEnvVars.end());

   // mark this as the system default R version
   core::system::setenv(&environment,
                        kRStudioDefaultRVersion,
                        rVersion.number());
   core::system::setenv(&environment,
                        kRStudioDefaultRVersionHome,
                        rVersion.homeDir().absolutePath());

   // forward the auth options
   core::system::setenv(&environment,
                        kRStudioRequiredUserGroup,
                        options.authRequiredUserGroup());
   core::system::setenv(&environment,
                        kRStudioMinimumUserId,
                        safe_convert::numberToString(
                                 options.authMinimumUserId()));

   // add monitor shared secret
   environment.push_back(std::make_pair(kMonitorSharedSecretEnvVar,
                                        options.monitorSharedSecret()));

   // stamp the version number of the rserver process that is launching this session
   // the session should log an error if its version does not match, as that is
   // likely an unsupported configuration
   environment.push_back({kRStudioVersion, RSTUDIO_VERSION});

   // forward over crash handler environment if we have it (used for development mode)
   if (!core::system::getenv(kCrashHandlerEnvVar).empty())
      environment.push_back({kCrashHandlerEnvVar, core::system::getenv(kCrashHandlerEnvVar)});

   if (!core::system::getenv(kCrashpadHandlerEnvVar).empty())
      environment.push_back({kCrashpadHandlerEnvVar, core::system::getenv(kCrashpadHandlerEnvVar)});

   // build the config object and return it
   core::system::ProcessConfig config;
   config.args = args;
   config.environment = environment;
   config.stdStreamBehavior = core::system::StdStreamInherit;
   return config;
}

void onProcessExit(const std::string& username, PidType pid)
{
}

} // anonymous namespace

SessionManager& sessionManager()
{
   static SessionManager instance;
   return instance;
}

SessionManager::SessionManager()
{
   // set default session launcher
   sessionLaunchFunction_ = boost::bind(&SessionManager::launchAndTrackSession,
                                           this, _1, _2);
}

Error SessionManager::launchSession(boost::asio::io_service& ioService,
                                    const r_util::SessionContext& context,
                                    const http::Request& request,
                                    const http::ResponseHandler& onLaunch,
                                    const http::ErrorHandler& onError)
{
   using namespace boost::posix_time;
   LOCK_MUTEX(launchesMutex_)
   {
      // check whether we already have a launch pending
      LaunchMap::const_iterator pos = pendingLaunches_.find(context);
      if (pos != pendingLaunches_.end())
      {
         // if the launch is less than one minute old then return success
         if ( (pos->second + boost::posix_time::minutes(1))
               > microsec_clock::universal_time() )
         {
            return Success();
         }
         // otherwise erase it from pending launches and then
         // re-launch (immediately below)
         else
         {
            // very unexpected condition
            LOG_WARNING_MESSAGE("Very long session launch delay for "
                                "user " + context.username +" (aborting wait)");

            pendingLaunches_.erase(context);
         }
      }

      // record the launch
      pendingLaunches_[context] =  microsec_clock::universal_time();
   }
   END_LOCK_MUTEX

   // translate querystring arguments into extra session args
   core::system::Options args;
   readRequestArgs(request, &args);

   // determine launch options
   r_util::SessionLaunchProfile profile;
   profile.context = context;
   profile.executablePath = server::options().rsessionPath();
   profile.config = sessionProcessConfig(context, args);

   // pass the profile to any filters we have
   for (SessionLaunchProfileFilter f : sessionLaunchProfileFilters_)
   {
      f(&profile);
   }

   // launch the session
   Error error = sessionLaunchFunction_(ioService, profile, request, onLaunch, onError);
   if (error)
   {
      removePendingLaunch(context);
      return error;
   }

   return Success();
}

namespace {

boost::mutex s_configFilterMutex;
core::system::ProcessConfigFilter s_processConfigFilter;

} // anonymous namespace

void setProcessConfigFilter(const core::system::ProcessConfigFilter& filter)
{
   LOCK_MUTEX(s_configFilterMutex)
   {
      s_processConfigFilter = filter;
   }
   END_LOCK_MUTEX
}

// default session launcher -- does the launch then tracks the pid
// for later reaping
Error SessionManager::launchAndTrackSession(
                           boost::asio::io_service&,
                           const core::r_util::SessionLaunchProfile& profile)
{
   // if we are root then assume the identity of the user
   using namespace rstudio::core::system;
   std::string runAsUser = realUserIsRoot() ? profile.context.username : "";

   core::system::ProcessConfigFilter configFilter;
   LOCK_MUTEX(s_configFilterMutex)
   {
      configFilter = s_processConfigFilter;
   }
   END_LOCK_MUTEX

   // launch the session
   PidType pid = 0;
   Error error = launchChildProcess(profile.executablePath,
                                    runAsUser,
                                    profile.config,
                                    configFilter,
                                    &pid);
   if (error)
      return error;

   // track it for subsequent reaping
   processTracker_.addProcess(pid, boost::bind(onProcessExit,
                                               profile.context.username,
                                               pid));

   // return success
   return Success();
}

void SessionManager::setSessionLaunchFunction(
                           const SessionLaunchFunction& launchFunction)
{
   sessionLaunchFunction_ = launchFunction;
}

void SessionManager::addSessionLaunchProfileFilter(
                              const SessionLaunchProfileFilter& filter)
{
   sessionLaunchProfileFilters_.push_back(filter);
}

void SessionManager::removePendingLaunch(const r_util::SessionContext& context)
{
   LOCK_MUTEX(launchesMutex_)
   {
      pendingLaunches_.erase(context);
   }
   END_LOCK_MUTEX
}

void SessionManager::notifySIGCHLD()
{
   processTracker_.notifySIGCHILD();
}

r_util::SessionLaunchProfile createSessionLaunchProfile(const r_util::SessionContext& context,
                                                        const core::system::Options& extraArgs)
{
   r_util::SessionLaunchProfile profile;
   profile.context = context;
   profile.executablePath = server::options().rsessionPath();
   profile.config = sessionProcessConfig(context, extraArgs);

   // pass the profile to any filters we have
   for (const SessionManager::SessionLaunchProfileFilter f : sessionManager().getSessionLaunchProfileFilters())
   {
      f(&profile);
   }

   return profile;
}

// helper function for verify-installation
Error launchSession(const r_util::SessionContext& context,
                    const core::system::Options& extraArgs,
                    PidType* pPid)
{
   // launch the session
   std::string username = context.username;
   std::string rsessionPath = server::options().rsessionPath();
   std::string runAsUser = core::system::realUserIsRoot() ? username : "";
   core::system::ProcessConfig config = sessionProcessConfig(context,
                                                             extraArgs);

   *pPid = -1;
   return core::system::launchChildProcess(rsessionPath,
                                           runAsUser,
                                           config,
                                           core::system::ProcessConfigFilter(),
                                           pPid);
}

} // namespace server
} // namespace rstudio

<commit_msg>forward env var for session local stream path (for open source server)<commit_after>/*
 * ServerSessionManager.cpp
 *
 * Copyright (C) 2009-19 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 <core/CrashHandler.hpp>

#include <server/ServerSessionManager.hpp>

#include <boost/format.hpp>

#include <core/SafeConvert.hpp>
#include <core/system/PosixUser.hpp>
#include <core/system/Environment.hpp>
#include <core/json/JsonRpc.hpp>

#include <monitor/MonitorClient.hpp>
#include <session/SessionConstants.hpp>

#include <server/ServerOptions.hpp>
#include <server/ServerPaths.hpp>
#include <server/ServerErrorCategory.hpp>

#include <server/auth/ServerValidateUser.hpp>

#include "ServerREnvironment.hpp"
#include "server-config.h"

using namespace rstudio::core;

namespace rstudio {
namespace server {

namespace {

static std::string s_launcherToken;

void readRequestArgs(const core::http::Request& request, core::system::Options *pArgs)
{
   // we only do this when establishing new sessions via client_init
   if (!boost::algorithm::ends_with(request.uri(), "client_init"))
      return;

   // parse the request (okay if it fails, none of the below is critical)
   json::JsonRpcRequest clientInit;
   Error error = json::parseJsonRpcRequest(request.body(), &clientInit);
   if (error)
      return;

   // read parameters from the request if present
   int restoreWorkspace = -1;
   json::getOptionalParam<int>(clientInit.kwparams, "restore_workspace", -1, &restoreWorkspace);
   if (restoreWorkspace != -1)
      pArgs->push_back(std::make_pair("--r-restore-workspace",
               safe_convert::numberToString(restoreWorkspace)));
   int runRprofile = -1;
   json::getOptionalParam<int>(clientInit.kwparams, "run_rprofile", -1, &runRprofile);
   if (runRprofile != -1)
      pArgs->push_back(std::make_pair("--r-run-rprofile",
            safe_convert::numberToString(runRprofile)));
}

core::system::ProcessConfig sessionProcessConfig(
         r_util::SessionContext context,
         const core::system::Options& extraArgs = core::system::Options())
{
   // prepare command line arguments
   server::Options& options = server::options();
   core::system::Options args ;

   // check for options-specified config file and add to command
   // line if specified
   std::string rsessionConfigFile(options.rsessionConfigFile());
   if (!rsessionConfigFile.empty())
      args.push_back(std::make_pair("--config-file", rsessionConfigFile));

   // pass the user-identity
   args.push_back(std::make_pair("-" kUserIdentitySessionOptionShort,
                                 context.username));

   // pass the project if specified
   if (!context.scope.project().empty())
   {
      args.push_back(std::make_pair("-" kProjectSessionOptionShort,
                                    context.scope.project()));
   }

   // pass the scope id if specified
   if (!context.scope.id().empty())
   {
      args.push_back(std::make_pair("-" kScopeSessionOptionShort,
                                    context.scope.id()));
   }

   // ensure cookies are marked secure if applicable
   bool useSecureCookies = options.authCookiesForceSecure() ||
                           options.getOverlayOption("ssl-enabled") == "1";
   args.push_back(std::make_pair("--" kUseSecureCookiesSessionOption,
                                 useSecureCookies ? "1" : "0"));

   // create launch token if we haven't already
   if (s_launcherToken.empty())
      s_launcherToken = core::system::generateShortenedUuid();
   args.push_back(std::make_pair("--launcher-token", s_launcherToken));

   // allow session timeout to be overridden via environment variable
   std::string timeout = core::system::getenv("RSTUDIO_SESSION_TIMEOUT");
   if (!timeout.empty())
      args.push_back(std::make_pair("--" kTimeoutSessionOption, timeout));

   // pass our uid to instruct rsession to limit rpc clients to us and itself
   core::system::Options environment;
   uid_t uid = core::system::user::currentUserIdentity().userId;
   environment.push_back(std::make_pair(
                           kRStudioLimitRpcClientUid,
                           safe_convert::numberToString(uid)));

   // set session scope project if we have one
   if (!context.scope.project().empty())
   {
      environment.push_back(std::make_pair(
                              kRStudioSessionScopeProject,
                              context.scope.project()));
   }

   // set session scope id if we have one
   if (!context.scope.id().empty())
   {
      environment.push_back(std::make_pair(
                              kRStudioSessionScopeId,
                              context.scope.id()));
   }

   sessionProcessConfigOverlay(&args, &environment);

   // pass extra params
   std::copy(extraArgs.begin(), extraArgs.end(), std::back_inserter(args));

   // append R environment variables
   r_util::RVersion rVersion = r_environment::rVersion();
   core::system::Options rEnvVars = rVersion.environment();
   environment.insert(environment.end(), rEnvVars.begin(), rEnvVars.end());

   // mark this as the system default R version
   core::system::setenv(&environment,
                        kRStudioDefaultRVersion,
                        rVersion.number());
   core::system::setenv(&environment,
                        kRStudioDefaultRVersionHome,
                        rVersion.homeDir().absolutePath());

   // forward the auth options
   core::system::setenv(&environment,
                        kRStudioRequiredUserGroup,
                        options.authRequiredUserGroup());
   core::system::setenv(&environment,
                        kRStudioMinimumUserId,
                        safe_convert::numberToString(
                                 options.authMinimumUserId()));

   // add monitor shared secret
   environment.push_back(std::make_pair(kMonitorSharedSecretEnvVar,
                                        options.monitorSharedSecret()));

   // stamp the version number of the rserver process that is launching this session
   // the session should log an error if its version does not match, as that is
   // likely an unsupported configuration
   environment.push_back({kRStudioVersion, RSTUDIO_VERSION});

   // forward over crash handler environment if we have it (used for development mode)
   if (!core::system::getenv(kCrashHandlerEnvVar).empty())
      environment.push_back({kCrashHandlerEnvVar, core::system::getenv(kCrashHandlerEnvVar)});

   if (!core::system::getenv(kCrashpadHandlerEnvVar).empty())
      environment.push_back({kCrashpadHandlerEnvVar, core::system::getenv(kCrashpadHandlerEnvVar)});

   // forward path for session temp dir (used for local stream path)
   environment.push_back(
         std::make_pair(kSessionTmpDirEnvVar, sessionTmpDir().absolutePath()));

   // build the config object and return it
   core::system::ProcessConfig config;
   config.args = args;
   config.environment = environment;
   config.stdStreamBehavior = core::system::StdStreamInherit;
   return config;
}

void onProcessExit(const std::string& username, PidType pid)
{
}

} // anonymous namespace

SessionManager& sessionManager()
{
   static SessionManager instance;
   return instance;
}

SessionManager::SessionManager()
{
   // set default session launcher
   sessionLaunchFunction_ = boost::bind(&SessionManager::launchAndTrackSession,
                                           this, _1, _2);
}

Error SessionManager::launchSession(boost::asio::io_service& ioService,
                                    const r_util::SessionContext& context,
                                    const http::Request& request,
                                    const http::ResponseHandler& onLaunch,
                                    const http::ErrorHandler& onError)
{
   using namespace boost::posix_time;
   LOCK_MUTEX(launchesMutex_)
   {
      // check whether we already have a launch pending
      LaunchMap::const_iterator pos = pendingLaunches_.find(context);
      if (pos != pendingLaunches_.end())
      {
         // if the launch is less than one minute old then return success
         if ( (pos->second + boost::posix_time::minutes(1))
               > microsec_clock::universal_time() )
         {
            return Success();
         }
         // otherwise erase it from pending launches and then
         // re-launch (immediately below)
         else
         {
            // very unexpected condition
            LOG_WARNING_MESSAGE("Very long session launch delay for "
                                "user " + context.username +" (aborting wait)");

            pendingLaunches_.erase(context);
         }
      }

      // record the launch
      pendingLaunches_[context] =  microsec_clock::universal_time();
   }
   END_LOCK_MUTEX

   // translate querystring arguments into extra session args
   core::system::Options args;
   readRequestArgs(request, &args);

   // determine launch options
   r_util::SessionLaunchProfile profile;
   profile.context = context;
   profile.executablePath = server::options().rsessionPath();
   profile.config = sessionProcessConfig(context, args);

   // pass the profile to any filters we have
   for (SessionLaunchProfileFilter f : sessionLaunchProfileFilters_)
   {
      f(&profile);
   }

   // launch the session
   Error error = sessionLaunchFunction_(ioService, profile, request, onLaunch, onError);
   if (error)
   {
      removePendingLaunch(context);
      return error;
   }

   return Success();
}

namespace {

boost::mutex s_configFilterMutex;
core::system::ProcessConfigFilter s_processConfigFilter;

} // anonymous namespace

void setProcessConfigFilter(const core::system::ProcessConfigFilter& filter)
{
   LOCK_MUTEX(s_configFilterMutex)
   {
      s_processConfigFilter = filter;
   }
   END_LOCK_MUTEX
}

// default session launcher -- does the launch then tracks the pid
// for later reaping
Error SessionManager::launchAndTrackSession(
                           boost::asio::io_service&,
                           const core::r_util::SessionLaunchProfile& profile)
{
   // if we are root then assume the identity of the user
   using namespace rstudio::core::system;
   std::string runAsUser = realUserIsRoot() ? profile.context.username : "";

   core::system::ProcessConfigFilter configFilter;
   LOCK_MUTEX(s_configFilterMutex)
   {
      configFilter = s_processConfigFilter;
   }
   END_LOCK_MUTEX

   // launch the session
   PidType pid = 0;
   Error error = launchChildProcess(profile.executablePath,
                                    runAsUser,
                                    profile.config,
                                    configFilter,
                                    &pid);
   if (error)
      return error;

   // track it for subsequent reaping
   processTracker_.addProcess(pid, boost::bind(onProcessExit,
                                               profile.context.username,
                                               pid));

   // return success
   return Success();
}

void SessionManager::setSessionLaunchFunction(
                           const SessionLaunchFunction& launchFunction)
{
   sessionLaunchFunction_ = launchFunction;
}

void SessionManager::addSessionLaunchProfileFilter(
                              const SessionLaunchProfileFilter& filter)
{
   sessionLaunchProfileFilters_.push_back(filter);
}

void SessionManager::removePendingLaunch(const r_util::SessionContext& context)
{
   LOCK_MUTEX(launchesMutex_)
   {
      pendingLaunches_.erase(context);
   }
   END_LOCK_MUTEX
}

void SessionManager::notifySIGCHLD()
{
   processTracker_.notifySIGCHILD();
}

r_util::SessionLaunchProfile createSessionLaunchProfile(const r_util::SessionContext& context,
                                                        const core::system::Options& extraArgs)
{
   r_util::SessionLaunchProfile profile;
   profile.context = context;
   profile.executablePath = server::options().rsessionPath();
   profile.config = sessionProcessConfig(context, extraArgs);

   // pass the profile to any filters we have
   for (const SessionManager::SessionLaunchProfileFilter f : sessionManager().getSessionLaunchProfileFilters())
   {
      f(&profile);
   }

   return profile;
}

// helper function for verify-installation
Error launchSession(const r_util::SessionContext& context,
                    const core::system::Options& extraArgs,
                    PidType* pPid)
{
   // launch the session
   std::string username = context.username;
   std::string rsessionPath = server::options().rsessionPath();
   std::string runAsUser = core::system::realUserIsRoot() ? username : "";
   core::system::ProcessConfig config = sessionProcessConfig(context,
                                                             extraArgs);

   *pPid = -1;
   return core::system::launchChildProcess(rsessionPath,
                                           runAsUser,
                                           config,
                                           core::system::ProcessConfigFilter(),
                                           pPid);
}

} // namespace server
} // namespace rstudio

<|endoftext|>


<commit_before>/******************************************************************************
 * Copyright 2014 Micah C Chambers (micahc.vt@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @file lbfgs.cpp Implemenation of the LBFGSOpt class which implements
 * a LBFGS optimization (energy minimization) algorithm.
 *
 *****************************************************************************/

#include "lbfgs.h"

#ifdef DEBUG
#include <iterator>
#include <iostream>
#include <iomanip>
using std::cerr;
using std::endl;
using std::setw;
using std::setprecision;
#endif //DEBUG

namespace npl {

/**
 * @brief Constructor for optimizer function.
 *
 * @param dim       Dimension of state variable
 * @param valfunc   Function which computes the energy of the underlying
 *                  mathematical function
 * @param gradfunc  Function which computes the gradient of energy in the
 *                  underlying mathematical function
 * @param callback  Function which should be called at the end of each
 *                  iteration (for instance, to debug)
 */
LBFGSOpt::LBFGSOpt(size_t dim, const ValFunc& valfunc,
        const GradFunc& gradfunc, const CallBackFunc& callback)
        : Optimizer(dim, valfunc, gradfunc, callback),
        m_lsearch(valfunc)
{
    m_hist.clear();
    opt_H0inv = VectorXd::Ones(dim);
    opt_histsize = 6;

	opt_ls_s = 1;
	opt_ls_beta = 0.5;
	opt_ls_sigma = 1e-5;
};

/**
 * @brief Constructor for optimizer function.
 *
 * @param dim       Dimension of state variable
 * @param valfunc   Function which computes the energy of the underlying
 *                  mathematical function
 * @param gradfunc  Function which computes the gradient of energy in the
 *                  underlying mathematical function
 * @param gradAndValFunc
 *                  Function which computes the energy and gradient in the
 *                  underlying mathematical function
 * @param callback  Function which should be called at the end of each
 *                  iteration (for instance, to debug)
 */
LBFGSOpt::LBFGSOpt(size_t dim, const ValFunc& valfunc, const GradFunc& gradfunc,
        const ValGradFunc& gradAndValFunc, const CallBackFunc& callback)
        : Optimizer(dim, valfunc, gradfunc, gradAndValFunc, callback),
        m_lsearch(valfunc)
{
    m_hist.clear();
    opt_H0inv = VectorXd::Ones(dim);
    opt_histsize = 6;

	opt_ls_s = 1;
	opt_ls_beta = 0.5;
	opt_ls_sigma = 1e-5;
};

/**
 * @brief Function for computing the hessian recursively
 * Based on the algorithm from Numerical Optimization (Nocedal)
 *
 * @param gamma Scale of initial (H0)
 * @param g Direction from right multiplication so far
 * @param it Position in history list
 *
 * @return Direction (d) after right multiplying d by H_k, the hessian
 * estimate for position it,
 */
VectorXd LBFGSOpt::hessFuncTwoLoop(double gamma, const VectorXd& g)
{
    VectorXd q = g;
    VectorXd alpha(m_hist.size());

    // iterate backward in time (forward in list)
    int ii = 0;
    for(auto it = m_hist.cbegin(); it != m_hist.cend(); ++it, ++ii) {
        double rho = std::get<0>(*it);
        const VectorXd& y = std::get<1>(*it); // or q
        const VectorXd& s = std::get<2>(*it); // or p
        alpha[ii] = rho*s.dot(q);
        q -= alpha[ii]*y;
    }
    VectorXd r = opt_H0inv.cwiseProduct(q)*gamma;
    // oldest first
    ii = m_hist.size()-1;
    for(auto it = m_hist.crbegin(); it != m_hist.crend(); ++it, --ii) {
        double rho = std::get<0>(*it);
        const VectorXd& y = std::get<1>(*it); // or q
        const VectorXd& s = std::get<2>(*it); // or p
        double beta = rho*y.dot(r);
        r += s*(alpha[ii]-beta);
    }

    return r;
}

/**
 * @brief Function for computing the hessian recursively
 *
 * @param gamma Scale of initial (H0)
 * @param d Direction from right multiplication so far
 * @param it Position in history list
 *
 * @return Direction (d) after right multiplying d by H_k, the hessian
 * estimate for position it,
 */
/**
 * @brief Optimize Based on a value function and gradient function
 * separately. When both gradient and value are needed it will call update,
 * when it needs just the gradient it will call gradFunc, and when it just
 * needs the value it will cal valFunc. This is always the most efficient,
 * assuming there is additional cost of computing the gradient or value, but
 * its obviously more complicated.
 *
 * Paper: On the limited memory BFGS method for large scale optimization
 * By: Liu, Dong C., Nocedal, Jorge
 *
 * @return          StopReason
 */
StopReason LBFGSOpt::optimize()
{
    double gradstop = this->stop_G >= 0 ? this->stop_G : 0;
    double stepstop = this->stop_X >= 0 ? this->stop_X : 0;
    double valstop = this->stop_F >= 0 ? this->stop_F : -1;

    // update linesearch with minimum step, an options
    m_lsearch.opt_s = opt_ls_s;
    m_lsearch.opt_minstep = stepstop;
    m_lsearch.opt_beta = opt_ls_beta;
    m_lsearch.opt_sigma = opt_ls_sigma;

    VectorXd gk(state_x.rows()); // gradient
    double f_xk; // value at current position
    double f_xkm1; // value at previous position

    VectorXd pk, qk, dk, vk;
    VectorXd H0 = VectorXd::Ones(state_x.rows());
    double gamma = 1;

    //D(k+1) += p(k)p(k)'   - D(k)q(k)q(k)'D(k) + Z(k)T(k)v(k)v(k)'
    //          ----------    -----------------
    //         (p(k)'q(k))      q(k)'D(k)q(k)
    m_compFG(state_x, f_xk, gk);
    dk = -gk;
    for(int iter = 0; stop_Its <= 0 || iter < stop_Its; iter++) {
		// reset history if grad . gk > 0 (ie they go the same direction)
		if(gk.dot(dk) >= 0) {
			dk = -gk;
			m_hist.clear();
		}

        // compute step size
        double alpha = m_lsearch.search(f_xk, state_x, gk, dk);
        pk = alpha*dk;

        if(alpha == 0 || pk.squaredNorm() < stepstop*stepstop) {
            return ENDSTEP;
        }

        // step
        state_x += pk;

        // update gradient, value
        qk = -gk;
        f_xkm1 = f_xk;
        m_compFG(state_x, f_xk, gk);
        qk += gk;

        if(gk.squaredNorm() < gradstop*gradstop)
            return ENDGRAD;

        if(abs(f_xk - f_xkm1) < valstop)
            return ENDVALUE;

        if(f_xk < this->stop_F_under || f_xk > this->stop_F_over)
            return ENDABSVALUE;

        // update history
        m_hist.push_front(std::make_tuple(1./qk.dot(pk), qk, pk));
        if(m_hist.size() > opt_histsize)
            m_hist.pop_back();

        /*
         * update direction
         * qk - change in gradient (yk in original paper)
         * pk - change in x (sk in original paper)
         */
        gamma = qk.dot(pk)/qk.squaredNorm();
        dk = -hessFuncTwoLoop(gamma, gk);
        m_callback(dk, f_xk, gk, iter);
    }

    return ENDFAIL;
}

}
<commit_msg>added debug statement for when Hessian goes non-negative definite<commit_after>/******************************************************************************
 * Copyright 2014 Micah C Chambers (micahc.vt@gmail.com)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *  http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 * @file lbfgs.cpp Implemenation of the LBFGSOpt class which implements
 * a LBFGS optimization (energy minimization) algorithm.
 *
 *****************************************************************************/

#include "lbfgs.h"

#ifdef DEBUG
#include <iterator>
#include <iostream>
#include <iomanip>
using std::cerr;
using std::endl;
using std::setw;
using std::setprecision;
#endif //DEBUG

namespace npl {

/**
 * @brief Constructor for optimizer function.
 *
 * @param dim       Dimension of state variable
 * @param valfunc   Function which computes the energy of the underlying
 *                  mathematical function
 * @param gradfunc  Function which computes the gradient of energy in the
 *                  underlying mathematical function
 * @param callback  Function which should be called at the end of each
 *                  iteration (for instance, to debug)
 */
LBFGSOpt::LBFGSOpt(size_t dim, const ValFunc& valfunc,
        const GradFunc& gradfunc, const CallBackFunc& callback)
        : Optimizer(dim, valfunc, gradfunc, callback),
        m_lsearch(valfunc)
{
    m_hist.clear();
    opt_H0inv = VectorXd::Ones(dim);
    opt_histsize = 6;

	opt_ls_s = 1;
	opt_ls_beta = 0.5;
	opt_ls_sigma = 1e-5;
};

/**
 * @brief Constructor for optimizer function.
 *
 * @param dim       Dimension of state variable
 * @param valfunc   Function which computes the energy of the underlying
 *                  mathematical function
 * @param gradfunc  Function which computes the gradient of energy in the
 *                  underlying mathematical function
 * @param gradAndValFunc
 *                  Function which computes the energy and gradient in the
 *                  underlying mathematical function
 * @param callback  Function which should be called at the end of each
 *                  iteration (for instance, to debug)
 */
LBFGSOpt::LBFGSOpt(size_t dim, const ValFunc& valfunc, const GradFunc& gradfunc,
        const ValGradFunc& gradAndValFunc, const CallBackFunc& callback)
        : Optimizer(dim, valfunc, gradfunc, gradAndValFunc, callback),
        m_lsearch(valfunc)
{
    m_hist.clear();
    opt_H0inv = VectorXd::Ones(dim);
    opt_histsize = 6;

	opt_ls_s = 1;
	opt_ls_beta = 0.5;
	opt_ls_sigma = 1e-5;
};

/**
 * @brief Function for computing the hessian recursively
 * Based on the algorithm from Numerical Optimization (Nocedal)
 *
 * @param gamma Scale of initial (H0)
 * @param g Direction from right multiplication so far
 * @param it Position in history list
 *
 * @return Direction (d) after right multiplying d by H_k, the hessian
 * estimate for position it,
 */
VectorXd LBFGSOpt::hessFuncTwoLoop(double gamma, const VectorXd& g)
{
    VectorXd q = g;
    VectorXd alpha(m_hist.size());

    // iterate backward in time (forward in list)
    int ii = 0;
    for(auto it = m_hist.cbegin(); it != m_hist.cend(); ++it, ++ii) {
        double rho = std::get<0>(*it);
        const VectorXd& y = std::get<1>(*it); // or q
        const VectorXd& s = std::get<2>(*it); // or p
        alpha[ii] = rho*s.dot(q);
        q -= alpha[ii]*y;
    }
    VectorXd r = opt_H0inv.cwiseProduct(q)*gamma;
    // oldest first
    ii = m_hist.size()-1;
    for(auto it = m_hist.crbegin(); it != m_hist.crend(); ++it, --ii) {
        double rho = std::get<0>(*it);
        const VectorXd& y = std::get<1>(*it); // or q
        const VectorXd& s = std::get<2>(*it); // or p
        double beta = rho*y.dot(r);
        r += s*(alpha[ii]-beta);
    }

    return r;
}

/**
 * @brief Function for computing the hessian recursively
 *
 * @param gamma Scale of initial (H0)
 * @param d Direction from right multiplication so far
 * @param it Position in history list
 *
 * @return Direction (d) after right multiplying d by H_k, the hessian
 * estimate for position it,
 */
/**
 * @brief Optimize Based on a value function and gradient function
 * separately. When both gradient and value are needed it will call update,
 * when it needs just the gradient it will call gradFunc, and when it just
 * needs the value it will cal valFunc. This is always the most efficient,
 * assuming there is additional cost of computing the gradient or value, but
 * its obviously more complicated.
 *
 * Paper: On the limited memory BFGS method for large scale optimization
 * By: Liu, Dong C., Nocedal, Jorge
 *
 * @return          StopReason
 */
StopReason LBFGSOpt::optimize()
{
    double gradstop = this->stop_G >= 0 ? this->stop_G : 0;
    double stepstop = this->stop_X >= 0 ? this->stop_X : 0;
    double valstop = this->stop_F >= 0 ? this->stop_F : -1;

    // update linesearch with minimum step, an options
    m_lsearch.opt_s = opt_ls_s;
    m_lsearch.opt_minstep = stepstop;
    m_lsearch.opt_beta = opt_ls_beta;
    m_lsearch.opt_sigma = opt_ls_sigma;

    VectorXd gk(state_x.rows()); // gradient
    double f_xk; // value at current position
    double f_xkm1; // value at previous position

    VectorXd pk, qk, dk, vk;
    VectorXd H0 = VectorXd::Ones(state_x.rows());
    double gamma = 1;

    //D(k+1) += p(k)p(k)'   - D(k)q(k)q(k)'D(k) + Z(k)T(k)v(k)v(k)'
    //          ----------    -----------------
    //         (p(k)'q(k))      q(k)'D(k)q(k)
    m_compFG(state_x, f_xk, gk);
    dk = -gk;
    for(int iter = 0; stop_Its <= 0 || iter < stop_Its; iter++) {
		// reset history if grad . gk > 0 (ie they go the same direction)
		if(gk.dot(dk) >= 0) {
			dk = -gk;
			m_hist.clear();
#ifdef DEBUG
			cerr << "Clearing LBFGS History!" << endl;
#endif //DEBUG
		}

        // compute step size
        double alpha = m_lsearch.search(f_xk, state_x, gk, dk);
        pk = alpha*dk;

        if(alpha == 0 || pk.squaredNorm() < stepstop*stepstop) {
            return ENDSTEP;
        }

        // step
        state_x += pk;

        // update gradient, value
        qk = -gk;
        f_xkm1 = f_xk;
        m_compFG(state_x, f_xk, gk);
        qk += gk;

        if(gk.squaredNorm() < gradstop*gradstop)
            return ENDGRAD;

        if(abs(f_xk - f_xkm1) < valstop)
            return ENDVALUE;

        if(f_xk < this->stop_F_under || f_xk > this->stop_F_over)
            return ENDABSVALUE;

        // update history
        m_hist.push_front(std::make_tuple(1./qk.dot(pk), qk, pk));
        if(m_hist.size() > opt_histsize)
            m_hist.pop_back();

        /*
         * update direction
         * qk - change in gradient (yk in original paper)
         * pk - change in x (sk in original paper)
         */
        gamma = qk.dot(pk)/qk.squaredNorm();
        dk = -hessFuncTwoLoop(gamma, gk);
        m_callback(dk, f_xk, gk, iter);
    }

    return ENDFAIL;
}

}
<|endoftext|>


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

#include <numberformatcode.hxx>
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
#include <com/sun/star/i18n/KNumberFormatType.hpp>

NumberFormatCodeMapper::NumberFormatCodeMapper(
            const ::com::sun::star::uno::Reference <
                ::com::sun::star::lang::XMultiServiceFactory >& rxMSF )
        :
        xMSF( rxMSF ),
        bFormatsValid( sal_False )
{
}

NumberFormatCodeMapper::~NumberFormatCodeMapper()
{
}

::com::sun::star::i18n::NumberFormatCode SAL_CALL
NumberFormatCodeMapper::getDefault( sal_Int16 formatType, sal_Int16 formatUsage, const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{

    ::rtl::OUString elementType = mapElementTypeShortToString(formatType);
    ::rtl::OUString elementUsage = mapElementUsageShortToString(formatUsage);

    getFormats( rLocale );

    for(sal_Int32 i = 0; i < aFormatSeq.getLength(); i++) {
        if(aFormatSeq[i].isDefault && aFormatSeq[i].formatType == elementType &&
            aFormatSeq[i].formatUsage == elementUsage) {
            com::sun::star::i18n::NumberFormatCode anumberFormatCode(formatType,
                                                                    formatUsage,
                                                                    aFormatSeq[i].formatCode,
                                                                    aFormatSeq[i].formatName,
                                                                    aFormatSeq[i].formatKey,
                                                                    aFormatSeq[i].formatIndex,
                                                                    sal_True);
            return anumberFormatCode;
        }
    }
    com::sun::star::i18n::NumberFormatCode defaultNumberFormatCode;
    return defaultNumberFormatCode;
}

::com::sun::star::i18n::NumberFormatCode SAL_CALL
NumberFormatCodeMapper::getFormatCode( sal_Int16 formatIndex, const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{
    getFormats( rLocale );

    for(sal_Int32 i = 0; i < aFormatSeq.getLength(); i++) {
        if(aFormatSeq[i].formatIndex == formatIndex) {
            com::sun::star::i18n::NumberFormatCode anumberFormatCode(mapElementTypeStringToShort(aFormatSeq[i].formatType),
                                                                    mapElementUsageStringToShort(aFormatSeq[i].formatUsage),
                                                                    aFormatSeq[i].formatCode,
                                                                    aFormatSeq[i].formatName,
                                                                    aFormatSeq[i].formatKey,
                                                                    aFormatSeq[i].formatIndex,
                                                                    aFormatSeq[i].isDefault);
            return anumberFormatCode;
        }
    }
    com::sun::star::i18n::NumberFormatCode defaultNumberFormatCode;
    return defaultNumberFormatCode;

}

::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode > SAL_CALL
NumberFormatCodeMapper::getAllFormatCode( sal_Int16 formatUsage, const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{
    getFormats( rLocale );

    sal_Int32 i, count;
    count = 0;
    for(i = 0; i < aFormatSeq.getLength(); i++) {
        sal_Int16 elementUsage = mapElementUsageStringToShort(aFormatSeq[i].formatUsage);
        if( elementUsage == formatUsage)
            count++;
    }

    ::com::sun::star::uno::Sequence<com::sun::star::i18n::NumberFormatCode> seq(count);
    sal_Int32 j = 0;
    for(i = 0; i < aFormatSeq.getLength(); i++) {
        sal_Int16 elementUsage = mapElementUsageStringToShort(aFormatSeq[i].formatUsage);
        if( elementUsage == formatUsage) {
            seq[j] = com::sun::star::i18n::NumberFormatCode(mapElementTypeStringToShort(aFormatSeq[i].formatType),
                                                            formatUsage,
                                                            aFormatSeq[i].formatCode,
                                                            aFormatSeq[i].formatName,
                                                            aFormatSeq[i].formatKey,
                                                            aFormatSeq[i].formatIndex,
                                                            aFormatSeq[i].isDefault);
            j++;
        }
    }
    return seq;

}

::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode > SAL_CALL
NumberFormatCodeMapper::getAllFormatCodes( const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{
    getFormats( rLocale );

    ::com::sun::star::uno::Sequence<com::sun::star::i18n::NumberFormatCode> seq(aFormatSeq.getLength());
    for(sal_Int32 i = 0; i < aFormatSeq.getLength(); i++)
    {
        seq[i] = com::sun::star::i18n::NumberFormatCode(mapElementTypeStringToShort(aFormatSeq[i].formatType),
                                                        mapElementUsageStringToShort(aFormatSeq[i].formatUsage),
                                                        aFormatSeq[i].formatCode,
                                                        aFormatSeq[i].formatName,
                                                        aFormatSeq[i].formatKey,
                                                        aFormatSeq[i].formatIndex,
                                                        aFormatSeq[i].isDefault);
    }
    return seq;
}

// --- private implementation -----------------------------------------

void NumberFormatCodeMapper::setupLocale( const ::com::sun::star::lang::Locale& rLocale )
{
    if ( aLocale.Country    != rLocale.Country
      || aLocale.Language   != rLocale.Language
      || aLocale.Variant    != rLocale.Variant )
    {
        bFormatsValid = sal_False;
        aLocale = rLocale;
    }
}

void NumberFormatCodeMapper::getFormats( const ::com::sun::star::lang::Locale& rLocale )
{
    setupLocale( rLocale );
    if ( !bFormatsValid )
    {
        createLocaleDataObject();
        if( !xlocaleData.is() )
            aFormatSeq = ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement > (0);
        else
            aFormatSeq = xlocaleData->getAllFormats( aLocale );
        bFormatsValid = sal_True;
    }
}

::rtl::OUString
NumberFormatCodeMapper::mapElementTypeShortToString(sal_Int16 formatType)
{

    switch ( formatType )
    {
        case com::sun::star::i18n::KNumberFormatType::SHORT :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "short" ) );
        case com::sun::star::i18n::KNumberFormatType::MEDIUM :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "medium" ) );
        case com::sun::star::i18n::KNumberFormatType::LONG :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "long" ) );
    }
    return ::rtl::OUString();
}

sal_Int16
NumberFormatCodeMapper::mapElementTypeStringToShort(const ::rtl::OUString& formatType)
{
    if(formatType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "short" ) ))
        return com::sun::star::i18n::KNumberFormatType::SHORT;
    if(formatType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "medium" ) ))
        return com::sun::star::i18n::KNumberFormatType::MEDIUM;
    if(formatType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "long" ) ))
        return com::sun::star::i18n::KNumberFormatType::LONG;

    return com::sun::star::i18n::KNumberFormatType::SHORT;
}

::rtl::OUString
NumberFormatCodeMapper::mapElementUsageShortToString(sal_Int16 formatUsage)
{
    switch ( formatUsage )
    {
        case com::sun::star::i18n::KNumberFormatUsage::DATE :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DATE" ) );
        case com::sun::star::i18n::KNumberFormatUsage::TIME :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TIME" ) );
        case com::sun::star::i18n::KNumberFormatUsage::DATE_TIME :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DATE_TIME" ) );
        case com::sun::star::i18n::KNumberFormatUsage::FIXED_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FIXED_NUMBER" ) );
        case com::sun::star::i18n::KNumberFormatUsage::FRACTION_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FRACTION_NUMBER" ) );
        case com::sun::star::i18n::KNumberFormatUsage::PERCENT_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PERCENT_NUMBER" ) );
        case com::sun::star::i18n::KNumberFormatUsage::CURRENCY :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CURRENCY" ) );
        case com::sun::star::i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SCIENTIFIC_NUMBER" ) );
    }
    return ::rtl::OUString();
}

sal_Int16
NumberFormatCodeMapper::mapElementUsageStringToShort(const ::rtl::OUString& formatUsage)
{
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DATE" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::DATE;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "TIME" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::TIME;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DATE_TIME" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::DATE_TIME;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FIXED_NUMBER" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::FIXED_NUMBER;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FRACTION_NUMBER" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::FRACTION_NUMBER;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PERCENT_NUMBER" ) ))
        return  com::sun::star::i18n::KNumberFormatUsage::PERCENT_NUMBER;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CURRENCY" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::CURRENCY;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "SCIENTIFIC_NUMBER" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER;

    return 0;
}

void
NumberFormatCodeMapper::createLocaleDataObject() {

    if(xlocaleData.is())
        return;

    ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface >
        xI = xMSF->createInstance(
        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.LocaleData" ) ));

    if ( xI.is() ) {
        ::com::sun::star::uno::Any x = xI->queryInterface( ::getCppuType((const ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XLocaleData >*)0) );
            x >>= xlocaleData;
    }
}

::rtl::OUString SAL_CALL
NumberFormatCodeMapper::getImplementationName(void)
                throw( ::com::sun::star::uno::RuntimeException )
{
    return ::rtl::OUString::createFromAscii("com.sun.star.i18n.NumberFormatCodeMapper");
}

const sal_Char cNumFormat[] = "com.sun.star.i18n.NumberFormatMapper";

sal_Bool SAL_CALL
NumberFormatCodeMapper::supportsService(const rtl::OUString& rServiceName)
                throw( ::com::sun::star::uno::RuntimeException )
{
    return !rServiceName.compareToAscii(cNumFormat);
}

::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL
NumberFormatCodeMapper::getSupportedServiceNames(void) throw( ::com::sun::star::uno::RuntimeException )
{
    ::com::sun::star::uno::Sequence< ::rtl::OUString > aRet(1);
    aRet[0] = ::rtl::OUString::createFromAscii(cNumFormat);
    return aRet;
}

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

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

#include <numberformatcode.hxx>
#include <com/sun/star/i18n/KNumberFormatUsage.hpp>
#include <com/sun/star/i18n/KNumberFormatType.hpp>

NumberFormatCodeMapper::NumberFormatCodeMapper(
            const ::com::sun::star::uno::Reference <
                ::com::sun::star::lang::XMultiServiceFactory >& rxMSF )
        :
        xMSF( rxMSF ),
        bFormatsValid( sal_False )
{
}

NumberFormatCodeMapper::~NumberFormatCodeMapper()
{
}

::com::sun::star::i18n::NumberFormatCode SAL_CALL
NumberFormatCodeMapper::getDefault( sal_Int16 formatType, sal_Int16 formatUsage, const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{

    ::rtl::OUString elementType = mapElementTypeShortToString(formatType);
    ::rtl::OUString elementUsage = mapElementUsageShortToString(formatUsage);

    getFormats( rLocale );

    for(sal_Int32 i = 0; i < aFormatSeq.getLength(); i++) {
        if(aFormatSeq[i].isDefault && aFormatSeq[i].formatType == elementType &&
            aFormatSeq[i].formatUsage == elementUsage) {
            com::sun::star::i18n::NumberFormatCode anumberFormatCode(formatType,
                                                                    formatUsage,
                                                                    aFormatSeq[i].formatCode,
                                                                    aFormatSeq[i].formatName,
                                                                    aFormatSeq[i].formatKey,
                                                                    aFormatSeq[i].formatIndex,
                                                                    sal_True);
            return anumberFormatCode;
        }
    }
    com::sun::star::i18n::NumberFormatCode defaultNumberFormatCode;
    return defaultNumberFormatCode;
}

::com::sun::star::i18n::NumberFormatCode SAL_CALL
NumberFormatCodeMapper::getFormatCode( sal_Int16 formatIndex, const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{
    getFormats( rLocale );

    for(sal_Int32 i = 0; i < aFormatSeq.getLength(); i++) {
        if(aFormatSeq[i].formatIndex == formatIndex) {
            com::sun::star::i18n::NumberFormatCode anumberFormatCode(mapElementTypeStringToShort(aFormatSeq[i].formatType),
                                                                    mapElementUsageStringToShort(aFormatSeq[i].formatUsage),
                                                                    aFormatSeq[i].formatCode,
                                                                    aFormatSeq[i].formatName,
                                                                    aFormatSeq[i].formatKey,
                                                                    aFormatSeq[i].formatIndex,
                                                                    aFormatSeq[i].isDefault);
            return anumberFormatCode;
        }
    }
    com::sun::star::i18n::NumberFormatCode defaultNumberFormatCode;
    return defaultNumberFormatCode;

}

::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode > SAL_CALL
NumberFormatCodeMapper::getAllFormatCode( sal_Int16 formatUsage, const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{
    getFormats( rLocale );

    sal_Int32 i, count;
    count = 0;
    for(i = 0; i < aFormatSeq.getLength(); i++) {
        sal_Int16 elementUsage = mapElementUsageStringToShort(aFormatSeq[i].formatUsage);
        if( elementUsage == formatUsage)
            count++;
    }

    ::com::sun::star::uno::Sequence<com::sun::star::i18n::NumberFormatCode> seq(count);
    sal_Int32 j = 0;
    for(i = 0; i < aFormatSeq.getLength(); i++) {
        sal_Int16 elementUsage = mapElementUsageStringToShort(aFormatSeq[i].formatUsage);
        if( elementUsage == formatUsage) {
            seq[j] = com::sun::star::i18n::NumberFormatCode(mapElementTypeStringToShort(aFormatSeq[i].formatType),
                                                            formatUsage,
                                                            aFormatSeq[i].formatCode,
                                                            aFormatSeq[i].formatName,
                                                            aFormatSeq[i].formatKey,
                                                            aFormatSeq[i].formatIndex,
                                                            aFormatSeq[i].isDefault);
            j++;
        }
    }
    return seq;

}

::com::sun::star::uno::Sequence< ::com::sun::star::i18n::NumberFormatCode > SAL_CALL
NumberFormatCodeMapper::getAllFormatCodes( const ::com::sun::star::lang::Locale& rLocale ) throw(::com::sun::star::uno::RuntimeException)
{
    getFormats( rLocale );

    ::com::sun::star::uno::Sequence<com::sun::star::i18n::NumberFormatCode> seq(aFormatSeq.getLength());
    for(sal_Int32 i = 0; i < aFormatSeq.getLength(); i++)
    {
        seq[i] = com::sun::star::i18n::NumberFormatCode(mapElementTypeStringToShort(aFormatSeq[i].formatType),
                                                        mapElementUsageStringToShort(aFormatSeq[i].formatUsage),
                                                        aFormatSeq[i].formatCode,
                                                        aFormatSeq[i].formatName,
                                                        aFormatSeq[i].formatKey,
                                                        aFormatSeq[i].formatIndex,
                                                        aFormatSeq[i].isDefault);
    }
    return seq;
}

// --- private implementation -----------------------------------------

void NumberFormatCodeMapper::setupLocale( const ::com::sun::star::lang::Locale& rLocale )
{
    if ( aLocale.Country    != rLocale.Country
      || aLocale.Language   != rLocale.Language
      || aLocale.Variant    != rLocale.Variant )
    {
        bFormatsValid = sal_False;
        aLocale = rLocale;
    }
}

void NumberFormatCodeMapper::getFormats( const ::com::sun::star::lang::Locale& rLocale )
{
    setupLocale( rLocale );
    if ( !bFormatsValid )
    {
        createLocaleDataObject();
        if( !xlocaleData.is() )
            aFormatSeq = ::com::sun::star::uno::Sequence< ::com::sun::star::i18n::FormatElement > (0);
        else
            aFormatSeq = xlocaleData->getAllFormats( aLocale );
        bFormatsValid = sal_True;
    }
}

::rtl::OUString
NumberFormatCodeMapper::mapElementTypeShortToString(sal_Int16 formatType)
{

    switch ( formatType )
    {
        case com::sun::star::i18n::KNumberFormatType::SHORT :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "short" ) );
        case com::sun::star::i18n::KNumberFormatType::MEDIUM :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "medium" ) );
        case com::sun::star::i18n::KNumberFormatType::LONG :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "long" ) );
    }
    return ::rtl::OUString();
}

sal_Int16
NumberFormatCodeMapper::mapElementTypeStringToShort(const ::rtl::OUString& formatType)
{
    if(formatType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "short" ) ))
        return com::sun::star::i18n::KNumberFormatType::SHORT;
    if(formatType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "medium" ) ))
        return com::sun::star::i18n::KNumberFormatType::MEDIUM;
    if(formatType.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "long" ) ))
        return com::sun::star::i18n::KNumberFormatType::LONG;

    return com::sun::star::i18n::KNumberFormatType::SHORT;
}

::rtl::OUString
NumberFormatCodeMapper::mapElementUsageShortToString(sal_Int16 formatUsage)
{
    switch ( formatUsage )
    {
        case com::sun::star::i18n::KNumberFormatUsage::DATE :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DATE" ) );
        case com::sun::star::i18n::KNumberFormatUsage::TIME :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "TIME" ) );
        case com::sun::star::i18n::KNumberFormatUsage::DATE_TIME :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "DATE_TIME" ) );
        case com::sun::star::i18n::KNumberFormatUsage::FIXED_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FIXED_NUMBER" ) );
        case com::sun::star::i18n::KNumberFormatUsage::FRACTION_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "FRACTION_NUMBER" ) );
        case com::sun::star::i18n::KNumberFormatUsage::PERCENT_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "PERCENT_NUMBER" ) );
        case com::sun::star::i18n::KNumberFormatUsage::CURRENCY :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "CURRENCY" ) );
        case com::sun::star::i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER :
            return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "SCIENTIFIC_NUMBER" ) );
    }
    return ::rtl::OUString();
}

sal_Int16
NumberFormatCodeMapper::mapElementUsageStringToShort(const ::rtl::OUString& formatUsage)
{
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DATE" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::DATE;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "TIME" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::TIME;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "DATE_TIME" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::DATE_TIME;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FIXED_NUMBER" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::FIXED_NUMBER;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "FRACTION_NUMBER" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::FRACTION_NUMBER;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "PERCENT_NUMBER" ) ))
        return  com::sun::star::i18n::KNumberFormatUsage::PERCENT_NUMBER;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "CURRENCY" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::CURRENCY;
    if(formatUsage.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "SCIENTIFIC_NUMBER" ) ))
        return com::sun::star::i18n::KNumberFormatUsage::SCIENTIFIC_NUMBER;

    return 0;
}

void
NumberFormatCodeMapper::createLocaleDataObject() {

    if(xlocaleData.is())
        return;

    ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface >
        xI = xMSF->createInstance(
        ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.i18n.LocaleData" ) ));

    if ( xI.is() ) {
        ::com::sun::star::uno::Any x = xI->queryInterface( ::getCppuType((const ::com::sun::star::uno::Reference< ::com::sun::star::i18n::XLocaleData >*)0) );
            x >>= xlocaleData;
    }
}

::rtl::OUString SAL_CALL
NumberFormatCodeMapper::getImplementationName(void)
                throw( ::com::sun::star::uno::RuntimeException )
{
    return ::rtl::OUString::createFromAscii("com.sun.star.i18n.NumberFormatCodeMapper");
}

const sal_Char cNumFormat[] = "com.sun.star.i18n.NumberFormatMapper";

sal_Bool SAL_CALL
NumberFormatCodeMapper::supportsService(const rtl::OUString& rServiceName)
                throw( ::com::sun::star::uno::RuntimeException )
{
    return !rServiceName.compareToAscii(cNumFormat);
}

::com::sun::star::uno::Sequence< ::rtl::OUString > SAL_CALL
NumberFormatCodeMapper::getSupportedServiceNames(void) throw( ::com::sun::star::uno::RuntimeException )
{
    ::com::sun::star::uno::Sequence< ::rtl::OUString > aRet(1);
    aRet[0] = ::rtl::OUString::createFromAscii(cNumFormat);
    return aRet;
}

<|endoftext|>