<commit_before>/******************************************************************************\
 *           ___        __                                                    *
 *          /\_ \    __/\ \                                                   *
 *          \//\ \  /\_\ \ \____    ___   _____   _____      __               *
 *            \ \ \ \/\ \ \ '__`\  /'___\/\ '__`\/\ '__`\  /'__`\             *
 *             \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_           *
 *             /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\          *
 *             \/____/ \/_/\/___/  \/____/ \ \ \/  \ \ \/  \/__/\/_/          *
 *                                          \ \_\   \ \_\                     *
 *                                           \/_/    \/_/                     *
 *                                                                            *
 * Copyright (C) 2011, 2012                                                   *
 * Dominik Charousset <dominik.charousset@haw-hamburg.de>                     *
 *                                                                            *
 * This file is part of libcppa.                                              *
 * libcppa is free software: you can redistribute it and/or modify it under   *
 * the terms of the GNU Lesser General Public License as published by the     *
 * Free Software Foundation, either version 3 of the License                  *
 * or (at your option) any later version.                                     *
 *                                                                            *
 * libcppa is distributed in the hope that it will be useful,                 *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                       *
 * See the GNU Lesser General Public License for more details.                *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with libcppa. If not, see <http://www.gnu.org/licenses/>.            *
\******************************************************************************/

#ifndef ABSTRACT_ACTOR_HPP
#define ABSTRACT_ACTOR_HPP

#include "cppa/config.hpp"

#include <list>
#include <atomic>
#include <vector>
#include <memory>
#include <algorithm>

#include "cppa/atom.hpp"
#include "cppa/actor.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/attachable.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/detail/thread.hpp"
#include "cppa/detail/recursive_queue_node.hpp"

#include "cppa/intrusive/single_reader_queue.hpp"

namespace cppa {

/**
 * @brief Implements linking and monitoring for actors.
 * @tparam Base Either {@link cppa::actor actor}
 *              or {@link cppa::local_actor local_actor}.
 */
template<class Base>
class abstract_actor : public Base
{

    typedef std::unique_ptr<attachable> attachable_ptr;
    typedef detail::lock_guard<detail::mutex> guard_type;

 public:

    typedef detail::recursive_queue_node mailbox_element;
    typedef intrusive::single_reader_queue<mailbox_element> mailbox_type;

    bool attach(attachable* ptr) // override
    {
        if (ptr == nullptr)
        {
            guard_type guard(m_mtx);
            return m_exit_reason.load() == exit_reason::not_exited;
        }
        else
        {
            attachable_ptr uptr(ptr);
            std::uint32_t reason;
            // lifetime scope of guard
            {
                guard_type guard(m_mtx);
                reason = m_exit_reason.load();
                if (reason == exit_reason::not_exited)
                {
                    m_attachables.push_back(std::move(uptr));
                    return true;
                }
            }
            uptr->actor_exited(reason);
            return false;
        }
    }

    void detach(attachable::token const& what) // override
    {
        attachable_ptr uptr;
        // lifetime scope of guard
        {
            guard_type guard(m_mtx);
            auto end = m_attachables.end();
            auto i = std::find_if(
                        m_attachables.begin(), end,
                        [&](attachable_ptr& p) { return p->matches(what); });
            if (i != end)
            {
                uptr = std::move(*i);
                m_attachables.erase(i);
            }
        }
        // uptr will be destroyed here, without locked mutex
    }

    void link_to(intrusive_ptr<actor>& other) // override
    {
        (void) link_to_impl(other);
    }

    void unlink_from(intrusive_ptr<actor>& other) // override
    {
        (void) unlink_from_impl(other);
    }

    bool remove_backlink(intrusive_ptr<actor>& other) // override
    {
        if (other && other != this)
        {
            guard_type guard(m_mtx);
            auto i = std::find(m_links.begin(), m_links.end(), other);
            if (i != m_links.end())
            {
                m_links.erase(i);
                return true;
            }
        }
        return false;
    }

    bool establish_backlink(intrusive_ptr<actor>& other) // override
    {
        std::uint32_t reason = exit_reason::not_exited;
        if (other && other != this)
        {
            guard_type guard(m_mtx);
            reason = m_exit_reason.load();
            if (reason == exit_reason::not_exited)
            {
                auto i = std::find(m_links.begin(), m_links.end(), other);
                if (i == m_links.end())
                {
                    m_links.push_back(other);
                    return true;
                }
            }
        }
        // send exit message without lock
        if (reason != exit_reason::not_exited)
        {
            other->enqueue(this, make_cow_tuple(atom(":Exit"), reason));
        }
        return false;
    }

 protected:

    mailbox_type m_mailbox;
    util::fixed_vector<mailbox_element*, 10> m_nodes;

    inline mailbox_element* fetch_node(actor* sender, any_tuple msg)
    {
        if (m_nodes.empty())
        {
            return new mailbox_element(sender, std::move(msg));
        }
        else
        {
            auto result = m_nodes.back();
            m_nodes.pop_back();
            result->marked = false;
            result->sender = sender;
            result->msg = std::move(msg);
            return result;
        }

    }

    inline void release_node(mailbox_element* node)
    {
        if (m_nodes.full())
        {
            delete node;
        }
        else
        {
            m_nodes.push_back(node);
        }
    }

    template<typename... Args>
    abstract_actor(Args&&... args) : Base(std::forward<Args>(args)...)
                                   , m_exit_reason(exit_reason::not_exited)
    {
        for (size_t i = 0; i < m_nodes.max_size(); ++i)
        {
            m_nodes.push_back(new mailbox_element);
        }
    }

    void cleanup(std::uint32_t reason)
    {
        if (reason == exit_reason::not_exited) return;
        decltype(m_links) mlinks;
        decltype(m_attachables) mattachables;
        // lifetime scope of guard
        {
            guard_type guard(m_mtx);
            if (m_exit_reason != exit_reason::not_exited)
            {
                // already exited
                return;
            }
            m_exit_reason = reason;
            mlinks = std::move(m_links);
            mattachables = std::move(m_attachables);
            // make sure lists are definitely empty
            m_links.clear();
            m_attachables.clear();
        }
        // send exit messages
        for (actor_ptr& aptr : mlinks)
        {
            aptr->enqueue(this, make_cow_tuple(atom(":Exit"), reason));
        }
        for (attachable_ptr& ptr : mattachables)
        {
            ptr->actor_exited(reason);
        }
    }

    bool link_to_impl(intrusive_ptr<actor>& other)
    {
        if (other && other != this)
        {
            guard_type guard(m_mtx);
            // send exit message if already exited
            if (exited())
            {
                other->enqueue(this, make_cow_tuple(atom(":Exit"),
                                                m_exit_reason.load()));
            }
            // add link if not already linked to other
            // (checked by establish_backlink)
            else if (other->establish_backlink(this))
            {
                m_links.push_back(other);
                return true;
            }
        }
        return false;
    }

    bool unlink_from_impl(intrusive_ptr<actor>& other)
    {
        guard_type guard(m_mtx);
        // remove_backlink returns true if this actor is linked to other
        if (other && !exited() && other->remove_backlink(this))
        {
            auto i = std::find(m_links.begin(), m_links.end(), other);
            CPPA_REQUIRE(i != m_links.end());
            m_links.erase(i);
            return true;
        }
        return false;
    }

 private:

    // @pre m_mtx.locked()
    bool exited() const
    {
        return m_exit_reason.load() != exit_reason::not_exited;
    }

    // true if the associated thread has finished execution
    std::atomic<std::uint32_t> m_exit_reason;
    // guards access to m_exited, m_subscriptions and m_links
    detail::mutex m_mtx;
    // links to other actors
    std::vector<actor_ptr> m_links;
    // code that is executed on cleanup
    std::vector<attachable_ptr> m_attachables;

};

} // namespace cppa

#endif // ABSTRACT_ACTOR_HPP
<commit_msg>pre-allocate only max_size()/2 elements<commit_after>/******************************************************************************\
 *           ___        __                                                    *
 *          /\_ \    __/\ \                                                   *
 *          \//\ \  /\_\ \ \____    ___   _____   _____      __               *
 *            \ \ \ \/\ \ \ '__`\  /'___\/\ '__`\/\ '__`\  /'__`\             *
 *             \_\ \_\ \ \ \ \L\ \/\ \__/\ \ \L\ \ \ \L\ \/\ \L\.\_           *
 *             /\____\\ \_\ \_,__/\ \____\\ \ ,__/\ \ ,__/\ \__/.\_\          *
 *             \/____/ \/_/\/___/  \/____/ \ \ \/  \ \ \/  \/__/\/_/          *
 *                                          \ \_\   \ \_\                     *
 *                                           \/_/    \/_/                     *
 *                                                                            *
 * Copyright (C) 2011, 2012                                                   *
 * Dominik Charousset <dominik.charousset@haw-hamburg.de>                     *
 *                                                                            *
 * This file is part of libcppa.                                              *
 * libcppa is free software: you can redistribute it and/or modify it under   *
 * the terms of the GNU Lesser General Public License as published by the     *
 * Free Software Foundation, either version 3 of the License                  *
 * or (at your option) any later version.                                     *
 *                                                                            *
 * libcppa is distributed in the hope that it will be useful,                 *
 * but WITHOUT ANY WARRANTY; without even the implied warranty of             *
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.                       *
 * See the GNU Lesser General Public License for more details.                *
 *                                                                            *
 * You should have received a copy of the GNU Lesser General Public License   *
 * along with libcppa. If not, see <http://www.gnu.org/licenses/>.            *
\******************************************************************************/

#ifndef ABSTRACT_ACTOR_HPP
#define ABSTRACT_ACTOR_HPP

#include "cppa/config.hpp"

#include <list>
#include <atomic>
#include <vector>
#include <memory>
#include <algorithm>

#include "cppa/atom.hpp"
#include "cppa/actor.hpp"
#include "cppa/local_actor.hpp"
#include "cppa/attachable.hpp"
#include "cppa/exit_reason.hpp"
#include "cppa/detail/thread.hpp"
#include "cppa/detail/recursive_queue_node.hpp"

#include "cppa/intrusive/single_reader_queue.hpp"

namespace cppa {

/**
 * @brief Implements linking and monitoring for actors.
 * @tparam Base Either {@link cppa::actor actor}
 *              or {@link cppa::local_actor local_actor}.
 */
template<class Base>
class abstract_actor : public Base
{

    typedef std::unique_ptr<attachable> attachable_ptr;
    typedef detail::lock_guard<detail::mutex> guard_type;

 public:

    typedef detail::recursive_queue_node mailbox_element;
    typedef intrusive::single_reader_queue<mailbox_element> mailbox_type;

    bool attach(attachable* ptr) // override
    {
        if (ptr == nullptr)
        {
            guard_type guard(m_mtx);
            return m_exit_reason.load() == exit_reason::not_exited;
        }
        else
        {
            attachable_ptr uptr(ptr);
            std::uint32_t reason;
            // lifetime scope of guard
            {
                guard_type guard(m_mtx);
                reason = m_exit_reason.load();
                if (reason == exit_reason::not_exited)
                {
                    m_attachables.push_back(std::move(uptr));
                    return true;
                }
            }
            uptr->actor_exited(reason);
            return false;
        }
    }

    void detach(attachable::token const& what) // override
    {
        attachable_ptr uptr;
        // lifetime scope of guard
        {
            guard_type guard(m_mtx);
            auto end = m_attachables.end();
            auto i = std::find_if(
                        m_attachables.begin(), end,
                        [&](attachable_ptr& p) { return p->matches(what); });
            if (i != end)
            {
                uptr = std::move(*i);
                m_attachables.erase(i);
            }
        }
        // uptr will be destroyed here, without locked mutex
    }

    void link_to(intrusive_ptr<actor>& other) // override
    {
        (void) link_to_impl(other);
    }

    void unlink_from(intrusive_ptr<actor>& other) // override
    {
        (void) unlink_from_impl(other);
    }

    bool remove_backlink(intrusive_ptr<actor>& other) // override
    {
        if (other && other != this)
        {
            guard_type guard(m_mtx);
            auto i = std::find(m_links.begin(), m_links.end(), other);
            if (i != m_links.end())
            {
                m_links.erase(i);
                return true;
            }
        }
        return false;
    }

    bool establish_backlink(intrusive_ptr<actor>& other) // override
    {
        std::uint32_t reason = exit_reason::not_exited;
        if (other && other != this)
        {
            guard_type guard(m_mtx);
            reason = m_exit_reason.load();
            if (reason == exit_reason::not_exited)
            {
                auto i = std::find(m_links.begin(), m_links.end(), other);
                if (i == m_links.end())
                {
                    m_links.push_back(other);
                    return true;
                }
            }
        }
        // send exit message without lock
        if (reason != exit_reason::not_exited)
        {
            other->enqueue(this, make_cow_tuple(atom(":Exit"), reason));
        }
        return false;
    }

 protected:

    mailbox_type m_mailbox;
    util::fixed_vector<mailbox_element*, 10> m_nodes;

    inline mailbox_element* fetch_node(actor* sender, any_tuple msg)
    {
        if (m_nodes.empty())
        {
            return new mailbox_element(sender, std::move(msg));
        }
        else
        {
            auto result = m_nodes.back();
            m_nodes.pop_back();
            result->marked = false;
            result->sender = sender;
            result->msg = std::move(msg);
            return result;
        }

    }

    inline void release_node(mailbox_element* node)
    {
        if (m_nodes.full())
        {
            delete node;
        }
        else
        {
            m_nodes.push_back(node);
        }
    }

    template<typename... Args>
    abstract_actor(Args&&... args) : Base(std::forward<Args>(args)...)
                                   , m_exit_reason(exit_reason::not_exited)
    {
        // pre-allocate some nodes
        for (size_t i = 0; i < m_nodes.max_size() / 2; ++i)
        {
            m_nodes.push_back(new mailbox_element);
        }
    }

    void cleanup(std::uint32_t reason)
    {
        if (reason == exit_reason::not_exited) return;
        decltype(m_links) mlinks;
        decltype(m_attachables) mattachables;
        // lifetime scope of guard
        {
            guard_type guard(m_mtx);
            if (m_exit_reason != exit_reason::not_exited)
            {
                // already exited
                return;
            }
            m_exit_reason = reason;
            mlinks = std::move(m_links);
            mattachables = std::move(m_attachables);
            // make sure lists are definitely empty
            m_links.clear();
            m_attachables.clear();
        }
        // send exit messages
        for (actor_ptr& aptr : mlinks)
        {
            aptr->enqueue(this, make_cow_tuple(atom(":Exit"), reason));
        }
        for (attachable_ptr& ptr : mattachables)
        {
            ptr->actor_exited(reason);
        }
    }

    bool link_to_impl(intrusive_ptr<actor>& other)
    {
        if (other && other != this)
        {
            guard_type guard(m_mtx);
            // send exit message if already exited
            if (exited())
            {
                other->enqueue(this, make_cow_tuple(atom(":Exit"),
                                                m_exit_reason.load()));
            }
            // add link if not already linked to other
            // (checked by establish_backlink)
            else if (other->establish_backlink(this))
            {
                m_links.push_back(other);
                return true;
            }
        }
        return false;
    }

    bool unlink_from_impl(intrusive_ptr<actor>& other)
    {
        guard_type guard(m_mtx);
        // remove_backlink returns true if this actor is linked to other
        if (other && !exited() && other->remove_backlink(this))
        {
            auto i = std::find(m_links.begin(), m_links.end(), other);
            CPPA_REQUIRE(i != m_links.end());
            m_links.erase(i);
            return true;
        }
        return false;
    }

 private:

    // @pre m_mtx.locked()
    bool exited() const
    {
        return m_exit_reason.load() != exit_reason::not_exited;
    }

    // true if the associated thread has finished execution
    std::atomic<std::uint32_t> m_exit_reason;
    // guards access to m_exited, m_subscriptions and m_links
    detail::mutex m_mtx;
    // links to other actors
    std::vector<actor_ptr> m_links;
    // code that is executed on cleanup
    std::vector<attachable_ptr> m_attachables;

};

} // namespace cppa

#endif // ABSTRACT_ACTOR_HPP
<|endoftext|>


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

#ifndef _SAL_I18N_STATUS_HXX
#define _SAL_I18N_STATUS_HXX

#ifndef _STRING_HXX
#include <tools/string.hxx>
#endif

#ifndef _LINK_HXX
#include <tools/link.hxx>
#endif

#ifndef _GEN_HXX
#include <tools/gen.hxx>
#endif

#ifndef _RTL_USTRING_HXX
#include <rtl/ustring.hxx>
#endif

#ifndef _SV_SALIMESTATUS_HXX
#include <salimestatus.hxx>
#endif

#include <vector>

class SalFrame;
class WorkWindow;
class ListBox;
class FixedText;
class PushButton;
class SalI18N_InputContext;

namespace vcl
{

class StatusWindow;

class X11ImeStatus : public SalI18NImeStatus
{
public:
    X11ImeStatus() {}
    virtual ~X11ImeStatus();

    virtual bool canToggle();
    virtual void toggle();
};

class I18NStatus
{
public:
    struct ChoiceData
    {
        String  aString;
        void*   pData;
    };
private:
    SalFrame*                       m_pParent;
    StatusWindow*                   m_pStatusWindow;
    String                          m_aCurrentIM;
    ::std::vector< ChoiceData >     m_aChoices;

    I18NStatus();
    ~I18NStatus();

    static I18NStatus* pInstance;

    static bool getStatusWindowMode();

public:
    static I18NStatus& get();
    static void free();

    void setParent( SalFrame* pParent );
    SalFrame* getParent() const { return  m_pParent; }
    SalFrame* getStatusFrame() const;

    void setStatusText( const String& rText );
    String getStatusText() const;

    enum ShowReason { focus, presentation, contextmap };
    void show( bool bShow, ShowReason eReason );

    const ::std::vector< ChoiceData >& getChoices() const { return m_aChoices; }
    void clearChoices();
    void addChoice( const String&, void* pData );

    void toTop() const;

    // for SwitchIMCallback
    void changeIM( const String& );

    // External Control:

    /** Return true if the status window can be toggled on and off externally.
     */
    bool canToggleStatusWindow() const;

    /** Toggle the status window on or off.

        This only works if canToggleStatusWindow returns true (otherwise, any
        calls of this method are ignored).
     */
    void toggleStatusWindow();
};

} // namespace

#endif // _SAL_I18N_STATUS_HXX
<commit_msg>INTEGRATION: CWS vcl74 (1.9.462); FILE MERGED 2007/02/14 18:14:24 pl 1.9.462.1: #i73965# handle possible own IME status window<commit_after>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: i18n_status.hxx,v $
 *
 *  $Revision: 1.10 $
 *
 *  last change: $Author: obo $ $Date: 2007-03-05 15:25:46 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library is distributed in the hope that it will be useful,
 *    but WITHOUT ANY WARRANTY; without even the implied warranty of
 *    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 *    Lesser General Public License for more details.
 *
 *    You should have received a copy of the GNU Lesser General Public
 *    License along with this library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _SAL_I18N_STATUS_HXX
#define _SAL_I18N_STATUS_HXX

#ifndef _STRING_HXX
#include <tools/string.hxx>
#endif

#ifndef _LINK_HXX
#include <tools/link.hxx>
#endif

#ifndef _GEN_HXX
#include <tools/gen.hxx>
#endif

#ifndef _RTL_USTRING_HXX
#include <rtl/ustring.hxx>
#endif

#ifndef _SV_SALIMESTATUS_HXX
#include <salimestatus.hxx>
#endif

#include <vector>

class SalFrame;
class WorkWindow;
class ListBox;
class FixedText;
class PushButton;
class SalI18N_InputContext;

namespace vcl
{

class StatusWindow;

class X11ImeStatus : public SalI18NImeStatus
{
public:
    X11ImeStatus() {}
    virtual ~X11ImeStatus();

    virtual bool canToggle();
    virtual void toggle();
};

class I18NStatus
{
public:
    struct ChoiceData
    {
        String  aString;
        void*   pData;
    };
private:
    SalFrame*                       m_pParent;
    StatusWindow*                   m_pStatusWindow;
    String                          m_aCurrentIM;
    ::std::vector< ChoiceData >     m_aChoices;

    I18NStatus();
    ~I18NStatus();

    static I18NStatus* pInstance;

    static bool getStatusWindowMode();

public:
    static I18NStatus& get();
    static bool exists();
    static void free();

    void setParent( SalFrame* pParent );
    SalFrame* getParent() const { return  m_pParent; }
    SalFrame* getStatusFrame() const;

    void setStatusText( const String& rText );
    String getStatusText() const;

    enum ShowReason { focus, presentation, contextmap };
    void show( bool bShow, ShowReason eReason );

    const ::std::vector< ChoiceData >& getChoices() const { return m_aChoices; }
    void clearChoices();
    void addChoice( const String&, void* pData );

    void toTop() const;

    // for SwitchIMCallback
    void changeIM( const String& );

    // External Control:

    /** Return true if the status window can be toggled on and off externally.
     */
    bool canToggleStatusWindow() const;

    /** Toggle the status window on or off.

        This only works if canToggleStatusWindow returns true (otherwise, any
        calls of this method are ignored).
     */
    void toggleStatusWindow();
};

} // namespace

#endif // _SAL_I18N_STATUS_HXX
<|endoftext|>


<commit_before>/**
 * @file
 */
#pragma once

#include "libbirch/external.hpp"
#include "libbirch/internal.hpp"
#include "libbirch/memory.hpp"
#include "libbirch/thread.hpp"
#include "libbirch/Atomic.hpp"

#include <iostream>

namespace libbirch {
/**
 * Flags used for cycle collection as in @ref Bacon2001
 * "Bacon & Rajan (2001)", replacing the colors described there. The reason
 * is to ensure that both the bookkeeping required during normal execution
 * can be multithreaded, and likewise that the operations required during
 * cycle collection can be multithreaded. The basic principle to ensure this
 * is that flags can be safely set during normal execution (with atomic
 * operations), but should only be unset with careful consideration of
 * thread safety.
 *
 * Notwithstanding, the flags do map to colors in @ref Bacon2001
 * "Bacon & Rajan (2001)":
 *
 *   - *acyclic* maps to *green*,
 *   - *buffered* maps to *purple*,
 *   - *marked* maps to *gray*,
 *   - *scanned* and *reachable* together map to *black* (both on) or
 *     *white* (first on, second off),
 *   - *collected* is set once a white object has been destroyed.
 *
 * The use of these flags also resolves some thread safety issues that can
 * otherwise exist during the scan operation, when coloring an object white
 * (eligible for collection) then later recoloring it black (reachable); the
 * sequencing of this coloring can become problematic with multiple threads.
 */
enum Flag : int16_t {
  ACYCLIC = (1 << 0),
  BUFFERED = (1 << 1),
  MARKED = (1 << 2),
  SCANNED = (1 << 3),
  REACHED = (1 << 4),
  COLLECTED = (1 << 5),
  POSSIBLE_ROOT = (1 << 6),
  CLAIMED = (1 << 7),
  HEAD = (1 << 8)
};

/**
 * Base class providing reference counting, cycle breaking, and lazy deep
 * copy support.
 *
 * @ingroup libbirch
 *
 * Members of Any use an underscore suffix (e.g. `n_` instead of `n`) in order
 * to avoid naming collisions with derived classes.
 */
class Any {
  friend class Marker;
  friend class Scanner;
  friend class Reacher;
  friend class Collector;
  friend class BiconnectedCollector;
  friend class Spanner;
  friend class Bridger;
  friend class Copier;
  friend class Memo;
  friend class BiconnectedCopier;
  friend class BiconnectedMemo;
public:
  using this_type_ = Any;

  /**
   * Constructor.
   */
  Any() :
      r_(0),
      a_(0),
      l_(std::numeric_limits<int>::max()),
      h_(0),
      p_(-1),
      f_(0) {
    //
  }

  /**
   * Copy constructor.
   */
  Any(const Any& o) :
      r_(0),
      a_(0),
      l_(std::numeric_limits<int>::max()),
      h_(0),
      p_(-1),
      f_(o.f_.load() & ACYCLIC) {
    //
  }

  /**
   * Destructor.
   */
  virtual ~Any() {
    assert(r_.load() == 0);
  }

  /**
   * New operator.
   */
  void* operator new(std::size_t size) {
    /* object destruction and deallocation are separated; an explicit call to
     * the destructor is used to destroy, and std::free() used to deallocate,
     * so std::malloc() should be used to allocate; using the default operator
     * new can result in a double free, as reported by valgrind */
    return std::malloc(size);
  }

  /**
   * Assignment operator.
   */
  Any& operator=(const Any&) {
    return *this;
  }

  /**
   * Destroy.
   */
  void destroy_() {
    this->~Any();
  }

  /**
   * Deallocate.
   */
  void deallocate_() {
    std::free(this);
  }

  /**
   * Reference count.
   */
  int numShared_() const {
    return r_.load();
  }

  /**
   * Increment the shared r.
   */
  void incShared_() {
    r_.increment();
  }

  /**
   * Decrement the shared reference count.
   */
  void decShared_() {
    assert(numShared_() > 0);

    if (isHead_()) {
      if (r_.load() == a_) {
        /* last external reference about to be removed, remainder are internal
         * to the biconnected component */
        biconnected_collect(this);
        r_.decrement();
        destroy_();
        deallocate_();
      } else {
        r_.decrement();
      }
    } else {
      /* first set the BUFFERED flag so that this call is uniquely responsible
      * for registering this object as a possible root */
      auto old = f_.exchangeOr(BUFFERED|POSSIBLE_ROOT);
      if (--r_ == 0) {
        /* destroy, and as long as haven't been previously buffered, can
         * deallocate too */
        destroy_();
        if (!(old & BUFFERED) || old & ACYCLIC) {
          /* hasn't been previously buffered, or is acyclic, so can
           * immediately deallocate */
          deallocate_();
        } else {
          /* has been previously buffered, so deallocation must be deferred
           * until collection, but certainly not a possible root, as has just
           * been destroyed */
          f_.maskAnd(~POSSIBLE_ROOT);
        }
      } else if (!(old & BUFFERED) && !(old & ACYCLIC)) {
        /* not already registered as a possible root, and not acyclic */
        register_possible_root(this);
      }
    }
  }

  /**
   * Decrement the shared count for an object that will remain reachable. The
   * caller asserts that the object will remain reachable after the operation.
   * The object will not be destroyed, and will not be registered as a
   * possible root for cycle collection.
   */
  void decSharedReachable_() {
    assert(numShared_() > 0);
    r_.decrement();
  }

  /**
   * Decrement the shared count during collection of a biconnected component,
   * where the caller is responsible for destruction and deallocation, if
   * necessary.
   */
  void decSharedBiconnected_() {
    assert(numShared_() > 0);
    if (--r_ == 0) {
      destroy_();
      deallocate_();
    }
  }

  /**
   * Is there only one pointer (of any type) to this object?
   */
  bool isUnique_() const {
    return numShared_() == 1;
  }

  /**
   * Is there only one pointer (of any type) to this biconnected component?
   */
  bool isUniqueHead_() const {
    assert(isHead_());
    return numShared_() == a_;
  }

  /**
   * Is this object of an acyclic class?
   */
  bool isAcyclic_() const {
    return f_.load() & ACYCLIC;
  }

  /**
   * Is this object the possible root of a cycle?
   */
  bool isPossibleRoot_() const {
    return f_.load() & POSSIBLE_ROOT;
  }

  /**
   * Is the head flag set?
   */
  bool isHead_() const {
    return f_.load() & HEAD;
  }

  /**
   * Set the acyclic flag.
   */
  void acyclic_() {
    f_.maskOr(ACYCLIC);
  }

  /**
   * Set the head flag.
   */
  void head_() {
    assert(r_.load() == a_);
    f_.maskOr(HEAD);
  }

  /**
   * Unset the head flag.
   */
  void unhead_() {
    f_.maskAnd(~(HEAD|BUFFERED|POSSIBLE_ROOT));
  }

  /**
   * Unset buffer flag.
   */
  void unbuffer_() {
    f_.maskAnd(~(BUFFERED|POSSIBLE_ROOT));
  }

  /**
   * Get the class name.
   */
  virtual const char* getClassName_() const {
    return "Any";
  }

  /**
   * Size of the object.
   */
  virtual int size_() const {
    return sizeof(Any);
  }

  /**
   * Shallow copy the object.
   */
  virtual Any* copy_() const = 0;

  virtual void accept_(Marker& visitor) {
    //
  }

  virtual void accept_(Scanner& visitor) {
    //
  }

  virtual void accept_(Reacher& visitor) {
    //
  }

  virtual void accept_(Collector& visitor) {
    //
  }

  virtual void accept_(BiconnectedCollector& visitor) {
    //
  }

  virtual std::tuple<int,int,int> accept_(Spanner& visitor, const int i,
      const int j) {
    return std::make_tuple(i, i, 0);
  }

  virtual std::tuple<int,int,int,int> accept_(Bridger& visitor, const int j,
      const int k) {
    return std::make_tuple(std::numeric_limits<int>::max(), 0, 0, 0);
  }

  virtual void accept_(Copier& visitor) {
    //
  }

  virtual void accept_(BiconnectedCopier& visitor) {
    //
  }

private:
  /**
   * Reference count.
   */
  Atomic<int> r_;

  /**
   * Account of references, used for bridge finding and cycle collection. For
   * the head of a biconnected component (i.e. HEAD flag is set), this is the
   * number of internal references to the object in its biconnected component,
   * plus one for the original bridge edge.
   */
  int a_;

  union {
    /**
     * Lowest reachable rank, used for bridge finding.
     */
    int l_;

    /**
     * Index base in biconnected component, used for copying.
     */
    int k_;
  };

  union {
    /**
     * Highest reachable rank, used for bridge finding.
     */
    int h_;

    /**
     * Size of biconnnected component, used for copying.
     */
    int n_;
  };

  /**
   * Id of the thread that claimed the object, used for bridge finding.
   */
  int16_t p_;

  /**
   * Bitfield containing flags, used for bridge finding and cycle collection.
   */
  Atomic<int16_t> f_;
};
}
<commit_msg>Added operator delete to Any, to silence some compiler warnings in Fedora packaging.<commit_after>/**
 * @file
 */
#pragma once

#include "libbirch/external.hpp"
#include "libbirch/internal.hpp"
#include "libbirch/memory.hpp"
#include "libbirch/thread.hpp"
#include "libbirch/Atomic.hpp"

#include <iostream>

namespace libbirch {
/**
 * Flags used for cycle collection as in @ref Bacon2001
 * "Bacon & Rajan (2001)", replacing the colors described there. The reason
 * is to ensure that both the bookkeeping required during normal execution
 * can be multithreaded, and likewise that the operations required during
 * cycle collection can be multithreaded. The basic principle to ensure this
 * is that flags can be safely set during normal execution (with atomic
 * operations), but should only be unset with careful consideration of
 * thread safety.
 *
 * Notwithstanding, the flags do map to colors in @ref Bacon2001
 * "Bacon & Rajan (2001)":
 *
 *   - *acyclic* maps to *green*,
 *   - *buffered* maps to *purple*,
 *   - *marked* maps to *gray*,
 *   - *scanned* and *reachable* together map to *black* (both on) or
 *     *white* (first on, second off),
 *   - *collected* is set once a white object has been destroyed.
 *
 * The use of these flags also resolves some thread safety issues that can
 * otherwise exist during the scan operation, when coloring an object white
 * (eligible for collection) then later recoloring it black (reachable); the
 * sequencing of this coloring can become problematic with multiple threads.
 */
enum Flag : int16_t {
  ACYCLIC = (1 << 0),
  BUFFERED = (1 << 1),
  MARKED = (1 << 2),
  SCANNED = (1 << 3),
  REACHED = (1 << 4),
  COLLECTED = (1 << 5),
  POSSIBLE_ROOT = (1 << 6),
  CLAIMED = (1 << 7),
  HEAD = (1 << 8)
};

/**
 * Base class providing reference counting, cycle breaking, and lazy deep
 * copy support.
 *
 * @ingroup libbirch
 *
 * Members of Any use an underscore suffix (e.g. `n_` instead of `n`) in order
 * to avoid naming collisions with derived classes.
 */
class Any {
  friend class Marker;
  friend class Scanner;
  friend class Reacher;
  friend class Collector;
  friend class BiconnectedCollector;
  friend class Spanner;
  friend class Bridger;
  friend class Copier;
  friend class Memo;
  friend class BiconnectedCopier;
  friend class BiconnectedMemo;
public:
  using this_type_ = Any;

  /**
   * Constructor.
   */
  Any() :
      r_(0),
      a_(0),
      l_(std::numeric_limits<int>::max()),
      h_(0),
      p_(-1),
      f_(0) {
    //
  }

  /**
   * Copy constructor.
   */
  Any(const Any& o) :
      r_(0),
      a_(0),
      l_(std::numeric_limits<int>::max()),
      h_(0),
      p_(-1),
      f_(o.f_.load() & ACYCLIC) {
    //
  }

  /**
   * Destructor.
   */
  virtual ~Any() {
    assert(r_.load() == 0);
  }

  /**
   * New operator.
   */
  void* operator new(std::size_t size) {
    /* object destruction and deallocation are separated; an explicit call to
     * the destructor is used to destroy, and std::free() used to deallocate,
     * so std::malloc() should be used to allocate; using the default operator
     * new can result in a double free, as reported by valgrind */
    return std::malloc(size);
  }

  /**
   * Delete operator.
   */
  void operator delete(void* ptr) {
    std::free(ptr);
  }

  /**
   * Assignment operator.
   */
  Any& operator=(const Any&) {
    return *this;
  }

  /**
   * Destroy.
   */
  void destroy_() {
    this->~Any();
  }

  /**
   * Deallocate.
   */
  void deallocate_() {
    std::free(this);
  }

  /**
   * Reference count.
   */
  int numShared_() const {
    return r_.load();
  }

  /**
   * Increment the shared r.
   */
  void incShared_() {
    r_.increment();
  }

  /**
   * Decrement the shared reference count.
   */
  void decShared_() {
    assert(numShared_() > 0);

    if (isHead_()) {
      if (r_.load() == a_) {
        /* last external reference about to be removed, remainder are internal
         * to the biconnected component */
        biconnected_collect(this);
        r_.decrement();
        destroy_();
        deallocate_();
      } else {
        r_.decrement();
      }
    } else {
      /* first set the BUFFERED flag so that this call is uniquely responsible
      * for registering this object as a possible root */
      auto old = f_.exchangeOr(BUFFERED|POSSIBLE_ROOT);
      if (--r_ == 0) {
        /* destroy, and as long as haven't been previously buffered, can
         * deallocate too */
        destroy_();
        if (!(old & BUFFERED) || old & ACYCLIC) {
          /* hasn't been previously buffered, or is acyclic, so can
           * immediately deallocate */
          deallocate_();
        } else {
          /* has been previously buffered, so deallocation must be deferred
           * until collection, but certainly not a possible root, as has just
           * been destroyed */
          f_.maskAnd(~POSSIBLE_ROOT);
        }
      } else if (!(old & BUFFERED) && !(old & ACYCLIC)) {
        /* not already registered as a possible root, and not acyclic */
        register_possible_root(this);
      }
    }
  }

  /**
   * Decrement the shared count for an object that will remain reachable. The
   * caller asserts that the object will remain reachable after the operation.
   * The object will not be destroyed, and will not be registered as a
   * possible root for cycle collection.
   */
  void decSharedReachable_() {
    assert(numShared_() > 0);
    r_.decrement();
  }

  /**
   * Decrement the shared count during collection of a biconnected component,
   * where the caller is responsible for destruction and deallocation, if
   * necessary.
   */
  void decSharedBiconnected_() {
    assert(numShared_() > 0);
    if (--r_ == 0) {
      destroy_();
      deallocate_();
    }
  }

  /**
   * Is there only one pointer (of any type) to this object?
   */
  bool isUnique_() const {
    return numShared_() == 1;
  }

  /**
   * Is there only one pointer (of any type) to this biconnected component?
   */
  bool isUniqueHead_() const {
    assert(isHead_());
    return numShared_() == a_;
  }

  /**
   * Is this object of an acyclic class?
   */
  bool isAcyclic_() const {
    return f_.load() & ACYCLIC;
  }

  /**
   * Is this object the possible root of a cycle?
   */
  bool isPossibleRoot_() const {
    return f_.load() & POSSIBLE_ROOT;
  }

  /**
   * Is the head flag set?
   */
  bool isHead_() const {
    return f_.load() & HEAD;
  }

  /**
   * Set the acyclic flag.
   */
  void acyclic_() {
    f_.maskOr(ACYCLIC);
  }

  /**
   * Set the head flag.
   */
  void head_() {
    assert(r_.load() == a_);
    f_.maskOr(HEAD);
  }

  /**
   * Unset the head flag.
   */
  void unhead_() {
    f_.maskAnd(~(HEAD|BUFFERED|POSSIBLE_ROOT));
  }

  /**
   * Unset buffer flag.
   */
  void unbuffer_() {
    f_.maskAnd(~(BUFFERED|POSSIBLE_ROOT));
  }

  /**
   * Get the class name.
   */
  virtual const char* getClassName_() const {
    return "Any";
  }

  /**
   * Size of the object.
   */
  virtual int size_() const {
    return sizeof(Any);
  }

  /**
   * Shallow copy the object.
   */
  virtual Any* copy_() const = 0;

  virtual void accept_(Marker& visitor) {
    //
  }

  virtual void accept_(Scanner& visitor) {
    //
  }

  virtual void accept_(Reacher& visitor) {
    //
  }

  virtual void accept_(Collector& visitor) {
    //
  }

  virtual void accept_(BiconnectedCollector& visitor) {
    //
  }

  virtual std::tuple<int,int,int> accept_(Spanner& visitor, const int i,
      const int j) {
    return std::make_tuple(i, i, 0);
  }

  virtual std::tuple<int,int,int,int> accept_(Bridger& visitor, const int j,
      const int k) {
    return std::make_tuple(std::numeric_limits<int>::max(), 0, 0, 0);
  }

  virtual void accept_(Copier& visitor) {
    //
  }

  virtual void accept_(BiconnectedCopier& visitor) {
    //
  }

private:
  /**
   * Reference count.
   */
  Atomic<int> r_;

  /**
   * Account of references, used for bridge finding and cycle collection. For
   * the head of a biconnected component (i.e. HEAD flag is set), this is the
   * number of internal references to the object in its biconnected component,
   * plus one for the original bridge edge.
   */
  int a_;

  union {
    /**
     * Lowest reachable rank, used for bridge finding.
     */
    int l_;

    /**
     * Index base in biconnected component, used for copying.
     */
    int k_;
  };

  union {
    /**
     * Highest reachable rank, used for bridge finding.
     */
    int h_;

    /**
     * Size of biconnnected component, used for copying.
     */
    int n_;
  };

  /**
   * Id of the thread that claimed the object, used for bridge finding.
   */
  int16_t p_;

  /**
   * Bitfield containing flags, used for bridge finding and cycle collection.
   */
  Atomic<int16_t> f_;
};
}
<|endoftext|>


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

  Library:   CTK

  Copyright (c) Kitware 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.txt

  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.

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

// Qt includes
#include <QApplication>
#include <QDebug>
#include <QGroupBox>
#include <QLabel>
#include <QStyle>
#include <QTimer>
#include <QTreeView>
#include <QVBoxLayout>
#include <QPlastiqueStyle>

// CTK includes
#include "ctkExpandableWidget.h"

// STD includes
#include <cstdlib>
#include <iostream>

//-----------------------------------------------------------------------------
int ctkExpandableWidgetTest1(int argc, char * argv [] )
{
  QApplication::setStyle(new QPlastiqueStyle);
  QApplication app(argc, argv);

  QWidget topLevel;
  QHBoxLayout* topLevelLayout = new QHBoxLayout;
  topLevel.setLayout(topLevelLayout);

  QWidget frame(&topLevel);
  //ctkExpandableWidget frame(&topLevel);
  topLevelLayout->addWidget(&frame);

  QGroupBox groupBox(&topLevel);
  topLevelLayout->addWidget(&groupBox);

  QLabel* label0 = new QLabel("should be invisible", &frame);
  QLabel* label1 = new QLabel("should be visible", &frame);
  QLabel* label2 = new QLabel("should be invisible", &frame);
  QLabel* label3 = new QLabel("should be visible", &frame);
  QLabel* label4 = new QLabel("should be invisible", &frame);
  QLabel* label5 = new QLabel("should be visible", &frame);
  QLabel* label6 = new QLabel("should be visible", &frame);
  QLabel* label7 = new QLabel("should be visible", &frame);
  QLabel* label8 = new QLabel("should be invisible", &frame);

  ctkExpandableWidget resizableFrame(&frame);
  resizableFrame.setSizeGripInside(false);
  QTreeView* treeView = new QTreeView(&resizableFrame);
  treeView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
  QVBoxLayout* resizableFrameLayout = new QVBoxLayout(&resizableFrame);
  resizableFrameLayout->setContentsMargins(0,0,0,0);
  //resizableFrame.setSizeGripMargins(QSize(2, 2));
  resizableFrame.setLayout(resizableFrameLayout);
  resizableFrameLayout->addWidget(treeView);
  resizableFrame.setOrientations(Qt::Horizontal);

  label8->setVisible(false);
  label2->setVisible(false);
  label4->setVisible(false);
  label8->setVisible(false);

  QVBoxLayout *vbox = new QVBoxLayout;
  vbox->setContentsMargins(0, 0, 0, 0);
  vbox->addWidget(label0);
  vbox->addWidget(label1);
  vbox->addWidget(label2);
  vbox->addWidget(label3);
  vbox->addWidget(label4);
  vbox->addWidget(label5);
  vbox->addWidget(label6);
  vbox->addWidget(label7);
  vbox->addWidget(label8);
  vbox->addWidget(&resizableFrame);
  frame.setLayout(vbox);

  topLevel.show();

  if (argc < 2 || QString(argv[1]) != "-I" )
    {
    QTimer::singleShot(200, &app, SLOT(quit()));
    }

  return app.exec();
}
<commit_msg>Improve ctkExpandableWidgetTest to better show features<commit_after>/*=========================================================================

  Library:   CTK

  Copyright (c) Kitware 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.txt

  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.

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

// Qt includes
#include <QApplication>
#include <QDebug>
#include <QGroupBox>
#include <QLabel>
#include <QListView>
#include <QStyle>
#include <QTableView>
#include <QTimer>
#include <QTreeView>
#include <QVBoxLayout>
#include <QPlastiqueStyle>

// CTK includes
#include "ctkExpandableWidget.h"

// STD includes
#include <cstdlib>
#include <iostream>

//-----------------------------------------------------------------------------
int ctkExpandableWidgetTest1(int argc, char * argv [] )
{
  QApplication::setStyle(new QPlastiqueStyle);
  QApplication app(argc, argv);

  QWidget topLevel;
  QHBoxLayout* topLevelLayout = new QHBoxLayout;
  topLevel.setLayout(topLevelLayout);

  QWidget frame;
  topLevelLayout->addWidget(&frame);

  ctkExpandableWidget resizableFrame1(&frame);
  QListView* listView = new QListView(&resizableFrame1);
  listView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
  QVBoxLayout* resizableFrameLayout1 = new QVBoxLayout(&resizableFrame1);
  resizableFrameLayout1->setContentsMargins(0,0,0,0);
  resizableFrame1.setSizeGripMargins(QSize(2, 2));
  resizableFrame1.setLayout(resizableFrameLayout1);
  resizableFrameLayout1->addWidget(listView);
  resizableFrame1.setOrientations(Qt::Horizontal | Qt::Vertical);

  ctkExpandableWidget resizableFrame(&frame);
  resizableFrame.setSizeGripInside(false);
  QTreeView* treeView = new QTreeView(&resizableFrame);
  treeView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
  QVBoxLayout* resizableFrameLayout = new QVBoxLayout(&resizableFrame);
  resizableFrameLayout->setContentsMargins(0,0,0,0);
  //resizableFrame.setSizeGripMargins(QSize(2, 2));
  resizableFrame.setLayout(resizableFrameLayout);
  resizableFrameLayout->addWidget(treeView);
  resizableFrame.setOrientations(Qt::Horizontal);

  QVBoxLayout *vbox = new QVBoxLayout;
  vbox->setContentsMargins(0, 0, 0, 0);
  vbox->addWidget(&resizableFrame1);
  vbox->addWidget(&resizableFrame);
  frame.setLayout(vbox);

  ctkExpandableWidget resizableFrame0(&topLevel);
  QTableView* tableView = new QTableView(&resizableFrame0);
  tableView->setSizePolicy(QSizePolicy(QSizePolicy::Preferred, QSizePolicy::Preferred));
  QVBoxLayout* resizableFrameLayout0 = new QVBoxLayout(&resizableFrame0);
  resizableFrameLayout0->setContentsMargins(0,0,0,0);
  resizableFrame0.setSizeGripMargins(QSize(2, 2));
  resizableFrame0.setLayout(resizableFrameLayout0);
  resizableFrameLayout0->addWidget(tableView);
  resizableFrame0.setOrientations(Qt::Vertical);
  topLevelLayout->addWidget(&resizableFrame0);

  topLevel.show();

  if (argc < 2 || QString(argv[1]) != "-I" )
    {
    QTimer::singleShot(200, &app, SLOT(quit()));
    }

  return app.exec();
}
<|endoftext|>


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

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

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

=========================================================================*/
#include "otbGDALDriverManagerWrapper.h"
#include <vector>
#include "otb_boost_string_header.h"
#include "otbSystem.h"

namespace otb
{

// GDALDriverManagerWrapper method implementation

GDALDriverManagerWrapper::GDALDriverManagerWrapper()
{
    GDALAllRegister();

    GDALDriver* driver = ITK_NULLPTR;

    // Ignore incompatible Jpeg2000 drivers (Jasper)
    driver = GetGDALDriverManager()->GetDriverByName( "JPEG2000" );
    if (driver)
      GetGDALDriverManager()->DeregisterDriver( driver );

// #ifndef CHECK_HDF4OPEN_SYMBOL
//     // Get rid of the HDF4 driver when it is buggy
//     driver = GetGDALDriverManager()->GetDriverByName( "hdf4" );
//     if (driver)
//       GetGDALDriverManager()->DeregisterDriver( driver );
// #endif
}

GDALDriverManagerWrapper::~GDALDriverManagerWrapper()
{
  GDALDestroyDriverManager();
}

// Open the file for reading and returns a smart dataset pointer
GDALDatasetWrapper::Pointer
GDALDriverManagerWrapper::Open( std::string filename ) const
{
  // This GDAL config option is there to handle UTF-8 filenames on Windows
  // It fixes mantis bug 1318 where images with filenames like 你好.tif could not be read on Windows
  CPLSetConfigOption("GDAL_FILENAME_IS_UTF8","NO");

  GDALDatasetWrapper::Pointer datasetWrapper;

  if (boost::algorithm::starts_with(filename, "http://")
      || boost::algorithm::starts_with(filename, "https://") )
    {
    // don't try to open it and exit
    return datasetWrapper;
    }

  // test if a driver can identify the dataset
  GDALDriverH identifyDriverH = GDALIdentifyDriver(filename.c_str(), ITK_NULLPTR);
  if(identifyDriverH == ITK_NULLPTR)
    {
    // don't try to open it and exit
    return datasetWrapper;
    }

  GDALDriver *identifyDriver = static_cast<GDALDriver*>(identifyDriverH);

  // check if Jasper will be used
  if (strcmp(identifyDriver->GetDescription(),"JPEG2000") == 0)
    {
    itkGenericExceptionMacro(<< "Error : tried to open the file "
      << filename.c_str() << " with GDAL driver Jasper "
      "(which fails on OTB). Try setting the environment variable GDAL_SKIP"
      " in order to avoid this driver.");
    }

  GDALDatasetH dataset = GDALOpen(filename.c_str(), GA_ReadOnly);

  if (dataset != ITK_NULLPTR)
    {
    datasetWrapper = GDALDatasetWrapper::New();
    datasetWrapper->m_Dataset = static_cast<GDALDataset*>(dataset);
    }
  return datasetWrapper;
}

// Open the new  file for writing and returns a smart dataset pointer
GDALDatasetWrapper::Pointer
GDALDriverManagerWrapper::Create( std::string driverShortName, std::string filename,
                                    int nXSize, int nYSize, int nBands,
                                    GDALDataType eType, char ** papszOptions ) const
{
  GDALDatasetWrapper::Pointer datasetWrapper;

  GDALDriver*  driver = GetDriverByName( driverShortName );
  if(driver != ITK_NULLPTR)
    {
    GDALDataset* dataset = driver->Create(filename.c_str(),
                                          nXSize, nYSize,
                                          nBands, eType,
                                          papszOptions );

    if (dataset != ITK_NULLPTR)
      {
      datasetWrapper = GDALDatasetWrapper::New();
      datasetWrapper->m_Dataset = dataset;
      }
    }
  return datasetWrapper;
}

GDALDriver*
GDALDriverManagerWrapper::GetDriverByName( std::string driverShortName ) const
{
  return GetGDALDriverManager()->GetDriverByName(driverShortName.c_str());
}

} // end namespace otb
<commit_msg>Revert "BUG: mantis-1318 fix GDAL UTF-8 on Windows"<commit_after>/*=========================================================================

  Program:   ORFEO Toolbox
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Centre National d'Etudes Spatiales. All rights reserved.
  See OTBCopyright.txt for details.

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

=========================================================================*/
#include "otbGDALDriverManagerWrapper.h"
#include <vector>
#include "otb_boost_string_header.h"
#include "otbSystem.h"

namespace otb
{

// GDALDriverManagerWrapper method implementation

GDALDriverManagerWrapper::GDALDriverManagerWrapper()
{
    GDALAllRegister();

    GDALDriver* driver = ITK_NULLPTR;

    // Ignore incompatible Jpeg2000 drivers (Jasper)
    driver = GetGDALDriverManager()->GetDriverByName( "JPEG2000" );
    if (driver)
      GetGDALDriverManager()->DeregisterDriver( driver );

// #ifndef CHECK_HDF4OPEN_SYMBOL
//     // Get rid of the HDF4 driver when it is buggy
//     driver = GetGDALDriverManager()->GetDriverByName( "hdf4" );
//     if (driver)
//       GetGDALDriverManager()->DeregisterDriver( driver );
// #endif
}

GDALDriverManagerWrapper::~GDALDriverManagerWrapper()
{
  GDALDestroyDriverManager();
}

// Open the file for reading and returns a smart dataset pointer
GDALDatasetWrapper::Pointer
GDALDriverManagerWrapper::Open( std::string filename ) const
{
  GDALDatasetWrapper::Pointer datasetWrapper;

  if (boost::algorithm::starts_with(filename, "http://")
      || boost::algorithm::starts_with(filename, "https://") )
    {
    // don't try to open it and exit
    return datasetWrapper;
    }

  // test if a driver can identify the dataset
  GDALDriverH identifyDriverH = GDALIdentifyDriver(filename.c_str(), ITK_NULLPTR);
  if(identifyDriverH == ITK_NULLPTR)
    {
    // don't try to open it and exit
    return datasetWrapper;
    }

  GDALDriver *identifyDriver = static_cast<GDALDriver*>(identifyDriverH);

  // check if Jasper will be used
  if (strcmp(identifyDriver->GetDescription(),"JPEG2000") == 0)
    {
    itkGenericExceptionMacro(<< "Error : tried to open the file "
      << filename.c_str() << " with GDAL driver Jasper "
      "(which fails on OTB). Try setting the environment variable GDAL_SKIP"
      " in order to avoid this driver.");
    }

  GDALDatasetH dataset = GDALOpen(filename.c_str(), GA_ReadOnly);

  if (dataset != ITK_NULLPTR)
    {
    datasetWrapper = GDALDatasetWrapper::New();
    datasetWrapper->m_Dataset = static_cast<GDALDataset*>(dataset);
    }
  return datasetWrapper;
}

// Open the new  file for writing and returns a smart dataset pointer
GDALDatasetWrapper::Pointer
GDALDriverManagerWrapper::Create( std::string driverShortName, std::string filename,
                                    int nXSize, int nYSize, int nBands,
                                    GDALDataType eType, char ** papszOptions ) const
{
  GDALDatasetWrapper::Pointer datasetWrapper;

  GDALDriver*  driver = GetDriverByName( driverShortName );
  if(driver != ITK_NULLPTR)
    {
    GDALDataset* dataset = driver->Create(filename.c_str(),
                                          nXSize, nYSize,
                                          nBands, eType,
                                          papszOptions );

    if (dataset != ITK_NULLPTR)
      {
      datasetWrapper = GDALDatasetWrapper::New();
      datasetWrapper->m_Dataset = dataset;
      }
    }
  return datasetWrapper;
}

GDALDriver*
GDALDriverManagerWrapper::GetDriverByName( std::string driverShortName ) const
{
  return GetGDALDriverManager()->GetDriverByName(driverShortName.c_str());
}

} // end namespace otb
<|endoftext|>


<commit_before>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#ifndef __itkImageSeriesWriter_hxx
#define __itkImageSeriesWriter_hxx

#include "itkImageSeriesWriter.h"
#include "itkDataObject.h"
#include "itkImageIOFactory.h"
#include "itkIOCommon.h"
#include "itkProgressReporter.h"
#include "itkImageRegionIterator.h"
#include "itkMetaDataObject.h"
#include "itkArray.h"
#include "vnl/algo/vnl_determinant.h"
#include <cstdio>

#if defined(_MSC_VER)
#define snprintf _snprintf
#endif // _MSC_VER

namespace itk
{
//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
ImageSeriesWriter< TInputImage, TOutputImage >
::ImageSeriesWriter():
  m_ImageIO(ITK_NULLPTR), m_UserSpecifiedImageIO(false),
  m_SeriesFormat("%d"),
  m_StartIndex(1), m_IncrementIndex(1), m_MetaDataDictionaryArray(ITK_NULLPTR)
{
  m_UseCompression = false;
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
ImageSeriesWriter< TInputImage, TOutputImage >
::~ImageSeriesWriter()
{}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::SetInput(const InputImageType *input)
{
  // ProcessObject is not const_correct so this cast is required here.
  this->ProcessObject::SetNthInput( 0,
                                    const_cast< TInputImage * >( input ) );
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
const typename ImageSeriesWriter< TInputImage, TOutputImage >::InputImageType *
ImageSeriesWriter< TInputImage, TOutputImage >
::GetInput(void)
{
  return itkDynamicCastInDebugMode< TInputImage * >( this->GetPrimaryInput() );
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
const typename ImageSeriesWriter< TInputImage, TOutputImage >::InputImageType *
ImageSeriesWriter< TInputImage, TOutputImage >
::GetInput(unsigned int idx)
{
  return itkDynamicCastInDebugMode< TInputImage * >( this->ProcessObject::GetInput(idx) );
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::Write(void)
{
  const InputImageType *inputImage = this->GetInput();

  itkDebugMacro(<< "Writing an image file");

  // Make sure input is available
  if ( inputImage == ITK_NULLPTR )
    {
    itkExceptionMacro(<< "No input to writer!");
    }

  // Make sure the data is up-to-date.
  // NOTE: this const_cast<> is due to the lack of const-correctness
  // of the ProcessObject.
  InputImageType *nonConstImage = const_cast< InputImageType * >( inputImage );
  nonConstImage->Update();

  // Notify start event observers
  this->InvokeEvent( StartEvent() );

  // Actually do something
  this->GenerateData();

  // Notify end event observers
  this->InvokeEvent( EndEvent() );

  // Release upstream data if requested
  if ( inputImage->ShouldIReleaseData() )
    {
    nonConstImage->ReleaseData();
    }
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::GenerateNumericFileNamesAndWrite(void)
{
  itkWarningMacro("This functionality has been DEPRECATED. Use NumericSeriesFileName for generating the filenames");
  this->GenerateNumericFileNames();
  this->WriteFiles();
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::GenerateNumericFileNames(void)
{
  const InputImageType *inputImage = this->GetInput();

  if ( !inputImage )
    {
    itkExceptionMacro(<< "Input image is NULL");
    }

  m_FileNames.clear();

  // We need two regions. One for the input, one for the output.
  ImageRegion< TInputImage::ImageDimension > inRegion = inputImage->GetRequestedRegion();

  SizeValueType fileNumber = this->m_StartIndex;
  char          fileName[IOCommon::ITK_MAXPATHLEN + 1];

  // Compute the number of files to be generated
  unsigned int numberOfFiles = 1;
  for ( unsigned int n = TOutputImage::ImageDimension; n < TInputImage::ImageDimension; n++ )
    {
    numberOfFiles *= inRegion.GetSize(n);
    }

  for ( unsigned int slice = 0; slice < numberOfFiles; slice++ )
    {
    snprintf (fileName, IOCommon::ITK_MAXPATHLEN + 1, m_SeriesFormat.c_str(), fileNumber);
    m_FileNames.push_back(fileName);
    fileNumber += this->m_IncrementIndex;
    }
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::GenerateData(void)
{
  itkDebugMacro(<< "Writing a series of files");
  if ( m_FileNames.empty() )
    {
    // this method will be deprecated. It is here only to maintain the old API
    this->GenerateNumericFileNamesAndWrite();
    return;
    }

  this->WriteFiles();
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::WriteFiles()
{
  const InputImageType *inputImage = this->GetInput();

  if ( !inputImage )
    {
    itkExceptionMacro(<< "Input image is NULL");
    }

  // We need two regions. One for the input, one for the output.
  ImageRegion< TInputImage::ImageDimension >  inRegion = inputImage->GetRequestedRegion();
  ImageRegion< TOutputImage::ImageDimension > outRegion;

  // The size of the output will match the input sizes, up to the
  // dimension of the input.
  for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
    {
    outRegion.SetSize(i, inputImage->GetRequestedRegion().GetSize()[i]);
    }

  // Allocate an image for output and create an iterator for it
  typename OutputImageType::Pointer outputImage = OutputImageType::New();
  outputImage->SetRegions(outRegion);
  outputImage->Allocate();
  ImageRegionIterator< OutputImageType > ot(outputImage, outRegion);

  // Set the origin and spacing of the output
  double spacing[TOutputImage::ImageDimension];
  double origin[TOutputImage::ImageDimension];
  typename TOutputImage::DirectionType direction;
  for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
    {
    origin[i] = inputImage->GetOrigin()[i];
    spacing[i] = inputImage->GetSpacing()[i];
    outRegion.SetSize(i, inputImage->GetRequestedRegion().GetSize()[i]);
    for ( unsigned int j = 0; j < TOutputImage::ImageDimension; j++ )
      {
      direction[j][i] = inputImage->GetDirection()[j][i];
      }
    }

  //
  // Address the fact that when taking a 2x2 sub-matrix from
  // the direction matrix we may obtain a singular matrix.
  // A 2x2 orientation can't represent a 3x3 orientation and
  // therefore, replacing the orientation with an identity
  // is as arbitrary as any other choice.
  //
  if ( vnl_determinant( direction.GetVnlMatrix() ) == 0.0 )
    {
    direction.SetIdentity();
    }

  outputImage->SetOrigin(origin);
  outputImage->SetSpacing(spacing);
  outputImage->SetDirection(direction);

  Index< TInputImage::ImageDimension > inIndex;
  Size< TInputImage::ImageDimension >  inSize;

  SizeValueType pixelsPerFile = outputImage->GetRequestedRegion().GetNumberOfPixels();

  inSize.Fill(1);
  for ( unsigned int ns = 0; ns < TOutputImage::ImageDimension; ns++ )
    {
    inSize[ns] = outRegion.GetSize()[ns];
    }

  unsigned int expectedNumberOfFiles = 1;
  for ( unsigned int n = TOutputImage::ImageDimension; n < TInputImage::ImageDimension; n++ )
    {
    expectedNumberOfFiles *= inRegion.GetSize(n);
    }

  if ( m_FileNames.size() != expectedNumberOfFiles )
    {
    itkExceptionMacro(
      << "The number of filenames passed is " << m_FileNames.size() << " but " << expectedNumberOfFiles
      << " were expected ");
    return;
    }

  itkDebugMacro( << "Number of files to write = " << m_FileNames.size() );

  ProgressReporter progress(this, 0,
                            expectedNumberOfFiles,
                            expectedNumberOfFiles);

  // For each "slice" in the input, copy the region to the output,
  // build a filename and write the file.

  typename InputImageType::OffsetValueType offset = 0;
  for ( unsigned int slice = 0; slice < m_FileNames.size(); slice++ )
    {
    // Select a "slice" of the image.
    inIndex = inputImage->ComputeIndex(offset);
    inRegion.SetIndex(inIndex);
    inRegion.SetSize(inSize);

    ImageRegionConstIterator< InputImageType > it (inputImage, inRegion);

    // Copy the selected "slice" into the output image.
    it.GoToBegin();
    ot.GoToBegin();
    while ( !ot.IsAtEnd() )
      {
      ot.Set( it.Get() );
      ++it;
      ++ot;
      }

    typename WriterType::Pointer writer = WriterType::New();

    writer->UseInputMetaDataDictionaryOff(); // use the dictionary from the
                                             // ImageIO class
    writer->SetInput(outputImage);

    if ( m_ImageIO )
      {
      writer->SetImageIO(m_ImageIO);
      }

    if ( m_MetaDataDictionaryArray )
      {
      if ( m_ImageIO )
        {
        if ( slice > m_MetaDataDictionaryArray->size() - 1 )
          {
          itkExceptionMacro (
            "The slice number: " << slice + 1 << " exceeds the size of the MetaDataDictionaryArray "
                                 << m_MetaDataDictionaryArray->size() << ".");
          }
        DictionaryRawPointer dictionary = ( *m_MetaDataDictionaryArray )[slice];
        m_ImageIO->SetMetaDataDictionary( ( *dictionary ) );
        }
      else
        {
        itkExceptionMacro(<< "Attempted to use a MetaDataDictionaryArray without specifying an ImageIO!");
        }
      }
    else
      {
      if ( m_ImageIO )
        {
        DictionaryType & dictionary = m_ImageIO->GetMetaDataDictionary();

        typename InputImageType::SpacingType spacing2 = inputImage->GetSpacing();

        // origin of the output slice in the
        // N-Dimensional space of the input image.
        typename InputImageType::PointType origin2;

        inputImage->TransformIndexToPhysicalPoint(inIndex, origin2);

        const unsigned int inputImageDimension = TInputImage::ImageDimension;

        typedef Array< double > DoubleArrayType;

        DoubleArrayType originArray(inputImageDimension);
        DoubleArrayType spacingArray(inputImageDimension);

        for ( unsigned int d = 0; d < inputImageDimension; d++ )
          {
          originArray[d]  = origin2[d];
          spacingArray[d] = spacing2[d];
          }

        EncapsulateMetaData< DoubleArrayType >(dictionary, ITK_Origin, originArray);
        EncapsulateMetaData< DoubleArrayType >(dictionary, ITK_Spacing, spacingArray);
        EncapsulateMetaData<  unsigned int   >(dictionary, ITK_NumberOfDimensions, inputImageDimension);

        typename InputImageType::DirectionType direction2 = inputImage->GetDirection();
        typedef Matrix< double, inputImageDimension, inputImageDimension> DoubleMatrixType;
        DoubleMatrixType directionMatrix;
        for( unsigned int i = 0; i < inputImageDimension; i++ )
          {
          for( unsigned int j = 0; j < inputImageDimension; j++ )
            {
            directionMatrix[j][i]  = direction2[i][j];
            }
          }
        EncapsulateMetaData< DoubleMatrixType >( dictionary, ITK_ZDirection, directionMatrix );
        }
      }

    writer->SetFileName( m_FileNames[slice].c_str() );
    writer->SetUseCompression(m_UseCompression);
    writer->Update();

    progress.CompletedPixel();
    offset += pixelsPerFile;
    }
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "Image IO: ";
  if ( m_ImageIO.IsNull() )
    {
    os << "(none)\n";
    }
  else
    {
    os << m_ImageIO << "\n";
    }

  os << indent << "StartIndex: " << m_StartIndex << std::endl;
  os << indent << "IncrementIndex: " << m_IncrementIndex << std::endl;
  os << indent << "SeriesFormat: " << m_SeriesFormat << std::endl;
  os << indent << "MetaDataDictionaryArray: " << m_MetaDataDictionaryArray << std::endl;

  if ( m_UseCompression )
    {
    os << indent << "Compression: On\n";
    }
  else
    {
    os << indent << "Compression: Off\n";
    }
}
} // end namespace itk

#endif
<commit_msg>BUG: Enable VectorImage to work with the ImageSeriesWriter<commit_after>/*=========================================================================
 *
 *  Copyright Insight Software Consortium
 *
 *  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.txt
 *
 *  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.
 *
 *=========================================================================*/
#ifndef __itkImageSeriesWriter_hxx
#define __itkImageSeriesWriter_hxx

#include "itkImageSeriesWriter.h"
#include "itkDataObject.h"
#include "itkImageIOFactory.h"
#include "itkIOCommon.h"
#include "itkProgressReporter.h"
#include "itkImageRegionIterator.h"
#include "itkMetaDataObject.h"
#include "itkArray.h"
#include "vnl/algo/vnl_determinant.h"
#include <cstdio>

#if defined(_MSC_VER)
#define snprintf _snprintf
#endif // _MSC_VER

namespace itk
{
//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
ImageSeriesWriter< TInputImage, TOutputImage >
::ImageSeriesWriter():
  m_ImageIO(ITK_NULLPTR), m_UserSpecifiedImageIO(false),
  m_SeriesFormat("%d"),
  m_StartIndex(1), m_IncrementIndex(1), m_MetaDataDictionaryArray(ITK_NULLPTR)
{
  m_UseCompression = false;
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
ImageSeriesWriter< TInputImage, TOutputImage >
::~ImageSeriesWriter()
{}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::SetInput(const InputImageType *input)
{
  // ProcessObject is not const_correct so this cast is required here.
  this->ProcessObject::SetNthInput( 0,
                                    const_cast< TInputImage * >( input ) );
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
const typename ImageSeriesWriter< TInputImage, TOutputImage >::InputImageType *
ImageSeriesWriter< TInputImage, TOutputImage >
::GetInput(void)
{
  return itkDynamicCastInDebugMode< TInputImage * >( this->GetPrimaryInput() );
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
const typename ImageSeriesWriter< TInputImage, TOutputImage >::InputImageType *
ImageSeriesWriter< TInputImage, TOutputImage >
::GetInput(unsigned int idx)
{
  return itkDynamicCastInDebugMode< TInputImage * >( this->ProcessObject::GetInput(idx) );
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::Write(void)
{
  const InputImageType *inputImage = this->GetInput();

  itkDebugMacro(<< "Writing an image file");

  // Make sure input is available
  if ( inputImage == ITK_NULLPTR )
    {
    itkExceptionMacro(<< "No input to writer!");
    }

  // Make sure the data is up-to-date.
  // NOTE: this const_cast<> is due to the lack of const-correctness
  // of the ProcessObject.
  InputImageType *nonConstImage = const_cast< InputImageType * >( inputImage );
  nonConstImage->Update();

  // Notify start event observers
  this->InvokeEvent( StartEvent() );

  // Actually do something
  this->GenerateData();

  // Notify end event observers
  this->InvokeEvent( EndEvent() );

  // Release upstream data if requested
  if ( inputImage->ShouldIReleaseData() )
    {
    nonConstImage->ReleaseData();
    }
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::GenerateNumericFileNamesAndWrite(void)
{
  itkWarningMacro("This functionality has been DEPRECATED. Use NumericSeriesFileName for generating the filenames");
  this->GenerateNumericFileNames();
  this->WriteFiles();
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::GenerateNumericFileNames(void)
{
  const InputImageType *inputImage = this->GetInput();

  if ( !inputImage )
    {
    itkExceptionMacro(<< "Input image is NULL");
    }

  m_FileNames.clear();

  // We need two regions. One for the input, one for the output.
  ImageRegion< TInputImage::ImageDimension > inRegion = inputImage->GetRequestedRegion();

  SizeValueType fileNumber = this->m_StartIndex;
  char          fileName[IOCommon::ITK_MAXPATHLEN + 1];

  // Compute the number of files to be generated
  unsigned int numberOfFiles = 1;
  for ( unsigned int n = TOutputImage::ImageDimension; n < TInputImage::ImageDimension; n++ )
    {
    numberOfFiles *= inRegion.GetSize(n);
    }

  for ( unsigned int slice = 0; slice < numberOfFiles; slice++ )
    {
    snprintf (fileName, IOCommon::ITK_MAXPATHLEN + 1, m_SeriesFormat.c_str(), fileNumber);
    m_FileNames.push_back(fileName);
    fileNumber += this->m_IncrementIndex;
    }
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::GenerateData(void)
{
  itkDebugMacro(<< "Writing a series of files");
  if ( m_FileNames.empty() )
    {
    // this method will be deprecated. It is here only to maintain the old API
    this->GenerateNumericFileNamesAndWrite();
    return;
    }

  this->WriteFiles();
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::WriteFiles()
{
  const InputImageType *inputImage = this->GetInput();

  if ( !inputImage )
    {
    itkExceptionMacro(<< "Input image is NULL");
    }

  // We need two regions. One for the input, one for the output.
  ImageRegion< TInputImage::ImageDimension >  inRegion = inputImage->GetRequestedRegion();
  ImageRegion< TOutputImage::ImageDimension > outRegion;

  // The size of the output will match the input sizes, up to the
  // dimension of the input.
  for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
    {
    outRegion.SetSize(i, inputImage->GetRequestedRegion().GetSize()[i]);
    }

  // Allocate an image for output and create an iterator for it
  typename OutputImageType::Pointer outputImage = OutputImageType::New();
  outputImage->SetRegions(outRegion);
  outputImage->SetNumberOfComponentsPerPixel(inputImage->GetNumberOfComponentsPerPixel());
  outputImage->Allocate();
  ImageRegionIterator< OutputImageType > ot(outputImage, outRegion);

  // Set the origin and spacing of the output
  double spacing[TOutputImage::ImageDimension];
  double origin[TOutputImage::ImageDimension];
  typename TOutputImage::DirectionType direction;
  for ( unsigned int i = 0; i < TOutputImage::ImageDimension; i++ )
    {
    origin[i] = inputImage->GetOrigin()[i];
    spacing[i] = inputImage->GetSpacing()[i];
    outRegion.SetSize(i, inputImage->GetRequestedRegion().GetSize()[i]);
    for ( unsigned int j = 0; j < TOutputImage::ImageDimension; j++ )
      {
      direction[j][i] = inputImage->GetDirection()[j][i];
      }
    }

  //
  // Address the fact that when taking a 2x2 sub-matrix from
  // the direction matrix we may obtain a singular matrix.
  // A 2x2 orientation can't represent a 3x3 orientation and
  // therefore, replacing the orientation with an identity
  // is as arbitrary as any other choice.
  //
  if ( vnl_determinant( direction.GetVnlMatrix() ) == 0.0 )
    {
    direction.SetIdentity();
    }

  outputImage->SetOrigin(origin);
  outputImage->SetSpacing(spacing);
  outputImage->SetDirection(direction);

  Index< TInputImage::ImageDimension > inIndex;
  Size< TInputImage::ImageDimension >  inSize;

  SizeValueType pixelsPerFile = outputImage->GetRequestedRegion().GetNumberOfPixels();

  inSize.Fill(1);
  for ( unsigned int ns = 0; ns < TOutputImage::ImageDimension; ns++ )
    {
    inSize[ns] = outRegion.GetSize()[ns];
    }

  unsigned int expectedNumberOfFiles = 1;
  for ( unsigned int n = TOutputImage::ImageDimension; n < TInputImage::ImageDimension; n++ )
    {
    expectedNumberOfFiles *= inRegion.GetSize(n);
    }

  if ( m_FileNames.size() != expectedNumberOfFiles )
    {
    itkExceptionMacro(
      << "The number of filenames passed is " << m_FileNames.size() << " but " << expectedNumberOfFiles
      << " were expected ");
    return;
    }

  itkDebugMacro( << "Number of files to write = " << m_FileNames.size() );

  ProgressReporter progress(this, 0,
                            expectedNumberOfFiles,
                            expectedNumberOfFiles);

  // For each "slice" in the input, copy the region to the output,
  // build a filename and write the file.

  typename InputImageType::OffsetValueType offset = 0;
  for ( unsigned int slice = 0; slice < m_FileNames.size(); slice++ )
    {
    // Select a "slice" of the image.
    inIndex = inputImage->ComputeIndex(offset);
    inRegion.SetIndex(inIndex);
    inRegion.SetSize(inSize);

    ImageRegionConstIterator< InputImageType > it (inputImage, inRegion);

    // Copy the selected "slice" into the output image.
    it.GoToBegin();
    ot.GoToBegin();
    while ( !ot.IsAtEnd() )
      {
      ot.Set( it.Get() );
      ++it;
      ++ot;
      }

    typename WriterType::Pointer writer = WriterType::New();

    writer->UseInputMetaDataDictionaryOff(); // use the dictionary from the
                                             // ImageIO class
    writer->SetInput(outputImage);

    if ( m_ImageIO )
      {
      writer->SetImageIO(m_ImageIO);
      }

    if ( m_MetaDataDictionaryArray )
      {
      if ( m_ImageIO )
        {
        if ( slice > m_MetaDataDictionaryArray->size() - 1 )
          {
          itkExceptionMacro (
            "The slice number: " << slice + 1 << " exceeds the size of the MetaDataDictionaryArray "
                                 << m_MetaDataDictionaryArray->size() << ".");
          }
        DictionaryRawPointer dictionary = ( *m_MetaDataDictionaryArray )[slice];
        m_ImageIO->SetMetaDataDictionary( ( *dictionary ) );
        }
      else
        {
        itkExceptionMacro(<< "Attempted to use a MetaDataDictionaryArray without specifying an ImageIO!");
        }
      }
    else
      {
      if ( m_ImageIO )
        {
        DictionaryType & dictionary = m_ImageIO->GetMetaDataDictionary();

        typename InputImageType::SpacingType spacing2 = inputImage->GetSpacing();

        // origin of the output slice in the
        // N-Dimensional space of the input image.
        typename InputImageType::PointType origin2;

        inputImage->TransformIndexToPhysicalPoint(inIndex, origin2);

        const unsigned int inputImageDimension = TInputImage::ImageDimension;

        typedef Array< double > DoubleArrayType;

        DoubleArrayType originArray(inputImageDimension);
        DoubleArrayType spacingArray(inputImageDimension);

        for ( unsigned int d = 0; d < inputImageDimension; d++ )
          {
          originArray[d]  = origin2[d];
          spacingArray[d] = spacing2[d];
          }

        EncapsulateMetaData< DoubleArrayType >(dictionary, ITK_Origin, originArray);
        EncapsulateMetaData< DoubleArrayType >(dictionary, ITK_Spacing, spacingArray);
        EncapsulateMetaData<  unsigned int   >(dictionary, ITK_NumberOfDimensions, inputImageDimension);

        typename InputImageType::DirectionType direction2 = inputImage->GetDirection();
        typedef Matrix< double, inputImageDimension, inputImageDimension> DoubleMatrixType;
        DoubleMatrixType directionMatrix;
        for( unsigned int i = 0; i < inputImageDimension; i++ )
          {
          for( unsigned int j = 0; j < inputImageDimension; j++ )
            {
            directionMatrix[j][i]  = direction2[i][j];
            }
          }
        EncapsulateMetaData< DoubleMatrixType >( dictionary, ITK_ZDirection, directionMatrix );
        }
      }

    writer->SetFileName( m_FileNames[slice].c_str() );
    writer->SetUseCompression(m_UseCompression);
    writer->Update();

    progress.CompletedPixel();
    offset += pixelsPerFile;
    }
}

//---------------------------------------------------------
template< typename TInputImage, typename TOutputImage >
void
ImageSeriesWriter< TInputImage, TOutputImage >
::PrintSelf(std::ostream & os, Indent indent) const
{
  Superclass::PrintSelf(os, indent);

  os << indent << "Image IO: ";
  if ( m_ImageIO.IsNull() )
    {
    os << "(none)\n";
    }
  else
    {
    os << m_ImageIO << "\n";
    }

  os << indent << "StartIndex: " << m_StartIndex << std::endl;
  os << indent << "IncrementIndex: " << m_IncrementIndex << std::endl;
  os << indent << "SeriesFormat: " << m_SeriesFormat << std::endl;
  os << indent << "MetaDataDictionaryArray: " << m_MetaDataDictionaryArray << std::endl;

  if ( m_UseCompression )
    {
    os << indent << "Compression: On\n";
    }
  else
    {
    os << indent << "Compression: Off\n";
    }
}
} // end namespace itk

#endif
<|endoftext|>