<commit_before>/* Copyright 2010 Jukka Jylnki

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. */

/** @file NetworkDialog.cpp
	@brief */

#include <QFile>
#include <QVBoxLayout>
#include <QLabel>
#include <QTreeWidget>

#ifdef KNET_USE_BOOST
#include <boost/thread/thread.hpp>
#endif

#include "kNet/DebugMemoryLeakCheck.h"
#include "kNet/qt/NetworkDialog.h"
#include "kNet/qt/MessageConnectionDialog.h"
#include "kNet/qt/ui/ui_NetworkDialog.h"

namespace kNet
{

const int dialogUpdateInterval = 1000;

NetworkDialog::NetworkDialog(QWidget *parent, Network *network_)
:network(network_), QWidget(parent)
{
	dialog = new Ui_NetworkDialog;
	dialog->setupUi(this);
/*
	QUiLoader loader;
	QFile file("NetworkDialog.ui");
	file.open(QFile::ReadOnly);
	QWidget *myWidget = loader.load(&file, this);
	file.close();
	*/
/*
	QVBoxLayout *layout = new QVBoxLayout;
	layout->addWidget(myWidget);
	setLayout(layout);
*/
	QTreeWidget *connectionsTree = findChild<QTreeWidget*>("connectionsTree");
	if (connectionsTree)
		connect(connectionsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(ItemDoubleClicked(QTreeWidgetItem *)));

	updateTimer = new QTimer(this);
	connect(updateTimer, SIGNAL(timeout()), this, SLOT(Update()));
	Update();

	this->setAttribute(Qt::WA_DeleteOnClose);
}

NetworkDialog::~NetworkDialog()
{
	delete dialog;
}

class MessageConnectionTreeItem : public QTreeWidgetItem
{
public:
	MessageConnectionTreeItem(QTreeWidgetItem *parent, Ptr(MessageConnection) connection_)
	:QTreeWidgetItem(parent), connection(connection_)
	{
		if (connection)
			setText(0, connection->ToString().c_str());
	}

	Ptr(MessageConnection) connection;
};

QTreeWidgetItem *NewTreeItemFromString(QTreeWidget *parent, const char *str)
{
	QTreeWidgetItem *item = new QTreeWidgetItem(parent);
	item->setText(0, str);
	return item;
}

void NetworkDialog::Update()
{
	if (!network)
		return;

	QLabel *machineIp = findChild<QLabel*>("machineIP");
	if (!machineIp)
		return;
	QLabel *numRunningThreads = findChild<QLabel*>("numRunningThreads");
	if (!numRunningThreads)
		return;
	QTreeWidget *connectionsTree = findChild<QTreeWidget*>("connectionsTree");
	if (!connectionsTree)
		return;

	machineIp->setText(network->LocalAddress());
	numRunningThreads->setText(QString::number(network->NumWorkerThreads()));

	connectionsTree->clear();

	Ptr(NetworkServer) server = network->GetServer();
	if (server)
	{
		QTreeWidgetItem *serverItem = NewTreeItemFromString(connectionsTree, server->ToString().c_str());
		connectionsTree->addTopLevelItem(serverItem);

		NetworkServer::ConnectionMap connections = server->GetConnections();
		for(NetworkServer::ConnectionMap::iterator iter = connections.begin(); iter != connections.end(); ++iter)
		{
			QTreeWidgetItem *connectionItem = new MessageConnectionTreeItem(serverItem, iter->second);
			serverItem->addChild(connectionItem);
			serverItem->setExpanded(true);
		}
	}

	std::set<MessageConnection*> connections = network->Connections();
	for(std::set<MessageConnection*>::iterator iter = connections.begin(); iter != connections.end(); ++iter)
		new MessageConnectionTreeItem(connectionsTree->invisibleRootItem(), *iter);

	updateTimer->start(dialogUpdateInterval);
}

void NetworkDialog::ItemDoubleClicked(QTreeWidgetItem *item)
{
	MessageConnectionTreeItem *msgItem = dynamic_cast<MessageConnectionTreeItem*>(item);
	if (!msgItem)
		return;

	MessageConnectionDialog *dialog = new MessageConnectionDialog(0, msgItem->connection);
	dialog->show();
	dialog->setAttribute(Qt::WA_DeleteOnClose);
}

} // ~kNet
<commit_msg>Don't display duplicate items in NetworkDialog.<commit_after>/* Copyright 2010 Jukka Jylnki

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License. */

/** @file NetworkDialog.cpp
	@brief */

#include <QFile>
#include <QVBoxLayout>
#include <QLabel>
#include <QTreeWidget>

#ifdef KNET_USE_BOOST
#include <boost/thread/thread.hpp>
#endif

#include "kNet/DebugMemoryLeakCheck.h"
#include "kNet/qt/NetworkDialog.h"
#include "kNet/qt/MessageConnectionDialog.h"
#include "kNet/qt/ui/ui_NetworkDialog.h"

namespace kNet
{

const int dialogUpdateInterval = 1000;

NetworkDialog::NetworkDialog(QWidget *parent, Network *network_)
:network(network_), QWidget(parent)
{
	dialog = new Ui_NetworkDialog;
	dialog->setupUi(this);
/*
	QUiLoader loader;
	QFile file("NetworkDialog.ui");
	file.open(QFile::ReadOnly);
	QWidget *myWidget = loader.load(&file, this);
	file.close();
	*/
/*
	QVBoxLayout *layout = new QVBoxLayout;
	layout->addWidget(myWidget);
	setLayout(layout);
*/
	QTreeWidget *connectionsTree = findChild<QTreeWidget*>("connectionsTree");
	if (connectionsTree)
		connect(connectionsTree, SIGNAL(itemDoubleClicked(QTreeWidgetItem *, int)), this, SLOT(ItemDoubleClicked(QTreeWidgetItem *)));

	updateTimer = new QTimer(this);
	connect(updateTimer, SIGNAL(timeout()), this, SLOT(Update()));
	Update();

	this->setAttribute(Qt::WA_DeleteOnClose);
}

NetworkDialog::~NetworkDialog()
{
	delete dialog;
}

class MessageConnectionTreeItem : public QTreeWidgetItem
{
public:
	MessageConnectionTreeItem(QTreeWidgetItem *parent, Ptr(MessageConnection) connection_)
	:QTreeWidgetItem(parent), connection(connection_)
	{
		if (connection)
			setText(0, connection->ToString().c_str());
	}

	Ptr(MessageConnection) connection;
};

QTreeWidgetItem *NewTreeItemFromString(QTreeWidget *parent, const char *str)
{
	QTreeWidgetItem *item = new QTreeWidgetItem(parent);
	item->setText(0, str);
	return item;
}

void NetworkDialog::Update()
{
	if (!network)
		return;

	QLabel *machineIp = findChild<QLabel*>("machineIP");
	if (!machineIp)
		return;
	QLabel *numRunningThreads = findChild<QLabel*>("numRunningThreads");
	if (!numRunningThreads)
		return;
	QTreeWidget *connectionsTree = findChild<QTreeWidget*>("connectionsTree");
	if (!connectionsTree)
		return;

	machineIp->setText(network->LocalAddress());
	numRunningThreads->setText(QString::number(network->NumWorkerThreads()));

	connectionsTree->clear();

	Ptr(NetworkServer) server = network->GetServer();
	if (server)
	{
		QTreeWidgetItem *serverItem = NewTreeItemFromString(connectionsTree, server->ToString().c_str());
		connectionsTree->addTopLevelItem(serverItem);

		NetworkServer::ConnectionMap connections = server->GetConnections();
		for(NetworkServer::ConnectionMap::iterator iter = connections.begin(); iter != connections.end(); ++iter)
		{
			QTreeWidgetItem *connectionItem = new MessageConnectionTreeItem(serverItem, iter->second);
			serverItem->addChild(connectionItem);
			serverItem->setExpanded(true);
		}
	}

	std::set<MessageConnection*> connections = network->Connections();
	for(std::set<MessageConnection*>::iterator iter = connections.begin(); iter != connections.end(); ++iter)
		if ((*iter)->GetSocket() && (*iter)->GetSocket()->Type() == ClientSocket)
			new MessageConnectionTreeItem(connectionsTree->invisibleRootItem(), *iter);

	updateTimer->start(dialogUpdateInterval);
}

void NetworkDialog::ItemDoubleClicked(QTreeWidgetItem *item)
{
	MessageConnectionTreeItem *msgItem = dynamic_cast<MessageConnectionTreeItem*>(item);
	if (!msgItem)
		return;

	MessageConnectionDialog *dialog = new MessageConnectionDialog(0, msgItem->connection);
	dialog->show();
	dialog->setAttribute(Qt::WA_DeleteOnClose);
}

} // ~kNet
<|endoftext|>


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

#include "config.h"

#include "SampleAggregatorImp.hpp"

#include "geopm.h"
#include "geopm_internal.h"
#include "geopm_hash.h"
#include "PlatformIO.hpp"
#include "PlatformTopo.hpp"
#include "Exception.hpp"
#include "Helper.hpp"
#include "Accumulator.hpp"

namespace geopm
{
    std::unique_ptr<SampleAggregator> SampleAggregator::make_unique(void)
    {
        return geopm::make_unique<SampleAggregatorImp>();
    }

    SampleAggregatorImp::SampleAggregatorImp()
        : SampleAggregatorImp(platform_io())
    {

    }

    SampleAggregatorImp::SampleAggregatorImp(PlatformIO &platio)
        : m_platform_io(platio)
        , m_time_idx(m_platform_io.push_signal("TIME", GEOPM_DOMAIN_BOARD, 0))
        , m_is_updated(false)
    {

    }

    int SampleAggregatorImp::push_signal_total(const std::string &signal_name,
                                               int domain_type,
                                               int domain_idx)
    {
        if (m_is_updated) {
           throw Exception("SampleAggregatorImp::push_signal_total(): called after update()",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        int result = m_platform_io.push_signal(signal_name, domain_type, domain_idx);
        auto bad_it = m_avg_signal.find(result);
        if (bad_it != m_avg_signal.end()) {
           throw Exception("SampleAggregatorImp::push_signal_total(): signal already pushed for average",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        auto signal_it = m_sum_signal.find(result);
        if (signal_it == m_sum_signal.end()) {
            m_sum_signal[result] = {
                NAN,
                m_platform_io.push_signal("REGION_HASH", domain_type, domain_idx),
                GEOPM_REGION_HASH_UNMARKED,
                m_platform_io.push_signal("EPOCH_COUNT", domain_type, domain_idx),
                0,
                SumAccumulator::make_unique(),
                SumAccumulator::make_unique(),
                {},
                {},
           };
        }
        return result;
    }

    int SampleAggregatorImp::push_signal_average(const std::string &signal_name,
                                                 int domain_type,
                                                 int domain_idx)
    {
        if (m_is_updated) {
           throw Exception("SampleAggregatorImp::push_signal_average(): called after update()",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        int result = m_platform_io.push_signal(signal_name, domain_type, domain_idx);
        auto bad_it = m_sum_signal.find(result);
        if (bad_it != m_sum_signal.end()) {
           throw Exception("SampleAggregatorImp::push_signal_average(): signal already pushed for total",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        auto signal_it = m_avg_signal.find(result);
        if (signal_it == m_avg_signal.end()) {
            m_avg_signal[result] = {
                NAN,
                m_platform_io.push_signal("REGION_HASH", domain_type, domain_idx),
                GEOPM_REGION_HASH_UNMARKED,
                m_platform_io.push_signal("EPOCH_COUNT", domain_type, domain_idx),
                0,
                AvgAccumulator::make_unique(),
                AvgAccumulator::make_unique(),
                {},
                {},
           };
        }
        return result;
    }

    template<typename type>
    typename std::map<uint64_t, std::shared_ptr<type> >::iterator
    sample_aggregator_emplace_hash(
        std::map<uint64_t, std::shared_ptr<type> > &hash_accum_map,
        uint64_t hash)
    {
        typename std::map<uint64_t, std::shared_ptr<type> >::iterator result;
        result = hash_accum_map.find(hash);
        if (result == hash_accum_map.end()) {
            auto empl_ret = hash_accum_map.emplace(hash, type::make_unique());
            result = empl_ret.first;
        }
        return result;
    }

    template <typename type>
    void sample_aggregator_update_epoch(type &signal, int epoch_count)
    {
        // If the epoch count has changed, call the exit/enter
        if (epoch_count != signal.epoch_count_last) {
            if (signal.epoch_count_last != 0) {
                signal.epoch_accum->exit();
            }
            signal.epoch_accum->enter();
            signal.epoch_count_last = epoch_count;
        }
    }

    template <typename type>
    void sample_aggregator_update_hash_exit(type &signal, uint64_t hash)
    {
        if (signal.region_hash_last != hash) {
            // If we have exited a valid region, call exit()
            if (signal.region_hash_last != GEOPM_REGION_HASH_UNMARKED) {
                signal.region_accum_it->second->exit();
            }
        }
    }

    template <typename type>
    void sample_aggregator_update_hash_enter(type &signal, uint64_t hash)
    {
        if (signal.region_hash_last != hash) {
            // If we have entered a valid region, call enter()
            if (hash != GEOPM_REGION_HASH_UNMARKED) {
                signal.region_accum_it->second->enter();
            }
            signal.region_hash_last = hash;
        }
    }

    void SampleAggregatorImp::update_total(void)
    {
        // Update all of the sum aggregators
        for (auto &signal_it : m_sum_signal) {
            int signal_idx = signal_it.first;
            m_sum_signal_s &signal = signal_it.second;
            double sample = m_platform_io.sample(signal_idx);
            uint64_t hash = m_platform_io.sample(signal.region_hash_idx);
            int epoch_count = m_platform_io.sample(signal.epoch_count_idx);
            if (!m_is_updated) {
                // On first call just initialize the signal values
                signal.sample_last = sample;
                signal.region_hash_last = hash;
                signal.epoch_count_last = epoch_count;
                signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
            }
            else {
                // Measure the change since the last update
                double delta = sample - signal.sample_last;
                // Update that application totals
                signal.app_accum->update(delta);
                // If we have observed our first epoch, update epoch totals
                if (signal.epoch_count_last != 0) {
                    signal.epoch_accum->update(delta);
                }
                // Update region totals
                signal.region_accum_it->second->update(delta);
                sample_aggregator_update_epoch(signal, epoch_count);
                sample_aggregator_update_hash_exit(signal, hash);
                if (signal.region_hash_last != hash) {
                    signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
                }
                sample_aggregator_update_hash_enter(signal, hash);
                signal.sample_last = sample;
            }
        }
    }

    void SampleAggregatorImp::update_average(void)
    {
        // Update all of the sum aggregators
        for (auto &signal_it : m_avg_signal) {
            int signal_idx = signal_it.first;
            m_avg_signal_s &signal = signal_it.second;
            double time = m_platform_io.sample(m_time_idx);
            double sample = m_platform_io.sample(signal_idx);
            uint64_t hash = m_platform_io.sample(signal.region_hash_idx);
            int epoch_count = m_platform_io.sample(signal.epoch_count_idx);
            if (!m_is_updated) {
                // On first call just initialize the signal values
                signal.time_last = time;
                signal.region_hash_last = hash;
                signal.epoch_count_last = epoch_count;
                signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
            }
            else {
                // Measure the time change since the last update
                double delta = time - signal.time_last;
                // Update that application totals
                signal.app_accum->update(delta, sample);
                // If we have observed our first epoch, update epoch totals
                if (signal.epoch_count_last != 0) {
                    signal.epoch_accum->update(delta, sample);
                }
                // Update region totals
                signal.region_accum_it->second->update(delta, sample);
                sample_aggregator_update_epoch(signal, epoch_count);
                sample_aggregator_update_hash_exit(signal, hash);
                if (signal.region_hash_last != hash) {
                    signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
                }
                sample_aggregator_update_hash_enter(signal, hash);
                signal.time_last = time;
            }
        }
    }

    void SampleAggregatorImp::update(void)
    {
        update_total();
        update_average();
        m_is_updated = true;
    }

    double SampleAggregatorImp::sample_application(int signal_idx)
    {
        double result = NAN;
        auto sum_it = m_sum_signal.find(signal_idx);
        if (sum_it != m_sum_signal.end()) {
            result = sum_it->second.app_accum->total();
        }
        else {
            auto avg_it = m_avg_signal.find(signal_idx);
            if (avg_it == m_avg_signal.end()) {
                throw Exception("SampleAggregator::sample_application(): Invalid signal index: signal index not pushed with push_signal_total() or push_signal_average()",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
            result = avg_it->second.app_accum->average();
        }
        return result;
    }

    double SampleAggregatorImp::sample_epoch_helper(int signal_idx, bool is_last)
    {
        double result = NAN;
        auto sum_it = m_sum_signal.find(signal_idx);
        if (sum_it != m_sum_signal.end()) {
            if (is_last) {
                result = sum_it->second.epoch_accum->interval_total();
            }
            else {
                result = sum_it->second.epoch_accum->total();
            }
        }
        else {
            auto avg_it = m_avg_signal.find(signal_idx);
            if (avg_it == m_avg_signal.end()) {
                throw Exception("SampleAggregator::sample_epoch(): Invalid signal index: signal index not pushed with push_signal_total() or push_signal_average()",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
            if (is_last) {
                result = avg_it->second.epoch_accum->interval_average();
            }
            else {
                result = avg_it->second.epoch_accum->average();
            }
        }
        return result;
    }

    double SampleAggregatorImp::sample_region_helper(int signal_idx, uint64_t region_hash, bool is_last)
    {
        double result = NAN;
        auto sum_it = m_sum_signal.find(signal_idx);
        if (sum_it != m_sum_signal.end()) {
            const auto &region_accum = sum_it->second.region_accum;
            auto region_accum_it = region_accum.find(region_hash);
            if (region_accum_it == region_accum.end()) {
                result = 0.0;
            }
            else {
                auto accum_ptr = region_accum_it->second;
                if (is_last) {
                    result = accum_ptr->interval_total();
                }
                else {
                    result = accum_ptr->total();
                }
            }
        }
        else {
            auto avg_it = m_avg_signal.find(signal_idx);
            if (avg_it == m_avg_signal.end()) {
                throw Exception("SampleAggregator::sample_region(): Invalid signal index: signal index not pushed with push_signal_total() or push_signal_average()",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
            const auto &region_accum = avg_it->second.region_accum;
            auto region_accum_it = region_accum.find(region_hash);
            if (region_accum_it != region_accum.end()) {
                auto accum_ptr = region_accum_it->second;
                if (is_last) {
                    result = accum_ptr->interval_average();
                }
                else {
                    result = accum_ptr->average();
                }
            }
        }
        return result;
    }

    double SampleAggregatorImp::sample_region(int signal_idx, uint64_t region_hash)
    {
        return sample_region_helper(signal_idx, region_hash, false);
    }

    double SampleAggregatorImp::sample_region_last(int signal_idx, uint64_t region_hash)
    {
        return sample_region_helper(signal_idx, region_hash, true);
    }

    double SampleAggregatorImp::sample_epoch(int signal_idx)
    {
        return sample_epoch_helper(signal_idx, false);
    }

    double SampleAggregatorImp::sample_epoch_last(int signal_idx)
    {
        return sample_epoch_helper(signal_idx, true);
    }
}
<commit_msg>Have SampleAgg::sample_region handle special hashes<commit_after>/*
 * Copyright (c) 2015, 2016, 2017, 2018, 2019, 2020, Intel Corporation
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 *
 *     * Redistributions of source code must retain the above copyright
 *       notice, this list of conditions and the following disclaimer.
 *
 *     * Redistributions in binary form must reproduce the above copyright
 *       notice, this list of conditions and the following disclaimer in
 *       the documentation and/or other materials provided with the
 *       distribution.
 *
 *     * Neither the name of Intel Corporation nor the names of its
 *       contributors may be used to endorse or promote products derived
 *       from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY LOG OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

#include "config.h"

#include "SampleAggregatorImp.hpp"

#include "geopm.h"
#include "geopm_internal.h"
#include "geopm_hash.h"
#include "PlatformIO.hpp"
#include "PlatformTopo.hpp"
#include "Exception.hpp"
#include "Helper.hpp"
#include "Accumulator.hpp"

namespace geopm
{
    std::unique_ptr<SampleAggregator> SampleAggregator::make_unique(void)
    {
        return geopm::make_unique<SampleAggregatorImp>();
    }

    SampleAggregatorImp::SampleAggregatorImp()
        : SampleAggregatorImp(platform_io())
    {

    }

    SampleAggregatorImp::SampleAggregatorImp(PlatformIO &platio)
        : m_platform_io(platio)
        , m_time_idx(m_platform_io.push_signal("TIME", GEOPM_DOMAIN_BOARD, 0))
        , m_is_updated(false)
    {

    }

    int SampleAggregatorImp::push_signal_total(const std::string &signal_name,
                                               int domain_type,
                                               int domain_idx)
    {
        if (m_is_updated) {
           throw Exception("SampleAggregatorImp::push_signal_total(): called after update()",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        int result = m_platform_io.push_signal(signal_name, domain_type, domain_idx);
        auto bad_it = m_avg_signal.find(result);
        if (bad_it != m_avg_signal.end()) {
           throw Exception("SampleAggregatorImp::push_signal_total(): signal already pushed for average",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        auto signal_it = m_sum_signal.find(result);
        if (signal_it == m_sum_signal.end()) {
            m_sum_signal[result] = {
                NAN,
                m_platform_io.push_signal("REGION_HASH", domain_type, domain_idx),
                GEOPM_REGION_HASH_UNMARKED,
                m_platform_io.push_signal("EPOCH_COUNT", domain_type, domain_idx),
                0,
                SumAccumulator::make_unique(),
                SumAccumulator::make_unique(),
                {},
                {},
           };
        }
        return result;
    }

    int SampleAggregatorImp::push_signal_average(const std::string &signal_name,
                                                 int domain_type,
                                                 int domain_idx)
    {
        if (m_is_updated) {
           throw Exception("SampleAggregatorImp::push_signal_average(): called after update()",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        int result = m_platform_io.push_signal(signal_name, domain_type, domain_idx);
        auto bad_it = m_sum_signal.find(result);
        if (bad_it != m_sum_signal.end()) {
           throw Exception("SampleAggregatorImp::push_signal_average(): signal already pushed for total",
                           GEOPM_ERROR_INVALID, __FILE__, __LINE__);
        }
        auto signal_it = m_avg_signal.find(result);
        if (signal_it == m_avg_signal.end()) {
            m_avg_signal[result] = {
                NAN,
                m_platform_io.push_signal("REGION_HASH", domain_type, domain_idx),
                GEOPM_REGION_HASH_UNMARKED,
                m_platform_io.push_signal("EPOCH_COUNT", domain_type, domain_idx),
                0,
                AvgAccumulator::make_unique(),
                AvgAccumulator::make_unique(),
                {},
                {},
           };
        }
        return result;
    }

    template<typename type>
    typename std::map<uint64_t, std::shared_ptr<type> >::iterator
    sample_aggregator_emplace_hash(
        std::map<uint64_t, std::shared_ptr<type> > &hash_accum_map,
        uint64_t hash)
    {
        typename std::map<uint64_t, std::shared_ptr<type> >::iterator result;
        result = hash_accum_map.find(hash);
        if (result == hash_accum_map.end()) {
            auto empl_ret = hash_accum_map.emplace(hash, type::make_unique());
            result = empl_ret.first;
        }
        return result;
    }

    template <typename type>
    void sample_aggregator_update_epoch(type &signal, int epoch_count)
    {
        // If the epoch count has changed, call the exit/enter
        if (epoch_count != signal.epoch_count_last) {
            if (signal.epoch_count_last != 0) {
                signal.epoch_accum->exit();
            }
            signal.epoch_accum->enter();
            signal.epoch_count_last = epoch_count;
        }
    }

    template <typename type>
    void sample_aggregator_update_hash_exit(type &signal, uint64_t hash)
    {
        if (signal.region_hash_last != hash) {
            // If we have exited a valid region, call exit()
            if (signal.region_hash_last != GEOPM_REGION_HASH_UNMARKED) {
                signal.region_accum_it->second->exit();
            }
        }
    }

    template <typename type>
    void sample_aggregator_update_hash_enter(type &signal, uint64_t hash)
    {
        if (signal.region_hash_last != hash) {
            // If we have entered a valid region, call enter()
            if (hash != GEOPM_REGION_HASH_UNMARKED) {
                signal.region_accum_it->second->enter();
            }
            signal.region_hash_last = hash;
        }
    }

    void SampleAggregatorImp::update_total(void)
    {
        // Update all of the sum aggregators
        for (auto &signal_it : m_sum_signal) {
            int signal_idx = signal_it.first;
            m_sum_signal_s &signal = signal_it.second;
            double sample = m_platform_io.sample(signal_idx);
            uint64_t hash = m_platform_io.sample(signal.region_hash_idx);
            int epoch_count = m_platform_io.sample(signal.epoch_count_idx);
            if (!m_is_updated) {
                // On first call just initialize the signal values
                signal.sample_last = sample;
                signal.region_hash_last = hash;
                signal.epoch_count_last = epoch_count;
                signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
            }
            else {
                // Measure the change since the last update
                double delta = sample - signal.sample_last;
                // Update that application totals
                signal.app_accum->update(delta);
                // If we have observed our first epoch, update epoch totals
                if (signal.epoch_count_last != 0) {
                    signal.epoch_accum->update(delta);
                }
                // Update region totals
                signal.region_accum_it->second->update(delta);
                sample_aggregator_update_epoch(signal, epoch_count);
                sample_aggregator_update_hash_exit(signal, hash);
                if (signal.region_hash_last != hash) {
                    signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
                }
                sample_aggregator_update_hash_enter(signal, hash);
                signal.sample_last = sample;
            }
        }
    }

    void SampleAggregatorImp::update_average(void)
    {
        // Update all of the sum aggregators
        for (auto &signal_it : m_avg_signal) {
            int signal_idx = signal_it.first;
            m_avg_signal_s &signal = signal_it.second;
            double time = m_platform_io.sample(m_time_idx);
            double sample = m_platform_io.sample(signal_idx);
            uint64_t hash = m_platform_io.sample(signal.region_hash_idx);
            int epoch_count = m_platform_io.sample(signal.epoch_count_idx);
            if (!m_is_updated) {
                // On first call just initialize the signal values
                signal.time_last = time;
                signal.region_hash_last = hash;
                signal.epoch_count_last = epoch_count;
                signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
            }
            else {
                // Measure the time change since the last update
                double delta = time - signal.time_last;
                // Update that application totals
                signal.app_accum->update(delta, sample);
                // If we have observed our first epoch, update epoch totals
                if (signal.epoch_count_last != 0) {
                    signal.epoch_accum->update(delta, sample);
                }
                // Update region totals
                signal.region_accum_it->second->update(delta, sample);
                sample_aggregator_update_epoch(signal, epoch_count);
                sample_aggregator_update_hash_exit(signal, hash);
                if (signal.region_hash_last != hash) {
                    signal.region_accum_it = sample_aggregator_emplace_hash(signal.region_accum, hash);
                }
                sample_aggregator_update_hash_enter(signal, hash);
                signal.time_last = time;
            }
        }
    }

    void SampleAggregatorImp::update(void)
    {
        update_total();
        update_average();
        m_is_updated = true;
    }

    double SampleAggregatorImp::sample_application(int signal_idx)
    {
        double result = NAN;
        auto sum_it = m_sum_signal.find(signal_idx);
        if (sum_it != m_sum_signal.end()) {
            result = sum_it->second.app_accum->total();
        }
        else {
            auto avg_it = m_avg_signal.find(signal_idx);
            if (avg_it == m_avg_signal.end()) {
                throw Exception("SampleAggregator::sample_application(): Invalid signal index: signal index not pushed with push_signal_total() or push_signal_average()",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
            result = avg_it->second.app_accum->average();
        }
        return result;
    }

    double SampleAggregatorImp::sample_epoch_helper(int signal_idx, bool is_last)
    {
        double result = NAN;
        auto sum_it = m_sum_signal.find(signal_idx);
        if (sum_it != m_sum_signal.end()) {
            if (is_last) {
                result = sum_it->second.epoch_accum->interval_total();
            }
            else {
                result = sum_it->second.epoch_accum->total();
            }
        }
        else {
            auto avg_it = m_avg_signal.find(signal_idx);
            if (avg_it == m_avg_signal.end()) {
                throw Exception("SampleAggregator::sample_epoch(): Invalid signal index: signal index not pushed with push_signal_total() or push_signal_average()",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
            if (is_last) {
                result = avg_it->second.epoch_accum->interval_average();
            }
            else {
                result = avg_it->second.epoch_accum->average();
            }
        }
        return result;
    }

    double SampleAggregatorImp::sample_region_helper(int signal_idx, uint64_t region_hash, bool is_last)
    {
        double result = NAN;
        auto sum_it = m_sum_signal.find(signal_idx);
        if (sum_it != m_sum_signal.end()) {
            const auto &region_accum = sum_it->second.region_accum;
            auto region_accum_it = region_accum.find(region_hash);
            if (region_accum_it == region_accum.end()) {
                result = 0.0;
            }
            else {
                auto accum_ptr = region_accum_it->second;
                if (is_last) {
                    result = accum_ptr->interval_total();
                }
                else {
                    result = accum_ptr->total();
                }
            }
        }
        else {
            auto avg_it = m_avg_signal.find(signal_idx);
            if (avg_it == m_avg_signal.end()) {
                throw Exception("SampleAggregator::sample_region(): Invalid signal index: signal index not pushed with push_signal_total() or push_signal_average()",
                                GEOPM_ERROR_INVALID, __FILE__, __LINE__);
            }
            const auto &region_accum = avg_it->second.region_accum;
            auto region_accum_it = region_accum.find(region_hash);
            if (region_accum_it != region_accum.end()) {
                auto accum_ptr = region_accum_it->second;
                if (is_last) {
                    result = accum_ptr->interval_average();
                }
                else {
                    result = accum_ptr->average();
                }
            }
        }
        return result;
    }

    double SampleAggregatorImp::sample_region(int signal_idx, uint64_t region_hash)
    {
        // TODO: bad hack using special region hashes instead of the dedicated API
        if (region_hash == GEOPM_REGION_HASH_EPOCH) {
            return sample_epoch(signal_idx);
        }
        else if (region_hash == GEOPM_REGION_HASH_APP) {
            return sample_application(signal_idx);
        }
        return sample_region_helper(signal_idx, region_hash, false);
    }

    double SampleAggregatorImp::sample_region_last(int signal_idx, uint64_t region_hash)
    {
        return sample_region_helper(signal_idx, region_hash, true);
    }

    double SampleAggregatorImp::sample_epoch(int signal_idx)
    {
        return sample_epoch_helper(signal_idx, false);
    }

    double SampleAggregatorImp::sample_epoch_last(int signal_idx)
    {
        return sample_epoch_helper(signal_idx, true);
    }
}
<|endoftext|>


<commit_before>#include "ScrollableWidget.hpp"
#include <iostream>

gsf::ScrollableWidget::ScrollableWidget(float width, float height)
: ChildWidget(width, height)
, m_totalWidth{ width }
, m_totalHeight{ height }
, m_scrollOffsetX{ 0.f }
, m_scrollOffsetY{ 0.f }
, m_scrollSpeed{ 6.0f }
, m_isVerticalScrollEnabled{ true }
, m_isHorizontalScrollEnabled{ true }
, m_scrollbarThickness{ 16.f }
, m_isVerticalScrollNeeded{ true }
, m_scrollbarVertical{ m_scrollbarThickness, 0.f }
, m_scrollbarVerMoveActive{ false }
, m_isHorizontalScrollNeeded{ true }
, m_scrollbarHorizontal{ 0.f, m_scrollbarThickness }
, m_scrollbarHorMoveActive{ false }
, SCROLLBAR_PAD_HOR{ 6.f }
, SCROLLBAR_PAD_VER{ 6.f }
{
    //m_scrollbarVertical.setPosition(getRight() - m_scrollbarVertical.getWidth() / 2.f - 3.f, m_scrollbarVertical.getHeight() / 2.f + 3.f );
    calculateScrollbarSizes();
}

gsf::ScrollableWidget::~ScrollableWidget()
{

}

void gsf::ScrollableWidget::calculateVerticalScrollbarSize()
{
    if (m_children.size() < 1)
    {
        return;
    }

    // get first element
    Widget *childWidget = { m_children.at(0).get() };
    float childrenHeight = { childWidget->getHeight() };

    // Vertical Scrollbar
    // check if there is any need for scrolling (Child is higher then ScrollableWidgets height or outside)
    m_isVerticalScrollNeeded = childWidget->getTop() < 0.f || childWidget->getBottom() > getHeight() ||
        childWidget->getHeight() > getHeight();
    // Only show scrollbar when there is any need to scroll and scrolling is enabled
    if (!m_isVerticalScrollEnabled || !m_isVerticalScrollNeeded) {
        return;
    }
    // Get proportion between the scrollable widget and its child
    float proportionVer = { getHeight() / childrenHeight };
    // Calculate the scrollbar size
    float scrollbarHeight = { (getHeight() - 2 * SCROLLBAR_PAD_VER) * proportionVer };
    m_scrollbarVertical.setHeight(scrollbarHeight);
    m_scrollbarVertical.setPosition(getWidth() - m_scrollbarVertical.getWidth() / 2.f - 3.f, 0.f + m_scrollbarVertical.getHeight() / 2.f + SCROLLBAR_PAD_VER);
    m_scrollbarVertical.setOrigin(m_scrollbarVertical.getWidth() / 2.f, m_scrollbarVertical.getHeight() / 2.f);
}

void gsf::ScrollableWidget::calculateHorizontalScrollbarSize()
{
        if (m_children.size() < 1)
        {
            return;
        }

        // get first element
        Widget *childWidget = { m_children.at(0).get() };
        float childrenWidth = { childWidget->getWidth() };

        // Horizontal Scrollbar
        m_isHorizontalScrollNeeded = childWidget->getLeft() < 0.f || childWidget->getRight() > getWidth() ||
            childWidget->getWidth() > getWidth();
        // Only show scrollbar when there is any need to scroll and scrolling is enabled
        if (!m_isHorizontalScrollEnabled || !m_isHorizontalScrollNeeded) {
            return;
        }
        // Get proportion between the scrollable widget and its child
        float proportionHor = { getWidth() / childrenWidth };
        // Calculate the scrollbar size
        float scrollbarWidth = { (getWidth() - 2 * SCROLLBAR_PAD_HOR) * proportionHor };
        m_scrollbarHorizontal.setWidth(scrollbarWidth);
        m_scrollbarHorizontal.setPosition(0.f + m_scrollbarHorizontal.getWidth() / 2.f + SCROLLBAR_PAD_HOR, getHeight() - m_scrollbarHorizontal.getHeight() / 2.f - 3.f);
        m_scrollbarHorizontal.setOrigin(m_scrollbarHorizontal.getWidth() / 2.f, m_scrollbarHorizontal.getHeight() / 2.f);
}

void gsf::ScrollableWidget::calculateScrollbarSizes()
{
    if (m_children.size() < 1)
    {
        return;
    }
    calculateVerticalScrollbarSize();
    calculateHorizontalScrollbarSize();
}

void gsf::ScrollableWidget::setIsVerticalScrollEnabled(bool isEnabled)
{
    m_isVerticalScrollEnabled = isEnabled;
}

bool gsf::ScrollableWidget::isVerticalScrollEnabled() const
{
    return m_isVerticalScrollEnabled;
}
void gsf::ScrollableWidget::setIsHorizontalScrollEnabled(bool isEnabled)
{
    m_isHorizontalScrollEnabled = isEnabled;
}

bool gsf::ScrollableWidget::isHorizontalScrollEnabled() const
{
    return m_isHorizontalScrollEnabled;
}

float gsf::ScrollableWidget::getTotalWidth() const
{
    return m_totalWidth;
}

float gsf::ScrollableWidget::getTotalHeight() const
{
    return m_totalHeight;
}

void gsf::ScrollableWidget::correctScrollBarPosition()
{
    // Vertical
    // If scrollbar is out of widget, correct its position
    if (m_scrollbarVertical.getTop() < 0.f + SCROLLBAR_PAD_VER) {
        m_scrollbarVertical.setPosition(m_scrollbarVertical.getPosition().x, 0.f + m_scrollbarVertical.getHeight() / 2.f + SCROLLBAR_PAD_VER);
    }
    else if (m_scrollbarVertical.getBottom() > getHeight() - SCROLLBAR_PAD_VER) {
        m_scrollbarVertical.setPosition(m_scrollbarVertical.getPosition().x, getHeight() - m_scrollbarVertical.getHeight() / 2.f - SCROLLBAR_PAD_VER);
    }
    // Horizontal
    if (m_scrollbarHorizontal.getLeft() < 0.f + SCROLLBAR_PAD_HOR) {
        m_scrollbarHorizontal.setPosition(0.f + m_scrollbarHorizontal.getWidth() / 2.f + SCROLLBAR_PAD_HOR, m_scrollbarHorizontal.getPosition().y);
    }
    else if (m_scrollbarHorizontal.getRight() > getWidth() - SCROLLBAR_PAD_VER) {
        m_scrollbarHorizontal.setPosition(getWidth() - m_scrollbarHorizontal.getWidth() / 2.f - SCROLLBAR_PAD_HOR, m_scrollbarHorizontal.getPosition().y);
    }
}

void gsf::ScrollableWidget::drawWidget(sf::RenderTarget &target, sf::RenderStates states) const
{
        states.transform *= getTransform();

        drawCurrent(target, states);

        // We change the view of the target, so that only the area of the widget and its child
        // which are in its shown area are drawn on the RenderTarget
        sf::View defaultView = target.getView();
        sf::View view = { getShownAreaView(target) };
        //sf::View view = defaultView;

        target.setView(view);

        drawChildren(target, states);
        //Widget::draw(target, states);

        target.setView(defaultView);

        // Draw Scroll Elements
        target.draw(m_scrollbarVertical, states);
        target.draw(m_scrollbarHorizontal, states);

}

void gsf::ScrollableWidget::drawCurrent(sf::RenderTarget &target, sf::RenderStates states) const
{
    // Draw background
    sf::RectangleShape bgShape({ getWidth(), getHeight() });
    bgShape.setFillColor(m_bgColor);
    target.draw(bgShape, states);
}

bool gsf::ScrollableWidget::handleSpecialEvents(sf::Event &event)
{
    // Is the mouse in the shown area of the widget
    bool isMouseInShownArea = { getShownArea().contains(sf::Vector2f(event.mouseButton.x , event.mouseButton.y)) };
    if (event.type == sf::Event::MouseWheelMoved &&
        isMouseInShownArea &&
        m_isVerticalScrollNeeded &&
        m_isVerticalScrollEnabled)
    {
        m_scrollOffsetY = { event.mouseWheel.delta * m_scrollSpeed };
        // We have to move the scrollbar too when we scroll with the scroll wheel
        if (m_children.size() > 0)
        {
            // get first element
            Widget *widget = m_children.at(0).get();
            float childHeight = widget->getHeight();
            // Calculate the offset
            float moveScr = -(m_scrollOffsetY * getHeight()) / childHeight;
            m_scrollbarVertical.moveAndStoreOldPos(0.f, moveScr);
            correctScrollBarPosition();
        }
        return true;
    }
    else if (event.type == sf::Event::MouseButtonPressed && isMouseInShownArea)
    {
        // We need the mouse pos as local position in the ScrollWidget
        sf::Vector2f localMousePos = { event.mouseButton.x - getWorldLeft() , event.mouseButton.y - getWorldTop() };
        // Vertical scrollbar
        if (event.mouseButton.button == sf::Mouse::Left &&
            m_scrollbarVertical.isPointIntersecting({ localMousePos.x , localMousePos.y }) &&
            m_isVerticalScrollNeeded &&
            m_isVerticalScrollEnabled)
        {
            m_scrollbarVerMoveActive = true;
            m_scrollbarVerMoveModeRelPos.x = localMousePos.x - m_scrollbarVertical.getPosition().x;
            m_scrollbarVerMoveModeRelPos.y = localMousePos.y - m_scrollbarVertical.getPosition().y;
            return true;
        }
        // Horizontal scrollbar
        else if (event.mouseButton.button == sf::Mouse::Left &&
            m_scrollbarHorizontal.isPointIntersecting({ localMousePos.x , localMousePos.y }) &&
            m_isHorizontalScrollNeeded &&
            m_isHorizontalScrollEnabled)
        {
            m_scrollbarHorMoveActive = true;
            m_scrollbarHorMoveModeRelPos.x = localMousePos.x - m_scrollbarHorizontal.getPosition().x;
            m_scrollbarHorMoveModeRelPos.y = localMousePos.y - m_scrollbarHorizontal.getPosition().y;
            return true;
        }
    }
    else if (event.type == sf::Event::MouseButtonReleased)
    {
        if (event.mouseButton.button == sf::Mouse::Left)
        {
            m_scrollbarVerMoveActive = false;
            m_scrollbarHorMoveActive = false;
            return true;
        }
    }
    else if (event.type == sf::Event::MouseMoved)
    {
        sf::Vector2f localMousePos = { event.mouseMove.x - getWorldLeft() , event.mouseMove.y - getWorldTop() };
        if (m_scrollbarVerMoveActive)
        {
            // Move vertical scollbar by mouse movement
            m_scrollbarVertical.setPositionAndStoreOld(m_scrollbarVertical.getPosition().x, localMousePos.y - m_scrollbarVerMoveModeRelPos.y);
            correctScrollBarPosition();
            // Only ste a offset when there is a child to move
            if (m_children.size() > 0)
            {
                // get first element
                Widget *widget = m_children.at(0).get();
                float childrenHeight = widget->getHeight();
                // Calculate the offset
                m_scrollOffsetY = ( (m_scrollbarVertical.getLastPosition().y - m_scrollbarVertical.getPosition().y) / (getHeight() - 2 * SCROLLBAR_PAD_VER ) ) * childrenHeight;
                return true;
            }
        }
        if (m_scrollbarHorMoveActive)
        {
            // Move horizontal scollbar by mouse movement
            m_scrollbarHorizontal.setPositionAndStoreOld(localMousePos.x - m_scrollbarHorMoveModeRelPos.x, m_scrollbarHorizontal.getPosition().y);
            correctScrollBarPosition();
            // Only ste a offset when there is a child to move
            if (m_children.size() > 0)
            {
                // get first element
                Widget *widget = m_children.at(0).get();
                float childrenWidth = widget->getWidth();
                // Calculate the offset
                m_scrollOffsetX = ( (m_scrollbarHorizontal.getLastPosition().x - m_scrollbarHorizontal.getPosition().x) / (getWidth() - 2 * SCROLLBAR_PAD_HOR ) ) * childrenWidth;
                return true;
            }
        }

    }
    return false;
}
/*
bool gsf::ScrollableWidget::handleEvent(sf::Event &event)
{
    bool handled = Widget::handleEvent(event);
    if (!handleSpecialEvents(event) && !handleEventChildren(event))
    {
        return handleEventCurrent(event);
    }
    return handled;
}
*/
bool gsf::ScrollableWidget::handleEventCurrent(sf::Event &event)
{
    return false;
}

bool gsf::ScrollableWidget::handleEventChildren(sf::Event &event)
{
    // Only handle child Events if the event is in the are of the
    // shown area (Not the real area)
    if (!isIntersecting(sf::Vector2f(event.mouseButton.x , event.mouseButton.y))) {
        return false;
    }
    for (const Ptr &child : m_children)
    {
        if (child->handleEventWidget(event))
        {
            return true;
        }
    }
    return false;
}

void gsf::ScrollableWidget::updateCurrent(float dt)
{
    // Do to: a scrollwidgit should only have one child
    for (const Ptr &child : m_children)
    {
        child->move(m_scrollOffsetX, m_scrollOffsetY);
        // Correct the position of the childs when there are out of the bounds and scrolling is needed
        if (child->getBottom() <= getHeight() && m_isVerticalScrollNeeded)
        {
            child->move(0.f, getHeight() - child->getBottom() );
        }
        if (child->getTop() > 0.f && m_isVerticalScrollNeeded)
        {
            child->move(0.f, 0.f - child->getTop() );
        }
        if (child->getRight() <= getWidth() && m_isHorizontalScrollNeeded)
        {
            child->move(getWidth() - child->getRight(), 0.f);
        }
        if (child->getLeft() > 0.f && m_isHorizontalScrollNeeded)
        {
            child->move(0.f - child->getLeft(), 0.f);
        }
    }
    // Scrolling handled
    m_scrollOffsetY = 0.f;
    m_scrollOffsetX = 0.f;
}

void gsf::ScrollableWidget::childAdded()
{
    calculateScrollbarSizes();
}

void gsf::ScrollableWidget::childRemoved()
{
    calculateScrollbarSizes();
}
<commit_msg>Vertical and Horizontal Scrollbar cant overlap any more<commit_after>#include "ScrollableWidget.hpp"
#include <iostream>

gsf::ScrollableWidget::ScrollableWidget(float width, float height)
: ChildWidget(width, height)
, m_totalWidth{ width }
, m_totalHeight{ height }
, m_scrollOffsetX{ 0.f }
, m_scrollOffsetY{ 0.f }
, m_scrollSpeed{ 6.0f }
, m_isVerticalScrollEnabled{ true }
, m_isHorizontalScrollEnabled{ true }
, m_scrollbarThickness{ 16.f }
, m_isVerticalScrollNeeded{ true }
, m_scrollbarVertical{ m_scrollbarThickness, 0.f }
, m_scrollbarVerMoveActive{ false }
, m_isHorizontalScrollNeeded{ true }
, m_scrollbarHorizontal{ 0.f, m_scrollbarThickness }
, m_scrollbarHorMoveActive{ false }
, SCROLLBAR_PAD_HOR{ 6.f }
, SCROLLBAR_PAD_VER{ 6.f }
{
    //m_scrollbarVertical.setPosition(getRight() - m_scrollbarVertical.getWidth() / 2.f - 3.f, m_scrollbarVertical.getHeight() / 2.f + 3.f );
    calculateScrollbarSizes();
}

gsf::ScrollableWidget::~ScrollableWidget()
{

}

void gsf::ScrollableWidget::calculateVerticalScrollbarSize()
{
    if (m_children.size() < 1)
    {
        return;
    }

    // get first element
    Widget *childWidget = { m_children.at(0).get() };
    float childrenHeight = { childWidget->getHeight() };

    // Vertical Scrollbar
    // check if there is any need for scrolling (Child is higher then ScrollableWidgets height or outside)
    m_isVerticalScrollNeeded = childWidget->getTop() < 0.f || childWidget->getBottom() > getHeight() ||
        childWidget->getHeight() > getHeight();
    // Only show scrollbar when there is any need to scroll and scrolling is enabled
    if (!m_isVerticalScrollEnabled || !m_isVerticalScrollNeeded) {
        return;
    }
    // Get proportion between the scrollable widget and its child ( We need to note the scrollbar thickness so the
    // two scrollbars can not overlap
    float proportionVer = { (getHeight() - m_scrollbarThickness) / childrenHeight };
    // Calculate the scrollbar size
    float scrollbarHeight = { (getHeight() - 2 * SCROLLBAR_PAD_VER) * proportionVer };
    m_scrollbarVertical.setHeight(scrollbarHeight);
    m_scrollbarVertical.setPosition(getWidth() - m_scrollbarVertical.getWidth() / 2.f - 3.f, 0.f + m_scrollbarVertical.getHeight() / 2.f + SCROLLBAR_PAD_VER);
    m_scrollbarVertical.setOrigin(m_scrollbarVertical.getWidth() / 2.f, m_scrollbarVertical.getHeight() / 2.f);
}

void gsf::ScrollableWidget::calculateHorizontalScrollbarSize()
{
        if (m_children.size() < 1)
        {
            return;
        }

        // get first element
        Widget *childWidget = { m_children.at(0).get() };
        float childrenWidth = { childWidget->getWidth() };

        // Horizontal Scrollbar
        m_isHorizontalScrollNeeded = childWidget->getLeft() < 0.f || childWidget->getRight() > getWidth() ||
            childWidget->getWidth() > getWidth();
        // Only show scrollbar when there is any need to scroll and scrolling is enabled
        if (!m_isHorizontalScrollEnabled || !m_isHorizontalScrollNeeded) {
            return;
        }
        // Get proportion between the scrollable widget and its child ( We need to note the scrollbar thickness so the
        // two scrollbars can not overlap
        float proportionHor = { (getWidth() - m_scrollbarThickness) / childrenWidth };
        // Calculate the scrollbar size
        float scrollbarWidth = { (getWidth() - 2 * SCROLLBAR_PAD_HOR) * proportionHor };
        m_scrollbarHorizontal.setWidth(scrollbarWidth);
        m_scrollbarHorizontal.setPosition(0.f + m_scrollbarHorizontal.getWidth() / 2.f + SCROLLBAR_PAD_HOR, getHeight() - m_scrollbarHorizontal.getHeight() / 2.f - 3.f);
        m_scrollbarHorizontal.setOrigin(m_scrollbarHorizontal.getWidth() / 2.f, m_scrollbarHorizontal.getHeight() / 2.f);
}

void gsf::ScrollableWidget::calculateScrollbarSizes()
{
    if (m_children.size() < 1)
    {
        return;
    }
    calculateVerticalScrollbarSize();
    calculateHorizontalScrollbarSize();
}

void gsf::ScrollableWidget::setIsVerticalScrollEnabled(bool isEnabled)
{
    m_isVerticalScrollEnabled = isEnabled;
}

bool gsf::ScrollableWidget::isVerticalScrollEnabled() const
{
    return m_isVerticalScrollEnabled;
}
void gsf::ScrollableWidget::setIsHorizontalScrollEnabled(bool isEnabled)
{
    m_isHorizontalScrollEnabled = isEnabled;
}

bool gsf::ScrollableWidget::isHorizontalScrollEnabled() const
{
    return m_isHorizontalScrollEnabled;
}

float gsf::ScrollableWidget::getTotalWidth() const
{
    return m_totalWidth;
}

float gsf::ScrollableWidget::getTotalHeight() const
{
    return m_totalHeight;
}

void gsf::ScrollableWidget::correctScrollBarPosition()
{
    // Vertical
    // If scrollbar is out of widget, correct its position
    if (m_scrollbarVertical.getTop() < 0.f + SCROLLBAR_PAD_VER) {
        m_scrollbarVertical.setPosition(m_scrollbarVertical.getPosition().x, 0.f + m_scrollbarVertical.getHeight() / 2.f + SCROLLBAR_PAD_VER);
    }
    else if (m_scrollbarVertical.getBottom() > getHeight() - SCROLLBAR_PAD_VER - m_scrollbarThickness) {
        m_scrollbarVertical.setPosition(m_scrollbarVertical.getPosition().x, getHeight() - m_scrollbarVertical.getHeight() / 2.f - SCROLLBAR_PAD_VER - m_scrollbarThickness);
    }
    // Horizontal
    if (m_scrollbarHorizontal.getLeft() < 0.f + SCROLLBAR_PAD_HOR) {
        m_scrollbarHorizontal.setPosition(0.f + m_scrollbarHorizontal.getWidth() / 2.f + SCROLLBAR_PAD_HOR, m_scrollbarHorizontal.getPosition().y);
    }
    else if (m_scrollbarHorizontal.getRight() > getWidth() - SCROLLBAR_PAD_VER - m_scrollbarThickness) {
        m_scrollbarHorizontal.setPosition(getWidth() - m_scrollbarHorizontal.getWidth() / 2.f - SCROLLBAR_PAD_HOR - m_scrollbarThickness, m_scrollbarHorizontal.getPosition().y);
    }
}

void gsf::ScrollableWidget::drawWidget(sf::RenderTarget &target, sf::RenderStates states) const
{
        states.transform *= getTransform();

        drawCurrent(target, states);

        // We change the view of the target, so that only the area of the widget and its child
        // which are in its shown area are drawn on the RenderTarget
        sf::View defaultView = target.getView();
        sf::View view = { getShownAreaView(target) };
        //sf::View view = defaultView;

        target.setView(view);

        drawChildren(target, states);
        //Widget::draw(target, states);

        target.setView(defaultView);

        // Draw Scroll Elements
        target.draw(m_scrollbarVertical, states);
        target.draw(m_scrollbarHorizontal, states);

}

void gsf::ScrollableWidget::drawCurrent(sf::RenderTarget &target, sf::RenderStates states) const
{
    // Draw background
    sf::RectangleShape bgShape({ getWidth(), getHeight() });
    bgShape.setFillColor(m_bgColor);
    target.draw(bgShape, states);
}

bool gsf::ScrollableWidget::handleSpecialEvents(sf::Event &event)
{
    // Is the mouse in the shown area of the widget
    bool isMouseInShownArea = { getShownArea().contains(sf::Vector2f(event.mouseButton.x , event.mouseButton.y)) };
    if (event.type == sf::Event::MouseWheelMoved &&
        isMouseInShownArea &&
        m_isVerticalScrollNeeded &&
        m_isVerticalScrollEnabled)
    {
        m_scrollOffsetY = { event.mouseWheel.delta * m_scrollSpeed };
        // We have to move the scrollbar too when we scroll with the scroll wheel
        if (m_children.size() > 0)
        {
            // get first element
            Widget *widget = m_children.at(0).get();
            float childHeight = widget->getHeight();
            // Calculate the offset
            float moveScr = -(m_scrollOffsetY * getHeight()) / childHeight;
            m_scrollbarVertical.moveAndStoreOldPos(0.f, moveScr);
            correctScrollBarPosition();
        }
        return true;
    }
    else if (event.type == sf::Event::MouseButtonPressed && isMouseInShownArea)
    {
        // We need the mouse pos as local position in the ScrollWidget
        sf::Vector2f localMousePos = { event.mouseButton.x - getWorldLeft() , event.mouseButton.y - getWorldTop() };
        // Vertical scrollbar
        if (event.mouseButton.button == sf::Mouse::Left &&
            m_scrollbarVertical.isPointIntersecting({ localMousePos.x , localMousePos.y }) &&
            m_isVerticalScrollNeeded &&
            m_isVerticalScrollEnabled)
        {
            m_scrollbarVerMoveActive = true;
            m_scrollbarVerMoveModeRelPos.x = localMousePos.x - m_scrollbarVertical.getPosition().x;
            m_scrollbarVerMoveModeRelPos.y = localMousePos.y - m_scrollbarVertical.getPosition().y;
            return true;
        }
        // Horizontal scrollbar
        else if (event.mouseButton.button == sf::Mouse::Left &&
            m_scrollbarHorizontal.isPointIntersecting({ localMousePos.x , localMousePos.y }) &&
            m_isHorizontalScrollNeeded &&
            m_isHorizontalScrollEnabled)
        {
            m_scrollbarHorMoveActive = true;
            m_scrollbarHorMoveModeRelPos.x = localMousePos.x - m_scrollbarHorizontal.getPosition().x;
            m_scrollbarHorMoveModeRelPos.y = localMousePos.y - m_scrollbarHorizontal.getPosition().y;
            return true;
        }
    }
    else if (event.type == sf::Event::MouseButtonReleased)
    {
        if (event.mouseButton.button == sf::Mouse::Left)
        {
            m_scrollbarVerMoveActive = false;
            m_scrollbarHorMoveActive = false;
            return true;
        }
    }
    else if (event.type == sf::Event::MouseMoved)
    {
        sf::Vector2f localMousePos = { event.mouseMove.x - getWorldLeft() , event.mouseMove.y - getWorldTop() };
        if (m_scrollbarVerMoveActive)
        {
            // Move vertical scollbar by mouse movement
            m_scrollbarVertical.setPositionAndStoreOld(m_scrollbarVertical.getPosition().x, localMousePos.y - m_scrollbarVerMoveModeRelPos.y);
            correctScrollBarPosition();
            // Only ste a offset when there is a child to move
            if (m_children.size() > 0)
            {
                // get first element
                Widget *widget = m_children.at(0).get();
                float childrenHeight = widget->getHeight();
                // Calculate the offset
                m_scrollOffsetY = ( (m_scrollbarVertical.getLastPosition().y - m_scrollbarVertical.getPosition().y) / (getHeight() - 2 * SCROLLBAR_PAD_VER ) ) * childrenHeight;
                return true;
            }
        }
        if (m_scrollbarHorMoveActive)
        {
            // Move horizontal scollbar by mouse movement
            m_scrollbarHorizontal.setPositionAndStoreOld(localMousePos.x - m_scrollbarHorMoveModeRelPos.x, m_scrollbarHorizontal.getPosition().y);
            correctScrollBarPosition();
            // Only ste a offset when there is a child to move
            if (m_children.size() > 0)
            {
                // get first element
                Widget *widget = m_children.at(0).get();
                float childrenWidth = widget->getWidth();
                // Calculate the offset
                m_scrollOffsetX = ( (m_scrollbarHorizontal.getLastPosition().x - m_scrollbarHorizontal.getPosition().x) / (getWidth() - 2 * SCROLLBAR_PAD_HOR ) ) * childrenWidth;
                return true;
            }
        }

    }
    return false;
}
/*
bool gsf::ScrollableWidget::handleEvent(sf::Event &event)
{
    bool handled = Widget::handleEvent(event);
    if (!handleSpecialEvents(event) && !handleEventChildren(event))
    {
        return handleEventCurrent(event);
    }
    return handled;
}
*/
bool gsf::ScrollableWidget::handleEventCurrent(sf::Event &event)
{
    return false;
}

bool gsf::ScrollableWidget::handleEventChildren(sf::Event &event)
{
    // Only handle child Events if the event is in the are of the
    // shown area (Not the real area)
    if (!isIntersecting(sf::Vector2f(event.mouseButton.x , event.mouseButton.y))) {
        return false;
    }
    for (const Ptr &child : m_children)
    {
        if (child->handleEventWidget(event))
        {
            return true;
        }
    }
    return false;
}

void gsf::ScrollableWidget::updateCurrent(float dt)
{
    // Do to: a scrollwidgit should only have one child
    for (const Ptr &child : m_children)
    {
        child->move(m_scrollOffsetX, m_scrollOffsetY);
        // Correct the position of the childs when there are out of the bounds and scrolling is needed
        if (child->getBottom() <= getHeight() && m_isVerticalScrollNeeded)
        {
            child->move(0.f, getHeight() - child->getBottom() );
        }
        if (child->getTop() > 0.f && m_isVerticalScrollNeeded)
        {
            child->move(0.f, 0.f - child->getTop() );
        }
        if (child->getRight() <= getWidth() && m_isHorizontalScrollNeeded)
        {
            child->move(getWidth() - child->getRight(), 0.f);
        }
        if (child->getLeft() > 0.f && m_isHorizontalScrollNeeded)
        {
            child->move(0.f - child->getLeft(), 0.f);
        }
    }
    // Scrolling handled
    m_scrollOffsetY = 0.f;
    m_scrollOffsetX = 0.f;
}

void gsf::ScrollableWidget::childAdded()
{
    calculateScrollbarSizes();
}

void gsf::ScrollableWidget::childRemoved()
{
    calculateScrollbarSizes();
}
<|endoftext|>


<commit_before>#include "CppUTest/TestHarness.h"
#include "timelib.h"
#include <string.h>

TEST_GROUP(transitions)
{
};

#define TEST_EXISTS(n,tz) \
	TEST(transitions, n) { \
		return; \
		int error; \
		timelib_tzinfo *tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &error); \
		CHECK(tzi != NULL); \
		CHECK(error == TIMELIB_ERROR_NO_ERROR || error == TIMELIB_ERROR_SLIM_FILE); \
		timelib_dump_tzinfo(tzi); \
		timelib_tzinfo_dtor(tzi); \
	}

#define TEST_TRANSITION(n,tz,ts,eab,eoff) \
	TEST(transitions, n) { \
		int error; \
		timelib_time_offset *tto; \
		timelib_tzinfo *tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &error); \
		CHECK(tzi != NULL); \
		tto = timelib_get_time_zone_info((ts), tzi); \
		CHECK(tto != NULL); \
		LONGS_EQUAL((eoff), tto->offset); \
		STRCMP_EQUAL((eab), tto->abbr); \
		timelib_time_offset_dtor(tto); \
		timelib_tzinfo_dtor(tzi); \
	}

TEST_EXISTS(utc_00, "UTC")
TEST_TRANSITION(utc_01, "UTC", INT64_MIN / 2, "UTC", 0)
TEST_TRANSITION(utc_02, "UTC", 0,         "UTC", 0)
TEST_TRANSITION(utc_03, "UTC", INT64_MAX / 2, "UTC", 0)

TEST_EXISTS(tokyo_00, "Asia/Tokyo")
TEST_TRANSITION(tokyo_01, "Asia/Tokyo",   INT64_MIN, "LMT", 33539)
TEST_TRANSITION(tokyo_02, "Asia/Tokyo", -2587712401, "LMT", 33539)
TEST_TRANSITION(tokyo_03, "Asia/Tokyo", -2587712400, "JST", 32400)
TEST_TRANSITION(tokyo_04, "Asia/Tokyo", -2587712399, "JST", 32400)
TEST_TRANSITION(tokyo_05, "Asia/Tokyo",  -577962001, "JDT", 36000)
TEST_TRANSITION(tokyo_06, "Asia/Tokyo",  -577962000, "JST", 32400)
TEST_TRANSITION(tokyo_07, "Asia/Tokyo",  -577961999, "JST", 32400)
TEST_TRANSITION(tokyo_08, "Asia/Tokyo",           0, "JST", 32400)
TEST_TRANSITION(tokyo_09, "Asia/Tokyo",   INT64_MAX, "JST", 32400)

TEST_EXISTS(ams_00, "Europe/Amsterdam")
TEST_TRANSITION(ams_01, "Europe/Amsterdam",   INT64_MIN, "LMT",   1172)
TEST_TRANSITION(ams_02, "Europe/Amsterdam", -4260212372, "AMT",   1172)
TEST_TRANSITION(ams_03, "Europe/Amsterdam", -1025745573, "NST",   4772)
TEST_TRANSITION(ams_04, "Europe/Amsterdam", -1025745572, "+0120", 4800)
TEST_TRANSITION(ams_05, "Europe/Amsterdam",   811904399, "CEST",  7200)
TEST_TRANSITION(ams_06, "Europe/Amsterdam",   811904440, "CET",   3600)
TEST_TRANSITION(ams_07, "Europe/Amsterdam",   828234000, "CEST",  7200)

TEST_TRANSITION(ams_08, "Europe/Amsterdam",   846377999, "CEST",  7200)
TEST_TRANSITION(ams_09, "Europe/Amsterdam",   846378000, "CET",   3600)
TEST_TRANSITION(ams_10, "Europe/Amsterdam",   846378001, "CET",   3600)

TEST_TRANSITION(ams_11, "Europe/Amsterdam",   859683599, "CET",   3600)
TEST_TRANSITION(ams_12, "Europe/Amsterdam",   859683600, "CEST",  7200)
TEST_TRANSITION(ams_13, "Europe/Amsterdam",   859683600, "CEST",  7200)

TEST_EXISTS(can_00, "Australia/Canberra")
TEST_TRANSITION(can_01, "Australia/Canberra",   1193500799, "AEST", 36000)
TEST_TRANSITION(can_02, "Australia/Canberra",   1193500800, "AEDT", 39600)
TEST_TRANSITION(can_03, "Australia/Canberra",   1193500801, "AEDT", 39600)

TEST_TRANSITION(can_04, "Australia/Canberra",   1207411199, "AEDT", 39600)
TEST_TRANSITION(can_05, "Australia/Canberra",   1207411200, "AEST", 36000)
TEST_TRANSITION(can_06, "Australia/Canberra",   1207411201, "AEST", 36000)

TEST_TRANSITION(can_07, "Australia/Canberra",   1223135999, "AEST", 36000)
TEST_TRANSITION(can_08, "Australia/Canberra",   1223136000, "AEDT", 39600)
TEST_TRANSITION(can_09, "Australia/Canberra",   1223136001, "AEDT", 39600)

TEST_TRANSITION(can_10, "Australia/Canberra",   1238860799, "AEDT", 39600)
TEST_TRANSITION(can_11, "Australia/Canberra",   1238860800, "AEST", 36000)
TEST_TRANSITION(can_12, "Australia/Canberra",   1238860801, "AEST", 36000)

TEST_EXISTS(lh_00, "Australia/Lord_Howe")
TEST_TRANSITION(lh_01, "Australia/Lord_Howe", 1207407599, "+11",   39600)
TEST_TRANSITION(lh_02, "Australia/Lord_Howe", 1207407600, "+1030", 37800)
TEST_TRANSITION(lh_03, "Australia/Lord_Howe", 1207407601, "+1030", 37800)

TEST_TRANSITION(lh_04, "Australia/Lord_Howe", 1317482999, "+1030", 37800)
TEST_TRANSITION(lh_05, "Australia/Lord_Howe", 1317483000, "+11",   39600)
TEST_TRANSITION(lh_06, "Australia/Lord_Howe", 1317483001, "+11",   39600)

TEST_TRANSITION(lh_07, "Australia/Lord_Howe", 1365260399, "+11",   39600)
TEST_TRANSITION(lh_08, "Australia/Lord_Howe", 1365260400, "+1030", 37800)
TEST_TRANSITION(lh_09, "Australia/Lord_Howe", 1365260401, "+1030", 37800)

// local new year 2011
TEST_TRANSITION(lh_10, "Australia/Lord_Howe", 1293800399, "+11",   39600)
TEST_TRANSITION(lh_11, "Australia/Lord_Howe", 1293800400, "+11",   39600)
TEST_TRANSITION(lh_12, "Australia/Lord_Howe", 1293800401, "+11",   39600)

// UT new year 2011
TEST_TRANSITION(lh_13, "Australia/Lord_Howe", 1293839999, "+11",   39600)
TEST_TRANSITION(lh_14, "Australia/Lord_Howe", 1293840000, "+11",   39600)
TEST_TRANSITION(lh_15, "Australia/Lord_Howe", 1293840001, "+11",   39600)

// Fiji has a large time-of-effect hour of 99
TEST_EXISTS(fiji_00, "Pacific/Fiji")
TEST_TRANSITION(fiji_01, "Pacific/Fiji", 1608386399, "+12", 43200)
TEST_TRANSITION(fiji_02, "Pacific/Fiji", 1608386400, "+13", 46800)
TEST_TRANSITION(fiji_03, "Pacific/Fiji", 1608386401, "+13", 46800)

// Asia/Tehran is the only one using J, and only for 2088, but still test it
TEST_EXISTS(teh_00, "Asia/Tehran")
TEST_TRANSITION(teh_01, "Asia/Tehran", 3746546999, "+0430", 16200)
TEST_TRANSITION(teh_02, "Asia/Tehran", 3746547000, "+0330", 12600)
TEST_TRANSITION(teh_03, "Asia/Tehran", 3746547001, "+0330", 12600)

// Has a switch at a non-hour barrier
TEST_EXISTS(chat_00, "Pacific/Chatham")
TEST_TRANSITION(chat_01, "Pacific/Chatham", 1821880799, "+1245", 45900)
TEST_TRANSITION(chat_02, "Pacific/Chatham", 1821880800, "+1345", 49500)
TEST_TRANSITION(chat_03, "Pacific/Chatham", 1821880801, "+1345", 49500)

#define TEST_TRANSITION_FROM_TEST_DIR(n,tz,ts,eab,eoff) \
	TEST(transitions, n) { \
		int error; \
		timelib_time_offset *tto; \
		timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); \
		CHECK(test_directory != NULL); \
		timelib_tzinfo *tzi = timelib_parse_tzfile(tz, test_directory, &error); \
		CHECK(tzi != NULL); \
		tto = timelib_get_time_zone_info((ts), tzi); \
		CHECK(tto != NULL); \
		LONGS_EQUAL((eoff), tto->offset); \
		STRCMP_EQUAL((eab), tto->abbr); \
		timelib_time_offset_dtor(tto); \
		timelib_tzinfo_dtor(tzi); \
		timelib_zoneinfo_dtor(test_directory); \
	}

// With a fake NY file, where I've modified the POSIX string to full year DST
TEST_TRANSITION_FROM_TEST_DIR(ny_01, "New_York_mod_Full_Year_DST", 1615483094, "EDT", -14400)
TEST_TRANSITION_FROM_TEST_DIR(ny_02, "New_York_mod_Full_Year_DST", 1609477199, "EDT", -14400)
TEST_TRANSITION_FROM_TEST_DIR(ny_03, "New_York_mod_Full_Year_DST", 1609477200, "EDT", -14400)
TEST_TRANSITION_FROM_TEST_DIR(ny_04, "New_York_mod_Full_Year_DST", 1609477201, "EDT", -14400)
<commit_msg>Also test the DST flag, because of Ireland, which has negative DST<commit_after>#include "CppUTest/TestHarness.h"
#include "timelib.h"
#include <string.h>

TEST_GROUP(transitions)
{
};

#define TEST_EXISTS(n,tz) \
	TEST(transitions, n) { \
		return; \
		int error; \
		timelib_tzinfo *tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &error); \
		CHECK(tzi != NULL); \
		CHECK(error == TIMELIB_ERROR_NO_ERROR || error == TIMELIB_ERROR_SLIM_FILE); \
		timelib_dump_tzinfo(tzi); \
		timelib_tzinfo_dtor(tzi); \
	}

#define TEST_TRANSITION(n,tz,ts,eab,eoff,edst) \
	TEST(transitions, n) { \
		int error; \
		timelib_time_offset *tto; \
		timelib_tzinfo *tzi = timelib_parse_tzfile(tz, timelib_builtin_db(), &error); \
		CHECK(tzi != NULL); \
		tto = timelib_get_time_zone_info((ts), tzi); \
		CHECK(tto != NULL); \
		LONGS_EQUAL((eoff), tto->offset); \
		STRCMP_EQUAL((eab), tto->abbr); \
		LONGS_EQUAL((edst), tto->is_dst); \
		timelib_time_offset_dtor(tto); \
		timelib_tzinfo_dtor(tzi); \
	}

TEST_EXISTS(utc_00, "UTC")
TEST_TRANSITION(utc_01, "UTC", INT64_MIN / 2, "UTC", 0, 0)
TEST_TRANSITION(utc_02, "UTC", 0,             "UTC", 0, 0)
TEST_TRANSITION(utc_03, "UTC", INT64_MAX / 2, "UTC", 0, 0)

TEST_EXISTS(tokyo_00, "Asia/Tokyo")
TEST_TRANSITION(tokyo_01, "Asia/Tokyo",   INT64_MIN, "LMT", 33539, 0)
TEST_TRANSITION(tokyo_02, "Asia/Tokyo", -2587712401, "LMT", 33539, 0)
TEST_TRANSITION(tokyo_03, "Asia/Tokyo", -2587712400, "JST", 32400, 0)
TEST_TRANSITION(tokyo_04, "Asia/Tokyo", -2587712399, "JST", 32400, 0)
TEST_TRANSITION(tokyo_05, "Asia/Tokyo",  -577962001, "JDT", 36000, 1)
TEST_TRANSITION(tokyo_06, "Asia/Tokyo",  -577962000, "JST", 32400, 0)
TEST_TRANSITION(tokyo_07, "Asia/Tokyo",  -577961999, "JST", 32400, 0)
TEST_TRANSITION(tokyo_08, "Asia/Tokyo",           0, "JST", 32400, 0)
TEST_TRANSITION(tokyo_09, "Asia/Tokyo",   INT64_MAX, "JST", 32400, 0)

TEST_EXISTS(ams_00, "Europe/Amsterdam")
TEST_TRANSITION(ams_01, "Europe/Amsterdam",   INT64_MIN, "LMT",   1172, 0)
TEST_TRANSITION(ams_02, "Europe/Amsterdam", -4260212372, "AMT",   1172, 0)
TEST_TRANSITION(ams_03, "Europe/Amsterdam", -1025745573, "NST",   4772, 1)
TEST_TRANSITION(ams_04, "Europe/Amsterdam", -1025745572, "+0120", 4800, 1)
TEST_TRANSITION(ams_05, "Europe/Amsterdam",   811904399, "CEST",  7200, 1)
TEST_TRANSITION(ams_06, "Europe/Amsterdam",   811904440, "CET",   3600, 0)
TEST_TRANSITION(ams_07, "Europe/Amsterdam",   828234000, "CEST",  7200, 1)

TEST_TRANSITION(ams_08, "Europe/Amsterdam",   846377999, "CEST",  7200, 1)
TEST_TRANSITION(ams_09, "Europe/Amsterdam",   846378000, "CET",   3600, 0)
TEST_TRANSITION(ams_10, "Europe/Amsterdam",   846378001, "CET",   3600, 0)

TEST_TRANSITION(ams_11, "Europe/Amsterdam",   859683599, "CET",   3600, 0)
TEST_TRANSITION(ams_12, "Europe/Amsterdam",   859683600, "CEST",  7200, 1)
TEST_TRANSITION(ams_13, "Europe/Amsterdam",   859683600, "CEST",  7200, 1)

TEST_EXISTS(can_00, "Australia/Canberra")
TEST_TRANSITION(can_01, "Australia/Canberra",   1193500799, "AEST", 36000, 0)
TEST_TRANSITION(can_02, "Australia/Canberra",   1193500800, "AEDT", 39600, 1)
TEST_TRANSITION(can_03, "Australia/Canberra",   1193500801, "AEDT", 39600, 1)

TEST_TRANSITION(can_04, "Australia/Canberra",   1207411199, "AEDT", 39600, 1)
TEST_TRANSITION(can_05, "Australia/Canberra",   1207411200, "AEST", 36000, 0)
TEST_TRANSITION(can_06, "Australia/Canberra",   1207411201, "AEST", 36000, 0)

TEST_TRANSITION(can_07, "Australia/Canberra",   1223135999, "AEST", 36000, 0)
TEST_TRANSITION(can_08, "Australia/Canberra",   1223136000, "AEDT", 39600, 1)
TEST_TRANSITION(can_09, "Australia/Canberra",   1223136001, "AEDT", 39600, 1)

TEST_TRANSITION(can_10, "Australia/Canberra",   1238860799, "AEDT", 39600, 1)
TEST_TRANSITION(can_11, "Australia/Canberra",   1238860800, "AEST", 36000, 0)
TEST_TRANSITION(can_12, "Australia/Canberra",   1238860801, "AEST", 36000, 0)

TEST_EXISTS(lh_00, "Australia/Lord_Howe")
TEST_TRANSITION(lh_01, "Australia/Lord_Howe", 1207407599, "+11",   39600, 1)
TEST_TRANSITION(lh_02, "Australia/Lord_Howe", 1207407600, "+1030", 37800, 0)
TEST_TRANSITION(lh_03, "Australia/Lord_Howe", 1207407601, "+1030", 37800, 0)

TEST_TRANSITION(lh_04, "Australia/Lord_Howe", 1317482999, "+1030", 37800, 0)
TEST_TRANSITION(lh_05, "Australia/Lord_Howe", 1317483000, "+11",   39600, 1)
TEST_TRANSITION(lh_06, "Australia/Lord_Howe", 1317483001, "+11",   39600, 1)

TEST_TRANSITION(lh_07, "Australia/Lord_Howe", 1365260399, "+11",   39600, 1)
TEST_TRANSITION(lh_08, "Australia/Lord_Howe", 1365260400, "+1030", 37800, 0)
TEST_TRANSITION(lh_09, "Australia/Lord_Howe", 1365260401, "+1030", 37800, 0)

// local new year 2011
TEST_TRANSITION(lh_10, "Australia/Lord_Howe", 1293800399, "+11",   39600, 1)
TEST_TRANSITION(lh_11, "Australia/Lord_Howe", 1293800400, "+11",   39600, 1)
TEST_TRANSITION(lh_12, "Australia/Lord_Howe", 1293800401, "+11",   39600, 1)

// UT new year 2011
TEST_TRANSITION(lh_13, "Australia/Lord_Howe", 1293839999, "+11",   39600, 1)
TEST_TRANSITION(lh_14, "Australia/Lord_Howe", 1293840000, "+11",   39600, 1)
TEST_TRANSITION(lh_15, "Australia/Lord_Howe", 1293840001, "+11",   39600, 1)

// Fiji has a large time-of-effect hour of 99
TEST_EXISTS(fiji_00, "Pacific/Fiji")
TEST_TRANSITION(fiji_01, "Pacific/Fiji", 1608386399, "+12", 43200, 0)
TEST_TRANSITION(fiji_02, "Pacific/Fiji", 1608386400, "+13", 46800, 1)
TEST_TRANSITION(fiji_03, "Pacific/Fiji", 1608386401, "+13", 46800, 1)

// Asia/Tehran is the only one using J, and only for 2088, but still test it
TEST_EXISTS(teh_00, "Asia/Tehran")
TEST_TRANSITION(teh_01, "Asia/Tehran", 3746546999, "+0430", 16200, 1)
TEST_TRANSITION(teh_02, "Asia/Tehran", 3746547000, "+0330", 12600, 0)
TEST_TRANSITION(teh_03, "Asia/Tehran", 3746547001, "+0330", 12600, 0)

// Has a switch at a non-hour barrier
TEST_EXISTS(chat_00, "Pacific/Chatham")
TEST_TRANSITION(chat_01, "Pacific/Chatham", 1821880799, "+1245", 45900, 0)
TEST_TRANSITION(chat_02, "Pacific/Chatham", 1821880800, "+1345", 49500, 1)
TEST_TRANSITION(chat_03, "Pacific/Chatham", 1821880801, "+1345", 49500, 1)

// Ireland has negative DST
TEST_EXISTS(eire_00, "Europe/Dublin")
TEST_TRANSITION(eire_01, "Europe/Dublin", 859683599, "GMT",    0, 1)
TEST_TRANSITION(eire_02, "Europe/Dublin", 859683600, "IST", 3600, 0)
TEST_TRANSITION(eire_03, "Europe/Dublin", 859683601, "IST", 3600, 0)

#define TEST_TRANSITION_FROM_TEST_DIR(n,tz,ts,eab,eoff) \
	TEST(transitions, n) { \
		int error; \
		timelib_time_offset *tto; \
		timelib_tzdb *test_directory = timelib_zoneinfo("tests/c/files"); \
		CHECK(test_directory != NULL); \
		timelib_tzinfo *tzi = timelib_parse_tzfile(tz, test_directory, &error); \
		CHECK(tzi != NULL); \
		tto = timelib_get_time_zone_info((ts), tzi); \
		CHECK(tto != NULL); \
		LONGS_EQUAL((eoff), tto->offset); \
		STRCMP_EQUAL((eab), tto->abbr); \
		timelib_time_offset_dtor(tto); \
		timelib_tzinfo_dtor(tzi); \
		timelib_zoneinfo_dtor(test_directory); \
	}

// With a fake NY file, where I've modified the POSIX string to full year DST
TEST_TRANSITION_FROM_TEST_DIR(ny_01, "New_York_mod_Full_Year_DST", 1615483094, "EDT", -14400)
TEST_TRANSITION_FROM_TEST_DIR(ny_02, "New_York_mod_Full_Year_DST", 1609477199, "EDT", -14400)
TEST_TRANSITION_FROM_TEST_DIR(ny_03, "New_York_mod_Full_Year_DST", 1609477200, "EDT", -14400)
TEST_TRANSITION_FROM_TEST_DIR(ny_04, "New_York_mod_Full_Year_DST", 1609477201, "EDT", -14400)
<|endoftext|>


<commit_before>/*
 * Copyright 2010-2012 Esrille Inc.
 *
 * 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 "U16InputStream.h"

#include <assert.h>
#include <string.h>

#include <unicode/ucnv.h>

#include <algorithm>

const char* U16InputStream::DefaultEncoding = "utf-8";

namespace {

struct Override
{
    const char* input;
    const char* replacement;
};

// cf. HTML Living Standard 13.2.2.2 Character encodings
// cf. http://bugs.icu-project.org/trac/browser/icu/trunk/source/data/mappings/convrtrs.txt
Override overrides[] = {
    { "euc-kr", "windows-949" },
    { "euc-jp", "windows-51932" },    // ucnv_open() may not understand 'CP51932'.
    { "gb2312", "GBK" },
    { "gb_2312-80", "GBK" },
    // { "iso-2022-jp", "CP50220" },  // ucnv_open() may not understand 'CP50220'; iso-2022-jp appears to be the same as CP50220 in ICU.
    { "iso-8859-1", "windows-1252" },
    { "iso-8859-9", "windows-1254" },
    { "iso-8859-11", "windows-874" },
    { "ks_c_5601-1987", "windows-949" },
    { "shift_jis", "Windows-31j" },
    { "tis-620", "windows-874" },
    { "us-ascii", "windows-1252" }
};

}  // namespace

U16InputStream::U16InputStream(std::istream& stream, const std::string& optionalEncoding) :
    confidence(Certain),
    encoding(optionalEncoding),
    stream(stream)
{
    initializeConverter();
    encoding = checkEncoding(encoding);
    if (encoding.empty())
        confidence = Tentative;
}

U16InputStream::~U16InputStream()
{
    if (converter)
        ucnv_close(converter);
}

const char* U16InputStream::skipSpace(const char* p)
{
    while (*p) {
        if (strchr(" \t\r\n\f", *p) == 0)
            return p;
        ++p;
    }
    return p;
};

const char* U16InputStream::skipOver(const char* p, const char* target, size_t length)
{
    while (*p) {
        if (strncmp(p, target, length) == 0)
            return p + length;
        ++p;
    }
    return p;
};

void U16InputStream::detect(const char* p)
{
    static char le[] = { 0x40, 0x00, 0x63, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, 0x00 };
    static char be[] = { 0x00, 0x40, 0x00, 0x63, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74 };

    if (confidence != Tentative)
        return;
    if (strncmp(p, "\xfe\xff", 2) == 0 || strncmp(p, be, sizeof(be)) == 0) {
        encoding = "utf-16be";
        confidence = Irrelevant;
        return;
    }
    if (strncmp(p, "\xff\xfe", 2) == 0 || strncmp(p, le, sizeof(le)) == 0) {
        encoding = "utf-16le";
        confidence = Irrelevant;
        return;
    }
    if (strncmp(p, "\xef\xbb\xbf", 3) == 0) {
        encoding = "utf-8";
        confidence = Irrelevant;
        return;
    }
    encoding = "";
}

std::string U16InputStream::checkEncoding(std::string value)
{
    // Remove any leading or trailing space characters
    std::string::iterator i = value.begin();
    while (i < value.end() && strchr("\t\n\f\r ", *i))
        ++i;
    value.erase(value.begin(), i);
    int j;
    for (j = value.length(); 0 < j && strchr("\t\n\f\r ", value[j - 1]); --j)
        ;
    value.erase(j, value.length());

    if (value.empty())
        return "";

    // Override character encoding
    for (Override* override = overrides;
         override < &overrides[sizeof overrides / sizeof overrides[0]];
         ++override) {
        if (!strcasecmp(value.c_str(), override->input)) {
            value = override->replacement;
            break;
        }
    }
    return value;
}

void U16InputStream::setEncoding(std::string value)
{
    value = checkEncoding(value);
    if (value.empty())
        value = DefaultEncoding;

    // Re-check encoding with ICU for conversion
    UErrorCode error = U_ZERO_ERROR;
    converter = ucnv_open(value.c_str(), &error);
    if (!converter) {
        value = DefaultEncoding;
        converter = ucnv_open(value.c_str(), &error);
        if (!converter)
            eof = true;
    }
    encoding = value;
}

void U16InputStream::initializeConverter()
{
    flush = false;
    eof = !stream;
    converter = 0;
    source = sourceLimit = sourceBuffer;
    target = targetBuffer;
    nextChar = target;
    lastChar = 0;
}

void U16InputStream::updateSource()
{
    assert(!flush);
    size_t count = sourceLimit - source;
    if (0 < count && sourceBuffer != source)
        memmove(sourceBuffer, source, count);
    source = sourceBuffer;
    sourceLimit = sourceBuffer + count;
    count = ChunkSize - count;
    if (0 < count) {
        stream.read(sourceLimit, count);
        count = stream.gcount();
        if (!converter) {
            if (encoding.empty()) {
                sourceLimit[count] = '\0';
                detect(sourceLimit);
            }
            setEncoding(encoding);
        }
        sourceLimit += count;
        if (count == 0)
            flush = true;
    }
}

void U16InputStream::readChunk()
{
    nextChar = target = targetBuffer;
    updateSource();
    UErrorCode err = U_ZERO_ERROR;
    ucnv_toUnicode(converter,
                   reinterpret_cast<UChar**>(&target),
                   reinterpret_cast<UChar*>(targetBuffer) + ChunkSize,
                   const_cast<const char**>(&source),
                   sourceLimit, 0, flush, &err);
}

U16InputStream::operator std::u16string()
{
    std::u16string text;
    char16_t c;
    while (getChar(c))
        text += c;
    return text;
}<commit_msg>(overrides) : Invalidate shift-jis; cf. at-charset-056.<commit_after>/*
 * Copyright 2010-2012 Esrille Inc.
 *
 * 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 "U16InputStream.h"

#include <assert.h>
#include <string.h>

#include <unicode/ucnv.h>

#include <algorithm>

const char* U16InputStream::DefaultEncoding = "utf-8";

namespace {

struct Override
{
    const char* input;
    const char* replacement;
};

// cf. HTML Living Standard 13.2.2.2 Character encodings
// cf. http://bugs.icu-project.org/trac/browser/icu/trunk/source/data/mappings/convrtrs.txt
Override overrides[] = {
    { "euc-kr", "windows-949" },
    { "euc-jp", "windows-51932" },    // ucnv_open() may not understand 'CP51932'.
    { "gb2312", "GBK" },
    { "gb_2312-80", "GBK" },
    // { "iso-2022-jp", "CP50220" },  // ucnv_open() may not understand 'CP50220'; iso-2022-jp appears to be the same as CP50220 in ICU.
    { "iso-8859-1", "windows-1252" },
    { "iso-8859-9", "windows-1254" },
    { "iso-8859-11", "windows-874" },
    { "ks_c_5601-1987", "windows-949" },
    { "shift_jis", "Windows-31j" },
    { "shift-jis", "" },              // ucnv_open() may accept shift-jis as shift_jis; cf. at-charset-056.
    { "tis-620", "windows-874" },
    { "us-ascii", "windows-1252" }
};

}  // namespace

U16InputStream::U16InputStream(std::istream& stream, const std::string& optionalEncoding) :
    confidence(Certain),
    encoding(optionalEncoding),
    stream(stream)
{
    initializeConverter();
    encoding = checkEncoding(encoding);
    if (encoding.empty())
        confidence = Tentative;
}

U16InputStream::~U16InputStream()
{
    if (converter)
        ucnv_close(converter);
}

const char* U16InputStream::skipSpace(const char* p)
{
    while (*p) {
        if (strchr(" \t\r\n\f", *p) == 0)
            return p;
        ++p;
    }
    return p;
};

const char* U16InputStream::skipOver(const char* p, const char* target, size_t length)
{
    while (*p) {
        if (strncmp(p, target, length) == 0)
            return p + length;
        ++p;
    }
    return p;
};

void U16InputStream::detect(const char* p)
{
    static char le[] = { 0x40, 0x00, 0x63, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74, 0x00 };
    static char be[] = { 0x00, 0x40, 0x00, 0x63, 0x00, 0x68, 0x00, 0x61, 0x00, 0x72, 0x00, 0x73, 0x00, 0x65, 0x00, 0x74 };

    if (confidence != Tentative)
        return;
    if (strncmp(p, "\xfe\xff", 2) == 0 || strncmp(p, be, sizeof(be)) == 0) {
        encoding = "utf-16be";
        confidence = Irrelevant;
        return;
    }
    if (strncmp(p, "\xff\xfe", 2) == 0 || strncmp(p, le, sizeof(le)) == 0) {
        encoding = "utf-16le";
        confidence = Irrelevant;
        return;
    }
    if (strncmp(p, "\xef\xbb\xbf", 3) == 0) {
        encoding = "utf-8";
        confidence = Irrelevant;
        return;
    }
    encoding = "";
}

std::string U16InputStream::checkEncoding(std::string value)
{
    // Remove any leading or trailing space characters
    std::string::iterator i = value.begin();
    while (i < value.end() && strchr("\t\n\f\r ", *i))
        ++i;
    value.erase(value.begin(), i);
    int j;
    for (j = value.length(); 0 < j && strchr("\t\n\f\r ", value[j - 1]); --j)
        ;
    value.erase(j, value.length());

    if (value.empty())
        return "";

    // Override character encoding
    for (Override* override = overrides;
         override < &overrides[sizeof overrides / sizeof overrides[0]];
         ++override) {
        if (!strcasecmp(value.c_str(), override->input)) {
            value = override->replacement;
            break;
        }
    }
    return value;
}

void U16InputStream::setEncoding(std::string value)
{
    value = checkEncoding(value);
    if (value.empty())
        value = DefaultEncoding;

    // Re-check encoding with ICU for conversion
    UErrorCode error = U_ZERO_ERROR;
    converter = ucnv_open(value.c_str(), &error);
    if (!converter) {
        value = DefaultEncoding;
        converter = ucnv_open(value.c_str(), &error);
        if (!converter)
            eof = true;
    }
    encoding = value;
}

void U16InputStream::initializeConverter()
{
    flush = false;
    eof = !stream;
    converter = 0;
    source = sourceLimit = sourceBuffer;
    target = targetBuffer;
    nextChar = target;
    lastChar = 0;
}

void U16InputStream::updateSource()
{
    assert(!flush);
    size_t count = sourceLimit - source;
    if (0 < count && sourceBuffer != source)
        memmove(sourceBuffer, source, count);
    source = sourceBuffer;
    sourceLimit = sourceBuffer + count;
    count = ChunkSize - count;
    if (0 < count) {
        stream.read(sourceLimit, count);
        count = stream.gcount();
        if (!converter) {
            if (encoding.empty()) {
                sourceLimit[count] = '\0';
                detect(sourceLimit);
            }
            setEncoding(encoding);
        }
        sourceLimit += count;
        if (count == 0)
            flush = true;
    }
}

void U16InputStream::readChunk()
{
    nextChar = target = targetBuffer;
    updateSource();
    UErrorCode err = U_ZERO_ERROR;
    ucnv_toUnicode(converter,
                   reinterpret_cast<UChar**>(&target),
                   reinterpret_cast<UChar*>(targetBuffer) + ChunkSize,
                   const_cast<const char**>(&source),
                   sourceLimit, 0, flush, &err);
}

U16InputStream::operator std::u16string()
{
    std::u16string text;
    char16_t c;
    while (getChar(c))
        text += c;
    return text;
}<|endoftext|>


<commit_before><commit_msg>Use setProperty instead of << operator<commit_after><|endoftext|>


<commit_before>/* This is free and unencumbered software released into the public domain. */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "rdf++/format.h"
#include "rdf++/writer/impl.h"
#include "rdf++/writer/jsonld.h"
#include "rdf++/writer/raptor.h"
#include "rdf++/writer/trix.h"

#include <cstring> /* for std::strcmp() */

using namespace rdf;

writer::implementation*
writer::implementation::create(const std::string& file_path,
                               const std::string& content_type,
                               const std::string& charset,
                               const std::string& base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }

  if (std::strcmp("raptor", format->module_name) == 0) {
    return new writer::raptor(file_path, content_type, charset, base_uri);
  }

  if (std::strcmp("jsonld", format->module_name) == 0) {
    //return new writer::jsonld(file_path, content_type, charset, base_uri);
  }

  if (std::strcmp("trix", format->module_name) == 0) {
    return new writer::trix(file_path, content_type, charset, base_uri);
  }

  throw std::invalid_argument("unknown content type: " + content_type);
}

writer::implementation*
writer::implementation::create(std::ostream& stream,
                               const std::string& content_type,
                               const std::string& charset,
                               const std::string& base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }

  if (std::strcmp("raptor", format->module_name) == 0) {
    return new writer::raptor(stream, content_type, charset, base_uri);
  }

  if (std::strcmp("jsonld", format->module_name) == 0) {
    //return new writer::jsonld(stream, content_type, charset, base_uri);
  }

  if (std::strcmp("trix", format->module_name) == 0) {
    return new writer::trix(stream, content_type, charset, base_uri);
  }

  throw std::invalid_argument("unknown content type: " + content_type);
}

writer::implementation*
writer::implementation::create(FILE* const stream,
                               const std::string& content_type,
                               const std::string& charset,
                               const std::string& base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }

  if (std::strcmp("raptor", format->module_name) == 0) {
    return new writer::raptor(stream, content_type, charset, base_uri);
  }

  if (std::strcmp("jsonld", format->module_name) == 0) {
    //return new writer::jsonld(stream, content_type, charset, base_uri);
  }

  if (std::strcmp("trix", format->module_name) == 0) {
    return new writer::trix(stream, content_type, charset, base_uri);
  }

  throw std::invalid_argument("unknown content type: " + content_type);
}
<commit_msg>Ensured that writer instantiation has the correct #ifndef guards.<commit_after>/* This is free and unencumbered software released into the public domain. */

#ifdef HAVE_CONFIG_H
#include <config.h>
#endif

#include "rdf++/format.h"
#include "rdf++/writer/impl.h"

#ifndef DISABLE_JSONLD
#include "rdf++/writer/jsonld.h"
#endif

#include "rdf++/writer/raptor.h"

#ifndef DISABLE_TRIX
#include "rdf++/writer/trix.h"
#endif

#include <cstring> /* for std::strcmp() */

using namespace rdf;

writer::implementation*
writer::implementation::create(const std::string& file_path,
                               const std::string& content_type,
                               const std::string& charset,
                               const std::string& base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }

  if (std::strcmp("raptor", format->module_name) == 0) {
    return new writer::raptor(file_path, content_type, charset, base_uri);
  }

#ifndef DISABLE_JSONLD
  if (std::strcmp("jsonld", format->module_name) == 0) {
    //return new writer::jsonld(file_path, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_TRIX
  if (std::strcmp("trix", format->module_name) == 0) {
    return new writer::trix(file_path, content_type, charset, base_uri);
  }
#endif

  throw std::invalid_argument("unknown content type: " + content_type);
}

writer::implementation*
writer::implementation::create(std::ostream& stream,
                               const std::string& content_type,
                               const std::string& charset,
                               const std::string& base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }

  if (std::strcmp("raptor", format->module_name) == 0) {
    return new writer::raptor(stream, content_type, charset, base_uri);
  }

#ifndef DISABLE_JSONLD
  if (std::strcmp("jsonld", format->module_name) == 0) {
    //return new writer::jsonld(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_TRIX
  if (std::strcmp("trix", format->module_name) == 0) {
    return new writer::trix(stream, content_type, charset, base_uri);
  }
#endif

  throw std::invalid_argument("unknown content type: " + content_type);
}

writer::implementation*
writer::implementation::create(FILE* const stream,
                               const std::string& content_type,
                               const std::string& charset,
                               const std::string& base_uri) {
  const format* const format = format::find_for_content_type(content_type);
  if (format == nullptr) {
    throw std::invalid_argument("unknown content type: " + content_type);
  }

  if (std::strcmp("raptor", format->module_name) == 0) {
    return new writer::raptor(stream, content_type, charset, base_uri);
  }

#ifndef DISABLE_JSONLD
  if (std::strcmp("jsonld", format->module_name) == 0) {
    //return new writer::jsonld(stream, content_type, charset, base_uri);
  }
#endif

#ifndef DISABLE_TRIX
  if (std::strcmp("trix", format->module_name) == 0) {
    return new writer::trix(stream, content_type, charset, base_uri);
  }
#endif

  throw std::invalid_argument("unknown content type: " + content_type);
}
<|endoftext|>