<commit_before>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * Description:
 *     a simple version of meta state service for development
 *
 * Revision history:
 *     2015-11-04, @imzhenyu (Zhenyu.Guo@microsoft.com), setup the sketch
 *     2015-11-11, Tianyi WANG, first version done
 *     xxxx-xx-xx, author, fix bug about xxx
 */

# include "meta_state_service_simple.h"
# include <dsn/internal/task.h>

# include <stack>
# include <utility>

namespace dsn
{
    namespace dist
    {
        // path: /, /n1/n2, /n1/n2/, /n2/n2/n3
        std::string meta_state_service_simple::normalize_path(const std::string& s)
        {
            if (s.empty() || s[0] != '/')
                return "";
            if (s.length() > 1 && *s.rbegin() == '/')
                return s.substr(0, s.length() - 1);
            return s;
        }

        error_code meta_state_service_simple::extract_name_parent_from_path(
            const std::string& s,
            /*out*/ std::string& name,
            /*out*/ std::string& parent
            )
        {
            auto pos = s.find_last_of('/');
            if (pos == std::string::npos)
                return ERR_INVALID_PARAMETERS;

            name = s.substr(pos + 1);
            if (pos > 0)
                parent = s.substr(0, pos);
            else
                parent = "/";
            return ERR_OK;
        }

        static void __err_cb_bind_and_enqueue(
            task_ptr lock_task,
            error_code err,
            int delay_milliseconds = 0
            )
        {
            auto t = dynamic_cast<safe_late_task<meta_state_service::err_callback>*>(lock_task.get());

            t->bind_and_enqueue(
                [&](meta_state_service::err_callback& cb)
                {
                    return bind(cb, err);
                },
                delay_milliseconds
                );
        }

        void meta_state_service_simple::write_log(blob&& log_blob, std::function<error_code()> internal_operation, task_ptr task)
        {
            _log_lock.lock();
            uint64_t log_offset = _offset;
            _offset += log_blob.length();
            auto continuation_task = std::unique_ptr<operation>(new operation(false, [=](bool log_succeed)
            {
                dassert(log_succeed, "we cannot handle logging failure now");
                __err_cb_bind_and_enqueue(task, internal_operation(), 0);
            }));
            auto continuation_task_ptr = continuation_task.get();
            _task_queue.emplace(move(continuation_task));
            _log_lock.unlock();

            file::write(
                _log,
                log_blob.buffer_ptr(),
                log_blob.length(),
                log_offset,
                LPC_META_STATE_SERVICE_SIMPLE_INTERNAL,
                this,
                [=](error_code err, size_t bytes)
                {
                    dassert(err == ERR_OK && bytes == log_blob.length(), "we cannot handle logging failure now");
                    _log_lock.lock();
                    continuation_task_ptr->done = true;
                    while (!_task_queue.empty())
                    {
                        if (!_task_queue.front()->done)
                        {
                            break;
                        }
                        _task_queue.front()->cb(true);
                        _task_queue.pop();
                    }
                    _log_lock.unlock();
                }
                );
        }

        error_code meta_state_service_simple::create_node_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it != _quick_map.end())
                return ERR_NODE_ALREADY_EXIST;

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            if (parent_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;

            state_node* n = new state_node(name, parent_it->second, value);
            parent_it->second->children.insert(quick_map::value_type(name, n));
            _quick_map.insert(quick_map::value_type(path, n));
            return ERR_OK;
        }

        error_code meta_state_service_simple::delete_node_internal(const std::string& node, bool recursive)
        {
            auto path = normalize_path(node);
            if (path == "/")
                return ERR_INVALID_PARAMETERS; // cannot delete root
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            if (!recursive && !me_it->second->children.empty())
                return ERR_INVALID_PARAMETERS;

            struct delete_state
            {
                std::string path;
                state_node *node;
                decltype(state_node::children)::iterator next_child_to_delete;
            };
            std::stack<delete_state> delete_stack;
            delete_stack.push({ path, me_it->second, me_it->second->children.begin() });
            for (; !delete_stack.empty();)
            {
                auto &node_pair = delete_stack.top();
                if (node_pair.node->children.end() == node_pair.next_child_to_delete)
                {
                    auto delnum = _quick_map.erase(node_pair.path);
                    dassert(delnum == 1, "inconsistent state between quick map and tree");
                    delete node_pair.node;
                    delete_stack.pop();
                }
                else
                {
                    auto child_it = node_pair.next_child_to_delete;
                    delete_stack.push({ node_pair.path + "/" + child_it->second->name, child_it->second, child_it->second->children.begin() });
                    ++node_pair.next_child_to_delete;
                }
            }

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            dassert(parent_it != _quick_map.end(), "unable to find parent node");
            //XXX we cannot delete root, right?

            auto erase_num = parent_it->second->children.erase(name);
            dassert(erase_num == 1, "inconsistent state between quick map and tree");
            return ERR_OK;
        }

        error_code meta_state_service_simple::set_data_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto it = _quick_map.find(path);
            if (it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            it->second->data = value;
            return ERR_OK;
        }

        error_code meta_state_service_simple::initialize(const char* dir)
        {
            _offset = 0;
            std::string log_path = dsn::utils::filesystem::path_combine(dir, "meta_state_service.log");
            if (utils::filesystem::file_exists(log_path))
            {
                if (FILE* fd = fopen(log_path.c_str(), "rb"))
                {
                    for (;;)
                    {
                        log_header header;
                        if (fread(&header, sizeof(log_header), 1, fd) != 1)
                        {
                            break;
                        }
                        if (header.magic != log_header::default_magic)
                        {
                            break;
                        }
                        std::shared_ptr<char> buffer(new char[header.size]);
                        if (fread(buffer.get(), header.size, 1, fd) != 1)
                        {
                            break;
                        }
                        _offset += sizeof(header) + header.size;
                        blob blob_wrapper(buffer, (int)header.size);
                        binary_reader reader(blob_wrapper);
                        int op_type;
                        unmarshall(reader, op_type);
                        switch (static_cast<operation_type>(op_type))
                        {
                        case operation_type::create_node:
                        {
                            std::string node;
                            blob data;
                            create_node_log::parse(reader, node, data);
                            create_node_internal(node, data).end_tracking();
                            break;
                        }
                        case operation_type::delete_node:
                        {
                            std::string node;
                            bool recursively_delete;
                            delete_node_log::parse(reader, node, recursively_delete);
                            delete_node_internal(node, recursively_delete).end_tracking();
                            break;
                        }
                        case operation_type::set_data:
                        {
                            std::string node;
                            blob data;
                            set_data_log::parse(reader, node, data);
                            set_data_internal(node, data).end_tracking();
                            break;
                        }
                        default:
                            //The log is complete but its content is modified by cosmic ray. This is unacceptable
                            dassert(false, "meta state server log corrupted");
                        }
                    }
                    fclose(fd);
                }
            }

            _log = dsn_file_open(log_path.c_str(), O_RDWR | O_CREAT | O_BINARY, 0666);
            if (!_log)
            {
                derror("open file failed: %s", log_path.c_str());
                return ERR_FILE_OPERATION_FAILED;
            }
            return ERR_OK;
        }

        task_ptr meta_state_service_simple::create_node(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_create,
            const blob& value,
            clientlet* tracker )
        {
            auto task = tasking::create_late_task(cb_code, cb_create, 0, tracker);
            write_log(
                create_node_log::get_log(node, value),
                [=]{
                    return create_node_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::delete_node(
            const std::string& node,
            bool recursively_delete,
            task_code cb_code,
            const err_callback& cb_delete,
            clientlet* tracker)
        {
            auto task = tasking::create_late_task(cb_code, cb_delete, 0, tracker);
            write_log(
                delete_node_log::get_log(node, recursively_delete),
                [=] {
                    return delete_node_internal(node, recursively_delete);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::node_exist(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_exist,
            clientlet* tracker)
        {
            error_code err;
            {
                zauto_lock _(_state_lock);
                err = _quick_map.find(normalize_path(node)) != _quick_map.end() ? ERR_OK : ERR_PATH_NOT_FOUND;
            }
            return tasking::enqueue(
                cb_code,
                tracker,
                [=]()
                {
                    cb_exist(err);
                }
                );
        }

        task_ptr meta_state_service_simple::get_data(
            const std::string& node,
            task_code cb_code,
            const err_value_callback& cb_get_data,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_data(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                auto data_copy = me_it->second->data;
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_data(ERR_OK, std::move(data_copy));
                    }
                    );
            }
        }

        task_ptr meta_state_service_simple::set_data(
            const std::string& node,
            const blob& value,
            task_code cb_code,
            const err_callback& cb_set_data,
            clientlet* tracker)
        {
            auto task = tasking::create_late_task(cb_code, cb_set_data, 0, tracker);
            write_log(
                set_data_log::get_log(node, value),
                [=] {
                    return set_data_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::get_children(
            const std::string& node,
            task_code cb_code,
            const err_stringv_callback& cb_get_children,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_children(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                std::vector<std::string> result;
                for (auto &child_pair : me_it->second->children)
                {
                    result.push_back(child_pair.first);
                }
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_children(ERR_OK, move(result));
                    }
                    );
            }
        }

        meta_state_service_simple::~meta_state_service_simple()
        {
            dsn_file_close(_log);
        }
    }
}
<commit_msg>fix bug: new created task should be received into ptr, or it may be destroyed before return<commit_after>/*
 * The MIT License (MIT)
 *
 * Copyright (c) 2015 Microsoft Corporation
 *
 * -=- Robust Distributed System Nucleus (rDSN) -=-
 *
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 *
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 *
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 */

/*
 * Description:
 *     a simple version of meta state service for development
 *
 * Revision history:
 *     2015-11-04, @imzhenyu (Zhenyu.Guo@microsoft.com), setup the sketch
 *     2015-11-11, Tianyi WANG, first version done
 *     xxxx-xx-xx, author, fix bug about xxx
 */

# include "meta_state_service_simple.h"
# include <dsn/internal/task.h>

# include <stack>
# include <utility>

namespace dsn
{
    namespace dist
    {
        // path: /, /n1/n2, /n1/n2/, /n2/n2/n3
        std::string meta_state_service_simple::normalize_path(const std::string& s)
        {
            if (s.empty() || s[0] != '/')
                return "";
            if (s.length() > 1 && *s.rbegin() == '/')
                return s.substr(0, s.length() - 1);
            return s;
        }

        error_code meta_state_service_simple::extract_name_parent_from_path(
            const std::string& s,
            /*out*/ std::string& name,
            /*out*/ std::string& parent
            )
        {
            auto pos = s.find_last_of('/');
            if (pos == std::string::npos)
                return ERR_INVALID_PARAMETERS;

            name = s.substr(pos + 1);
            if (pos > 0)
                parent = s.substr(0, pos);
            else
                parent = "/";
            return ERR_OK;
        }

        static void __err_cb_bind_and_enqueue(
            task_ptr lock_task,
            error_code err,
            int delay_milliseconds = 0
            )
        {
            auto t = dynamic_cast<safe_late_task<meta_state_service::err_callback>*>(lock_task.get());

            t->bind_and_enqueue(
                [&](meta_state_service::err_callback& cb)
                {
                    return bind(cb, err);
                },
                delay_milliseconds
                );
        }

        void meta_state_service_simple::write_log(blob&& log_blob, std::function<error_code()> internal_operation, task_ptr task)
        {
            _log_lock.lock();
            uint64_t log_offset = _offset;
            _offset += log_blob.length();
            auto continuation_task = std::unique_ptr<operation>(new operation(false, [=](bool log_succeed)
            {
                dassert(log_succeed, "we cannot handle logging failure now");
                __err_cb_bind_and_enqueue(task, internal_operation(), 0);
            }));
            auto continuation_task_ptr = continuation_task.get();
            _task_queue.emplace(move(continuation_task));
            _log_lock.unlock();

            file::write(
                _log,
                log_blob.buffer_ptr(),
                log_blob.length(),
                log_offset,
                LPC_META_STATE_SERVICE_SIMPLE_INTERNAL,
                this,
                [=](error_code err, size_t bytes)
                {
                    dassert(err == ERR_OK && bytes == log_blob.length(), "we cannot handle logging failure now");
                    _log_lock.lock();
                    continuation_task_ptr->done = true;
                    while (!_task_queue.empty())
                    {
                        if (!_task_queue.front()->done)
                        {
                            break;
                        }
                        _task_queue.front()->cb(true);
                        _task_queue.pop();
                    }
                    _log_lock.unlock();
                }
                );
        }

        error_code meta_state_service_simple::create_node_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it != _quick_map.end())
                return ERR_NODE_ALREADY_EXIST;

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            if (parent_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;

            state_node* n = new state_node(name, parent_it->second, value);
            parent_it->second->children.insert(quick_map::value_type(name, n));
            _quick_map.insert(quick_map::value_type(path, n));
            return ERR_OK;
        }

        error_code meta_state_service_simple::delete_node_internal(const std::string& node, bool recursive)
        {
            auto path = normalize_path(node);
            if (path == "/")
                return ERR_INVALID_PARAMETERS; // cannot delete root
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            if (!recursive && !me_it->second->children.empty())
                return ERR_INVALID_PARAMETERS;

            struct delete_state
            {
                std::string path;
                state_node *node;
                decltype(state_node::children)::iterator next_child_to_delete;
            };
            std::stack<delete_state> delete_stack;
            delete_stack.push({ path, me_it->second, me_it->second->children.begin() });
            for (; !delete_stack.empty();)
            {
                auto &node_pair = delete_stack.top();
                if (node_pair.node->children.end() == node_pair.next_child_to_delete)
                {
                    auto delnum = _quick_map.erase(node_pair.path);
                    dassert(delnum == 1, "inconsistent state between quick map and tree");
                    delete node_pair.node;
                    delete_stack.pop();
                }
                else
                {
                    auto child_it = node_pair.next_child_to_delete;
                    delete_stack.push({ node_pair.path + "/" + child_it->second->name, child_it->second, child_it->second->children.begin() });
                    ++node_pair.next_child_to_delete;
                }
            }

            std::string name, parent;
            auto err = extract_name_parent_from_path(path, name, parent);
            if (err != ERR_OK)
            {
                return err;
            }

            auto parent_it = _quick_map.find(parent);
            dassert(parent_it != _quick_map.end(), "unable to find parent node");
            //XXX we cannot delete root, right?

            auto erase_num = parent_it->second->children.erase(name);
            dassert(erase_num == 1, "inconsistent state between quick map and tree");
            return ERR_OK;
        }

        error_code meta_state_service_simple::set_data_internal(const std::string& node, const blob& value)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto it = _quick_map.find(path);
            if (it == _quick_map.end())
                return ERR_OBJECT_NOT_FOUND;
            it->second->data = value;
            return ERR_OK;
        }

        error_code meta_state_service_simple::initialize(const char* dir)
        {
            _offset = 0;
            std::string log_path = dsn::utils::filesystem::path_combine(dir, "meta_state_service.log");
            if (utils::filesystem::file_exists(log_path))
            {
                if (FILE* fd = fopen(log_path.c_str(), "rb"))
                {
                    for (;;)
                    {
                        log_header header;
                        if (fread(&header, sizeof(log_header), 1, fd) != 1)
                        {
                            break;
                        }
                        if (header.magic != log_header::default_magic)
                        {
                            break;
                        }
                        std::shared_ptr<char> buffer(new char[header.size]);
                        if (fread(buffer.get(), header.size, 1, fd) != 1)
                        {
                            break;
                        }
                        _offset += sizeof(header) + header.size;
                        blob blob_wrapper(buffer, (int)header.size);
                        binary_reader reader(blob_wrapper);
                        int op_type;
                        unmarshall(reader, op_type);
                        switch (static_cast<operation_type>(op_type))
                        {
                        case operation_type::create_node:
                        {
                            std::string node;
                            blob data;
                            create_node_log::parse(reader, node, data);
                            create_node_internal(node, data).end_tracking();
                            break;
                        }
                        case operation_type::delete_node:
                        {
                            std::string node;
                            bool recursively_delete;
                            delete_node_log::parse(reader, node, recursively_delete);
                            delete_node_internal(node, recursively_delete).end_tracking();
                            break;
                        }
                        case operation_type::set_data:
                        {
                            std::string node;
                            blob data;
                            set_data_log::parse(reader, node, data);
                            set_data_internal(node, data).end_tracking();
                            break;
                        }
                        default:
                            //The log is complete but its content is modified by cosmic ray. This is unacceptable
                            dassert(false, "meta state server log corrupted");
                        }
                    }
                    fclose(fd);
                }
            }

            _log = dsn_file_open(log_path.c_str(), O_RDWR | O_CREAT | O_BINARY, 0666);
            if (!_log)
            {
                derror("open file failed: %s", log_path.c_str());
                return ERR_FILE_OPERATION_FAILED;
            }
            return ERR_OK;
        }

        task_ptr meta_state_service_simple::create_node(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_create,
            const blob& value,
            clientlet* tracker )
        {
            task_ptr task = tasking::create_late_task(cb_code, cb_create, 0, tracker);
            write_log(
                create_node_log::get_log(node, value),
                [=]{
                    return create_node_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::delete_node(
            const std::string& node,
            bool recursively_delete,
            task_code cb_code,
            const err_callback& cb_delete,
            clientlet* tracker)
        {
            task_ptr task = tasking::create_late_task(cb_code, cb_delete, 0, tracker);
            write_log(
                delete_node_log::get_log(node, recursively_delete),
                [=] {
                    return delete_node_internal(node, recursively_delete);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::node_exist(
            const std::string& node,
            task_code cb_code,
            const err_callback& cb_exist,
            clientlet* tracker)
        {
            error_code err;
            {
                zauto_lock _(_state_lock);
                err = _quick_map.find(normalize_path(node)) != _quick_map.end() ? ERR_OK : ERR_PATH_NOT_FOUND;
            }
            return tasking::enqueue(
                cb_code,
                tracker,
                [=]()
                {
                    cb_exist(err);
                }
                );
        }

        task_ptr meta_state_service_simple::get_data(
            const std::string& node,
            task_code cb_code,
            const err_value_callback& cb_get_data,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_data(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                auto data_copy = me_it->second->data;
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_data(ERR_OK, std::move(data_copy));
                    }
                    );
            }
        }

        task_ptr meta_state_service_simple::set_data(
            const std::string& node,
            const blob& value,
            task_code cb_code,
            const err_callback& cb_set_data,
            clientlet* tracker)
        {
            task_ptr task = tasking::create_late_task(cb_code, cb_set_data, 0, tracker);
            write_log(
                set_data_log::get_log(node, value),
                [=] {
                    return set_data_internal(node, value);
                },
                task
                );
            return task;
        }

        task_ptr meta_state_service_simple::get_children(
            const std::string& node,
            task_code cb_code,
            const err_stringv_callback& cb_get_children,
            clientlet* tracker)
        {
            auto path = normalize_path(node);
            zauto_lock _(_state_lock);
            auto me_it = _quick_map.find(path);
            if (me_it == _quick_map.end())
            {
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]()
                    {
                        cb_get_children(ERR_OBJECT_NOT_FOUND, {});
                    }
                    );
            }
            else
            {
                std::vector<std::string> result;
                for (auto &child_pair : me_it->second->children)
                {
                    result.push_back(child_pair.first);
                }
                return tasking::enqueue(
                    cb_code,
                    tracker,
                    [=]() mutable
                    {
                        cb_get_children(ERR_OK, move(result));
                    }
                    );
            }
        }

        meta_state_service_simple::~meta_state_service_simple()
        {
            dsn_file_close(_log);
        }
    }
}
<|endoftext|>


<commit_before>#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "framework/data.h"
#include "framework/framework.h"
#include "framework/fs.h"
#include "framework/logger.h"
#include "framework/trace.h"
#include <physfs.h>

#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif

#ifdef ANDROID
#define be16toh(x) htobe16(x)
#define be32toh(x) htobe32(x)
#define be64toh(x) htobe64(x)
#define le16toh(x) htole16(x)
#define le32toh(x) htole32(x)
#define le64toh(x) htole64(x)
#elif defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE)
#include <endian.h>
#else
/* We assume all other platforms are little endian for now */
static inline uint16_t le16toh(uint16_t val) { return val; }
static inline uint32_t le32toh(uint32_t val) { return val; }
#endif

#include "fs/physfs_archiver_cue.h"

namespace
{

using namespace OpenApoc;

class PhysfsIFileImpl : public std::streambuf, public IFileImpl
{
  public:
	size_t bufferSize;
	std::unique_ptr<char[]> buffer;
	UString systemPath;
	UString suppliedPath;

	PHYSFS_File *file;

	PhysfsIFileImpl(const UString &path, size_t bufferSize = 512)
	    : bufferSize(bufferSize), buffer(new char[bufferSize]), suppliedPath(path)
	{
		file = PHYSFS_openRead(path.cStr());
		if (!file)
		{
			LogError("Failed to open file \"%s\" : \"%s\"", path.cStr(), PHYSFS_getLastError());
			return;
		}
		systemPath = PHYSFS_getRealDir(path.cStr());
		systemPath += "/" + path;
	}
	~PhysfsIFileImpl() override
	{
		if (file)
			PHYSFS_close(file);
	}

	int_type underflow() override
	{
		if (PHYSFS_eof(file))
		{
			return traits_type::eof();
		}
		size_t bytesRead = PHYSFS_readBytes(file, buffer.get(), bufferSize);
		if (bytesRead < 1)
		{
			return traits_type::eof();
		}
		setg(buffer.get(), buffer.get(), buffer.get() + bytesRead);
		return static_cast<unsigned char>(*gptr());
	}

	pos_type seekoff(off_type pos, std::ios_base::seekdir dir,
	                 std::ios_base::openmode mode) override
	{
		switch (dir)
		{
			case std::ios_base::beg:
				PHYSFS_seek(file, pos);
				break;
			case std::ios_base::cur:
				PHYSFS_seek(file, (PHYSFS_tell(file) + pos) - (egptr() - gptr()));
				break;
			case std::ios_base::end:
				PHYSFS_seek(file, PHYSFS_fileLength(file) + pos);
				break;
			default:
				LogError("Unknown direction in seekoff (%d)", dir);
				LogAssert(0);
		}

		if (mode & std::ios_base::in)
		{
			setg(egptr(), egptr(), egptr());
		}

		if (mode & std::ios_base::out)
		{
			LogError("ios::out set on read-only IFile \"%s\"", this->systemPath.cStr());
			LogAssert(0);
			setp(buffer.get(), buffer.get());
		}

		return PHYSFS_tell(file);
	}

	pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
	{
		PHYSFS_seek(file, pos);
		if (mode & std::ios_base::in)
		{
			setg(egptr(), egptr(), egptr());
		}

		if (mode & std::ios_base::out)
		{
			LogError("ios::out set on read-only IFile \"%s\"", this->systemPath.cStr());
			LogAssert(0);
			setp(buffer.get(), buffer.get());
		}

		return PHYSFS_tell(file);
	}

	int_type overflow(int_type) override
	{
		LogError("overflow called on read-only IFile \"%s\"", this->systemPath.cStr());
		LogAssert(0);
		return 0;
	}
};
} // anonymous namespace

namespace OpenApoc
{

IFile::IFile() : std::istream(nullptr) {}
// FIXME: MSVC needs this, GCC fails with it?
#ifdef _WIN32
IFile::IFile(IFile &&other) : std::istream(std::move(other))
{
	this->f = std::move(other.f);
	rdbuf(other.rdbuf());
	other.rdbuf(nullptr);
}
#endif

IFileImpl::~IFileImpl() = default;

bool IFile::readule16(uint16_t &val)
{
	this->read(reinterpret_cast<char *>(&val), sizeof(val));
	val = le16toh(val);
	return !!(*this);
}

bool IFile::readule32(uint32_t &val)
{
	this->read(reinterpret_cast<char *>(&val), sizeof(val));
	val = le32toh(val);
	return !!(*this);
}

size_t IFile::size() const
{
	if (this->f)
		return PHYSFS_fileLength(dynamic_cast<PhysfsIFileImpl *>(f.get())->file);
	return 0;
}

const UString &IFile::fileName() const
{
	static const UString emptyString = "";
	if (this->f)
		return dynamic_cast<PhysfsIFileImpl *>(f.get())->suppliedPath;
	return emptyString;
}

const UString &IFile::systemPath() const
{
	static const UString emptyString = "";
	if (this->f)
		return dynamic_cast<PhysfsIFileImpl *>(f.get())->systemPath;
	return emptyString;
}

std::unique_ptr<char[]> IFile::readAll()
{
	auto memsize = this->size();
	std::unique_ptr<char[]> mem(new char[memsize]);
	if (!mem)
	{
		LogError("Failed to allocate memory for %llu bytes",
		         static_cast<long long unsigned>(memsize));
		return nullptr;
	}

	// We don't want this to change the state (such as the offset) of the file
	// stream so store off the current pos and restore it after the read
	auto currentPos = this->tellg();
	this->seekg(0, this->beg);

	this->read(mem.get(), memsize);

	this->seekg(currentPos);

	return mem;
}

IFile::~IFile() = default;

FileSystem::FileSystem(std::vector<UString> paths)
{
	// FIXME: Is this the right thing to do that?
	LogInfo("Registering external archivers...");
	PHYSFS_registerArchiver(getCueArchiver());
	// Paths are supplied in inverse-search order (IE the last in 'paths' should be the first
	// searched)
	for (auto &p : paths)
	{
		if (!PHYSFS_mount(p.cStr(), "/", 0))
		{
			LogInfo("Failed to add resource dir \"%s\", error: %s", p.cStr(),
			        PHYSFS_getLastError());
			continue;
		}
		else
			LogInfo("Resource dir \"%s\" mounted to \"%s\"", p.cStr(),
			        PHYSFS_getMountPoint(p.cStr()));
	}
	this->writeDir = PHYSFS_getPrefDir(PROGRAM_ORGANISATION, PROGRAM_NAME);
	LogInfo("Setting write directory to \"%s\"", this->writeDir.cStr());
	PHYSFS_setWriteDir(this->writeDir.cStr());
	// Finally, the write directory trumps all
	PHYSFS_mount(this->writeDir.cStr(), "/", 0);
}

FileSystem::~FileSystem() = default;

IFile FileSystem::open(const UString &path)
{
	TRACE_FN_ARGS1("PATH", path);
	IFile f;

	auto lowerPath = path.toLower();
	if (path != lowerPath)
	{
		LogError("Path \"%s\" contains CAPITAL - cut it out!", path.cStr());
	}

	if (!PHYSFS_exists(path.cStr()))
	{
		LogInfo("Failed to find \"%s\"", path.cStr());
		LogAssert(!f);
		return f;
	}
	f.f.reset(new PhysfsIFileImpl(path));
	f.rdbuf(dynamic_cast<PhysfsIFileImpl *>(f.f.get()));
	LogInfo("Loading \"%s\" from \"%s\"", path.cStr(), f.systemPath().cStr());
	return f;
}

std::list<UString> FileSystem::enumerateDirectory(const UString &basePath,
                                                  const UString &extension) const
{
	std::list<UString> result;
	bool filterByExtension = !extension.empty();

	char **elements = PHYSFS_enumerateFiles(basePath.cStr());
	for (char **element = elements; *element != NULL; element++)
	{
		if (!filterByExtension)
		{
			result.push_back(*element);
		}
		else
		{
			const UString elementString = (*element);
			if (elementString.endsWith(extension))
			{
				result.push_back(elementString);
			}
		}
	}
	PHYSFS_freeList(elements);
	return result;
}

static std::list<UString> recursiveFindFilesInDirectory(UString path, const UString &extension)
{
	std::list<UString> foundFiles;
	auto list = fw().data->fs.enumerateDirectory(path, "");
	for (auto &entry : list)
	{
		if (entry.endsWith(extension))
		{
			foundFiles.push_back(path + "/" + entry);
		}
		else
		{
			auto subdirFiles = recursiveFindFilesInDirectory(path + "/" + entry, extension);
			foundFiles.insert(foundFiles.end(), subdirFiles.begin(), subdirFiles.end());
		}
	}
	return foundFiles;
}

std::list<UString> FileSystem::enumerateDirectoryRecursive(const UString &basePath,
                                                           const UString &extension) const
{
	return recursiveFindFilesInDirectory(basePath, extension);
}

} // namespace OpenApoc
<commit_msg>Fix crash due to readAliases() being called before fw()->data is set<commit_after>#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif

#include "framework/data.h"
#include "framework/framework.h"
#include "framework/fs.h"
#include "framework/logger.h"
#include "framework/trace.h"
#include <physfs.h>

#ifdef _WIN32
#define _CRT_SECURE_NO_WARNINGS
#endif

#ifdef ANDROID
#define be16toh(x) htobe16(x)
#define be32toh(x) htobe32(x)
#define be64toh(x) htobe64(x)
#define le16toh(x) htole16(x)
#define le32toh(x) htole32(x)
#define le64toh(x) htole64(x)
#elif defined(_DEFAULT_SOURCE) || defined(_BSD_SOURCE)
#include <endian.h>
#else
/* We assume all other platforms are little endian for now */
static inline uint16_t le16toh(uint16_t val) { return val; }
static inline uint32_t le32toh(uint32_t val) { return val; }
#endif

#include "fs/physfs_archiver_cue.h"

namespace
{

using namespace OpenApoc;

class PhysfsIFileImpl : public std::streambuf, public IFileImpl
{
  public:
	size_t bufferSize;
	std::unique_ptr<char[]> buffer;
	UString systemPath;
	UString suppliedPath;

	PHYSFS_File *file;

	PhysfsIFileImpl(const UString &path, size_t bufferSize = 512)
	    : bufferSize(bufferSize), buffer(new char[bufferSize]), suppliedPath(path)
	{
		file = PHYSFS_openRead(path.cStr());
		if (!file)
		{
			LogError("Failed to open file \"%s\" : \"%s\"", path.cStr(), PHYSFS_getLastError());
			return;
		}
		systemPath = PHYSFS_getRealDir(path.cStr());
		systemPath += "/" + path;
	}
	~PhysfsIFileImpl() override
	{
		if (file)
			PHYSFS_close(file);
	}

	int_type underflow() override
	{
		if (PHYSFS_eof(file))
		{
			return traits_type::eof();
		}
		size_t bytesRead = PHYSFS_readBytes(file, buffer.get(), bufferSize);
		if (bytesRead < 1)
		{
			return traits_type::eof();
		}
		setg(buffer.get(), buffer.get(), buffer.get() + bytesRead);
		return static_cast<unsigned char>(*gptr());
	}

	pos_type seekoff(off_type pos, std::ios_base::seekdir dir,
	                 std::ios_base::openmode mode) override
	{
		switch (dir)
		{
			case std::ios_base::beg:
				PHYSFS_seek(file, pos);
				break;
			case std::ios_base::cur:
				PHYSFS_seek(file, (PHYSFS_tell(file) + pos) - (egptr() - gptr()));
				break;
			case std::ios_base::end:
				PHYSFS_seek(file, PHYSFS_fileLength(file) + pos);
				break;
			default:
				LogError("Unknown direction in seekoff (%d)", dir);
				LogAssert(0);
		}

		if (mode & std::ios_base::in)
		{
			setg(egptr(), egptr(), egptr());
		}

		if (mode & std::ios_base::out)
		{
			LogError("ios::out set on read-only IFile \"%s\"", this->systemPath.cStr());
			LogAssert(0);
			setp(buffer.get(), buffer.get());
		}

		return PHYSFS_tell(file);
	}

	pos_type seekpos(pos_type pos, std::ios_base::openmode mode) override
	{
		PHYSFS_seek(file, pos);
		if (mode & std::ios_base::in)
		{
			setg(egptr(), egptr(), egptr());
		}

		if (mode & std::ios_base::out)
		{
			LogError("ios::out set on read-only IFile \"%s\"", this->systemPath.cStr());
			LogAssert(0);
			setp(buffer.get(), buffer.get());
		}

		return PHYSFS_tell(file);
	}

	int_type overflow(int_type) override
	{
		LogError("overflow called on read-only IFile \"%s\"", this->systemPath.cStr());
		LogAssert(0);
		return 0;
	}
};
} // anonymous namespace

namespace OpenApoc
{

IFile::IFile() : std::istream(nullptr) {}
// FIXME: MSVC needs this, GCC fails with it?
#ifdef _WIN32
IFile::IFile(IFile &&other) : std::istream(std::move(other))
{
	this->f = std::move(other.f);
	rdbuf(other.rdbuf());
	other.rdbuf(nullptr);
}
#endif

IFileImpl::~IFileImpl() = default;

bool IFile::readule16(uint16_t &val)
{
	this->read(reinterpret_cast<char *>(&val), sizeof(val));
	val = le16toh(val);
	return !!(*this);
}

bool IFile::readule32(uint32_t &val)
{
	this->read(reinterpret_cast<char *>(&val), sizeof(val));
	val = le32toh(val);
	return !!(*this);
}

size_t IFile::size() const
{
	if (this->f)
		return PHYSFS_fileLength(dynamic_cast<PhysfsIFileImpl *>(f.get())->file);
	return 0;
}

const UString &IFile::fileName() const
{
	static const UString emptyString = "";
	if (this->f)
		return dynamic_cast<PhysfsIFileImpl *>(f.get())->suppliedPath;
	return emptyString;
}

const UString &IFile::systemPath() const
{
	static const UString emptyString = "";
	if (this->f)
		return dynamic_cast<PhysfsIFileImpl *>(f.get())->systemPath;
	return emptyString;
}

std::unique_ptr<char[]> IFile::readAll()
{
	auto memsize = this->size();
	std::unique_ptr<char[]> mem(new char[memsize]);
	if (!mem)
	{
		LogError("Failed to allocate memory for %llu bytes",
		         static_cast<long long unsigned>(memsize));
		return nullptr;
	}

	// We don't want this to change the state (such as the offset) of the file
	// stream so store off the current pos and restore it after the read
	auto currentPos = this->tellg();
	this->seekg(0, this->beg);

	this->read(mem.get(), memsize);

	this->seekg(currentPos);

	return mem;
}

IFile::~IFile() = default;

FileSystem::FileSystem(std::vector<UString> paths)
{
	// FIXME: Is this the right thing to do that?
	LogInfo("Registering external archivers...");
	PHYSFS_registerArchiver(getCueArchiver());
	// Paths are supplied in inverse-search order (IE the last in 'paths' should be the first
	// searched)
	for (auto &p : paths)
	{
		if (!PHYSFS_mount(p.cStr(), "/", 0))
		{
			LogInfo("Failed to add resource dir \"%s\", error: %s", p.cStr(),
			        PHYSFS_getLastError());
			continue;
		}
		else
			LogInfo("Resource dir \"%s\" mounted to \"%s\"", p.cStr(),
			        PHYSFS_getMountPoint(p.cStr()));
	}
	this->writeDir = PHYSFS_getPrefDir(PROGRAM_ORGANISATION, PROGRAM_NAME);
	LogInfo("Setting write directory to \"%s\"", this->writeDir.cStr());
	PHYSFS_setWriteDir(this->writeDir.cStr());
	// Finally, the write directory trumps all
	PHYSFS_mount(this->writeDir.cStr(), "/", 0);
}

FileSystem::~FileSystem() = default;

IFile FileSystem::open(const UString &path)
{
	TRACE_FN_ARGS1("PATH", path);
	IFile f;

	auto lowerPath = path.toLower();
	if (path != lowerPath)
	{
		LogError("Path \"%s\" contains CAPITAL - cut it out!", path.cStr());
	}

	if (!PHYSFS_exists(path.cStr()))
	{
		LogInfo("Failed to find \"%s\"", path.cStr());
		LogAssert(!f);
		return f;
	}
	f.f.reset(new PhysfsIFileImpl(path));
	f.rdbuf(dynamic_cast<PhysfsIFileImpl *>(f.f.get()));
	LogInfo("Loading \"%s\" from \"%s\"", path.cStr(), f.systemPath().cStr());
	return f;
}

std::list<UString> FileSystem::enumerateDirectory(const UString &basePath,
                                                  const UString &extension) const
{
	std::list<UString> result;
	bool filterByExtension = !extension.empty();

	char **elements = PHYSFS_enumerateFiles(basePath.cStr());
	for (char **element = elements; *element != NULL; element++)
	{
		if (!filterByExtension)
		{
			result.push_back(*element);
		}
		else
		{
			const UString elementString = (*element);
			if (elementString.endsWith(extension))
			{
				result.push_back(elementString);
			}
		}
	}
	PHYSFS_freeList(elements);
	return result;
}

static std::list<UString> recursiveFindFilesInDirectory(const FileSystem &fs, UString path,
                                                        const UString &extension)
{
	std::list<UString> foundFiles;
	auto list = fs.enumerateDirectory(path, "");
	for (auto &entry : list)
	{
		if (entry.endsWith(extension))
		{
			foundFiles.push_back(path + "/" + entry);
		}
		else
		{
			auto subdirFiles = recursiveFindFilesInDirectory(fs, path + "/" + entry, extension);
			foundFiles.insert(foundFiles.end(), subdirFiles.begin(), subdirFiles.end());
		}
	}
	return foundFiles;
}

std::list<UString> FileSystem::enumerateDirectoryRecursive(const UString &basePath,
                                                           const UString &extension) const
{
	return recursiveFindFilesInDirectory(*this, basePath, extension);
}

} // namespace OpenApoc
<|endoftext|>


<commit_before>//
//  IsingTest.cpp
//  jdemon
//
//  Created by Mark Larus on 3/4/13.
//  Copyright (c) 2013 Kenyon College. All rights reserved.
//

#include <boost/test/unit_test.hpp>
#include <set>
#include <algorithm>
#include <stack>

#include "Ising.h"
#include "Utilities.h"

#include "TestFixtures.h"
#include "MockObjects.h"

class TransitionRuleTestFixture : \
    public DefaultTransitionRuleTestFixture, \
    public ValidStatesTestFixture \
{};

BOOST_FIXTURE_TEST_SUITE(TransitionRuleTest, TransitionRuleTestFixture);

BOOST_AUTO_TEST_CASE ( testTableSize ) {
    for (TransitionRule::iterator it = defaultRule.begin(); it!=defaultRule.end(); ++it) {
        BOOST_REQUIRE(it->second.size() == 8 && "Transition table size should be 8");
    }
}

BOOST_AUTO_TEST_CASE ( testTableDeadEnds ) {
    typedef TransitionRule::iterator RuleIterator;
    typedef TransitionTable::iterator TableIterator;

    for (RuleIterator currentRuleIterator = defaultRule.begin(); \
         currentRuleIterator!=defaultRule.end(); ++currentRuleIterator) {

        TransitionTable currentRule = currentRuleIterator->second;

        for (TableIterator currentTableIterator = currentRule.begin();\
             currentTableIterator != currentRule.end(); ++currentTableIterator) {
            BOOST_CHECK(currentTableIterator->second);
        }
    }
}

BOOST_AUTO_TEST_CASE ( testValidTargetStates ) {
    typedef TransitionRule::iterator RuleIterator;
    typedef TransitionTable::iterator TableIterator;

    for (RuleIterator currentRuleIterator = defaultRule.begin(); \
         currentRuleIterator!=defaultRule.end(); ++currentRuleIterator) {

        TransitionTable currentRule = currentRuleIterator->second;

        for (TableIterator currentTableIterator = currentRule.begin();\
             currentTableIterator != currentRule.end(); ++currentTableIterator) {
            SystemState *targetState = currentTableIterator->second;
            BOOST_CHECK(validStates.count(targetState));
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()

class IsingReservoirTestFixture : \
    public RandomNumberTestFixture,
    public ConstantsTestFixture,
    public ValidStatesTestFixture
{};

BOOST_FIXTURE_TEST_SUITE(IsingReservoirTest, IsingReservoirTestFixture);

BOOST_AUTO_TEST_CASE ( testWheelStep ) {
    IsingReservoir reservoir(rng,c,6,6);
    Reservoir::InteractionResult result;
    BOOST_REQUIRE_NO_THROW(reservoir.wheelStep(result));
}

BOOST_AUTO_TEST_CASE ( testEmptyTransitionRule ) {
    TransitionRule emptyRule;
    IsingReservoir reservoir(rng,c,6,6,emptyRule);
    Reservoir::InteractionResult result;
    BOOST_REQUIRE_THROW(reservoir.wheelStep(result), EmptyTransitionRuleError);
}

BOOST_AUTO_TEST_CASE ( testDeadEndTransitionRule ) {
    TransitionRule deadEndRule;
    TransitionTable emptyTable;
    for (char k=0; k<8; k++) {
        emptyTable[k] = NULL;
    }

    Reservoir::InteractionResult result;

    for (StateSet::iterator it = validStates.begin(); it!=validStates.end(); ++it) {
        deadEndRule[*it] = emptyTable;
    }
    IsingReservoir reservoir(rng,c,6,6,deadEndRule);
    BOOST_REQUIRE_THROW(reservoir.wheelStep(result),TransitionDeadEndError);
}

BOOST_AUTO_TEST_CASE ( testTooSmallTransitionTable ) {
    TransitionRule invalidRule;
    TransitionTable tooSmallTable;

    for (char k=0; k<3; k++) {
        tooSmallTable[k] = NULL;
    }

    for (StateSet::iterator it = validStates.begin(); it!=validStates.end(); ++it) {
        invalidRule[*it] = tooSmallTable;
    }

    IsingReservoir reservoir(rng,c,6,6,invalidRule);
    Reservoir::InteractionResult result;
    BOOST_REQUIRE_THROW(reservoir.wheelStep(result),InvalidTableSizeError);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(IsingUtilityTest)

BOOST_AUTO_TEST_CASE( testNonbinaryParity ) {
    int inputState = Ising::s(0,0,157);
    BOOST_REQUIRE(inputState == 1);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(ClusterMethodTest, GridOperationTestFixture)

BOOST_AUTO_TEST_CASE( includeNoCells ) {
    Ising::Cell *startingPoint = grid[5];
    MockRandomnessDelegate delegate(false,0);
    ClusterMethodAgent agent(&delegate,1);
    agent.performMethodAtCell(startingPoint);

    std::set<Ising::Cell *> expectedCells;
    expectedCells.insert(startingPoint);

    std::set<Ising::Cell *> actualCells = changedCells();
    BOOST_REQUIRE_EQUAL_COLLECTIONS(expectedCells.begin(), expectedCells.end(), actualCells.begin(), actualCells.end());
}

BOOST_AUTO_TEST_CASE( includeAllCells ) {
    // This breaks if the default cell value is non-zero.

    Ising::Cell *startingPoint = grid[5];
    MockRandomnessDelegate delegate(true,0);
    ClusterMethodAgent agent(&delegate,2);
    agent.performMethodAtCell(startingPoint);

    std::set<Ising::Cell *> expectedCells;
    expectedCells.insert(grid.begin(),grid.end());

    std::set<Ising::Cell *> actualCells = changedCells();

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expectedCells.begin(), expectedCells.end(), actualCells.begin(), actualCells.end());
}

BOOST_AUTO_TEST_CASE( includeSomeCells ) {
    Ising::Cell *startingPoint = grid[5];
    std::set<Ising::Cell *> expectedCells;
    std::stack<Ising::Cell *> stack;
    stack.push(startingPoint);

    size_t size = 12;

    while (expectedCells.size()!=size) {
        if (stack.empty()) {
            BOOST_FAIL("Stack should not be empty.");
        }
        Ising::Cell *currentCell = stack.top();
        stack.pop();
        Ising::Cell::Neighbors neighbors = currentCell->getNeighbors();
        for (Ising::Cell::Neighbors::iterator it = neighbors.begin(); it!=neighbors.end(); ++it) {
            if (!expectedCells.count(*it)) {
                stack.push(*it);
            }
        }
        expectedCells.insert(currentCell);
    }

    BOOST_REQUIRE_EQUAL(expectedCells.size(),size);

    for (Ising::Grid::iterator it = grid.begin(); it!=grid.end(); ++it) {
        if (expectedCells.count(*it)) {
            (*it)->setValue(1);
        } else {
            (*it)->setValue(0);
        }
    }

    resetInitialValues();

    MockRandomnessDelegate delegate(true,0);
    ClusterMethodAgent agent(&delegate,1);
    agent.performMethodAtCell(startingPoint);

    std::set<Ising::Cell *> actualCells = changedCells();

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expectedCells.begin(), expectedCells.end(), actualCells.begin(), actualCells.end());
}

BOOST_AUTO_TEST_CASE( testInclusionProbability ) {
    BOOST_CHECK_THROW(ClusterMethodAgent(NULL,-0.01), InvalidProbabilityError);
    BOOST_CHECK_THROW(ClusterMethodAgent(NULL,1.01), InvalidProbabilityError);
}

BOOST_AUTO_TEST_SUITE_END()<commit_msg>Fix error in ClusterMethodAgent unit test.<commit_after>//
//  IsingTest.cpp
//  jdemon
//
//  Created by Mark Larus on 3/4/13.
//  Copyright (c) 2013 Kenyon College. All rights reserved.
//

#include <boost/test/unit_test.hpp>
#include <set>
#include <algorithm>
#include <stack>

#include "Ising.h"
#include "Utilities.h"

#include "TestFixtures.h"
#include "MockObjects.h"

class TransitionRuleTestFixture : \
    public DefaultTransitionRuleTestFixture, \
    public ValidStatesTestFixture \
{};

BOOST_FIXTURE_TEST_SUITE(TransitionRuleTest, TransitionRuleTestFixture);

BOOST_AUTO_TEST_CASE ( testTableSize ) {
    for (TransitionRule::iterator it = defaultRule.begin(); it!=defaultRule.end(); ++it) {
        BOOST_REQUIRE(it->second.size() == 8 && "Transition table size should be 8");
    }
}

BOOST_AUTO_TEST_CASE ( testTableDeadEnds ) {
    typedef TransitionRule::iterator RuleIterator;
    typedef TransitionTable::iterator TableIterator;

    for (RuleIterator currentRuleIterator = defaultRule.begin(); \
         currentRuleIterator!=defaultRule.end(); ++currentRuleIterator) {

        TransitionTable currentRule = currentRuleIterator->second;

        for (TableIterator currentTableIterator = currentRule.begin();\
             currentTableIterator != currentRule.end(); ++currentTableIterator) {
            BOOST_CHECK(currentTableIterator->second);
        }
    }
}

BOOST_AUTO_TEST_CASE ( testValidTargetStates ) {
    typedef TransitionRule::iterator RuleIterator;
    typedef TransitionTable::iterator TableIterator;

    for (RuleIterator currentRuleIterator = defaultRule.begin(); \
         currentRuleIterator!=defaultRule.end(); ++currentRuleIterator) {

        TransitionTable currentRule = currentRuleIterator->second;

        for (TableIterator currentTableIterator = currentRule.begin();\
             currentTableIterator != currentRule.end(); ++currentTableIterator) {
            SystemState *targetState = currentTableIterator->second;
            BOOST_CHECK(validStates.count(targetState));
        }
    }
}

BOOST_AUTO_TEST_SUITE_END()

class IsingReservoirTestFixture : \
    public RandomNumberTestFixture,
    public ConstantsTestFixture,
    public ValidStatesTestFixture
{};

BOOST_FIXTURE_TEST_SUITE(IsingReservoirTest, IsingReservoirTestFixture);

BOOST_AUTO_TEST_CASE ( testWheelStep ) {
    IsingReservoir reservoir(rng,c,6,6);
    Reservoir::InteractionResult result;
    BOOST_REQUIRE_NO_THROW(reservoir.wheelStep(result));
}

BOOST_AUTO_TEST_CASE ( testEmptyTransitionRule ) {
    TransitionRule emptyRule;
    IsingReservoir reservoir(rng,c,6,6,emptyRule);
    Reservoir::InteractionResult result;
    BOOST_REQUIRE_THROW(reservoir.wheelStep(result), EmptyTransitionRuleError);
}

BOOST_AUTO_TEST_CASE ( testDeadEndTransitionRule ) {
    TransitionRule deadEndRule;
    TransitionTable emptyTable;
    for (char k=0; k<8; k++) {
        emptyTable[k] = NULL;
    }

    Reservoir::InteractionResult result;

    for (StateSet::iterator it = validStates.begin(); it!=validStates.end(); ++it) {
        deadEndRule[*it] = emptyTable;
    }
    IsingReservoir reservoir(rng,c,6,6,deadEndRule);
    BOOST_REQUIRE_THROW(reservoir.wheelStep(result),TransitionDeadEndError);
}

BOOST_AUTO_TEST_CASE ( testTooSmallTransitionTable ) {
    TransitionRule invalidRule;
    TransitionTable tooSmallTable;

    for (char k=0; k<3; k++) {
        tooSmallTable[k] = NULL;
    }

    for (StateSet::iterator it = validStates.begin(); it!=validStates.end(); ++it) {
        invalidRule[*it] = tooSmallTable;
    }

    IsingReservoir reservoir(rng,c,6,6,invalidRule);
    Reservoir::InteractionResult result;
    BOOST_REQUIRE_THROW(reservoir.wheelStep(result),InvalidTableSizeError);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_AUTO_TEST_SUITE(IsingUtilityTest)

BOOST_AUTO_TEST_CASE( testNonbinaryParity ) {
    int inputState = Ising::s(0,0,157);
    BOOST_REQUIRE(inputState == 1);
}

BOOST_AUTO_TEST_SUITE_END()

BOOST_FIXTURE_TEST_SUITE(ClusterMethodTest, GridOperationTestFixture)

BOOST_AUTO_TEST_CASE( includeNoCells ) {
    Ising::Cell *startingPoint = grid[5];
    MockRandomnessDelegate delegate(false,0);
    ClusterMethodAgent agent(&delegate,1);
    agent.performMethodAtCell(startingPoint);

    std::set<Ising::Cell *> expectedCells;
    expectedCells.insert(startingPoint);

    std::set<Ising::Cell *> actualCells = changedCells();
    BOOST_REQUIRE_EQUAL_COLLECTIONS(expectedCells.begin(), expectedCells.end(), actualCells.begin(), actualCells.end());
}

BOOST_AUTO_TEST_CASE( includeAllCells ) {
    // This breaks if the default cell value is non-zero.

    Ising::Cell *startingPoint = grid[5];
    MockRandomnessDelegate delegate(true,0);
    ClusterMethodAgent agent(&delegate,1);
    agent.performMethodAtCell(startingPoint);

    std::set<Ising::Cell *> expectedCells;
    expectedCells.insert(grid.begin(),grid.end());

    std::set<Ising::Cell *> actualCells = changedCells();

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expectedCells.begin(), expectedCells.end(), actualCells.begin(), actualCells.end());
}

BOOST_AUTO_TEST_CASE( includeSomeCells ) {
    Ising::Cell *startingPoint = grid[5];
    std::set<Ising::Cell *> expectedCells;
    std::stack<Ising::Cell *> stack;
    stack.push(startingPoint);

    size_t size = 12;

    while (expectedCells.size()!=size) {
        if (stack.empty()) {
            BOOST_FAIL("Stack should not be empty.");
        }
        Ising::Cell *currentCell = stack.top();
        stack.pop();
        Ising::Cell::Neighbors neighbors = currentCell->getNeighbors();
        for (Ising::Cell::Neighbors::iterator it = neighbors.begin(); it!=neighbors.end(); ++it) {
            if (!expectedCells.count(*it)) {
                stack.push(*it);
            }
        }
        expectedCells.insert(currentCell);
    }

    BOOST_REQUIRE_EQUAL(expectedCells.size(),size);

    for (Ising::Grid::iterator it = grid.begin(); it!=grid.end(); ++it) {
        if (expectedCells.count(*it)) {
            (*it)->setValue(1);
        } else {
            (*it)->setValue(0);
        }
    }

    resetInitialValues();

    MockRandomnessDelegate delegate(true,0);
    ClusterMethodAgent agent(&delegate,1);
    agent.performMethodAtCell(startingPoint);

    std::set<Ising::Cell *> actualCells = changedCells();

    BOOST_REQUIRE_EQUAL_COLLECTIONS(expectedCells.begin(), expectedCells.end(), actualCells.begin(), actualCells.end());
}

BOOST_AUTO_TEST_CASE( testInclusionProbability ) {
    BOOST_CHECK_THROW(ClusterMethodAgent(NULL,-0.01), InvalidProbabilityError);
    BOOST_CHECK_THROW(ClusterMethodAgent(NULL,1.01), InvalidProbabilityError);
}

BOOST_AUTO_TEST_SUITE_END()<|endoftext|>


<commit_before>/* -*- mode: c++; c-basic-offset:4 -*-
    uiserver/decryptverifywizard.cpp

    This file is part of Kleopatra, the KDE keymanager
    Copyright (c) 2007 Klarälvdalens Datakonsult AB

    Kleopatra is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Kleopatra is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include "decryptverifywizard.h"

#include "decryptverifyoperationwidget.h"
#include "decryptverifyresultwidget.h"

#include <utils/filenamerequester.h>
#include <utils/stl_util.h>

#include <KLocale>

#include <QScrollArea>
#include <QWizardPage>
#include <QLayout>
#include <QLabel>
#include <QEventLoop>
#include <QPointer>
#include <QAbstractButton>
#include <QScrollBar>

#include <boost/bind.hpp>

#include <vector>
#include <cassert>

using namespace Kleo;
using namespace boost;

namespace {

    static QSize getMinimumSizeHint( const QWidget * w ) {
        return w ? w->minimumSizeHint() : QSize( 0, 0 );
    }

    static QSize getSizeHint( const QWidget * w ) {
        return w ? w->sizeHint() : QSize( 0, 0 );
    }

    class ScrollArea : public QScrollArea {
        Q_OBJECT
    public:
        explicit ScrollArea( QWidget * p=0 )
            : QScrollArea( p )
        {
            setWidget( new QWidget );
            new QVBoxLayout( widget() );
            setWidgetResizable( true );
        }
        ~ScrollArea() {}

        /* reimp */ QSize minimumSizeHint() const {
            return QSize( getMinimumSizeHint( widget() ).width() + getSizeHint( verticalScrollBar() ).width() + 2*frameWidth(), 0 )
                .expandedTo( QScrollArea::minimumSizeHint() );
        }
        /* reimp */ QSize sizeHint() const {
            const QSize widgetSizeHint = getSizeHint( widget() );
            const int fw = frameWidth();
            return QScrollArea::sizeHint().expandedTo( widgetSizeHint + QSize( 2*fw, 2*fw ) + QSize( getSizeHint( verticalScrollBar() ).width(), 0 ) );
        }
    };

    class HLine : public QFrame {
        Q_OBJECT
    public:
        explicit HLine( QWidget * p=0, Qt::WindowFlags f=0 )
            : QFrame( p, f )
        {
            setFrameStyle( QFrame::HLine|QFrame::Sunken );
        }
    };

    class OperationsPage : public QWizardPage {
        Q_OBJECT
    public:
        explicit OperationsPage( QWidget * p=0 );
        ~OperationsPage();

        void setOutputDirectory( const QString & dir ) {
            m_ui.outputDirectoryFNR.setFileName( dir );
        }

        QString outputDirectory() const {
            return m_ui.outputDirectoryFNR.fileName();
        }

        void ensureIndexAvailable( unsigned int idx );

        DecryptVerifyOperationWidget * widget( unsigned int idx ) {
            return m_widgets.at( idx );
        }

    private:
        std::vector<DecryptVerifyOperationWidget*> m_widgets;

        struct UI {
            QLabel            outputDirectoryLB;
            FileNameRequester outputDirectoryFNR;
            ScrollArea       scrollArea; // ### replace with KDScrollArea when done
            QVBoxLayout     vlay;
            QHBoxLayout      hlay;

            explicit UI( OperationsPage * q );
        } m_ui;
    };

    class ResultPage : public QWizardPage {
        Q_OBJECT
    public:
        explicit ResultPage( QWidget * p=0 );
        ~ResultPage();

        void ensureIndexAvailable( unsigned int idx );

        DecryptVerifyResultWidget * widget( unsigned int idx ) {
            return m_widgets.at( idx );
        }

    private Q_SLOTS:
        void slotMaybeNoMoreProgress() {
            wizard()->button( QWizard::CancelButton )
                ->setEnabled( kdtools::any( m_widgets.begin(), m_widgets.end(),
                                            bind( &DecryptVerifyResultWidget::operationInProgress, _1 ) ) );
        }

    private:
        std::vector<DecryptVerifyResultWidget*> m_widgets;

        struct UI {
            ScrollArea   scrollArea; // ### replace with KDScrollArea when done
            QVBoxLayout vlay;

            explicit UI( ResultPage * q );
        } m_ui;
    };
}

class DecryptVerifyWizard::Private {
    friend class ::Kleo::DecryptVerifyWizard;
    DecryptVerifyWizard * const q;
public:
    Private( DecryptVerifyWizard * qq );
    ~Private();

    void ensureIndexAvailable( unsigned int idx ) {
        operationsPage.ensureIndexAvailable( idx );
        resultPage.ensureIndexAvailable( idx );
    }

private:
    OperationsPage operationsPage;
    ResultPage resultPage;
};

DecryptVerifyWizard::DecryptVerifyWizard( QWidget * p, Qt::WindowFlags f )
    : QWizard( p, f ), d( new Private( this ) )
{

}

DecryptVerifyWizard::~DecryptVerifyWizard() {}

void DecryptVerifyWizard::setOutputDirectory( const QString & dir ) {
    d->operationsPage.setOutputDirectory( dir );
}

QString DecryptVerifyWizard::outputDirectory() const {
    return d->operationsPage.outputDirectory();
}

DecryptVerifyOperationWidget * DecryptVerifyWizard::operationWidget( unsigned int idx ) {
    d->ensureIndexAvailable( idx );
    return d->operationsPage.widget( idx );
}

DecryptVerifyResultWidget * DecryptVerifyWizard::resultWidget( unsigned int idx ) {
    d->ensureIndexAvailable( idx );
    return d->resultPage.widget( idx );
}

bool DecryptVerifyWizard::waitForOperationSelection() {
    if ( !isVisible() )
        return true;

    assert( button( NextButton ) );
    assert( button( CancelButton ) );

    QEventLoop loop;
    QPointer<QObject> that = this;
    connect( this, SIGNAL(currentIdChanged(int)), &loop, SLOT(quit()) );
    connect( this, SIGNAL(finished(int)), &loop, SLOT(quit()) );
    loop.exec();

    return that && currentPage() == &d->resultPage ;
}

DecryptVerifyWizard::Private::Private( DecryptVerifyWizard * qq )
    : q( qq ),
      operationsPage( q ),
      resultPage( q )
{
    q->addPage( &operationsPage );
    q->addPage( &resultPage );
}

DecryptVerifyWizard::Private::~Private() {}

OperationsPage::OperationsPage( QWidget * p )
    : QWizardPage( p ), m_widgets(), m_ui( this )
{
    setTitle( i18n("FIXME Choose operations to be performed") );
    setSubTitle( i18n("FIXME Here, you can check and, if needed, override "
                      "the operations Kleopatra detected for the input given.") );
    setCommitPage( true );
}

OperationsPage::~OperationsPage() {}

OperationsPage::UI::UI( OperationsPage * q )
    : outputDirectoryLB( i18n("&Output directory:"), q ),
      outputDirectoryFNR( q ),
      scrollArea( q ),
      vlay( q ),
      hlay()
{
    KDAB_SET_OBJECT_NAME( outputDirectoryLB );
    KDAB_SET_OBJECT_NAME( outputDirectoryFNR );
    KDAB_SET_OBJECT_NAME( scrollArea );

    KDAB_SET_OBJECT_NAME( vlay );
    KDAB_SET_OBJECT_NAME( hlay );

    assert( qobject_cast<QBoxLayout*>(scrollArea.widget()->layout()) );
    static_cast<QBoxLayout*>(scrollArea.widget()->layout())->addStretch( 1 );
    outputDirectoryLB.setBuddy( &outputDirectoryFNR );

    hlay.setMargin( 0 );

    vlay.addWidget( &scrollArea, 1 );
    vlay.addLayout( &hlay );
    hlay.addWidget( &outputDirectoryLB );
    hlay.addWidget( &outputDirectoryFNR );
}

void OperationsPage::ensureIndexAvailable( unsigned int idx ) {

    if ( idx < m_widgets.size() )
        return;

    assert( m_ui.scrollArea.widget() );
    assert( qobject_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() ) );
    QBoxLayout & blay = *static_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() );

    for ( unsigned int i = m_widgets.size() ; i < idx+1 ; ++i ) {
        if ( i )
            blay.insertWidget( blay.count()-1, new HLine( m_ui.scrollArea.widget() ) );
        DecryptVerifyOperationWidget * w = new DecryptVerifyOperationWidget( m_ui.scrollArea.widget() );
        blay.insertWidget( blay.count()-1, w );
        w->show();
        m_widgets.push_back( w );
    }
}

ResultPage::ResultPage( QWidget * p )
    : QWizardPage( p ), m_widgets(), m_ui( this )
{
    setTitle( i18n("Results") );
    setSubTitle( i18n("FIXME") );
    setButtonText( QWizard::FinishButton, i18n("&OK") );
}

ResultPage::~ResultPage() {}

ResultPage::UI::UI( ResultPage * q )
    : scrollArea( q ),
      vlay( q )
{
    KDAB_SET_OBJECT_NAME( scrollArea );
    KDAB_SET_OBJECT_NAME( vlay );

    assert( qobject_cast<QBoxLayout*>(scrollArea.widget()->layout()) );
    static_cast<QBoxLayout*>(scrollArea.widget()->layout())->addStretch( 1 );

    vlay.addWidget( &scrollArea );
}

void ResultPage::ensureIndexAvailable( unsigned int idx ) {

    if ( idx < m_widgets.size() )
        return;

    assert( m_ui.scrollArea.widget() );
    assert( qobject_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() ) );
    QBoxLayout & blay = *static_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() );

    for ( unsigned int i = m_widgets.size() ; i < idx+1 ; ++i ) {
        if ( i )
            blay.insertWidget( blay.count()-1, new HLine( m_ui.scrollArea.widget() ) );
        DecryptVerifyResultWidget * w = new DecryptVerifyResultWidget( m_ui.scrollArea.widget() );
        connect( w, SIGNAL(operationStateChanged()), this, SLOT(slotMaybeNoMoreProgress()) );
        blay.insertWidget( blay.count()-1, w );
        w->show();
        m_widgets.push_back( w );
    }

}

#include "decryptverifywizard.moc"
#include "moc_decryptverifywizard.cpp"
<commit_msg>Fine-tuning<commit_after>/* -*- mode: c++; c-basic-offset:4 -*-
    uiserver/decryptverifywizard.cpp

    This file is part of Kleopatra, the KDE keymanager
    Copyright (c) 2007 Klarälvdalens Datakonsult AB

    Kleopatra is free software; you can redistribute it and/or modify
    it under the terms of the GNU General Public License as published by
    the Free Software Foundation; either version 2 of the License, or
    (at your option) any later version.

    Kleopatra is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    General Public License for more details.

    You should have received a copy of the GNU General Public License
    along with this program; if not, write to the Free Software
    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA

    In addition, as a special exception, the copyright holders give
    permission to link the code of this program with any edition of
    the Qt library by Trolltech AS, Norway (or with modified versions
    of Qt that use the same license as Qt), and distribute linked
    combinations including the two.  You must obey the GNU General
    Public License in all respects for all of the code used other than
    Qt.  If you modify this file, you may extend this exception to
    your version of the file, but you are not obligated to do so.  If
    you do not wish to do so, delete this exception statement from
    your version.
*/

#include "decryptverifywizard.h"

#include "decryptverifyoperationwidget.h"
#include "decryptverifyresultwidget.h"

#include <utils/filenamerequester.h>
#include <utils/stl_util.h>

#include <KLocale>

#include <QScrollArea>
#include <QWizardPage>
#include <QLayout>
#include <QLabel>
#include <QEventLoop>
#include <QPointer>
#include <QAbstractButton>
#include <QScrollBar>

#include <boost/bind.hpp>

#include <vector>
#include <cassert>

using namespace Kleo;
using namespace boost;

namespace {

    static QSize getMinimumSizeHint( const QWidget * w ) {
        return w ? w->minimumSizeHint() : QSize( 0, 0 );
    }

    static QSize getSizeHint( const QWidget * w ) {
        return w ? w->sizeHint() : QSize( 0, 0 );
    }

    class ScrollArea : public QScrollArea {
        Q_OBJECT
    public:
        explicit ScrollArea( QWidget * p=0 )
            : QScrollArea( p )
        {
            setWidget( new QWidget );
            new QVBoxLayout( widget() );
            setWidgetResizable( true );
        }
        ~ScrollArea() {}

        /* reimp */ QSize minimumSizeHint() const {
            return QSize( getMinimumSizeHint( widget() ).width() + getSizeHint( verticalScrollBar() ).width() + 2*frameWidth(), 0 )
                .expandedTo( QScrollArea::minimumSizeHint() );
        }
        /* reimp */ QSize sizeHint() const {
            const QSize widgetSizeHint = getSizeHint( widget() );
            const int fw = frameWidth();
            return QScrollArea::sizeHint().expandedTo( widgetSizeHint + QSize( 2*fw, 2*fw ) + QSize( getSizeHint( verticalScrollBar() ).width(), 0 ) );
        }
    };

    class HLine : public QFrame {
        Q_OBJECT
    public:
        explicit HLine( QWidget * p=0, Qt::WindowFlags f=0 )
            : QFrame( p, f )
        {
            setFrameStyle( QFrame::HLine|QFrame::Sunken );
        }
    };

    class OperationsPage : public QWizardPage {
        Q_OBJECT
    public:
        explicit OperationsPage( QWidget * p=0 );
        ~OperationsPage();

        void setOutputDirectory( const QString & dir ) {
            m_ui.outputDirectoryFNR.setFileName( dir );
        }

        QString outputDirectory() const {
            return m_ui.outputDirectoryFNR.fileName();
        }

        void ensureIndexAvailable( unsigned int idx );

        DecryptVerifyOperationWidget * widget( unsigned int idx ) {
            return m_widgets.at( idx );
        }

    private:
        std::vector<DecryptVerifyOperationWidget*> m_widgets;

        struct UI {
            QLabel            outputDirectoryLB;
            FileNameRequester outputDirectoryFNR;
            ScrollArea       scrollArea; // ### replace with KDScrollArea when done
            QVBoxLayout     vlay;
            QHBoxLayout      hlay;

            explicit UI( OperationsPage * q );
        } m_ui;
    };

    class ResultPage : public QWizardPage {
        Q_OBJECT
    public:
        explicit ResultPage( QWidget * p=0 );
        ~ResultPage();

        void ensureIndexAvailable( unsigned int idx );

        DecryptVerifyResultWidget * widget( unsigned int idx ) {
            return m_widgets.at( idx );
        }

    private Q_SLOTS:
        void slotMaybeNoMoreProgress() {
            wizard()->button( QWizard::CancelButton )
                ->setEnabled( kdtools::any( m_widgets.begin(), m_widgets.end(),
                                            bind( &DecryptVerifyResultWidget::operationInProgress, _1 ) ) );
        }

    private:
        std::vector<DecryptVerifyResultWidget*> m_widgets;

        struct UI {
            ScrollArea   scrollArea; // ### replace with KDScrollArea when done
            QVBoxLayout vlay;

            explicit UI( ResultPage * q );
        } m_ui;
    };
}

class DecryptVerifyWizard::Private {
    friend class ::Kleo::DecryptVerifyWizard;
    DecryptVerifyWizard * const q;
public:
    Private( DecryptVerifyWizard * qq );
    ~Private();

    void ensureIndexAvailable( unsigned int idx ) {
        operationsPage.ensureIndexAvailable( idx );
        resultPage.ensureIndexAvailable( idx );
    }

private:
    OperationsPage operationsPage;
    ResultPage resultPage;
};

DecryptVerifyWizard::DecryptVerifyWizard( QWidget * p, Qt::WindowFlags f )
    : QWizard( p, f ), d( new Private( this ) )
{

}

DecryptVerifyWizard::~DecryptVerifyWizard() {}

void DecryptVerifyWizard::setOutputDirectory( const QString & dir ) {
    d->operationsPage.setOutputDirectory( dir );
}

QString DecryptVerifyWizard::outputDirectory() const {
    return d->operationsPage.outputDirectory();
}

DecryptVerifyOperationWidget * DecryptVerifyWizard::operationWidget( unsigned int idx ) {
    d->ensureIndexAvailable( idx );
    return d->operationsPage.widget( idx );
}

DecryptVerifyResultWidget * DecryptVerifyWizard::resultWidget( unsigned int idx ) {
    d->ensureIndexAvailable( idx );
    return d->resultPage.widget( idx );
}

bool DecryptVerifyWizard::waitForOperationSelection() {
    if ( !isVisible() )
        return true;

    assert( button( NextButton ) );
    assert( button( CancelButton ) );

    QEventLoop loop;
    QPointer<QObject> that = this;
    connect( this, SIGNAL(currentIdChanged(int)), &loop, SLOT(quit()) );
    connect( this, SIGNAL(finished(int)), &loop, SLOT(quit()) );
    loop.exec();

    return that && currentPage() == &d->resultPage ;
}

DecryptVerifyWizard::Private::Private( DecryptVerifyWizard * qq )
    : q( qq ),
      operationsPage( q ),
      resultPage( q )
{
    q->setOptions( q->options() | NoBackButtonOnStartPage | NoBackButtonOnLastPage );
    q->addPage( &operationsPage );
    q->addPage( &resultPage );
}

DecryptVerifyWizard::Private::~Private() {}

OperationsPage::OperationsPage( QWidget * p )
    : QWizardPage( p ), m_widgets(), m_ui( this )
{
    setTitle( i18n("FIXME Choose operations to be performed") );
    setSubTitle( i18n("FIXME Here, you can check and, if needed, override "
                      "the operations Kleopatra detected for the input given.") );
    setCommitPage( true );
    setButtonText( QWizard::CommitButton, i18n("&Decrypt/Verify") );
}

OperationsPage::~OperationsPage() {}

OperationsPage::UI::UI( OperationsPage * q )
    : outputDirectoryLB( i18n("&Output directory:"), q ),
      outputDirectoryFNR( q ),
      scrollArea( q ),
      vlay( q ),
      hlay()
{
    KDAB_SET_OBJECT_NAME( outputDirectoryLB );
    KDAB_SET_OBJECT_NAME( outputDirectoryFNR );
    KDAB_SET_OBJECT_NAME( scrollArea );

    KDAB_SET_OBJECT_NAME( vlay );
    KDAB_SET_OBJECT_NAME( hlay );

    assert( qobject_cast<QBoxLayout*>(scrollArea.widget()->layout()) );
    static_cast<QBoxLayout*>(scrollArea.widget()->layout())->addStretch( 1 );
    outputDirectoryLB.setBuddy( &outputDirectoryFNR );

    hlay.setMargin( 0 );

    vlay.addWidget( &scrollArea, 1 );
    vlay.addLayout( &hlay );
    hlay.addWidget( &outputDirectoryLB );
    hlay.addWidget( &outputDirectoryFNR );
}

void OperationsPage::ensureIndexAvailable( unsigned int idx ) {

    if ( idx < m_widgets.size() )
        return;

    assert( m_ui.scrollArea.widget() );
    assert( qobject_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() ) );
    QBoxLayout & blay = *static_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() );

    for ( unsigned int i = m_widgets.size() ; i < idx+1 ; ++i ) {
        if ( i )
            blay.insertWidget( blay.count()-1, new HLine( m_ui.scrollArea.widget() ) );
        DecryptVerifyOperationWidget * w = new DecryptVerifyOperationWidget( m_ui.scrollArea.widget() );
        blay.insertWidget( blay.count()-1, w );
        w->show();
        m_widgets.push_back( w );
    }
}

ResultPage::ResultPage( QWidget * p )
    : QWizardPage( p ), m_widgets(), m_ui( this )
{
    setTitle( i18n("Results") );
    setSubTitle( i18n("FIXME") );
    setButtonText( QWizard::FinishButton, i18n("&OK") );
}

ResultPage::~ResultPage() {}

ResultPage::UI::UI( ResultPage * q )
    : scrollArea( q ),
      vlay( q )
{
    KDAB_SET_OBJECT_NAME( scrollArea );
    KDAB_SET_OBJECT_NAME( vlay );

    assert( qobject_cast<QBoxLayout*>(scrollArea.widget()->layout()) );
    static_cast<QBoxLayout*>(scrollArea.widget()->layout())->addStretch( 1 );

    vlay.addWidget( &scrollArea );
}

void ResultPage::ensureIndexAvailable( unsigned int idx ) {

    if ( idx < m_widgets.size() )
        return;

    assert( m_ui.scrollArea.widget() );
    assert( qobject_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() ) );
    QBoxLayout & blay = *static_cast<QBoxLayout*>( m_ui.scrollArea.widget()->layout() );

    for ( unsigned int i = m_widgets.size() ; i < idx+1 ; ++i ) {
        if ( i )
            blay.insertWidget( blay.count()-1, new HLine( m_ui.scrollArea.widget() ) );
        DecryptVerifyResultWidget * w = new DecryptVerifyResultWidget( m_ui.scrollArea.widget() );
        connect( w, SIGNAL(operationStateChanged()), this, SLOT(slotMaybeNoMoreProgress()) );
        blay.insertWidget( blay.count()-1, w );
        w->show();
        m_widgets.push_back( w );
    }

}

#include "decryptverifywizard.moc"
#include "moc_decryptverifywizard.cpp"
<|endoftext|>