<commit_before>#include <BALL/PLUGIN/pluginManager.h>
#include <BALL/PLUGIN/BALLPlugin.h>
#include <BALL/COMMON/logStream.h>

#include <BALL/VIEW/WIDGETS/scene.h>
#include <BALL/VIEW/KERNEL/mainControl.h>
#include <BALL/VIEW/KERNEL/iconLoader.h>
#include <BALL/VIEW/DIALOGS/preferences.h>
#include <BALL/VIEW/DIALOGS/displayProperties.h>
#include <BALL/VIEW/DIALOGS/pluginDialog.h>
#include <BALL/VIEW/PLUGIN/VIEWPlugin.h>
#include <BALL/STRUCTURE/bindingPocketProcessor.h>

#include <BALL/VIEW/UIC/ui_pluginDialog.h>

Q_DECLARE_METATYPE(BALL::VIEW::VIEWPlugin*)

#include <QtWidgets/QFileDialog>
#include <QtCore/QObject>
#include <QtCore/QDebug>
#include <QtCore/QLibraryInfo>

namespace BALL
{
	namespace VIEW
	{
		/*
		 * Implementation of PluginModel
		 */
		PluginModel::PluginModel(QObject* parent)
			: QAbstractListModel(parent),
			  num_rows_(0)
		{
		}

		int PluginModel::rowCount(const QModelIndex& /* parent */) const
		{
			return num_rows_;
		}

		void PluginModel::pluginsLoaded()
		{
			int nrow = num_rows_;
			num_rows_ = PluginManager::instance().getPluginCount();
			beginInsertRows(QModelIndex(), nrow, num_rows_);
			endInsertRows();
		}

		QVariant PluginModel::data(const QModelIndex& i, int role) const
		{
			if(!i.isValid())
			{
				return QVariant();
			}

			PluginManager& man = PluginManager::instance();

			if(i.row() >= num_rows_)
			{
				return QVariant();
			}

			switch(role)
			{
				case Qt::UserRole:
					return qVariantFromValue(man.getPluginInstance(i.row()));
				case Qt::DisplayRole:
				{
					BALLPlugin* ptr = qobject_cast<BALLPlugin*>(man.getPluginInstance(i.row()));

					// we *know* that this is a BALLPlugin (the loader takes care of that), so we don't need
					// to test if the cast succeeded
					return ptr->getName() + " (" + (ptr->isActive() ? tr("active") : tr("inactive")) + ")";
				}
				case Qt::DecorationRole:
				{
					VIEWPlugin* ptr = qobject_cast<VIEWPlugin*>(man.getPluginInstance(i.row()));

					if (ptr)
						return *(ptr->getIcon());
					else
						return QVariant();
				}
				default:
					return QVariant();
			}
		}

		/*
		 * Implementation of PluginDirectoryModel
		 */

		PluginDirectoryModel::PluginDirectoryModel(QObject* parent)
			: QAbstractListModel(parent)
		{
		}

		int PluginDirectoryModel::rowCount(const QModelIndex& /* parent */) const
		{
			PluginManager& man = PluginManager::instance();
			return man.getPluginDirectories().size();
		}

		QVariant PluginDirectoryModel::data(const QModelIndex& i, int role) const
		{
			if(!i.isValid()) {
				return QVariant();
			}

			PluginManager& man = PluginManager::instance();

			if((size_t)i.row() >= man.getPluginDirectories().size())
			{
				return QVariant();
			}

			switch(role)
			{
				case Qt::DisplayRole:
				{
					return man.getPluginDirectories()[i.row()];
				}

				default:
					return QVariant();
			}
		}

		void PluginDirectoryModel::addDirectory(const QString& dir)
		{
			PluginManager& man = PluginManager::instance();

			if(man.addPluginDirectory(dir)) {
				int idx = man.getPluginDirectories().size() - 1;

				beginInsertRows(QModelIndex(), idx, idx);
				endInsertRows();
			}
		}

		void PluginDirectoryModel::removeDirectory(const QModelIndex& idx)
		{
			PluginManager& man = PluginManager::instance();

			if(man.removePluginDirectory(idx.data(Qt::DisplayRole).toString())) {
				beginRemoveRows(QModelIndex(), idx.row(), idx.row());
				endRemoveRows();
			}
		}

		/*
		 * Implementation of PluginDialog
		 */
		PluginDialog::PluginDialog(Preferences* preferences, QWidget* parent, const char* name)
			: QWidget(parent),
			  ModularWidget(name),
			  preferences_(preferences)
		{
			ui_ = new Ui::PluginDialogData;
			ui_->setupUi(this);

			// define the dialogs section name in the INIFile
			setINIFileSectionName("PLUGINS");
			stack_pages_.push_back(std::make_pair(this, tr("Plugins").toStdString()));

			preferences_->insertEntry(this);

			setObjectName(name);

			IconLoader& icon_loader = IconLoader::instance();

			ui_->plugin_dir_button_add->setIcon(icon_loader.getIcon("actions/list-add"));
			ui_->plugin_dir_button_remove->setIcon(icon_loader.getIcon("actions/edit-delete"));

			// register all visible childs
			registerWidgets_();

			//TODO
			//registerWidgetForHelpSystem_(widget_stack->widget(0), "plugins.html#plugins");

			ui_->plugin_view->setModel(&plugin_model_);
			ui_->plugin_directories_view->setModel(&plugin_dir_model_);

//			QString plugin_path(BALL_PATH);
//			plugin_path += "/plugins";
			QString plugin_path(QLibraryInfo::location(QLibraryInfo::PluginsPath));

			plugin_dir_model_.addDirectory(plugin_path);
			plugin_model_.pluginsLoaded();

			setObjectName(name);
			ModularWidget::registerWidget(this);

			connect(ui_->plugin_dir_button_add, SIGNAL(clicked()), this, SLOT(addPluginDirectory()));
			connect(ui_->plugin_dir_button_remove, SIGNAL(clicked()), this, SLOT(removePluginDirectory()));
			connect(ui_->plugin_directories_view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(directorySelectionChanged(const QModelIndex&, const QModelIndex&)));
			connect(ui_->plugin_view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(pluginChanged(QModelIndex, const QModelIndex&)));
		}

		PluginDialog::~PluginDialog()
		{
			PluginManager::instance().unregisterHandler(this);
			delete ui_;
		}

		void PluginDialog::initializeWidget(MainControl&)
		{
			PreferencesEntry::restoreValues();
		}

		void PluginDialog::finalizeWidget(MainControl& mc)
		{
			ModularWidget::finalizeWidget(mc);
			PluginManager::instance().unloadAllPlugins();
		}

		void PluginDialog::writePreferenceEntries(INIFile& inifile)
		{
			PreferencesEntry::writePreferenceEntries(inifile);

			for (std::list<PreferencesEntry*>::iterator child_it = child_entries_.begin(); child_it != child_entries_.end(); ++child_it)
			{
				(*child_it)->writePreferenceEntries(inifile);
			}

			PluginManager& man = PluginManager::instance();

			if(!inifile.hasSection(getINIFileSectionName())) {
				inifile.appendSection(getINIFileSectionName());
			}

			String value;
			man.getPluginDirectories(value);

			inifile.insertValue(getINIFileSectionName(), "PluginDirectories", value);
			inifile.insertValue(getINIFileSectionName(), "ActivePlugins", man.getAutoActivatePlugins().toStdString());
		}

		void PluginDialog::readPreferenceEntries(const INIFile& inifile)
		{
			PreferencesEntry::readPreferenceEntries(inifile);

			plugin_model_.pluginsLoaded();

			for (std::list<PreferencesEntry*>::iterator child_it = child_entries_.begin();
					 child_it != child_entries_.end();
					 ++child_it)
			{
				(*child_it)->readPreferenceEntries(inifile);
			}

			PluginManager& man = PluginManager::instance();

			if(inifile.hasEntry(getINIFileSectionName(), "ActivePlugins")) {
				String active_plugins = inifile.getValue(getINIFileSectionName(), "ActivePlugins");
				man.setAutoActivatePlugins(active_plugins.c_str());
			}

			if(inifile.hasEntry(getINIFileSectionName(), "PluginDirectories")) {
				String plugin_directories = inifile.getValue(getINIFileSectionName(), "PluginDirectories");
				man.setPluginDirectories(plugin_directories);
			}
		}

		void PluginDialog::pluginChanged(QModelIndex current, const QModelIndex& /* previous */)
		{
			//Cleanup the old config dialog
			active_index_ = current;
			QObject* active_object = qvariant_cast<QObject*>(plugin_model_.data(current, Qt::UserRole));

			BALLPlugin* active_plugin = qobject_cast<BALLPlugin*>(active_object);
			if (!active_plugin)
			{
				return;
			}

			ui_->plugin_toggle_button->setEnabled(true);
			if (active_plugin->isActive())
			{
				ui_->plugin_toggle_button->setText(tr("Deactivate"));
			}
			else
			{
				ui_->plugin_toggle_button->setText(tr("Activate"));
			}
		}

		void PluginDialog::addPluginDirectory()
		{
			QString plugin_path(BALL_PATH);
			plugin_path += "/plugins";

			QString dir = QFileDialog::getExistingDirectory(0, tr("Select a plugin directory"), plugin_path);
			plugin_dir_model_.addDirectory(dir);
			plugin_model_.pluginsLoaded();
		}

		void PluginDialog::removePluginDirectory()
		{
			// TODO: disable remove button if no item selected
			if (!ui_->plugin_directories_view->currentIndex().isValid())
			{
				return;
			}

			plugin_dir_model_.removeDirectory(ui_->plugin_directories_view->currentIndex());
			plugin_model_.pluginsLoaded();
		}

		void PluginDialog::directorySelectionChanged(const QModelIndex& current, const QModelIndex& /* previous */)
		{
			ui_->plugin_dir_button_remove->setEnabled(current.isValid());
		}

		void PluginDialog::togglePluginState()
		{
			if (!active_index_.isValid())
			{
				return;
			}

			QObject* active_object = qvariant_cast<QObject*>(plugin_model_.data(active_index_, Qt::UserRole));
			BALLPlugin* active_plugin = qobject_cast<BALLPlugin*>(active_object);
			if (active_plugin->isActive())
			{
				PluginManager::instance().doNotAutoActivatePlugin(active_plugin->getName());
				PluginManager::instance().stopPlugin(active_plugin);
				ui_->plugin_toggle_button->setText(tr("Activate"));
			}
			else
			{
				PluginManager::instance().autoActivatePlugin(active_plugin->getName());
				PluginManager::instance().startPlugin(active_plugin);
				ui_->plugin_toggle_button->setText(tr("Deactivate"));
			}

			ui_->plugin_view->update(active_index_);
		}

		void PluginDialog::registerChildEntry(PreferencesEntry* child)
		{
			if(!child) return;

			preferences_->insertChildEntry("PLUGINS", child);
			child_entries_.push_back(child);
		}

		void PluginDialog::unregisterChildEntry(PreferencesEntry* child)
		{
			if(!child) return;

			std::list<PreferencesEntry*>::iterator child_it = std::find(child_entries_.begin(), child_entries_.end(), child);
			child_entries_.erase(child_it);
			preferences_->removeEntry(child);
		}

		bool PluginDialog::canHandle(BALLPlugin* plugin) const
		{
			return dynamic_cast<VIEWPlugin*>(plugin) != 0;
		}

		bool PluginDialog::specificSetup_(BALLPlugin* plugin)
		{
			VIEWPlugin* view_plugin = dynamic_cast<VIEWPlugin*>(plugin);

			if(view_plugin->getConfigDialog() != 0 && preferences_ != 0)
			{
				registerChildEntry(view_plugin->getConfigDialog());
			}

			return true;
		}

		bool PluginDialog::specificShutdown_(BALLPlugin* plugin)
		{
			VIEWPlugin* view_plugin = dynamic_cast<VIEWPlugin*>(plugin);

			if(view_plugin->getConfigDialog() != 0 && preferences_ != 0)
			{
				unregisterChildEntry(view_plugin->getConfigDialog());
				view_plugin->getConfigDialog()->setParent(0);
			}

			return true;
		}

	}
}
<commit_msg>[VIEW] Fix: Plugin directory selection w/o selection Canceling the plugin dir selection dialog previously lead to an empty entry in the directory list.<commit_after>#include <BALL/PLUGIN/pluginManager.h>
#include <BALL/PLUGIN/BALLPlugin.h>
#include <BALL/COMMON/logStream.h>

#include <BALL/VIEW/WIDGETS/scene.h>
#include <BALL/VIEW/KERNEL/mainControl.h>
#include <BALL/VIEW/KERNEL/iconLoader.h>
#include <BALL/VIEW/DIALOGS/preferences.h>
#include <BALL/VIEW/DIALOGS/displayProperties.h>
#include <BALL/VIEW/DIALOGS/pluginDialog.h>
#include <BALL/VIEW/PLUGIN/VIEWPlugin.h>
#include <BALL/STRUCTURE/bindingPocketProcessor.h>

#include <BALL/VIEW/UIC/ui_pluginDialog.h>

Q_DECLARE_METATYPE(BALL::VIEW::VIEWPlugin*)

#include <QtWidgets/QFileDialog>
#include <QtCore/QObject>
#include <QtCore/QDebug>
#include <QtCore/QLibraryInfo>

namespace BALL
{
	namespace VIEW
	{
		/*
		 * Implementation of PluginModel
		 */
		PluginModel::PluginModel(QObject* parent)
			: QAbstractListModel(parent),
			  num_rows_(0)
		{
		}

		int PluginModel::rowCount(const QModelIndex& /* parent */) const
		{
			return num_rows_;
		}

		void PluginModel::pluginsLoaded()
		{
			int nrow = num_rows_;
			num_rows_ = PluginManager::instance().getPluginCount();
			beginInsertRows(QModelIndex(), nrow, num_rows_);
			endInsertRows();
		}

		QVariant PluginModel::data(const QModelIndex& i, int role) const
		{
			if(!i.isValid())
			{
				return QVariant();
			}

			PluginManager& man = PluginManager::instance();

			if(i.row() >= num_rows_)
			{
				return QVariant();
			}

			switch(role)
			{
				case Qt::UserRole:
					return qVariantFromValue(man.getPluginInstance(i.row()));
				case Qt::DisplayRole:
				{
					BALLPlugin* ptr = qobject_cast<BALLPlugin*>(man.getPluginInstance(i.row()));

					// we *know* that this is a BALLPlugin (the loader takes care of that), so we don't need
					// to test if the cast succeeded
					return ptr->getName() + " (" + (ptr->isActive() ? tr("active") : tr("inactive")) + ")";
				}
				case Qt::DecorationRole:
				{
					VIEWPlugin* ptr = qobject_cast<VIEWPlugin*>(man.getPluginInstance(i.row()));

					if (ptr)
						return *(ptr->getIcon());
					else
						return QVariant();
				}
				default:
					return QVariant();
			}
		}

		/*
		 * Implementation of PluginDirectoryModel
		 */

		PluginDirectoryModel::PluginDirectoryModel(QObject* parent)
			: QAbstractListModel(parent)
		{
		}

		int PluginDirectoryModel::rowCount(const QModelIndex& /* parent */) const
		{
			PluginManager& man = PluginManager::instance();
			return man.getPluginDirectories().size();
		}

		QVariant PluginDirectoryModel::data(const QModelIndex& i, int role) const
		{
			if(!i.isValid()) {
				return QVariant();
			}

			PluginManager& man = PluginManager::instance();

			if((size_t)i.row() >= man.getPluginDirectories().size())
			{
				return QVariant();
			}

			switch(role)
			{
				case Qt::DisplayRole:
				{
					return man.getPluginDirectories()[i.row()];
				}

				default:
					return QVariant();
			}
		}

		void PluginDirectoryModel::addDirectory(const QString& dir)
		{
			PluginManager& man = PluginManager::instance();

			if(man.addPluginDirectory(dir)) {
				int idx = man.getPluginDirectories().size() - 1;

				beginInsertRows(QModelIndex(), idx, idx);
				endInsertRows();
			}
		}

		void PluginDirectoryModel::removeDirectory(const QModelIndex& idx)
		{
			PluginManager& man = PluginManager::instance();

			if(man.removePluginDirectory(idx.data(Qt::DisplayRole).toString())) {
				beginRemoveRows(QModelIndex(), idx.row(), idx.row());
				endRemoveRows();
			}
		}

		/*
		 * Implementation of PluginDialog
		 */
		PluginDialog::PluginDialog(Preferences* preferences, QWidget* parent, const char* name)
			: QWidget(parent),
			  ModularWidget(name),
			  preferences_(preferences)
		{
			ui_ = new Ui::PluginDialogData;
			ui_->setupUi(this);

			// define the dialogs section name in the INIFile
			setINIFileSectionName("PLUGINS");
			stack_pages_.push_back(std::make_pair(this, tr("Plugins").toStdString()));

			preferences_->insertEntry(this);

			setObjectName(name);

			IconLoader& icon_loader = IconLoader::instance();

			ui_->plugin_dir_button_add->setIcon(icon_loader.getIcon("actions/list-add"));
			ui_->plugin_dir_button_remove->setIcon(icon_loader.getIcon("actions/edit-delete"));

			// register all visible childs
			registerWidgets_();

			//TODO
			//registerWidgetForHelpSystem_(widget_stack->widget(0), "plugins.html#plugins");

			ui_->plugin_view->setModel(&plugin_model_);
			ui_->plugin_directories_view->setModel(&plugin_dir_model_);

//			QString plugin_path(BALL_PATH);
//			plugin_path += "/plugins";
			QString plugin_path(QLibraryInfo::location(QLibraryInfo::PluginsPath));

			plugin_dir_model_.addDirectory(plugin_path);
			plugin_model_.pluginsLoaded();

			setObjectName(name);
			ModularWidget::registerWidget(this);

			connect(ui_->plugin_dir_button_add, SIGNAL(clicked()), this, SLOT(addPluginDirectory()));
			connect(ui_->plugin_dir_button_remove, SIGNAL(clicked()), this, SLOT(removePluginDirectory()));
			connect(ui_->plugin_directories_view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(directorySelectionChanged(const QModelIndex&, const QModelIndex&)));
			connect(ui_->plugin_view->selectionModel(), SIGNAL(currentChanged(const QModelIndex&, const QModelIndex&)), this, SLOT(pluginChanged(QModelIndex, const QModelIndex&)));
		}

		PluginDialog::~PluginDialog()
		{
			PluginManager::instance().unregisterHandler(this);
			delete ui_;
		}

		void PluginDialog::initializeWidget(MainControl&)
		{
			PreferencesEntry::restoreValues();
		}

		void PluginDialog::finalizeWidget(MainControl& mc)
		{
			ModularWidget::finalizeWidget(mc);
			PluginManager::instance().unloadAllPlugins();
		}

		void PluginDialog::writePreferenceEntries(INIFile& inifile)
		{
			PreferencesEntry::writePreferenceEntries(inifile);

			for (std::list<PreferencesEntry*>::iterator child_it = child_entries_.begin(); child_it != child_entries_.end(); ++child_it)
			{
				(*child_it)->writePreferenceEntries(inifile);
			}

			PluginManager& man = PluginManager::instance();

			if(!inifile.hasSection(getINIFileSectionName())) {
				inifile.appendSection(getINIFileSectionName());
			}

			String value;
			man.getPluginDirectories(value);

			inifile.insertValue(getINIFileSectionName(), "PluginDirectories", value);
			inifile.insertValue(getINIFileSectionName(), "ActivePlugins", man.getAutoActivatePlugins().toStdString());
		}

		void PluginDialog::readPreferenceEntries(const INIFile& inifile)
		{
			PreferencesEntry::readPreferenceEntries(inifile);

			plugin_model_.pluginsLoaded();

			for (std::list<PreferencesEntry*>::iterator child_it = child_entries_.begin();
					 child_it != child_entries_.end();
					 ++child_it)
			{
				(*child_it)->readPreferenceEntries(inifile);
			}

			PluginManager& man = PluginManager::instance();

			if(inifile.hasEntry(getINIFileSectionName(), "ActivePlugins")) {
				String active_plugins = inifile.getValue(getINIFileSectionName(), "ActivePlugins");
				man.setAutoActivatePlugins(active_plugins.c_str());
			}

			if(inifile.hasEntry(getINIFileSectionName(), "PluginDirectories")) {
				String plugin_directories = inifile.getValue(getINIFileSectionName(), "PluginDirectories");
				man.setPluginDirectories(plugin_directories);
			}
		}

		void PluginDialog::pluginChanged(QModelIndex current, const QModelIndex& /* previous */)
		{
			//Cleanup the old config dialog
			active_index_ = current;
			QObject* active_object = qvariant_cast<QObject*>(plugin_model_.data(current, Qt::UserRole));

			BALLPlugin* active_plugin = qobject_cast<BALLPlugin*>(active_object);
			if (!active_plugin)
			{
				return;
			}

			ui_->plugin_toggle_button->setEnabled(true);
			if (active_plugin->isActive())
			{
				ui_->plugin_toggle_button->setText(tr("Deactivate"));
			}
			else
			{
				ui_->plugin_toggle_button->setText(tr("Activate"));
			}
		}

		void PluginDialog::addPluginDirectory()
		{
			QString plugin_path(BALL_PATH);
			plugin_path += "/plugins";

			QString dir = QFileDialog::getExistingDirectory(0, tr("Select a plugin directory"), plugin_path);
			if(!dir.isNull())
			{
				plugin_dir_model_.addDirectory(dir);
				plugin_model_.pluginsLoaded();
			}
		}

		void PluginDialog::removePluginDirectory()
		{
			// TODO: disable remove button if no item selected
			if (!ui_->plugin_directories_view->currentIndex().isValid())
			{
				return;
			}

			plugin_dir_model_.removeDirectory(ui_->plugin_directories_view->currentIndex());
			plugin_model_.pluginsLoaded();
		}

		void PluginDialog::directorySelectionChanged(const QModelIndex& current, const QModelIndex& /* previous */)
		{
			ui_->plugin_dir_button_remove->setEnabled(current.isValid());
		}

		void PluginDialog::togglePluginState()
		{
			if (!active_index_.isValid())
			{
				return;
			}

			QObject* active_object = qvariant_cast<QObject*>(plugin_model_.data(active_index_, Qt::UserRole));
			BALLPlugin* active_plugin = qobject_cast<BALLPlugin*>(active_object);
			if (active_plugin->isActive())
			{
				PluginManager::instance().doNotAutoActivatePlugin(active_plugin->getName());
				PluginManager::instance().stopPlugin(active_plugin);
				ui_->plugin_toggle_button->setText(tr("Activate"));
			}
			else
			{
				PluginManager::instance().autoActivatePlugin(active_plugin->getName());
				PluginManager::instance().startPlugin(active_plugin);
				ui_->plugin_toggle_button->setText(tr("Deactivate"));
			}

			ui_->plugin_view->update(active_index_);
		}

		void PluginDialog::registerChildEntry(PreferencesEntry* child)
		{
			if(!child) return;

			preferences_->insertChildEntry("PLUGINS", child);
			child_entries_.push_back(child);
		}

		void PluginDialog::unregisterChildEntry(PreferencesEntry* child)
		{
			if(!child) return;

			std::list<PreferencesEntry*>::iterator child_it = std::find(child_entries_.begin(), child_entries_.end(), child);
			child_entries_.erase(child_it);
			preferences_->removeEntry(child);
		}

		bool PluginDialog::canHandle(BALLPlugin* plugin) const
		{
			return dynamic_cast<VIEWPlugin*>(plugin) != 0;
		}

		bool PluginDialog::specificSetup_(BALLPlugin* plugin)
		{
			VIEWPlugin* view_plugin = dynamic_cast<VIEWPlugin*>(plugin);

			if(view_plugin->getConfigDialog() != 0 && preferences_ != 0)
			{
				registerChildEntry(view_plugin->getConfigDialog());
			}

			return true;
		}

		bool PluginDialog::specificShutdown_(BALLPlugin* plugin)
		{
			VIEWPlugin* view_plugin = dynamic_cast<VIEWPlugin*>(plugin);

			if(view_plugin->getConfigDialog() != 0 && preferences_ != 0)
			{
				unregisterChildEntry(view_plugin->getConfigDialog());
				view_plugin->getConfigDialog()->setParent(0);
			}

			return true;
		}

	}
}
<|endoftext|>


<commit_before>/** \copyright
 * Copyright (c) 2015, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file Lpc17xx40xxUart.cxx
 * This file implements a UART device driver layer specific to LPC17xx and
 * LPC40xx devices.
 *
 * @author Stuart W. Baker
 * @date 9 April 2015
 */

#include "Lpc17xx40xxUart.hxx"

#include <algorithm>

/** Instance pointers help us get context from the interrupt handler(s) */
#if  defined (CHIP_LPC177X_8X) || defined (CHIP_LPC407X_8X)
LpcUart *LpcUart::instances[5] = {NULL};
#else
LpcUart *LpcUart::instances[4] = {NULL};
#endif

/** Constructor.
 * @param name name of this device instance in the file system
 * @param base base address of this device
 * @param interrupt interrupt number of this device
 */
LpcUart::LpcUart(const char *name, LPC_USART_T *base, IRQn_Type interrupt)
    : Serial(name)
    , base(base)
    , interrupt(interrupt)
{
    switch (interrupt)
    {
        default:
            HASSERT(0);
        case UART0_IRQn:
            instances[0] = this;
            break;
        case UART1_IRQn:
            instances[1] = this;
            break;
        case UART2_IRQn:
            instances[2] = this;
            break;
        case UART3_IRQn:
            instances[3] = this;
            break;
#if  defined (CHIP_LPC177X_8X) || defined (CHIP_LPC407X_8X)
        case UART4_IRQn:
            instances[4] = this;
            break;
#endif
    }

    /* should already be disabled, but just in case */
    NVIC_DisableIRQ(interrupt);
    /* We set the priority so that it is slightly lower than the highest needed
     * for FreeRTOS compatibility. This will ensure that CAN interrupts take
     * precedence over UART. */
    NVIC_SetPriority(interrupt,
                     std::min(0xff, configKERNEL_INTERRUPT_PRIORITY + 0x20));
}

/** Enable use of the device.
 * @param dev device to enable
 */
void LpcUart::enable()
{
    Chip_UART_Init(base);
    Chip_UART_SetBaud(base, 115200);
    Chip_UART_ConfigData(base, (UART_LCR_WLEN8 | UART_LCR_SBS_1BIT));
    Chip_UART_SetupFIFOS(base, (UART_FCR_FIFO_EN | UART_FCR_RX_RS |
                                UART_FCR_TX_RS | UART_FCR_TRG_LEV0));
    Chip_UART_TXEnable(base);
    Chip_UART_IntEnable(base, (UART_IER_RBRINT | UART_IER_RLSINT));
    NVIC_EnableIRQ(interrupt);
}

/** Disable use of the device.
 */
void LpcUart::disable()
{
    NVIC_DisableIRQ(interrupt);
    Chip_UART_IntDisable(base, (UART_IER_RBRINT | UART_IER_RLSINT));
    Chip_UART_TXDisable(base);
    Chip_UART_DeInit(base);
}

/** Try and transmit a message.
 */
void LpcUart::tx_char()
{
    if ((Chip_UART_GetIntsEnabled(base) & UART_IER_THREINT) == 0)
    {
        uint8_t data = 0;
        if (txBuf->get(&data, 1))
        {
            Chip_UART_SendByte(base, (uint8_t)data);
            Chip_UART_IntEnable(base, UART_IER_THREINT);
            txBuf->signal_condition();
        }
    }
}

/** Common interrupt handler for all UART devices.
 */
void LpcUart::interrupt_handler()
{
    /** @todo (Stuart Baker) optimization opportunity by getting a write
     * pointer to fill the fifo and then advance the buffer when finished
     */
    /* receive charaters as long as we can */
    while (Chip_UART_ReadLineStatus(base) & UART_LSR_RDR)
    {
        uint8_t data = Chip_UART_ReadByte(base);
        if (rxBuf->put(&data, 1) == 0)
        {
            overrunCount++;
        }
        rxBuf->signal_condition_from_isr();
    }

    /* tranmit a character if we have pending tx data */
    if (Chip_UART_GetIntsEnabled(base) & UART_IER_THREINT)
    {
        /** @todo (Stuart Baker) optimization opportunity by getting a read
         * pointer to fill the fifo and then consume the buffer when finished.
         */
        while (Chip_UART_ReadLineStatus(base) & UART_LSR_THRE)
        {
            uint8_t data;
            if (txBuf->get(&data, 1) != 0)
            {
                Chip_UART_SendByte(base, (uint8_t)data);
                txBuf->signal_condition_from_isr();
            }
            else
            {
                /* no more data pending */
                Chip_UART_IntDisable(base, UART_IER_THREINT);
                break;
            }
        }
    }
}

extern "C" {
/** UART0 interrupt handler.
 */
void uart0_interrupt_handler(void)
{
    LpcUart::interrupt_handler(0);
}

/** UART1 interrupt handler.
 */
void uart1_interrupt_handler(void)
{
    LpcUart::interrupt_handler(1);
}

/** UART2 interrupt handler.
 */
void uart2_interrupt_handler(void)
{
    LpcUart::interrupt_handler(2);
}

/** UART3 interrupt handler.
 */
void uart3_interrupt_handler(void)
{
    LpcUart::interrupt_handler(3);
}

#if  defined (CHIP_LPC177X_8X) || defined (CHIP_LPC407X_8X)
/** UART4 interrupt handler.
 */
void uart4_interrupt_handler(void)
{
    LpcUart::interrupt_handler(4);
}
#endif

} // extern C
<commit_msg>Remove unnecessary comment.<commit_after>/** \copyright
 * Copyright (c) 2015, Stuart W Baker
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are  permitted provided that the following conditions are met:
 *
 *  - Redistributions of source code must retain the above copyright notice,
 *    this list of conditions and the following disclaimer.
 *
 *  - Redistributions in binary form must reproduce the above copyright notice,
 *    this list of conditions and the following disclaimer in the documentation
 *    and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 *
 * \file Lpc17xx40xxUart.cxx
 * This file implements a UART device driver layer specific to LPC17xx and
 * LPC40xx devices.
 *
 * @author Stuart W. Baker
 * @date 9 April 2015
 */

#include "Lpc17xx40xxUart.hxx"

#include <algorithm>

#if  defined (CHIP_LPC177X_8X) || defined (CHIP_LPC407X_8X)
LpcUart *LpcUart::instances[5] = {NULL};
#else
LpcUart *LpcUart::instances[4] = {NULL};
#endif

/** Constructor.
 * @param name name of this device instance in the file system
 * @param base base address of this device
 * @param interrupt interrupt number of this device
 */
LpcUart::LpcUart(const char *name, LPC_USART_T *base, IRQn_Type interrupt)
    : Serial(name)
    , base(base)
    , interrupt(interrupt)
{
    switch (interrupt)
    {
        default:
            HASSERT(0);
        case UART0_IRQn:
            instances[0] = this;
            break;
        case UART1_IRQn:
            instances[1] = this;
            break;
        case UART2_IRQn:
            instances[2] = this;
            break;
        case UART3_IRQn:
            instances[3] = this;
            break;
#if  defined (CHIP_LPC177X_8X) || defined (CHIP_LPC407X_8X)
        case UART4_IRQn:
            instances[4] = this;
            break;
#endif
    }

    /* should already be disabled, but just in case */
    NVIC_DisableIRQ(interrupt);
    /* We set the priority so that it is slightly lower than the highest needed
     * for FreeRTOS compatibility. This will ensure that CAN interrupts take
     * precedence over UART. */
    NVIC_SetPriority(interrupt,
                     std::min(0xff, configKERNEL_INTERRUPT_PRIORITY + 0x20));
}

/** Enable use of the device.
 * @param dev device to enable
 */
void LpcUart::enable()
{
    Chip_UART_Init(base);
    Chip_UART_SetBaud(base, 115200);
    Chip_UART_ConfigData(base, (UART_LCR_WLEN8 | UART_LCR_SBS_1BIT));
    Chip_UART_SetupFIFOS(base, (UART_FCR_FIFO_EN | UART_FCR_RX_RS |
                                UART_FCR_TX_RS | UART_FCR_TRG_LEV0));
    Chip_UART_TXEnable(base);
    Chip_UART_IntEnable(base, (UART_IER_RBRINT | UART_IER_RLSINT));
    NVIC_EnableIRQ(interrupt);
}

/** Disable use of the device.
 */
void LpcUart::disable()
{
    NVIC_DisableIRQ(interrupt);
    Chip_UART_IntDisable(base, (UART_IER_RBRINT | UART_IER_RLSINT));
    Chip_UART_TXDisable(base);
    Chip_UART_DeInit(base);
}

/** Try and transmit a message.
 */
void LpcUart::tx_char()
{
    if ((Chip_UART_GetIntsEnabled(base) & UART_IER_THREINT) == 0)
    {
        uint8_t data = 0;
        if (txBuf->get(&data, 1))
        {
            Chip_UART_SendByte(base, (uint8_t)data);
            Chip_UART_IntEnable(base, UART_IER_THREINT);
            txBuf->signal_condition();
        }
    }
}

/** Common interrupt handler for all UART devices.
 */
void LpcUart::interrupt_handler()
{
    /** @todo (Stuart Baker) optimization opportunity by getting a write
     * pointer to fill the fifo and then advance the buffer when finished
     */
    /* receive charaters as long as we can */
    while (Chip_UART_ReadLineStatus(base) & UART_LSR_RDR)
    {
        uint8_t data = Chip_UART_ReadByte(base);
        if (rxBuf->put(&data, 1) == 0)
        {
            overrunCount++;
        }
        rxBuf->signal_condition_from_isr();
    }

    /* tranmit a character if we have pending tx data */
    if (Chip_UART_GetIntsEnabled(base) & UART_IER_THREINT)
    {
        /** @todo (Stuart Baker) optimization opportunity by getting a read
         * pointer to fill the fifo and then consume the buffer when finished.
         */
        while (Chip_UART_ReadLineStatus(base) & UART_LSR_THRE)
        {
            uint8_t data;
            if (txBuf->get(&data, 1) != 0)
            {
                Chip_UART_SendByte(base, (uint8_t)data);
                txBuf->signal_condition_from_isr();
            }
            else
            {
                /* no more data pending */
                Chip_UART_IntDisable(base, UART_IER_THREINT);
                break;
            }
        }
    }
}

extern "C" {
/** UART0 interrupt handler.
 */
void uart0_interrupt_handler(void)
{
    LpcUart::interrupt_handler(0);
}

/** UART1 interrupt handler.
 */
void uart1_interrupt_handler(void)
{
    LpcUart::interrupt_handler(1);
}

/** UART2 interrupt handler.
 */
void uart2_interrupt_handler(void)
{
    LpcUart::interrupt_handler(2);
}

/** UART3 interrupt handler.
 */
void uart3_interrupt_handler(void)
{
    LpcUart::interrupt_handler(3);
}

#if  defined (CHIP_LPC177X_8X) || defined (CHIP_LPC407X_8X)
/** UART4 interrupt handler.
 */
void uart4_interrupt_handler(void)
{
    LpcUart::interrupt_handler(4);
}
#endif

} // extern C
<|endoftext|>


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

#include <iostream>
#include "topology.h"
#include "gmxtrajectoryreader.h"

using namespace std;

bool GMXTrajectoryReader::Open(const string &file)
{
       _filename = file;
       return true;
}

void GMXTrajectoryReader::Close()
{
    gmx::close_trx(_gmx_status);
}

bool GMXTrajectoryReader::FirstFrame(Topology &conf)
{
#ifdef GMX4DEV
    gmx::output_env_t oenv; // wtf is this
    if(!gmx::read_first_frame(oenv, &_gmx_status,(char*)_filename.c_str(),&_gmx_frame,TRX_READ_X | TRX_READ_F))
        throw std::runtime_error(string("cannot open ") + _filename);
#else
    if(!gmx::read_first_frame(&_gmx_status,(char*)_filename.c_str(),&_gmx_frame,TRX_READ_X | TRX_READ_F))
        throw std::runtime_error(string("cannot open ") + _filename);
#endif

    matrix m;
    for(int i=0; i<3; i++)
        for(int j=0; j<3; j++)
            m[i][j] = _gmx_frame.box[j][i];
    conf.setBox(m);
    conf.setTime(_gmx_frame.time);
    conf.setStep(_gmx_frame.step);
    cout << endl;

    if(_gmx_frame.natoms != conf.Beads().size())
        throw std::runtime_error("number of beads in trajectory do not match topology");

    //conf.HasPos(true);
    //conf.HasF(_gmx_frame.bF);

    for(int i=0; i<_gmx_frame.natoms; i++) {
        double r[3] = { _gmx_frame.x[i][XX],  _gmx_frame.x[i][YY], _gmx_frame.x[i][ZZ] };
        conf.getBead(i)->setPos(r);
        if(_gmx_frame.bF) {
            double f[3] = { _gmx_frame.f[i][XX],  _gmx_frame.f[i][YY], _gmx_frame.f[i][ZZ] };
            conf.getBead(i)->setF(f);
        }
    }
    return true;
}

bool GMXTrajectoryReader::NextFrame(Topology &conf)
{
#ifdef GMX4DEV
    gmx::output_env_t oenv; // wtf is this
    if(!gmx::read_next_frame(oenv, _gmx_status,&_gmx_frame))
        return false;
#else
    if(!gmx::read_next_frame(_gmx_status,&_gmx_frame))
        return false;
#endif

    matrix m;
    for(int i=0; i<3; i++)
        for(int j=0; j<3; j++)
            m[i][j] = _gmx_frame.box[j][i];
    conf.setTime(_gmx_frame.time);
    conf.setStep(_gmx_frame.step);
    conf.setBox(m);

    //conf.HasF(_gmx_frame.bF);

    for(int i=0; i<_gmx_frame.natoms; i++) {
        double r[3] = { _gmx_frame.x[i][XX],  _gmx_frame.x[i][YY], _gmx_frame.x[i][ZZ] };
        conf.getBead(i)->setPos(r);
        if(_gmx_frame.bF) {
            double f[3] = { _gmx_frame.f[i][XX],  _gmx_frame.f[i][YY], _gmx_frame.f[i][ZZ] };
            conf.getBead(i)->setF(f);
        }
    }
    return true;
}
<commit_msg>reader with dev version still doesn't work<commit_after>/*
 * Copyright 2009 The VOTCA Development Team (http://www.votca.org)
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */

#include <iostream>
#include "topology.h"
#include "gmxtrajectoryreader.h"

using namespace std;

bool GMXTrajectoryReader::Open(const string &file)
{
       _filename = file;
       return true;
}

void GMXTrajectoryReader::Close()
{
    gmx::close_trx(_gmx_status);
}

bool GMXTrajectoryReader::FirstFrame(Topology &conf)
{
#ifdef GMX4DEV
    gmx::output_env_t oenv;
    char name[10] = "noname";
    char *argv[1]; argv[0]=name;
    output_env_init(oenv,  1, argv,
                     gmx::time_ps, false, gmx::exvgXMGRACE,
                     0, 0);

    if(!gmx::read_first_frame(oenv, &_gmx_status,(char*)_filename.c_str(),&_gmx_frame,TRX_READ_X | TRX_READ_F))
        throw std::runtime_error(string("cannot open ") + _filename);
#else
    if(!gmx::read_first_frame(&_gmx_status,(char*)_filename.c_str(),&_gmx_frame,TRX_READ_X | TRX_READ_F))
        throw std::runtime_error(string("cannot open ") + _filename);
#endif

    matrix m;
    for(int i=0; i<3; i++)
        for(int j=0; j<3; j++)
            m[i][j] = _gmx_frame.box[j][i];
    conf.setBox(m);
    conf.setTime(_gmx_frame.time);
    conf.setStep(_gmx_frame.step);
    cout << endl;

    if(_gmx_frame.natoms != conf.Beads().size())
        throw std::runtime_error("number of beads in trajectory do not match topology");

    //conf.HasPos(true);
    //conf.HasF(_gmx_frame.bF);

    for(int i=0; i<_gmx_frame.natoms; i++) {
        double r[3] = { _gmx_frame.x[i][XX],  _gmx_frame.x[i][YY], _gmx_frame.x[i][ZZ] };
        conf.getBead(i)->setPos(r);
        if(_gmx_frame.bF) {
            double f[3] = { _gmx_frame.f[i][XX],  _gmx_frame.f[i][YY], _gmx_frame.f[i][ZZ] };
            conf.getBead(i)->setF(f);
        }
    }
    return true;
}

bool GMXTrajectoryReader::NextFrame(Topology &conf)
{
#ifdef GMX4DEV
    gmx::output_env_t oenv;
    char name[10] = "noname";
    char *argv[2]; argv[0]=name;
    output_env_init(oenv,  1, argv,
                     gmx::time_ps, false, gmx::exvgXMGRACE,
                     0, 0);
    if(!gmx::read_next_frame(oenv, _gmx_status,&_gmx_frame))
        return false;
#else
    if(!gmx::read_next_frame(_gmx_status,&_gmx_frame))
        return false;
#endif

    matrix m;
    for(int i=0; i<3; i++)
        for(int j=0; j<3; j++)
            m[i][j] = _gmx_frame.box[j][i];
    conf.setTime(_gmx_frame.time);
    conf.setStep(_gmx_frame.step);
    conf.setBox(m);

    //conf.HasF(_gmx_frame.bF);

    for(int i=0; i<_gmx_frame.natoms; i++) {
        double r[3] = { _gmx_frame.x[i][XX],  _gmx_frame.x[i][YY], _gmx_frame.x[i][ZZ] };
        conf.getBead(i)->setPos(r);
        if(_gmx_frame.bF) {
            double f[3] = { _gmx_frame.f[i][XX],  _gmx_frame.f[i][YY], _gmx_frame.f[i][ZZ] };
            conf.getBead(i)->setF(f);
        }
    }
    return true;
}
<|endoftext|>


<commit_before>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "kudu/rpc/serialization.h"

#include <ostream>
#include <string>

#include <gflags/gflags_declare.h>
#include <glog/logging.h>
#include <google/protobuf/message_lite.h>
#include <google/protobuf/io/coded_stream.h>

#include "kudu/gutil/endian.h"
#include "kudu/gutil/port.h"
#include "kudu/gutil/stringprintf.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/rpc/constants.h"
#include "kudu/util/faststring.h"
#include "kudu/util/logging.h"
#include "kudu/util/slice.h"
#include "kudu/util/status.h"

DECLARE_int32(rpc_max_message_size);

using google::protobuf::MessageLite;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::CodedOutputStream;
using strings::Substitute;

namespace kudu {
namespace rpc {
namespace serialization {

enum {
  kHeaderPosVersion = 0,
  kHeaderPosServiceClass = 1,
  kHeaderPosAuthProto = 2
};

void SerializeMessage(const MessageLite& message, faststring* param_buf,
                        int additional_size, bool use_cached_size) {
  int pb_size = use_cached_size ? message.GetCachedSize() : message.ByteSize();
  DCHECK_EQ(message.ByteSize(), pb_size);
  int recorded_size = pb_size + additional_size;
  int size_with_delim = pb_size + CodedOutputStream::VarintSize32(recorded_size);
  int total_size = size_with_delim + additional_size;

  if (total_size > FLAGS_rpc_max_message_size) {
    LOG(WARNING) << Substitute("Serialized $0 ($1 bytes) is larger than the maximum configured "
                               "RPC message size ($2 bytes). "
                               "Sending anyway, but peer may reject the data.",
                               message.GetTypeName(), total_size, FLAGS_rpc_max_message_size);
  }

  param_buf->resize(size_with_delim);
  uint8_t* dst = param_buf->data();
  dst = CodedOutputStream::WriteVarint32ToArray(recorded_size, dst);
  dst = message.SerializeWithCachedSizesToArray(dst);
  CHECK_EQ(dst, param_buf->data() + size_with_delim);
}

void SerializeHeader(const MessageLite& header,
                     size_t param_len,
                     faststring* header_buf) {

  CHECK(header.IsInitialized())
      << "RPC header missing fields: " << header.InitializationErrorString();

  // Compute all the lengths for the packet.
  size_t header_pb_len = header.ByteSize();
  size_t header_tot_len = kMsgLengthPrefixLength        // Int prefix for the total length.
      + CodedOutputStream::VarintSize32(header_pb_len)  // Varint delimiter for header PB.
      + header_pb_len;                                  // Length for the header PB itself.
  size_t total_size = header_tot_len + param_len;

  header_buf->resize(header_tot_len);
  uint8_t* dst = header_buf->data();

  // 1. The length for the whole request, not including the 4-byte
  // length prefix.
  NetworkByteOrder::Store32(dst, total_size - kMsgLengthPrefixLength);
  dst += sizeof(uint32_t);

  // 2. The varint-prefixed RequestHeader PB
  dst = CodedOutputStream::WriteVarint32ToArray(header_pb_len, dst);
  dst = header.SerializeWithCachedSizesToArray(dst);

  // We should have used the whole buffer we allocated.
  CHECK_EQ(dst, header_buf->data() + header_tot_len);
}

Status ParseMessage(const Slice& buf,
                    MessageLite* parsed_header,
                    Slice* parsed_main_message) {

  // First grab the total length
  if (PREDICT_FALSE(buf.size() < kMsgLengthPrefixLength)) {
    return Status::Corruption("Invalid packet: not enough bytes for length header",
                              KUDU_REDACT(buf.ToDebugString()));
  }

  int total_len = NetworkByteOrder::Load32(buf.data());
  DCHECK_EQ(total_len + kMsgLengthPrefixLength, buf.size())
    << "Got mis-sized buffer: " << KUDU_REDACT(buf.ToDebugString());

  CodedInputStream in(buf.data(), buf.size());
  in.Skip(kMsgLengthPrefixLength);

  uint32_t header_len;
  if (PREDICT_FALSE(!in.ReadVarint32(&header_len))) {
    return Status::Corruption("Invalid packet: missing header delimiter",
                              KUDU_REDACT(buf.ToDebugString()));
  }

  CodedInputStream::Limit l;
  l = in.PushLimit(header_len);
  if (PREDICT_FALSE(!parsed_header->ParseFromCodedStream(&in))) {
    return Status::Corruption("Invalid packet: header too short",
                              KUDU_REDACT(buf.ToDebugString()));
  }
  in.PopLimit(l);

  uint32_t main_msg_len;
  if (PREDICT_FALSE(!in.ReadVarint32(&main_msg_len))) {
    return Status::Corruption("Invalid packet: missing main msg length",
                              KUDU_REDACT(buf.ToDebugString()));
  }

  if (PREDICT_FALSE(!in.Skip(main_msg_len))) {
    return Status::Corruption(
        StringPrintf("Invalid packet: data too short, expected %d byte main_msg", main_msg_len),
        KUDU_REDACT(buf.ToDebugString()));
  }

  if (PREDICT_FALSE(in.BytesUntilLimit() > 0)) {
    return Status::Corruption(
      StringPrintf("Invalid packet: %d extra bytes at end of packet", in.BytesUntilLimit()),
      KUDU_REDACT(buf.ToDebugString()));
  }

  *parsed_main_message = Slice(buf.data() + buf.size() - main_msg_len,
                              main_msg_len);
  return Status::OK();
}

void SerializeConnHeader(uint8_t* buf) {
  memcpy(reinterpret_cast<char *>(buf), kMagicNumber, kMagicNumberLength);
  buf += kMagicNumberLength;
  buf[kHeaderPosVersion] = kCurrentRpcVersion;
  buf[kHeaderPosServiceClass] = 0; // TODO: implement
  buf[kHeaderPosAuthProto] = 0; // TODO: implement
}

// validate the entire rpc header (magic number + flags)
Status ValidateConnHeader(const Slice& slice) {
  DCHECK_EQ(kMagicNumberLength + kHeaderFlagsLength, slice.size())
    << "Invalid RPC header length";

  // validate actual magic
  if (!slice.starts_with(kMagicNumber)) {
    if (slice.starts_with("GET ") ||
        slice.starts_with("POST") ||
        slice.starts_with("HEAD")) {
      return Status::InvalidArgument("invalid negotation, appears to be an HTTP client on "
                                     "the RPC port");
    }
    return Status::InvalidArgument("connection must begin with magic number", kMagicNumber);
  }

  const uint8_t *data = slice.data();
  data += kMagicNumberLength;

  // validate version
  if (data[kHeaderPosVersion] != kCurrentRpcVersion) {
    return Status::InvalidArgument("Unsupported RPC version",
        StringPrintf("Received: %d, Supported: %d",
            data[kHeaderPosVersion], kCurrentRpcVersion));
  }

  // TODO: validate additional header flags:
  // RPC_SERVICE_CLASS
  // RPC_AUTH_PROTOCOL

  return Status::OK();
}

} // namespace serialization
} // namespace rpc
} // namespace kudu
<commit_msg>KUDU-2296: Fix deserialization of messages larger than 64MB<commit_after>// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements.  See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership.  The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License.  You may obtain a copy of the License at
//
//   http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied.  See the License for the
// specific language governing permissions and limitations
// under the License.

#include "kudu/rpc/serialization.h"

#include <ostream>
#include <string>

#include <gflags/gflags_declare.h>
#include <glog/logging.h>
#include <google/protobuf/message_lite.h>
#include <google/protobuf/io/coded_stream.h>

#include "kudu/gutil/endian.h"
#include "kudu/gutil/port.h"
#include "kudu/gutil/stringprintf.h"
#include "kudu/gutil/strings/substitute.h"
#include "kudu/rpc/constants.h"
#include "kudu/util/faststring.h"
#include "kudu/util/logging.h"
#include "kudu/util/slice.h"
#include "kudu/util/status.h"

DECLARE_int32(rpc_max_message_size);

using google::protobuf::MessageLite;
using google::protobuf::io::CodedInputStream;
using google::protobuf::io::CodedOutputStream;
using strings::Substitute;

namespace kudu {
namespace rpc {
namespace serialization {

enum {
  kHeaderPosVersion = 0,
  kHeaderPosServiceClass = 1,
  kHeaderPosAuthProto = 2
};

void SerializeMessage(const MessageLite& message, faststring* param_buf,
                        int additional_size, bool use_cached_size) {
  int pb_size = use_cached_size ? message.GetCachedSize() : message.ByteSize();
  DCHECK_EQ(message.ByteSize(), pb_size);
  int recorded_size = pb_size + additional_size;
  int size_with_delim = pb_size + CodedOutputStream::VarintSize32(recorded_size);
  int total_size = size_with_delim + additional_size;

  if (total_size > FLAGS_rpc_max_message_size) {
    LOG(WARNING) << Substitute("Serialized $0 ($1 bytes) is larger than the maximum configured "
                               "RPC message size ($2 bytes). "
                               "Sending anyway, but peer may reject the data.",
                               message.GetTypeName(), total_size, FLAGS_rpc_max_message_size);
  }

  param_buf->resize(size_with_delim);
  uint8_t* dst = param_buf->data();
  dst = CodedOutputStream::WriteVarint32ToArray(recorded_size, dst);
  dst = message.SerializeWithCachedSizesToArray(dst);
  CHECK_EQ(dst, param_buf->data() + size_with_delim);
}

void SerializeHeader(const MessageLite& header,
                     size_t param_len,
                     faststring* header_buf) {

  CHECK(header.IsInitialized())
      << "RPC header missing fields: " << header.InitializationErrorString();

  // Compute all the lengths for the packet.
  size_t header_pb_len = header.ByteSize();
  size_t header_tot_len = kMsgLengthPrefixLength        // Int prefix for the total length.
      + CodedOutputStream::VarintSize32(header_pb_len)  // Varint delimiter for header PB.
      + header_pb_len;                                  // Length for the header PB itself.
  size_t total_size = header_tot_len + param_len;

  header_buf->resize(header_tot_len);
  uint8_t* dst = header_buf->data();

  // 1. The length for the whole request, not including the 4-byte
  // length prefix.
  NetworkByteOrder::Store32(dst, total_size - kMsgLengthPrefixLength);
  dst += sizeof(uint32_t);

  // 2. The varint-prefixed RequestHeader PB
  dst = CodedOutputStream::WriteVarint32ToArray(header_pb_len, dst);
  dst = header.SerializeWithCachedSizesToArray(dst);

  // We should have used the whole buffer we allocated.
  CHECK_EQ(dst, header_buf->data() + header_tot_len);
}

Status ParseMessage(const Slice& buf,
                    MessageLite* parsed_header,
                    Slice* parsed_main_message) {

  // First grab the total length
  if (PREDICT_FALSE(buf.size() < kMsgLengthPrefixLength)) {
    return Status::Corruption("Invalid packet: not enough bytes for length header",
                              KUDU_REDACT(buf.ToDebugString()));
  }

  int total_len = NetworkByteOrder::Load32(buf.data());
  DCHECK_EQ(total_len + kMsgLengthPrefixLength, buf.size())
    << "Got mis-sized buffer: " << KUDU_REDACT(buf.ToDebugString());

  CodedInputStream in(buf.data(), buf.size());
  // Protobuf enforces a 64MB total bytes limit on CodedInputStream by default.
  // Override this default with the actual size of the buffer to allow messages
  // larger than 64MB.
  in.SetTotalBytesLimit(buf.size(), -1);
  in.Skip(kMsgLengthPrefixLength);

  uint32_t header_len;
  if (PREDICT_FALSE(!in.ReadVarint32(&header_len))) {
    return Status::Corruption("Invalid packet: missing header delimiter",
                              KUDU_REDACT(buf.ToDebugString()));
  }

  CodedInputStream::Limit l;
  l = in.PushLimit(header_len);
  if (PREDICT_FALSE(!parsed_header->ParseFromCodedStream(&in))) {
    return Status::Corruption("Invalid packet: header too short",
                              KUDU_REDACT(buf.ToDebugString()));
  }
  in.PopLimit(l);

  uint32_t main_msg_len;
  if (PREDICT_FALSE(!in.ReadVarint32(&main_msg_len))) {
    return Status::Corruption("Invalid packet: missing main msg length",
                              KUDU_REDACT(buf.ToDebugString()));
  }

  if (PREDICT_FALSE(!in.Skip(main_msg_len))) {
    return Status::Corruption(
        StringPrintf("Invalid packet: data too short, expected %d byte main_msg", main_msg_len),
        KUDU_REDACT(buf.ToDebugString()));
  }

  if (PREDICT_FALSE(in.BytesUntilLimit() > 0)) {
    return Status::Corruption(
      StringPrintf("Invalid packet: %d extra bytes at end of packet", in.BytesUntilLimit()),
      KUDU_REDACT(buf.ToDebugString()));
  }

  *parsed_main_message = Slice(buf.data() + buf.size() - main_msg_len,
                              main_msg_len);
  return Status::OK();
}

void SerializeConnHeader(uint8_t* buf) {
  memcpy(reinterpret_cast<char *>(buf), kMagicNumber, kMagicNumberLength);
  buf += kMagicNumberLength;
  buf[kHeaderPosVersion] = kCurrentRpcVersion;
  buf[kHeaderPosServiceClass] = 0; // TODO: implement
  buf[kHeaderPosAuthProto] = 0; // TODO: implement
}

// validate the entire rpc header (magic number + flags)
Status ValidateConnHeader(const Slice& slice) {
  DCHECK_EQ(kMagicNumberLength + kHeaderFlagsLength, slice.size())
    << "Invalid RPC header length";

  // validate actual magic
  if (!slice.starts_with(kMagicNumber)) {
    if (slice.starts_with("GET ") ||
        slice.starts_with("POST") ||
        slice.starts_with("HEAD")) {
      return Status::InvalidArgument("invalid negotation, appears to be an HTTP client on "
                                     "the RPC port");
    }
    return Status::InvalidArgument("connection must begin with magic number", kMagicNumber);
  }

  const uint8_t *data = slice.data();
  data += kMagicNumberLength;

  // validate version
  if (data[kHeaderPosVersion] != kCurrentRpcVersion) {
    return Status::InvalidArgument("Unsupported RPC version",
        StringPrintf("Received: %d, Supported: %d",
            data[kHeaderPosVersion], kCurrentRpcVersion));
  }

  // TODO: validate additional header flags:
  // RPC_SERVICE_CLASS
  // RPC_AUTH_PROTOCOL

  return Status::OK();
}

} // namespace serialization
} // namespace rpc
} // namespace kudu
<|endoftext|>


<commit_before>// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
//
// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// 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
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//

#include "Launcher_Job_Writer.hxx"
#include "Launcher_Job.hxx"

void Launcher::addToXmlDocument(xmlNodePtr root_node, Launcher::Job* job)
{
  // Begin job
  xmlNodePtr job_node = xmlNewChild(root_node, NULL, xmlCharStrdup("job"), NULL);
  xmlNewProp(job_node, xmlCharStrdup("type"), xmlCharStrdup(job->getJobType().c_str()));
  xmlNewProp(job_node, xmlCharStrdup("name"), xmlCharStrdup(job->getJobName().c_str()));

  // Add user part
  xmlNodePtr node = xmlNewChild(job_node, NULL, xmlCharStrdup("user_part"), NULL);

  xmlNewChild(node, NULL, xmlCharStrdup("job_file"),         xmlCharStrdup(job->getJobFile().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("env_file"),         xmlCharStrdup(job->getEnvFile().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("work_directory"),   xmlCharStrdup(job->getWorkDirectory().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("local_directory"),  xmlCharStrdup(job->getLocalDirectory().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("result_directory"), xmlCharStrdup(job->getResultDirectory().c_str()));

  // Parameters for COORM
  xmlNewChild(node, NULL, xmlCharStrdup("launcher_file"), xmlCharStrdup(job->getLauncherFile().c_str()));

  // Files
  xmlNodePtr files_node = xmlNewChild(node, NULL, xmlCharStrdup("files"), NULL);
  std::list<std::string> in_files  = job->get_in_files();
  std::list<std::string> out_files = job->get_out_files();
  for(std::list<std::string>::iterator it = in_files.begin(); it != in_files.end(); it++)
    xmlNewChild(files_node, NULL, xmlCharStrdup("in_file"), xmlCharStrdup((*it).c_str()));
  for(std::list<std::string>::iterator it = out_files.begin(); it != out_files.end(); it++)
    xmlNewChild(files_node, NULL, xmlCharStrdup("out_file"), xmlCharStrdup((*it).c_str()));

  // Resource part
  resourceParams resource_params = job->getResourceRequiredParams();
  xmlNodePtr res_node = xmlNewChild(node, NULL, xmlCharStrdup("resource_params"), NULL);
  xmlNewChild(res_node, NULL, xmlCharStrdup("name"),   xmlCharStrdup(resource_params.name.c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("hostname"),   xmlCharStrdup(resource_params.hostname.c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("OS"),   xmlCharStrdup(resource_params.OS.c_str()));
  std::ostringstream nb_proc_stream;
  std::ostringstream nb_node_stream;
  std::ostringstream nb_proc_per_node_stream;
  std::ostringstream cpu_clock_stream;
  std::ostringstream mem_mb_stream;
  nb_proc_stream << resource_params.nb_proc;
  nb_node_stream << resource_params.nb_node;
  nb_proc_per_node_stream << resource_params.nb_proc_per_node;
  cpu_clock_stream << resource_params.cpu_clock;
  mem_mb_stream << resource_params.mem_mb;
  xmlNewChild(res_node, NULL, xmlCharStrdup("nb_proc"),            xmlCharStrdup(nb_proc_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("nb_node"),            xmlCharStrdup(nb_node_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("nb_proc_per_node"),   xmlCharStrdup(nb_proc_per_node_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("cpu_clock"),          xmlCharStrdup(cpu_clock_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("mem_mb"),             xmlCharStrdup(mem_mb_stream.str().c_str()));

  xmlNewChild(node, NULL, xmlCharStrdup("maximum_duration"), xmlCharStrdup(job->getMaximumDuration().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("queue"),            xmlCharStrdup(job->getQueue().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("exclusive"),        xmlCharStrdup(job->getExclusiveStr().c_str()));
  std::ostringstream mem_per_cpu_stream;
  mem_per_cpu_stream << job->getMemPerCpu();
  xmlNewChild(node, NULL, xmlCharStrdup("mem_per_cpu"),      xmlCharStrdup(mem_per_cpu_stream.str().c_str()));

  // For COORM
  xmlNewChild(node, NULL, xmlCharStrdup("launcher_args"), xmlCharStrdup(job->getLauncherArgs().c_str()));

  // Specific parameters part
  xmlNodePtr specific_parameters_node = xmlNewChild(node, NULL, xmlCharStrdup("specific_parameters"), NULL);
  std::map<std::string, std::string> specific_parameters = job->getSpecificParameters();
  for(std::map<std::string, std::string>::iterator it = specific_parameters.begin(); it != specific_parameters.end(); it++)
  {
    xmlNodePtr specific_parameter_node = xmlNewChild(specific_parameters_node, NULL, xmlCharStrdup("specific_parameter"), NULL);
    xmlNewChild(specific_parameter_node, NULL, xmlCharStrdup("name"), xmlCharStrdup((it->first).c_str()));
    xmlNewChild(specific_parameter_node, NULL, xmlCharStrdup("value"), xmlCharStrdup((it->second).c_str()));
  }

  // Run part
  xmlNodePtr run_node = xmlNewChild(job_node, NULL, xmlCharStrdup("run_part"), NULL);
  xmlNewChild(run_node, NULL, xmlCharStrdup("job_state"), xmlCharStrdup(job->getState().c_str()));
  ParserResourcesType resource_definition = job->getResourceDefinition();
  xmlNewChild(run_node, NULL, xmlCharStrdup("resource_choosed_name"), xmlCharStrdup(resource_definition.Name.c_str()));

#ifdef WITH_LIBBATCH
  Batch::JobId job_id = getBatchManagerJobId();
  xmlNewChild(run_node, NULL, xmlCharStrdup("job_reference"), xmlCharStrdup(job_id.getReference().c_str()));
#endif
}

<commit_msg>Fix compilation pb caused by previous integration<commit_after>// Copyright (C) 2007-2013  CEA/DEN, EDF R&D, OPEN CASCADE
//
// Copyright (C) 2003-2007  OPEN CASCADE, EADS/CCR, LIP6, CEA/DEN,
// CEDRAT, EDF R&D, LEG, PRINCIPIA R&D, BUREAU VERITAS
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License.
//
// 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
//
// See http://www.salome-platform.org/ or email : webmaster.salome@opencascade.com
//

#include "Launcher_Job_Writer.hxx"
#include "Launcher_Job.hxx"

void Launcher::addToXmlDocument(xmlNodePtr root_node, Launcher::Job* job)
{
  // Begin job
  xmlNodePtr job_node = xmlNewChild(root_node, NULL, xmlCharStrdup("job"), NULL);
  xmlNewProp(job_node, xmlCharStrdup("type"), xmlCharStrdup(job->getJobType().c_str()));
  xmlNewProp(job_node, xmlCharStrdup("name"), xmlCharStrdup(job->getJobName().c_str()));

  // Add user part
  xmlNodePtr node = xmlNewChild(job_node, NULL, xmlCharStrdup("user_part"), NULL);

  xmlNewChild(node, NULL, xmlCharStrdup("job_file"),         xmlCharStrdup(job->getJobFile().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("env_file"),         xmlCharStrdup(job->getEnvFile().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("work_directory"),   xmlCharStrdup(job->getWorkDirectory().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("local_directory"),  xmlCharStrdup(job->getLocalDirectory().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("result_directory"), xmlCharStrdup(job->getResultDirectory().c_str()));

  // Parameters for COORM
  xmlNewChild(node, NULL, xmlCharStrdup("launcher_file"), xmlCharStrdup(job->getLauncherFile().c_str()));

  // Files
  xmlNodePtr files_node = xmlNewChild(node, NULL, xmlCharStrdup("files"), NULL);
  std::list<std::string> in_files  = job->get_in_files();
  std::list<std::string> out_files = job->get_out_files();
  for(std::list<std::string>::iterator it = in_files.begin(); it != in_files.end(); it++)
    xmlNewChild(files_node, NULL, xmlCharStrdup("in_file"), xmlCharStrdup((*it).c_str()));
  for(std::list<std::string>::iterator it = out_files.begin(); it != out_files.end(); it++)
    xmlNewChild(files_node, NULL, xmlCharStrdup("out_file"), xmlCharStrdup((*it).c_str()));

  // Resource part
  resourceParams resource_params = job->getResourceRequiredParams();
  xmlNodePtr res_node = xmlNewChild(node, NULL, xmlCharStrdup("resource_params"), NULL);
  xmlNewChild(res_node, NULL, xmlCharStrdup("name"),   xmlCharStrdup(resource_params.name.c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("hostname"),   xmlCharStrdup(resource_params.hostname.c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("OS"),   xmlCharStrdup(resource_params.OS.c_str()));
  std::ostringstream nb_proc_stream;
  std::ostringstream nb_node_stream;
  std::ostringstream nb_proc_per_node_stream;
  std::ostringstream cpu_clock_stream;
  std::ostringstream mem_mb_stream;
  nb_proc_stream << resource_params.nb_proc;
  nb_node_stream << resource_params.nb_node;
  nb_proc_per_node_stream << resource_params.nb_proc_per_node;
  cpu_clock_stream << resource_params.cpu_clock;
  mem_mb_stream << resource_params.mem_mb;
  xmlNewChild(res_node, NULL, xmlCharStrdup("nb_proc"),            xmlCharStrdup(nb_proc_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("nb_node"),            xmlCharStrdup(nb_node_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("nb_proc_per_node"),   xmlCharStrdup(nb_proc_per_node_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("cpu_clock"),          xmlCharStrdup(cpu_clock_stream.str().c_str()));
  xmlNewChild(res_node, NULL, xmlCharStrdup("mem_mb"),             xmlCharStrdup(mem_mb_stream.str().c_str()));

  xmlNewChild(node, NULL, xmlCharStrdup("maximum_duration"), xmlCharStrdup(job->getMaximumDuration().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("queue"),            xmlCharStrdup(job->getQueue().c_str()));
  xmlNewChild(node, NULL, xmlCharStrdup("exclusive"),        xmlCharStrdup(job->getExclusiveStr().c_str()));
  std::ostringstream mem_per_cpu_stream;
  mem_per_cpu_stream << job->getMemPerCpu();
  xmlNewChild(node, NULL, xmlCharStrdup("mem_per_cpu"),      xmlCharStrdup(mem_per_cpu_stream.str().c_str()));

  // For COORM
  xmlNewChild(node, NULL, xmlCharStrdup("launcher_args"), xmlCharStrdup(job->getLauncherArgs().c_str()));

  // Specific parameters part
  xmlNodePtr specific_parameters_node = xmlNewChild(node, NULL, xmlCharStrdup("specific_parameters"), NULL);
  std::map<std::string, std::string> specific_parameters = job->getSpecificParameters();
  for(std::map<std::string, std::string>::iterator it = specific_parameters.begin(); it != specific_parameters.end(); it++)
  {
    xmlNodePtr specific_parameter_node = xmlNewChild(specific_parameters_node, NULL, xmlCharStrdup("specific_parameter"), NULL);
    xmlNewChild(specific_parameter_node, NULL, xmlCharStrdup("name"), xmlCharStrdup((it->first).c_str()));
    xmlNewChild(specific_parameter_node, NULL, xmlCharStrdup("value"), xmlCharStrdup((it->second).c_str()));
  }

  // Run part
  xmlNodePtr run_node = xmlNewChild(job_node, NULL, xmlCharStrdup("run_part"), NULL);
  xmlNewChild(run_node, NULL, xmlCharStrdup("job_state"), xmlCharStrdup(job->getState().c_str()));
  ParserResourcesType resource_definition = job->getResourceDefinition();
  xmlNewChild(run_node, NULL, xmlCharStrdup("resource_choosed_name"), xmlCharStrdup(resource_definition.Name.c_str()));

#ifdef WITH_LIBBATCH
  Batch::JobId job_id = job->getBatchManagerJobId();
  xmlNewChild(run_node, NULL, xmlCharStrdup("job_reference"), xmlCharStrdup(job_id.getReference().c_str()));
#endif
}

<|endoftext|>


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

  Program:   Visualization Toolkit
  Module:    vtkMySQLQuery.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

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

#include "vtkMySQLQuery.h"
#include "vtkMySQLDatabase.h"
#include "vtkMySQLDatabasePrivate.h"
#include <vtkObjectFactory.h>
#include <vtkStringArray.h>
#include <vtkVariant.h>
#include <vtkVariantArray.h>

#include <mysql.h>

#include <assert.h>

#include <vtksys/ios/sstream>

// ----------------------------------------------------------------------

class vtkMySQLQueryInternals
{
public:
  vtkMySQLQueryInternals()
    {
      this->Result = NULL;
      this->CurrentRow = NULL;
    }

  ~vtkMySQLQueryInternals()
    {
      this->FreeResult();
    }

  void FreeResult()
    {
      if (this->Result)
        {
        mysql_free_result(this->Result);
        this->Result = NULL;
        }
    }

public:
  MYSQL_RES *Result;
  MYSQL_ROW CurrentRow;
};

// ----------------------------------------------------------------------

vtkCxxRevisionMacro(vtkMySQLQuery, "1.3");
vtkStandardNewMacro(vtkMySQLQuery);

// ----------------------------------------------------------------------

vtkMySQLQuery::vtkMySQLQuery()
{
  this->Internals = new vtkMySQLQueryInternals;
  this->InitialFetch = true;
  this->LastErrorText = NULL;
}

// ----------------------------------------------------------------------

vtkMySQLQuery::~vtkMySQLQuery()
{
  this->SetLastErrorText(NULL);
  delete this->Internals;
}

// ----------------------------------------------------------------------

void
vtkMySQLQuery::PrintSelf(ostream  &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}

// ----------------------------------------------------------------------
bool
vtkMySQLQuery::Execute()
{
  this->Active = false;

  if (this->Query == NULL)
    {
    vtkErrorMacro(<<"Cannot execute before a query has been set.");
    return false;
    }

  this->Internals->FreeResult();

  vtkMySQLDatabase *dbContainer =
    static_cast<vtkMySQLDatabase *>(this->Database);
  assert(dbContainer != NULL);

  if (!dbContainer->IsOpen())
    {
    vtkErrorMacro(<<"Cannot execute query.  Database is closed.");
    return false;
    }

  MYSQL *db = dbContainer->Private->Connection;
  assert(db != NULL);

  vtkDebugMacro(<<"Execute(): Query ready to execute.");

  int result = mysql_query(db, this->Query);
  if (result == 0)
    {
    // The query succeeded.
    this->SetLastErrorText(NULL);
    this->Active = true;
    this->Internals->Result = mysql_store_result(db);
    return true;
    }
  else
    {
    this->Active = false;
    this->SetLastErrorText(mysql_error(db));
    vtkErrorMacro(<<"Query returned an error: "
                  << this->GetLastErrorText());
    return false;
    }
}

// ----------------------------------------------------------------------

int
vtkMySQLQuery::GetNumberOfFields()
{
  if (! this->Active)
    {
    vtkErrorMacro(<<"GetNumberOfFields(): Query is not active!");
    return 0;
    }
  else
    {
    return static_cast<int>(mysql_num_fields(this->Internals->Result));
    }
}

// ----------------------------------------------------------------------

const char *
vtkMySQLQuery::GetFieldName(int column)
{
  if (! this->Active)
    {
    vtkErrorMacro(<<"GetFieldName(): Query is not active!");
    return NULL;
    }
  else if (column < 0 || column >= this->GetNumberOfFields())
    {
    vtkErrorMacro(<<"GetFieldName(): Illegal field index "
                  << column);
    return NULL;
    }
  else
    {
    MYSQL_FIELD *field = mysql_fetch_field_direct(this->Internals->Result, column);
    if (field == NULL)
      {
      vtkErrorMacro(<<"GetFieldName(): MySQL returned null field for column "
                    << column );
      return NULL;
      }
    else
      {
      return field->name;
      }
    }
}

// ----------------------------------------------------------------------

int
vtkMySQLQuery::GetFieldType(int column)
{
  if (! this->Active)
    {
    vtkErrorMacro(<<"GetFieldType(): Query is not active!");
    return VTK_VOID;
    }
  else if (column < 0 || column >= this->GetNumberOfFields())
    {
    vtkErrorMacro(<<"GetFieldType(): Illegal field index "
                  << column);
    return VTK_VOID;
    }
  else
    {
    vtkMySQLDatabase *dbContainer =
      static_cast<vtkMySQLDatabase *>(this->Database);
    assert(dbContainer != NULL);
    if (!dbContainer->IsOpen())
      {
      vtkErrorMacro(<<"Cannot get field type.  Database is closed.");
      return VTK_VOID;
      }

    MYSQL *db = dbContainer->Private->Connection;
    assert(db != NULL);

    MYSQL_FIELD *field = mysql_fetch_field_direct(this->Internals->Result,
                                                  column);
    if (field == NULL)
      {
      vtkErrorMacro(<<"GetFieldType(): MySQL returned null field for column "
                    << column );
      return -1;
      }
    else
      {

      switch (field->type)
      {
      case MYSQL_TYPE_DECIMAL:
      case MYSQL_TYPE_ENUM:
      case MYSQL_TYPE_TINY:
      case MYSQL_TYPE_INT24:
#if MYSQL_VERSION >= 50000
      case MYSQL_TYPE_NEWDECIMAL:
#endif
        return VTK_INT;

      case MYSQL_TYPE_SHORT:
        return VTK_SHORT;

      case MYSQL_TYPE_LONG:
        return VTK_LONG;

      case MYSQL_TYPE_TIMESTAMP:
      case MYSQL_TYPE_LONGLONG:
      case MYSQL_TYPE_DATE:
      case MYSQL_TYPE_TIME:
      case MYSQL_TYPE_DATETIME:
      case MYSQL_TYPE_YEAR:
      case MYSQL_TYPE_NEWDATE:
        return VTK_UNSIGNED_LONG;  // vtkTimePoint uses longs

#if MYSQL_VERSION >= 50000
      case MYSQL_TYPE_BIT:
        return VTK_BIT;
#endif

      case MYSQL_TYPE_FLOAT:
        return VTK_FLOAT;

      case MYSQL_TYPE_DOUBLE:
        return VTK_DOUBLE;

      case MYSQL_TYPE_NULL:
        return VTK_VOID;

      case MYSQL_TYPE_TINY_BLOB:
      case MYSQL_TYPE_MEDIUM_BLOB:
      case MYSQL_TYPE_LONG_BLOB:
      case MYSQL_TYPE_BLOB:
        return VTK_STRING; // MySQL treats text fields as blobs

      case MYSQL_TYPE_STRING:
      case MYSQL_TYPE_VAR_STRING:
#if MYSQL_VERSION >= 50000
      case MYSQL_TYPE_VARCHAR:
#endif
        return VTK_STRING;

      case MYSQL_TYPE_SET:
      case MYSQL_TYPE_GEOMETRY:
      default:
      {
      vtkErrorMacro( <<"GetFieldType(): Unknown data type "
                     << field->type );
      return VTK_VOID;
      }
      }
      }
    }
}

// ----------------------------------------------------------------------

bool
vtkMySQLQuery::NextRow()
{
  if (! this->IsActive())
    {
    vtkErrorMacro(<<"NextRow(): Query is not active!");
    return false;
    }

  MYSQL_ROW row = mysql_fetch_row(this->Internals->Result);
  this->Internals->CurrentRow = row;

  if (!row)
    {
    // A null row will come back in one of two situations.  The first
    // is when there's an error, in which case mysql_errno will return
    // some nonzero error code.  The second is when there are no more
    // rows to fetch.  Discriminate between the two by checking the
    // errno.
    this->Active = false;
    vtkMySQLDatabase *dbContainer =
      static_cast<vtkMySQLDatabase *>(this->Database);
    assert(dbContainer != NULL);
    if (!dbContainer->IsOpen())
      {
      vtkErrorMacro(<<"Cannot get field type.  Database is closed.");
      this->SetLastErrorText("Database is closed.");
      return VTK_VOID;
      }
    MYSQL *db = dbContainer->Private->Connection;
    assert(db != NULL);

    if (mysql_errno(db) != 0)
      {
      this->SetLastErrorText(mysql_error(db));
      vtkErrorMacro(<<"NextRow(): MySQL returned error message "
                    << this->GetLastErrorText());
      }
    else
      {
      // Nothing's wrong.  We're just out of results.
      this->SetLastErrorText(NULL);
      }

    return false;
    }
  else
    {
    this->SetLastErrorText(NULL);
    return true;
    }
}

// ----------------------------------------------------------------------

vtkVariant
vtkMySQLQuery::DataValue(vtkIdType column)
{
  if (this->IsActive() == false)
    {
    vtkWarningMacro(<<"DataValue() called on inactive query");
    return vtkVariant();
    }
  else if (column < 0 || column >= this->GetNumberOfFields())
    {
    vtkWarningMacro(<<"DataValue() called with out-of-range column index "
                    << column);
    return vtkVariant();
    }
  else
    {
    assert(this->Internals->CurrentRow);
    vtkVariant result;

    // It would be a royal pain to try to convert the string to each
    // different value in turn.  Fortunately, there is already code in
    // vtkVariant to do exactly that using the C++ standard library
    // sstream.  We'll exploit that.
    vtkVariant base(this->Internals->CurrentRow[column]);

    switch (this->GetFieldType(column))
      {
      case VTK_INT:
      case VTK_SHORT:
      case VTK_BIT:
        return vtkVariant(base.ToInt());

      case VTK_LONG:
      case VTK_UNSIGNED_LONG:
        return vtkVariant(base.ToLong());

      case VTK_FLOAT:
        return vtkVariant(base.ToFloat());

      case VTK_DOUBLE:
        return vtkVariant(base.ToDouble());

      case VTK_STRING:
        return base; // it's already a string

      case VTK_VOID:
        return vtkVariant();

      default:
      {
      vtkWarningMacro(<<"Unhandled type " << this->GetFieldType(column)
                      <<" in DataValue().");
      return vtkVariant();
      }
      }
    }
}

// ----------------------------------------------------------------------

const char *
vtkMySQLQuery::GetLastErrorText()
{
  return this->LastErrorText;
}

// ----------------------------------------------------------------------

bool
vtkMySQLQuery::HasError()
{
  return (this->GetLastErrorText() != NULL);
}
<commit_msg>BUG: Handle NULL column values.<commit_after>/*=========================================================================

  Program:   Visualization Toolkit
  Module:    vtkMySQLQuery.cxx

  Copyright (c) Ken Martin, Will Schroeder, Bill Lorensen
  All rights reserved.
  See Copyright.txt or http://www.kitware.com/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notice for more information.

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

#include "vtkMySQLQuery.h"
#include "vtkMySQLDatabase.h"
#include "vtkMySQLDatabasePrivate.h"
#include <vtkObjectFactory.h>
#include <vtkStringArray.h>
#include <vtkVariant.h>
#include <vtkVariantArray.h>

#include <mysql.h>

#include <assert.h>

#include <vtksys/ios/sstream>

// ----------------------------------------------------------------------

class vtkMySQLQueryInternals
{
public:
  vtkMySQLQueryInternals()
    {
      this->Result = NULL;
      this->CurrentRow = NULL;
    }

  ~vtkMySQLQueryInternals()
    {
      this->FreeResult();
    }

  void FreeResult()
    {
      if (this->Result)
        {
        mysql_free_result(this->Result);
        this->Result = NULL;
        }
    }

public:
  MYSQL_RES *Result;
  MYSQL_ROW CurrentRow;
};

// ----------------------------------------------------------------------

vtkCxxRevisionMacro(vtkMySQLQuery, "1.4");
vtkStandardNewMacro(vtkMySQLQuery);

// ----------------------------------------------------------------------

vtkMySQLQuery::vtkMySQLQuery()
{
  this->Internals = new vtkMySQLQueryInternals;
  this->InitialFetch = true;
  this->LastErrorText = NULL;
}

// ----------------------------------------------------------------------

vtkMySQLQuery::~vtkMySQLQuery()
{
  this->SetLastErrorText(NULL);
  delete this->Internals;
}

// ----------------------------------------------------------------------

void
vtkMySQLQuery::PrintSelf(ostream  &os, vtkIndent indent)
{
  this->Superclass::PrintSelf(os, indent);
}

// ----------------------------------------------------------------------
bool
vtkMySQLQuery::Execute()
{
  this->Active = false;

  if (this->Query == NULL)
    {
    vtkErrorMacro(<<"Cannot execute before a query has been set.");
    return false;
    }

  this->Internals->FreeResult();

  vtkMySQLDatabase *dbContainer =
    static_cast<vtkMySQLDatabase *>(this->Database);
  assert(dbContainer != NULL);

  if (!dbContainer->IsOpen())
    {
    vtkErrorMacro(<<"Cannot execute query.  Database is closed.");
    return false;
    }

  MYSQL *db = dbContainer->Private->Connection;
  assert(db != NULL);

  vtkDebugMacro(<<"Execute(): Query ready to execute.");

  int result = mysql_query(db, this->Query);
  if (result == 0)
    {
    // The query succeeded.
    this->SetLastErrorText(NULL);
    this->Active = true;
    this->Internals->Result = mysql_store_result(db);
    return true;
    }
  else
    {
    this->Active = false;
    this->SetLastErrorText(mysql_error(db));
    vtkErrorMacro(<<"Query returned an error: "
                  << this->GetLastErrorText());
    return false;
    }
}

// ----------------------------------------------------------------------

int
vtkMySQLQuery::GetNumberOfFields()
{
  if (! this->Active)
    {
    vtkErrorMacro(<<"GetNumberOfFields(): Query is not active!");
    return 0;
    }
  else
    {
    return static_cast<int>(mysql_num_fields(this->Internals->Result));
    }
}

// ----------------------------------------------------------------------

const char *
vtkMySQLQuery::GetFieldName(int column)
{
  if (! this->Active)
    {
    vtkErrorMacro(<<"GetFieldName(): Query is not active!");
    return NULL;
    }
  else if (column < 0 || column >= this->GetNumberOfFields())
    {
    vtkErrorMacro(<<"GetFieldName(): Illegal field index "
                  << column);
    return NULL;
    }
  else
    {
    MYSQL_FIELD *field = mysql_fetch_field_direct(this->Internals->Result, column);
    if (field == NULL)
      {
      vtkErrorMacro(<<"GetFieldName(): MySQL returned null field for column "
                    << column );
      return NULL;
      }
    else
      {
      return field->name;
      }
    }
}

// ----------------------------------------------------------------------

int
vtkMySQLQuery::GetFieldType(int column)
{
  if (! this->Active)
    {
    vtkErrorMacro(<<"GetFieldType(): Query is not active!");
    return VTK_VOID;
    }
  else if (column < 0 || column >= this->GetNumberOfFields())
    {
    vtkErrorMacro(<<"GetFieldType(): Illegal field index "
                  << column);
    return VTK_VOID;
    }
  else
    {
    vtkMySQLDatabase *dbContainer =
      static_cast<vtkMySQLDatabase *>(this->Database);
    assert(dbContainer != NULL);
    if (!dbContainer->IsOpen())
      {
      vtkErrorMacro(<<"Cannot get field type.  Database is closed.");
      return VTK_VOID;
      }

    MYSQL *db = dbContainer->Private->Connection;
    assert(db != NULL);

    MYSQL_FIELD *field = mysql_fetch_field_direct(this->Internals->Result,
                                                  column);
    if (field == NULL)
      {
      vtkErrorMacro(<<"GetFieldType(): MySQL returned null field for column "
                    << column );
      return -1;
      }
    else
      {

      switch (field->type)
      {
      case MYSQL_TYPE_DECIMAL:
      case MYSQL_TYPE_ENUM:
      case MYSQL_TYPE_TINY:
      case MYSQL_TYPE_INT24:
#if MYSQL_VERSION >= 50000
      case MYSQL_TYPE_NEWDECIMAL:
#endif
        return VTK_INT;

      case MYSQL_TYPE_SHORT:
        return VTK_SHORT;

      case MYSQL_TYPE_LONG:
        return VTK_LONG;

      case MYSQL_TYPE_TIMESTAMP:
      case MYSQL_TYPE_LONGLONG:
      case MYSQL_TYPE_DATE:
      case MYSQL_TYPE_TIME:
      case MYSQL_TYPE_DATETIME:
      case MYSQL_TYPE_YEAR:
      case MYSQL_TYPE_NEWDATE:
        return VTK_UNSIGNED_LONG;  // vtkTimePoint uses longs

#if MYSQL_VERSION >= 50000
      case MYSQL_TYPE_BIT:
        return VTK_BIT;
#endif

      case MYSQL_TYPE_FLOAT:
        return VTK_FLOAT;

      case MYSQL_TYPE_DOUBLE:
        return VTK_DOUBLE;

      case MYSQL_TYPE_NULL:
        return VTK_VOID;

      case MYSQL_TYPE_TINY_BLOB:
      case MYSQL_TYPE_MEDIUM_BLOB:
      case MYSQL_TYPE_LONG_BLOB:
      case MYSQL_TYPE_BLOB:
        return VTK_STRING; // MySQL treats text fields as blobs

      case MYSQL_TYPE_STRING:
      case MYSQL_TYPE_VAR_STRING:
#if MYSQL_VERSION >= 50000
      case MYSQL_TYPE_VARCHAR:
#endif
        return VTK_STRING;

      case MYSQL_TYPE_SET:
      case MYSQL_TYPE_GEOMETRY:
      default:
      {
      vtkErrorMacro( <<"GetFieldType(): Unknown data type "
                     << field->type );
      return VTK_VOID;
      }
      }
      }
    }
}

// ----------------------------------------------------------------------

bool
vtkMySQLQuery::NextRow()
{
  if (! this->IsActive())
    {
    vtkErrorMacro(<<"NextRow(): Query is not active!");
    return false;
    }

  MYSQL_ROW row = mysql_fetch_row(this->Internals->Result);
  this->Internals->CurrentRow = row;

  if (!row)
    {
    // A null row will come back in one of two situations.  The first
    // is when there's an error, in which case mysql_errno will return
    // some nonzero error code.  The second is when there are no more
    // rows to fetch.  Discriminate between the two by checking the
    // errno.
    this->Active = false;
    vtkMySQLDatabase *dbContainer =
      static_cast<vtkMySQLDatabase *>(this->Database);
    assert(dbContainer != NULL);
    if (!dbContainer->IsOpen())
      {
      vtkErrorMacro(<<"Cannot get field type.  Database is closed.");
      this->SetLastErrorText("Database is closed.");
      return VTK_VOID;
      }
    MYSQL *db = dbContainer->Private->Connection;
    assert(db != NULL);

    if (mysql_errno(db) != 0)
      {
      this->SetLastErrorText(mysql_error(db));
      vtkErrorMacro(<<"NextRow(): MySQL returned error message "
                    << this->GetLastErrorText());
      }
    else
      {
      // Nothing's wrong.  We're just out of results.
      this->SetLastErrorText(NULL);
      }

    return false;
    }
  else
    {
    this->SetLastErrorText(NULL);
    return true;
    }
}

// ----------------------------------------------------------------------

vtkVariant
vtkMySQLQuery::DataValue(vtkIdType column)
{
  if (this->IsActive() == false)
    {
    vtkWarningMacro(<<"DataValue() called on inactive query");
    return vtkVariant();
    }
  else if (column < 0 || column >= this->GetNumberOfFields())
    {
    vtkWarningMacro(<<"DataValue() called with out-of-range column index "
                    << column);
    return vtkVariant();
    }
  else
    {
    assert(this->Internals->CurrentRow);
    vtkVariant result;

    // Initialize base as a VTK_VOID value... only populate with
    // data when a column value is non-NULL.
    vtkVariant base;
    if ( this->Internals->CurrentRow[column] )
      base = vtkVariant( this->Internals->CurrentRow[column] );

    // It would be a royal pain to try to convert the string to each
    // different value in turn.  Fortunately, there is already code in
    // vtkVariant to do exactly that using the C++ standard library
    // sstream.  We'll exploit that.
    switch (this->GetFieldType(column))
      {
      case VTK_INT:
      case VTK_SHORT:
      case VTK_BIT:
        return vtkVariant(base.ToInt());

      case VTK_LONG:
      case VTK_UNSIGNED_LONG:
        return vtkVariant(base.ToLong());

      case VTK_FLOAT:
        return vtkVariant(base.ToFloat());

      case VTK_DOUBLE:
        return vtkVariant(base.ToDouble());

      case VTK_STRING:
        return base; // it's already a string

      case VTK_VOID:
        return vtkVariant();

      default:
      {
      vtkWarningMacro(<<"Unhandled type " << this->GetFieldType(column)
                      <<" in DataValue().");
      return vtkVariant();
      }
      }
    }
}

// ----------------------------------------------------------------------

const char *
vtkMySQLQuery::GetLastErrorText()
{
  return this->LastErrorText;
}

// ----------------------------------------------------------------------

bool
vtkMySQLQuery::HasError()
{
  return (this->GetLastErrorText() != NULL);
}
<|endoftext|>


<commit_before>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen 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 or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"
#include <unsupported/Eigen/MatrixFunctions>

// Returns either a matrix with iid random entries or a matrix with
// clustered eigenvalues. Matrices with clustered eigenvalue clusters
// lead to different code paths in MatrixFunction.h and are thus
// useful for testing.
template<typename MatrixType>
MatrixType createRandomMatrix(const int size)
{
  typedef typename MatrixType::Scalar Scalar;
  typedef typename MatrixType::RealScalar RealScalar;
  MatrixType result;
  if (ei_random<int>(0,1) == 0) {
    result = MatrixType::Random(size, size);
  } else {
    MatrixType diag = MatrixType::Zero(size, size);
    for (int i = 0; i < size; ++i) {
      diag(i, i) = Scalar(RealScalar(ei_random<int>(0,2)))
	+ ei_random<Scalar>() * Scalar(RealScalar(0.01));
    }
    MatrixType A = MatrixType::Random(size, size);
    result = A.inverse() * diag * A;
  }
  return result;
}

template<typename MatrixType>
void testMatrixExponential(const MatrixType& A)
{
  typedef typename ei_traits<MatrixType>::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef std::complex<RealScalar> ComplexScalar;

  for (int i = 0; i < g_repeat; i++) {
    MatrixType expA1, expA2;
    ei_matrix_exponential(A, &expA1);
    ei_matrix_function(A, StdStemFunctions<ComplexScalar>::exp, &expA2);
    VERIFY_IS_APPROX(expA1, expA2);
  }
}

template<typename MatrixType>
void testHyperbolicFunctions(const MatrixType& A)
{
  for (int i = 0; i < g_repeat; i++) {
    MatrixType sinhA, coshA, expA;
    ei_matrix_sinh(A, &sinhA);
    ei_matrix_cosh(A, &coshA);
    ei_matrix_exponential(A, &expA);
    VERIFY_IS_APPROX(sinhA, (expA - expA.inverse())/2);
    VERIFY_IS_APPROX(coshA, (expA + expA.inverse())/2);
  }
}

template<typename MatrixType>
void testGonioFunctions(const MatrixType& A)
{
  typedef ei_traits<MatrixType> Traits;
  typedef typename Traits::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef std::complex<RealScalar> ComplexScalar;
  typedef Matrix<ComplexScalar, Traits::RowsAtCompileTime,
                 Traits::ColsAtCompileTime, MatrixType::Options> ComplexMatrix;

  ComplexScalar imagUnit(0,1);
  ComplexScalar two(2,0);

  for (int i = 0; i < g_repeat; i++) {
    ComplexMatrix Ac = A.template cast<ComplexScalar>();

    ComplexMatrix exp_iA;
    ei_matrix_exponential(imagUnit * Ac, &exp_iA);

    MatrixType sinA;
    ei_matrix_sin(A, &sinA);
    ComplexMatrix sinAc = sinA.template cast<ComplexScalar>();
    VERIFY_IS_APPROX(sinAc, (exp_iA - exp_iA.inverse()) / (two*imagUnit));

    MatrixType cosA;
    ei_matrix_cos(A, &cosA);
    ComplexMatrix cosAc = cosA.template cast<ComplexScalar>();
    VERIFY_IS_APPROX(cosAc, (exp_iA + exp_iA.inverse()) / 2);
  }
}

template<typename MatrixType>
void testMatrixType(const MatrixType& m)
{
  for (int i = 0; i < g_repeat; i++) {
    MatrixType A = createRandomMatrix<MatrixType>(m.rows());
    testMatrixExponential(A);
    testHyperbolicFunctions(A);
    testGonioFunctions(A);
  }
}

void test_matrix_function()
{
  CALL_SUBTEST_1(testMatrixType(Matrix<float,1,1>()));
  CALL_SUBTEST_2(testMatrixType(Matrix3cf()));
  CALL_SUBTEST_3(testMatrixType(MatrixXf(8,8)));
  CALL_SUBTEST_4(testMatrixType(Matrix2d()));
  CALL_SUBTEST_5(testMatrixType(Matrix<double,5,5,RowMajor>()));
  CALL_SUBTEST_6(testMatrixType(Matrix4cd()));
  CALL_SUBTEST_7(testMatrixType(MatrixXd(13,13)));
}
<commit_msg>Refactor matrix_function test in preparation of next commit.<commit_after>// This file is part of Eigen, a lightweight C++ template library
// for linear algebra.
//
// Copyright (C) 2010 Jitse Niesen <jitse@maths.leeds.ac.uk>
//
// Eigen is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3 of the License, or (at your option) any later version.
//
// Alternatively, you can redistribute it and/or
// modify it under the terms of the GNU General Public License as
// published by the Free Software Foundation; either version 2 of
// the License, or (at your option) any later version.
//
// Eigen 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 or the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License and a copy of the GNU General Public License along with
// Eigen. If not, see <http://www.gnu.org/licenses/>.

#include "main.h"
#include <unsupported/Eigen/MatrixFunctions>

// Returns a matrix with eigenvalues clustered around 0, 1 and 2.
template<typename MatrixType>
MatrixType randomMatrixWithRealEivals(const int size)
{
  typedef typename MatrixType::Scalar Scalar;
  typedef typename MatrixType::RealScalar RealScalar;
  MatrixType diag = MatrixType::Zero(size, size);
  for (int i = 0; i < size; ++i) {
    diag(i, i) = Scalar(RealScalar(ei_random<int>(0,2)))
      + ei_random<Scalar>() * Scalar(RealScalar(0.01));
  }
  MatrixType A = MatrixType::Random(size, size);
  return A.inverse() * diag * A;
}

template<typename MatrixType>
void testMatrixExponential(const MatrixType& A)
{
  typedef typename ei_traits<MatrixType>::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef std::complex<RealScalar> ComplexScalar;

  for (int i = 0; i < g_repeat; i++) {
    MatrixType expA1, expA2;
    ei_matrix_exponential(A, &expA1);
    ei_matrix_function(A, StdStemFunctions<ComplexScalar>::exp, &expA2);
    VERIFY_IS_APPROX(expA1, expA2);
  }
}

template<typename MatrixType>
void testHyperbolicFunctions(const MatrixType& A)
{
  for (int i = 0; i < g_repeat; i++) {
    MatrixType sinhA, coshA, expA;
    ei_matrix_sinh(A, &sinhA);
    ei_matrix_cosh(A, &coshA);
    ei_matrix_exponential(A, &expA);
    VERIFY_IS_APPROX(sinhA, (expA - expA.inverse())/2);
    VERIFY_IS_APPROX(coshA, (expA + expA.inverse())/2);
  }
}

template<typename MatrixType>
void testGonioFunctions(const MatrixType& A)
{
  typedef ei_traits<MatrixType> Traits;
  typedef typename Traits::Scalar Scalar;
  typedef typename NumTraits<Scalar>::Real RealScalar;
  typedef std::complex<RealScalar> ComplexScalar;
  typedef Matrix<ComplexScalar, Traits::RowsAtCompileTime,
                 Traits::ColsAtCompileTime, MatrixType::Options> ComplexMatrix;

  ComplexScalar imagUnit(0,1);
  ComplexScalar two(2,0);

  for (int i = 0; i < g_repeat; i++) {
    ComplexMatrix Ac = A.template cast<ComplexScalar>();

    ComplexMatrix exp_iA;
    ei_matrix_exponential(imagUnit * Ac, &exp_iA);

    MatrixType sinA;
    ei_matrix_sin(A, &sinA);
    ComplexMatrix sinAc = sinA.template cast<ComplexScalar>();
    VERIFY_IS_APPROX(sinAc, (exp_iA - exp_iA.inverse()) / (two*imagUnit));

    MatrixType cosA;
    ei_matrix_cos(A, &cosA);
    ComplexMatrix cosAc = cosA.template cast<ComplexScalar>();
    VERIFY_IS_APPROX(cosAc, (exp_iA + exp_iA.inverse()) / 2);
  }
}

template<typename MatrixType>
void testMatrix(const MatrixType& A)
{
  testMatrixExponential(A);
  testHyperbolicFunctions(A);
  testGonioFunctions(A);
}

template<typename MatrixType>
void testMatrixType(const MatrixType& m)
{
  // Matrices with clustered eigenvalue lead to different code paths
  // in MatrixFunction.h and are thus useful for testing.

  const int size = m.rows();
  for (int i = 0; i < g_repeat; i++) {
    testMatrix(MatrixType::Random(size, size).eval());
    testMatrix(randomMatrixWithRealEivals<MatrixType>(size));
  }
}

void test_matrix_function()
{
  CALL_SUBTEST_1(testMatrixType(Matrix<float,1,1>()));
  CALL_SUBTEST_2(testMatrixType(Matrix3cf()));
  CALL_SUBTEST_3(testMatrixType(MatrixXf(8,8)));
  CALL_SUBTEST_4(testMatrixType(Matrix2d()));
  CALL_SUBTEST_5(testMatrixType(Matrix<double,5,5,RowMajor>()));
  CALL_SUBTEST_6(testMatrixType(Matrix4cd()));
  CALL_SUBTEST_7(testMatrixType(MatrixXd(13,13)));
}
<|endoftext|>