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

#ifndef __BASE_MISC_HH__
#define __BASE_MISC_HH__

#include "base/compiler.hh"
#include "base/cprintf.hh"
#include "base/varargs.hh"

#if defined(__SUNPRO_CC)
#define __FUNCTION__ "how to fix me?"
#endif

// General exit message, these functions will never return and will
// either abort() if code is < 0 or exit with the code if >= 0
void __exit_message(const char *prefix, int code,
    const char *func, const char *file, int line,
    const char *format, CPRINTF_DECLARATION) M5_ATTR_NORETURN;

void __exit_message(const char *prefix, int code,
    const char *func, const char *file, int line,
    const std::string &format, CPRINTF_DECLARATION) M5_ATTR_NORETURN;

inline void
__exit_message(const char *prefix, int code,
    const char *func, const char *file, int line,
    const std::string& format, CPRINTF_DEFINITION)
{
    __exit_message(prefix, code, func, file, line, format.c_str(),
                   VARARGS_ALLARGS);
}

M5_PRAGMA_NORETURN(__exit_message)
#define exit_message(prefix, code, ...)                            \
    __exit_message(prefix, code, __FUNCTION__, __FILE__, __LINE__, \
                   __VA_ARGS__)

//
// This implements a cprintf based panic() function.  panic() should
// be called when something happens that should never ever happen
// regardless of what the user does (i.e., an acutal m5 bug).  panic()
// calls abort which can dump core or enter the debugger.
//
//
#define panic(...) exit_message("panic", -1, __VA_ARGS__)

//
// This implements a cprintf based fatal() function.  fatal() should
// be called when the simulation cannot continue due to some condition
// that is the user's fault (bad configuration, invalid arguments,
// etc.) and not a simulator bug.  fatal() calls  abort() like
// panic() does.
//
#define fatal(...) exit_message("fatal", -1, __VA_ARGS__)

void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
          const char *func, const char *file, int line,
          const char *format, CPRINTF_DECLARATION);

inline void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
          const char *func, const char *file, int line,
          const std::string &format, CPRINTF_DECLARATION)
{
    __base_message(stream, prefix, verbose, func, file, line, format.c_str(),
              VARARGS_ALLARGS);
}

#define base_message(stream, prefix, verbose, ...)                      \
    __base_message(stream, prefix, verbose, __FUNCTION__, __FILE__, __LINE__, \
                   __VA_ARGS__)

// Only print the message the first time this expression is
// encountered.  i.e.  This doesn't check the string itself and
// prevent duplicate strings, this prevents the statement from
// happening more than once. So, even if the arguments change and that
// would have resulted in a different message thoes messages would be
// supressed.
#define base_message_once(...) do {                     \
        static bool once = false;                       \
        if (!once) {                                    \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

#define cond_message(cond, ...) do {            \
        if (cond)                               \
            base_message(__VA_ARGS__);          \
    } while (0)

#define cond_message_once(cond, ...) do {               \
        static bool once = false;                       \
        if (!once && cond) {                            \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

extern bool want_warn, warn_verbose;
extern bool want_info, info_verbose;
extern bool want_hack, hack_verbose;

#define warn(...) \
    cond_message(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform(...) \
    cond_message(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack(...) \
    cond_message(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

#define warn_once(...) \
    cond_message_once(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform_once(...) \
    cond_message_once(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack_once(...) \
    cond_message_once(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

#endif // __BASE_MISC_HH__
<commit_msg>misc: Add panic_if / fatal_if / chatty_assert<commit_after>/*
 * Copyright (c) 2014 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Nathan Binkert
 *          Dave Greene
 */

#ifndef __BASE_MISC_HH__
#define __BASE_MISC_HH__

#include "base/compiler.hh"
#include "base/cprintf.hh"
#include "base/varargs.hh"

#if defined(__SUNPRO_CC)
#define __FUNCTION__ "how to fix me?"
#endif

// General exit message, these functions will never return and will
// either abort() if code is < 0 or exit with the code if >= 0
void __exit_message(const char *prefix, int code,
    const char *func, const char *file, int line,
    const char *format, CPRINTF_DECLARATION) M5_ATTR_NORETURN;

void __exit_message(const char *prefix, int code,
    const char *func, const char *file, int line,
    const std::string &format, CPRINTF_DECLARATION) M5_ATTR_NORETURN;

inline void
__exit_message(const char *prefix, int code,
    const char *func, const char *file, int line,
    const std::string& format, CPRINTF_DEFINITION)
{
    __exit_message(prefix, code, func, file, line, format.c_str(),
                   VARARGS_ALLARGS);
}

M5_PRAGMA_NORETURN(__exit_message)
#define exit_message(prefix, code, ...)                            \
    __exit_message(prefix, code, __FUNCTION__, __FILE__, __LINE__, \
                   __VA_ARGS__)

//
// This implements a cprintf based panic() function.  panic() should
// be called when something happens that should never ever happen
// regardless of what the user does (i.e., an acutal m5 bug).  panic()
// calls abort which can dump core or enter the debugger.
//
//
#define panic(...) exit_message("panic", -1, __VA_ARGS__)

//
// This implements a cprintf based fatal() function.  fatal() should
// be called when the simulation cannot continue due to some condition
// that is the user's fault (bad configuration, invalid arguments,
// etc.) and not a simulator bug.  fatal() calls  abort() like
// panic() does.
//
#define fatal(...) exit_message("fatal", -1, __VA_ARGS__)

/**
 * Conditional panic macro that checks the supplied condition and only panics
 * if the condition is true and allows the programmer to specify diagnostic
 * printout.  Useful to replace if + panic, or if + print + assert, etc.
 *
 * @param cond Condition that is checked; if true -> panic
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define panic_if(cond, ...) \
    do { \
        if ((cond)) \
            exit_message("panic condition "#cond" occurred", -1, __VA_ARGS__); \
    } while (0)

/**
 * Conditional fatal macro that checks the supplied condition and only causes a
 * fatal error if the condition is true and allows the programmer to specify
 * diagnostic printout.  Useful to replace if + fatal, or if + print + assert,
 * etc.
 *
 * @param cond Condition that is checked; if true -> fatal
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define fatal_if(cond, ...) \
    do { \
        if ((cond)) \
            exit_message("fatal condition "#cond" occurred", 1, __VA_ARGS__); \
    } while (0)

void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
          const char *func, const char *file, int line,
          const char *format, CPRINTF_DECLARATION);

inline void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
          const char *func, const char *file, int line,
          const std::string &format, CPRINTF_DECLARATION)
{
    __base_message(stream, prefix, verbose, func, file, line, format.c_str(),
              VARARGS_ALLARGS);
}

#define base_message(stream, prefix, verbose, ...)                      \
    __base_message(stream, prefix, verbose, __FUNCTION__, __FILE__, __LINE__, \
                   __VA_ARGS__)

// Only print the message the first time this expression is
// encountered.  i.e.  This doesn't check the string itself and
// prevent duplicate strings, this prevents the statement from
// happening more than once. So, even if the arguments change and that
// would have resulted in a different message thoes messages would be
// supressed.
#define base_message_once(...) do {                     \
        static bool once = false;                       \
        if (!once) {                                    \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

#define cond_message(cond, ...) do {            \
        if (cond)                               \
            base_message(__VA_ARGS__);          \
    } while (0)

#define cond_message_once(cond, ...) do {               \
        static bool once = false;                       \
        if (!once && cond) {                            \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

extern bool want_warn, warn_verbose;
extern bool want_info, info_verbose;
extern bool want_hack, hack_verbose;

#define warn(...) \
    cond_message(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform(...) \
    cond_message(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack(...) \
    cond_message(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

#define warn_once(...) \
    cond_message_once(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform_once(...) \
    cond_message_once(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack_once(...) \
    cond_message_once(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

/**
 * The chatty assert macro will function like a normal assert, but will allow the
 * specification of additional, helpful material to aid debugging why the
 * assertion actually failed.  Like the normal assertion, the chatty_assert
 * will not be active in fast builds.
 *
 * @param cond Condition that is checked; if false -> assert
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#ifdef NDEBUG
#define chatty_assert(cond, ...)
#else //!NDEBUG
#define chatty_assert(cond, ...)                                                \
    do {                                                                        \
        if (!(cond)) {                                                          \
            base_message(std::cerr, "assert("#cond") failing", 1, __VA_ARGS__); \
            assert(cond);                                                       \
        }                                                                       \
    } while (0)
#endif // NDEBUG
#endif // __BASE_MISC_HH__
<|endoftext|>


<commit_before>/*
 * Copyright (c) 2014 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Nathan Binkert
 *          Dave Greene
 *          Andreas Sandberg
 */

#ifndef __BASE_MISC_HH__
#define __BASE_MISC_HH__

#include <cassert>
#include <iostream>

#include "base/compiler.hh"
#include "base/cprintf.hh"

#if defined(__SUNPRO_CC)
#define __FUNCTION__ "how to fix me?"
#endif

void __exit_epilogue(int code,
                     const char *func, const char *file, int line,
                     const char *format) M5_ATTR_NORETURN;

// General exit message, these functions will never return and will
// either abort() if code is < 0 or exit with the code if >= 0
template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const char *format, const Args &...args) M5_ATTR_NORETURN;
template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const std::string &format, const Args &...args) M5_ATTR_NORETURN;

template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const char *format, const Args &...args)
{
    std::cerr << prefix << ": ";
    ccprintf(std::cerr, format, args...);

    __exit_epilogue(code, func, file, line, format);
}

template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const std::string &format, const Args &...args)
{
    __exit_message(prefix, code, func, file, line, format.c_str(),
                   args...);
}

#define exit_message(prefix, code, ...)                                 \
    __exit_message(prefix, code, __FUNCTION__, __FILE__, __LINE__,      \
                   __VA_ARGS__)

//
// This implements a cprintf based panic() function.  panic() should
// be called when something happens that should never ever happen
// regardless of what the user does (i.e., an acutal m5 bug).  panic()
// calls abort which can dump core or enter the debugger.
//
//
#define panic(...) exit_message("panic", -1, __VA_ARGS__)

//
// This implements a cprintf based fatal() function.  fatal() should
// be called when the simulation cannot continue due to some condition
// that is the user's fault (bad configuration, invalid arguments,
// etc.) and not a simulator bug.  fatal() calls  abort() like
// panic() does.
//
#define fatal(...) exit_message("fatal", -1, __VA_ARGS__)

/**
 * Conditional panic macro that checks the supplied condition and only panics
 * if the condition is true and allows the programmer to specify diagnostic
 * printout.  Useful to replace if + panic, or if + print + assert, etc.
 *
 * @param cond Condition that is checked; if true -> panic
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define panic_if(cond, ...) \
    do { \
        if ((cond)) \
            exit_message("panic condition "#cond" occurred", -1, __VA_ARGS__); \
    } while (0)

/**
 * Conditional fatal macro that checks the supplied condition and only causes a
 * fatal error if the condition is true and allows the programmer to specify
 * diagnostic printout.  Useful to replace if + fatal, or if + print + assert,
 * etc.
 *
 * @param cond Condition that is checked; if true -> fatal
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define fatal_if(cond, ...) \
    do { \
        if ((cond)) \
            exit_message("fatal condition "#cond" occurred", 1, __VA_ARGS__); \
    } while (0)

void
__base_message_epilogue(std::ostream &stream, bool verbose,
                        const char *func, const char *file, int line,
                        const char *format);

template<typename ...Args> void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
               const char *func, const char *file, int line,
               const char *format, const Args &...args)
{
    stream << prefix << ": ";
    ccprintf(stream, format, args...);

    __base_message_epilogue(stream, verbose, func, file, line, format);
}

template<typename ...Args> void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
               const char *func, const char *file, int line,
               const std::string &format, const Args &...args)
{
    __base_message(stream, prefix, verbose, func, file, line, format.c_str(),
                   args...);
}

#define base_message(stream, prefix, verbose, ...)                      \
    __base_message(stream, prefix, verbose, __FUNCTION__, __FILE__, __LINE__, \
                   __VA_ARGS__)

// Only print the message the first time this expression is
// encountered.  i.e.  This doesn't check the string itself and
// prevent duplicate strings, this prevents the statement from
// happening more than once. So, even if the arguments change and that
// would have resulted in a different message thoes messages would be
// supressed.
#define base_message_once(...) do {                     \
        static bool once = false;                       \
        if (!once) {                                    \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

#define cond_message(cond, ...) do {            \
        if (cond)                               \
            base_message(__VA_ARGS__);          \
    } while (0)

#define cond_message_once(cond, ...) do {               \
        static bool once = false;                       \
        if (!once && cond) {                            \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

extern bool want_warn, warn_verbose;
extern bool want_info, info_verbose;
extern bool want_hack, hack_verbose;

#define warn(...) \
    cond_message(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform(...) \
    cond_message(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack(...) \
    cond_message(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

#define warn_once(...) \
    cond_message_once(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform_once(...) \
    cond_message_once(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack_once(...) \
    cond_message_once(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

/**
 * The chatty assert macro will function like a normal assert, but will allow the
 * specification of additional, helpful material to aid debugging why the
 * assertion actually failed.  Like the normal assertion, the chatty_assert
 * will not be active in fast builds.
 *
 * @param cond Condition that is checked; if false -> assert
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#ifdef NDEBUG
#define chatty_assert(cond, ...)
#else //!NDEBUG
#define chatty_assert(cond, ...)                                                \
    do {                                                                        \
        if (!(cond)) {                                                          \
            base_message(std::cerr, "assert("#cond") failing", 1, __VA_ARGS__); \
            assert(cond);                                                       \
        }                                                                       \
    } while (0)
#endif // NDEBUG
#endif // __BASE_MISC_HH__
<commit_msg>base: Add a warn_if macro<commit_after>/*
 * Copyright (c) 2014 ARM Limited
 * All rights reserved
 *
 * The license below extends only to copyright in the software and shall
 * not be construed as granting a license to any other intellectual
 * property including but not limited to intellectual property relating
 * to a hardware implementation of the functionality of the software
 * licensed hereunder.  You may use the software subject to the license
 * terms below provided that you ensure that this notice is replicated
 * unmodified and in its entirety in all distributions of the software,
 * modified or unmodified, in source code or in binary form.
 *
 * Copyright (c) 2002-2005 The Regents of The University of Michigan
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are
 * met: redistributions of source code must retain the above copyright
 * notice, this list of conditions and the following disclaimer;
 * redistributions in binary form must reproduce the above copyright
 * notice, this list of conditions and the following disclaimer in the
 * documentation and/or other materials provided with the distribution;
 * neither the name of the copyright holders nor the names of its
 * contributors may be used to endorse or promote products derived from
 * this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
 * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
 * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
 * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
 * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 *
 * Authors: Nathan Binkert
 *          Dave Greene
 *          Andreas Sandberg
 */

#ifndef __BASE_MISC_HH__
#define __BASE_MISC_HH__

#include <cassert>
#include <iostream>

#include "base/compiler.hh"
#include "base/cprintf.hh"

#if defined(__SUNPRO_CC)
#define __FUNCTION__ "how to fix me?"
#endif

void __exit_epilogue(int code,
                     const char *func, const char *file, int line,
                     const char *format) M5_ATTR_NORETURN;

// General exit message, these functions will never return and will
// either abort() if code is < 0 or exit with the code if >= 0
template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const char *format, const Args &...args) M5_ATTR_NORETURN;
template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const std::string &format, const Args &...args) M5_ATTR_NORETURN;

template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const char *format, const Args &...args)
{
    std::cerr << prefix << ": ";
    ccprintf(std::cerr, format, args...);

    __exit_epilogue(code, func, file, line, format);
}

template<typename ...Args> void
__exit_message(const char *prefix, int code,
               const char *func, const char *file, int line,
               const std::string &format, const Args &...args)
{
    __exit_message(prefix, code, func, file, line, format.c_str(),
                   args...);
}

#define exit_message(prefix, code, ...)                                 \
    __exit_message(prefix, code, __FUNCTION__, __FILE__, __LINE__,      \
                   __VA_ARGS__)

//
// This implements a cprintf based panic() function.  panic() should
// be called when something happens that should never ever happen
// regardless of what the user does (i.e., an acutal m5 bug).  panic()
// calls abort which can dump core or enter the debugger.
//
//
#define panic(...) exit_message("panic", -1, __VA_ARGS__)

//
// This implements a cprintf based fatal() function.  fatal() should
// be called when the simulation cannot continue due to some condition
// that is the user's fault (bad configuration, invalid arguments,
// etc.) and not a simulator bug.  fatal() calls  abort() like
// panic() does.
//
#define fatal(...) exit_message("fatal", -1, __VA_ARGS__)

/**
 * Conditional panic macro that checks the supplied condition and only panics
 * if the condition is true and allows the programmer to specify diagnostic
 * printout.  Useful to replace if + panic, or if + print + assert, etc.
 *
 * @param cond Condition that is checked; if true -> panic
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define panic_if(cond, ...) \
    do { \
        if ((cond)) \
            exit_message("panic condition "#cond" occurred", -1, __VA_ARGS__); \
    } while (0)

/**
 * Conditional fatal macro that checks the supplied condition and only causes a
 * fatal error if the condition is true and allows the programmer to specify
 * diagnostic printout.  Useful to replace if + fatal, or if + print + assert,
 * etc.
 *
 * @param cond Condition that is checked; if true -> fatal
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define fatal_if(cond, ...) \
    do { \
        if ((cond)) \
            exit_message("fatal condition "#cond" occurred", 1, __VA_ARGS__); \
    } while (0)

void
__base_message_epilogue(std::ostream &stream, bool verbose,
                        const char *func, const char *file, int line,
                        const char *format);

template<typename ...Args> void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
               const char *func, const char *file, int line,
               const char *format, const Args &...args)
{
    stream << prefix << ": ";
    ccprintf(stream, format, args...);

    __base_message_epilogue(stream, verbose, func, file, line, format);
}

template<typename ...Args> void
__base_message(std::ostream &stream, const char *prefix, bool verbose,
               const char *func, const char *file, int line,
               const std::string &format, const Args &...args)
{
    __base_message(stream, prefix, verbose, func, file, line, format.c_str(),
                   args...);
}

#define base_message(stream, prefix, verbose, ...)                      \
    __base_message(stream, prefix, verbose, __FUNCTION__, __FILE__, __LINE__, \
                   __VA_ARGS__)

// Only print the message the first time this expression is
// encountered.  i.e.  This doesn't check the string itself and
// prevent duplicate strings, this prevents the statement from
// happening more than once. So, even if the arguments change and that
// would have resulted in a different message thoes messages would be
// supressed.
#define base_message_once(...) do {                     \
        static bool once = false;                       \
        if (!once) {                                    \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

#define cond_message(cond, ...) do {            \
        if (cond)                               \
            base_message(__VA_ARGS__);          \
    } while (0)

#define cond_message_once(cond, ...) do {               \
        static bool once = false;                       \
        if (!once && cond) {                            \
            base_message(__VA_ARGS__);                  \
            once = true;                                \
        }                                               \
    } while (0)

extern bool want_warn, warn_verbose;
extern bool want_info, info_verbose;
extern bool want_hack, hack_verbose;

#define warn(...) \
    cond_message(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform(...) \
    cond_message(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack(...) \
    cond_message(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

#define warn_once(...) \
    cond_message_once(want_warn, std::cerr, "warn", warn_verbose, __VA_ARGS__)
#define inform_once(...) \
    cond_message_once(want_info, std::cout, "info", info_verbose, __VA_ARGS__)
#define hack_once(...) \
    cond_message_once(want_hack, std::cerr, "hack", hack_verbose, __VA_ARGS__)

/**
 * Conditional warning macro that checks the supplied condition and
 * only prints a warning if the condition is true. Useful to replace
 * if + warn.
 *
 * @param cond Condition that is checked; if true -> warn
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#define warn_if(cond, ...) \
    do { \
        if ((cond)) \
            warn(__VA_ARGS__); \
    } while (0)

/**
 * The chatty assert macro will function like a normal assert, but will allow the
 * specification of additional, helpful material to aid debugging why the
 * assertion actually failed.  Like the normal assertion, the chatty_assert
 * will not be active in fast builds.
 *
 * @param cond Condition that is checked; if false -> assert
 * @param ...  Printf-based format string with arguments, extends printout.
 */
#ifdef NDEBUG
#define chatty_assert(cond, ...)
#else //!NDEBUG
#define chatty_assert(cond, ...)                                                \
    do {                                                                        \
        if (!(cond)) {                                                          \
            base_message(std::cerr, "assert("#cond") failing", 1, __VA_ARGS__); \
            assert(cond);                                                       \
        }                                                                       \
    } while (0)
#endif // NDEBUG
#endif // __BASE_MISC_HH__
<|endoftext|>


<commit_before>/*  This file is part of Fabula.
    Copyright (C) 2010 Mike McQuaid <mike@mikemcquaid.com>

    Fabula 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 3 of the License, or
    (at your option) any later version.

    Fabula 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 Fabula. If not, see <http://www.gnu.org/licenses/>.
*/

#include "SqlTreeModel.h"

#include <QStringList>
#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
#include <QMultiMap>

SqlTreeItem::SqlTreeItem(const QString &data, const QString &table, SqlTreeItem *parent, qint64 id, QPixmap icon)
    : m_table(table), m_data(data), m_parent(parent), m_id(id), m_icon(icon), m_dirty(false)
{
}

SqlTreeItem::~SqlTreeItem()
{
    if (m_parent)
        m_parent->m_children.removeAll(this);
    qDeleteAll(m_children);
}

SqlTreeItem *SqlTreeItem::child(int row)
{
    return m_children.value(row);
}

void SqlTreeItem::appendChild(SqlTreeItem* child)
{
    m_children.append(child);
}

int SqlTreeItem::childCount() const
{
    return m_children.count();
}

int SqlTreeItem::row() const
{
    if (m_parent)
        return m_parent->m_children.indexOf(const_cast<SqlTreeItem*>(this));

    return 0;
}

const QString &SqlTreeItem::table() const
{
    return m_table;
}

const QString &SqlTreeItem::data() const
{
    return m_data;
}

qint64 SqlTreeItem::id() const
{
    return m_id;
}

void SqlTreeItem::setId(qint64 id)
{
    m_id = id;
}

SqlTreeItem *SqlTreeItem::parent()
{
    return m_parent;
}

void SqlTreeItem::setData(const QString &data)
{
    m_data = data;
    m_dirty = true;
}

bool SqlTreeItem::dirty() const
{
    return m_dirty;
}

void SqlTreeItem::setDirty(bool dirty)
{
    m_dirty = dirty;
}

const QList<SqlTreeItem*>& SqlTreeItem::children() const
{
    return m_children;
}

QPixmap SqlTreeItem::icon() const
{
    return m_icon;
}

SqlTreeModel::SqlTreeModel(QObject *parent)
    : QAbstractItemModel(parent), m_rootItem(0)
{
    loadData();
}

void SqlTreeModel::reset()
{
    beginResetModel();
    tables.clear();
    delete m_rootItem;
    loadData();
    endResetModel();
}

void SqlTreeModel::loadData()
{
    tables.append("characters");
    tables.append("conversations");

    m_rootItem = new SqlTreeItem("Conversations");

    QSqlQuery query;
    QMultiMap<QString, QString> charactersConversations;
    QMap<QString, qint64> charactersIds;
    QMap<QString, qint64> conversationsIds;

    QPixmap characterIcon(":/icons/view-media-artist.png");
    QPixmap conversationIcon(":/icons/irc-voice.png");

    // TODO: Make this generic and recursive
    query.exec("select characters.id, conversations.id, characters.name, conversations.name from events "
               "inner join characters on events.character_id = characters.id "
               "inner join conversations on events.conversation_id = conversations.id");
    // TODO: Use indexOf("fieldname")
    while (query.next()) {
        const qint64 characterId = query.value(0).toLongLong();
        const qint64 conversationId = query.value(1).toLongLong();
        const QString characterName = query.value(2).toString();
        const QString conversationName = query.value(3).toString();
        charactersConversations.insert(characterName, conversationName);
        charactersIds.insert(characterName, characterId);
        conversationsIds.insert(conversationName, conversationId);
    }

    foreach(const QString& characterName, charactersConversations.keys()) {
        const qint64 characterId = charactersIds.value(characterName);
        SqlTreeItem* character = new SqlTreeItem(characterName, "characters", m_rootItem, characterId, characterIcon);
        m_rootItem->appendChild(character);
        foreach(const QString& conversationName, charactersConversations.values(characterName)) {
            const qint64 conversationId = conversationsIds.value(conversationName);
            SqlTreeItem* conversation = new SqlTreeItem(conversationName, "conversations", character, conversationId, conversationIcon);
            character->appendChild(conversation);
        }
    }
}

SqlTreeModel::~SqlTreeModel()
{
    delete m_rootItem;
}

QModelIndex SqlTreeModel::index(int row, int column, const QModelIndex &parent)
        const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    SqlTreeItem *parentItem = 0;

    if (!parent.isValid())
        parentItem = m_rootItem;
    else
        parentItem = static_cast<SqlTreeItem*>(parent.internalPointer());

    Q_ASSERT(parentItem);
    if (!parentItem)
        return QModelIndex();

    SqlTreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex SqlTreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    SqlTreeItem *childItem = static_cast<SqlTreeItem*>(index.internalPointer());
    Q_ASSERT(childItem);
    if (!childItem)
        return QModelIndex();

    SqlTreeItem *parentItem = childItem->parent();
    Q_ASSERT(parentItem);
    if (!parentItem)
        return QModelIndex();

    if (parentItem == m_rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int SqlTreeModel::rowCount(const QModelIndex &parent) const
{
    SqlTreeItem *parentItem = 0;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = m_rootItem;
    else
        parentItem = static_cast<SqlTreeItem*>(parent.internalPointer());

    Q_ASSERT(parentItem);
    if (!parentItem)
        return 0;

    return parentItem->childCount();
}

int SqlTreeModel::columnCount(const QModelIndex&) const
{
    return 1;
}

QVariant SqlTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    SqlTreeItem *item = static_cast<SqlTreeItem*>(index.internalPointer());
    Q_ASSERT(item);
    if (!item)
        return QVariant();

    if (role == Qt::DisplayRole)
        return item->data();

    if (role == Qt::DecorationRole)
        return item->icon();

    return QVariant();
}

bool SqlTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    if (role != Qt::EditRole)
        return false;

    if (value.toString().isEmpty())
        return false;

    SqlTreeItem *item = static_cast<SqlTreeItem*>(index.internalPointer());
    Q_ASSERT(item);
    if (!item)
        return false;

    const int id = item->id();

    if (id != SqlTreeItem::INVALID_ID) {
        foreach (SqlTreeItem* characterItem, m_rootItem->children()) {
            Q_ASSERT(characterItem);
            if (!characterItem)
                return false;
            if (id == characterItem->id())
                characterItem->setData(value.toString());
            foreach (SqlTreeItem* conversationItem, characterItem->children()) {
                Q_ASSERT(conversationItem);
                if (!conversationItem)
                    return false;
                if (id == conversationItem->id())
                    conversationItem->setData(value.toString());
            }
        }
    }
    else
        item->setData(value.toString());

    emit dataChanged(index, index);
    return true;
}

bool SqlTreeModel::insertRow(int, const QModelIndex &parent)
{
    SqlTreeItem *parentItem = static_cast<SqlTreeItem*>(parent.internalPointer());
    Q_ASSERT(parentItem);
    if (!parentItem)
        return false;

    if (!parentItem)
        parentItem = m_rootItem;

    beginInsertRows(parent, 0, parentItem->childCount());
    const QString newItemTable = getChildTable(parentItem->table());

    SqlTreeItem* newItem = new SqlTreeItem("New Item", newItemTable, parentItem);
    newItem->setDirty(true);
    parentItem->appendChild(newItem);
    endInsertRows();
    return true;
}

QString SqlTreeModel::getChildTable(const QString& parentTable)
{
    QString previousTable;
    foreach(const QString& table, tables) {
        if (parentTable == previousTable)
            return table;
        previousTable = table;
    }
    return QString();
}

Qt::ItemFlags SqlTreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}

QVariant SqlTreeModel::headerData(int, Qt::Orientation orientation,
                               int role) const
{
    if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
        return QVariant();

    Q_ASSERT(m_rootItem);
    if (!m_rootItem)
        return QVariant();

    return m_rootItem->data();
}

bool SqlTreeModel::submit()
{
    QList<SqlTreeItem*> newCharacters;
    QList<SqlTreeItem*> newConversations;
    QList<SqlTreeItem*> updatedCharacters;
    QList<SqlTreeItem*> updatedConversations;

    foreach (SqlTreeItem* characterItem, m_rootItem->children()) {
        Q_ASSERT(characterItem);
        if (!characterItem)
            return false;

        if (characterItem->data() == "New Item")
            continue;

        if (characterItem->dirty()) {
            if (characterItem->id() == SqlTreeItem::INVALID_ID)
                newCharacters.append(characterItem);
            else
                updatedCharacters.append(characterItem);
        }
        foreach (SqlTreeItem* conversationItem, characterItem->children()) {
            Q_ASSERT(conversationItem);
            if (!conversationItem)
                return false;

            if (conversationItem->data() == "New Item")
                continue;

            if (conversationItem->dirty()) {
                if (conversationItem->id() == SqlTreeItem::INVALID_ID)
                    newConversations.append(conversationItem);
                else
                    updatedConversations.append(conversationItem);
            }
        }
    }

    QSqlQuery query;
    bool querySuccess = false;

    foreach (SqlTreeItem* newCharacter, newCharacters) {
        Q_ASSERT(newCharacter);
        if (!newCharacter)
            return false;

        query.prepare("insert into characters(name) values(:name)");
        query.bindValue(":name", newCharacter->data());
        querySuccess = query.exec();
        if (querySuccess) {
            newCharacter->setId(query.lastInsertId().toInt());
            newCharacter->setDirty(false);
        }
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    foreach (SqlTreeItem* newConversation, newConversations) {
        Q_ASSERT(newConversation);
        if (!newConversation)
            return false;

        query.prepare("insert into conversations(name) values(:name)");
        query.bindValue(":name", newConversation->data());
        querySuccess = query.exec();
        if (querySuccess) {
            newConversation->setId(query.lastInsertId().toInt());
            newConversation->setDirty(false);
        }
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    // TODO: Check we update at least one row

    foreach (SqlTreeItem* updatedCharacter, updatedCharacters) {
        Q_ASSERT(updatedCharacter);
        if (!updatedCharacter)
            return false;

        query.prepare("update characters set name=:name where id=:id");
        query.bindValue(":name", updatedCharacter->data());
        query.bindValue(":id", updatedCharacter->id());
        querySuccess = query.exec();
        if (querySuccess)
            updatedCharacter->setDirty(false);
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    foreach (SqlTreeItem* updatedConversation, updatedConversations) {
        Q_ASSERT(updatedConversation);
        if (!updatedConversation)
            return false;

        query.prepare("update conversations set name=:name where id=:id");
        query.bindValue(":name", updatedConversation->data());
        query.bindValue(":id", updatedConversation->id());
        querySuccess = query.exec();
        if (querySuccess)
            updatedConversation->setDirty(false);
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    emit submitted();

    return true;
}
<commit_msg>Only list one of each character in SqlTreeModel.<commit_after>/*  This file is part of Fabula.
    Copyright (C) 2010 Mike McQuaid <mike@mikemcquaid.com>

    Fabula 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 3 of the License, or
    (at your option) any later version.

    Fabula 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 Fabula. If not, see <http://www.gnu.org/licenses/>.
*/

#include "SqlTreeModel.h"

#include <QStringList>
#include <QDebug>
#include <QSqlQuery>
#include <QSqlError>
#include <QMultiMap>

SqlTreeItem::SqlTreeItem(const QString &data, const QString &table, SqlTreeItem *parent, qint64 id, QPixmap icon)
    : m_table(table), m_data(data), m_parent(parent), m_id(id), m_icon(icon), m_dirty(false)
{
}

SqlTreeItem::~SqlTreeItem()
{
    if (m_parent)
        m_parent->m_children.removeAll(this);
    qDeleteAll(m_children);
}

SqlTreeItem *SqlTreeItem::child(int row)
{
    return m_children.value(row);
}

void SqlTreeItem::appendChild(SqlTreeItem* child)
{
    m_children.append(child);
}

int SqlTreeItem::childCount() const
{
    return m_children.count();
}

int SqlTreeItem::row() const
{
    if (m_parent)
        return m_parent->m_children.indexOf(const_cast<SqlTreeItem*>(this));

    return 0;
}

const QString &SqlTreeItem::table() const
{
    return m_table;
}

const QString &SqlTreeItem::data() const
{
    return m_data;
}

qint64 SqlTreeItem::id() const
{
    return m_id;
}

void SqlTreeItem::setId(qint64 id)
{
    m_id = id;
}

SqlTreeItem *SqlTreeItem::parent()
{
    return m_parent;
}

void SqlTreeItem::setData(const QString &data)
{
    m_data = data;
    m_dirty = true;
}

bool SqlTreeItem::dirty() const
{
    return m_dirty;
}

void SqlTreeItem::setDirty(bool dirty)
{
    m_dirty = dirty;
}

const QList<SqlTreeItem*>& SqlTreeItem::children() const
{
    return m_children;
}

QPixmap SqlTreeItem::icon() const
{
    return m_icon;
}

SqlTreeModel::SqlTreeModel(QObject *parent)
    : QAbstractItemModel(parent), m_rootItem(0)
{
    loadData();
}

void SqlTreeModel::reset()
{
    beginResetModel();
    tables.clear();
    delete m_rootItem;
    loadData();
    endResetModel();
}

void SqlTreeModel::loadData()
{
    tables.append("characters");
    tables.append("conversations");

    m_rootItem = new SqlTreeItem("Conversations");

    QSqlQuery query;
    QMultiMap<QString, QString> charactersConversations;
    QMap<QString, qint64> charactersIds;
    QMap<QString, qint64> conversationsIds;

    QPixmap characterIcon(":/icons/view-media-artist.png");
    QPixmap conversationIcon(":/icons/irc-voice.png");

    // TODO: Make this generic and recursive
    query.exec("select characters.id, conversations.id, characters.name, conversations.name from events "
               "inner join characters on events.character_id = characters.id "
               "inner join conversations on events.conversation_id = conversations.id");
    // TODO: Use indexOf("fieldname")
    while (query.next()) {
        const qint64 characterId = query.value(0).toLongLong();
        const qint64 conversationId = query.value(1).toLongLong();
        const QString characterName = query.value(2).toString();
        const QString conversationName = query.value(3).toString();
        charactersConversations.insert(characterName, conversationName);
        charactersIds.insert(characterName, characterId);
        conversationsIds.insert(conversationName, conversationId);
    }

    foreach(const QString& characterName, charactersConversations.uniqueKeys()) {
        const qint64 characterId = charactersIds.value(characterName);
        SqlTreeItem* character = new SqlTreeItem(characterName, "characters", m_rootItem, characterId, characterIcon);
        m_rootItem->appendChild(character);
        foreach(const QString& conversationName, charactersConversations.values(characterName)) {
            const qint64 conversationId = conversationsIds.value(conversationName);
            SqlTreeItem* conversation = new SqlTreeItem(conversationName, "conversations", character, conversationId, conversationIcon);
            character->appendChild(conversation);
        }
    }
}

SqlTreeModel::~SqlTreeModel()
{
    delete m_rootItem;
}

QModelIndex SqlTreeModel::index(int row, int column, const QModelIndex &parent)
        const
{
    if (!hasIndex(row, column, parent))
        return QModelIndex();

    SqlTreeItem *parentItem = 0;

    if (!parent.isValid())
        parentItem = m_rootItem;
    else
        parentItem = static_cast<SqlTreeItem*>(parent.internalPointer());

    Q_ASSERT(parentItem);
    if (!parentItem)
        return QModelIndex();

    SqlTreeItem *childItem = parentItem->child(row);
    if (childItem)
        return createIndex(row, column, childItem);
    else
        return QModelIndex();
}

QModelIndex SqlTreeModel::parent(const QModelIndex &index) const
{
    if (!index.isValid())
        return QModelIndex();

    SqlTreeItem *childItem = static_cast<SqlTreeItem*>(index.internalPointer());
    Q_ASSERT(childItem);
    if (!childItem)
        return QModelIndex();

    SqlTreeItem *parentItem = childItem->parent();
    Q_ASSERT(parentItem);
    if (!parentItem)
        return QModelIndex();

    if (parentItem == m_rootItem)
        return QModelIndex();

    return createIndex(parentItem->row(), 0, parentItem);
}

int SqlTreeModel::rowCount(const QModelIndex &parent) const
{
    SqlTreeItem *parentItem = 0;
    if (parent.column() > 0)
        return 0;

    if (!parent.isValid())
        parentItem = m_rootItem;
    else
        parentItem = static_cast<SqlTreeItem*>(parent.internalPointer());

    Q_ASSERT(parentItem);
    if (!parentItem)
        return 0;

    return parentItem->childCount();
}

int SqlTreeModel::columnCount(const QModelIndex&) const
{
    return 1;
}

QVariant SqlTreeModel::data(const QModelIndex &index, int role) const
{
    if (!index.isValid())
        return QVariant();

    SqlTreeItem *item = static_cast<SqlTreeItem*>(index.internalPointer());
    Q_ASSERT(item);
    if (!item)
        return QVariant();

    if (role == Qt::DisplayRole)
        return item->data();

    if (role == Qt::DecorationRole)
        return item->icon();

    return QVariant();
}

bool SqlTreeModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if (!index.isValid())
        return false;

    if (role != Qt::EditRole)
        return false;

    if (value.toString().isEmpty())
        return false;

    SqlTreeItem *item = static_cast<SqlTreeItem*>(index.internalPointer());
    Q_ASSERT(item);
    if (!item)
        return false;

    const int id = item->id();

    if (id != SqlTreeItem::INVALID_ID) {
        foreach (SqlTreeItem* characterItem, m_rootItem->children()) {
            Q_ASSERT(characterItem);
            if (!characterItem)
                return false;
            if (id == characterItem->id())
                characterItem->setData(value.toString());
            foreach (SqlTreeItem* conversationItem, characterItem->children()) {
                Q_ASSERT(conversationItem);
                if (!conversationItem)
                    return false;
                if (id == conversationItem->id())
                    conversationItem->setData(value.toString());
            }
        }
    }
    else
        item->setData(value.toString());

    emit dataChanged(index, index);
    return true;
}

bool SqlTreeModel::insertRow(int, const QModelIndex &parent)
{
    SqlTreeItem *parentItem = static_cast<SqlTreeItem*>(parent.internalPointer());
    Q_ASSERT(parentItem);
    if (!parentItem)
        return false;

    if (!parentItem)
        parentItem = m_rootItem;

    beginInsertRows(parent, 0, parentItem->childCount());
    const QString newItemTable = getChildTable(parentItem->table());

    SqlTreeItem* newItem = new SqlTreeItem("New Item", newItemTable, parentItem);
    newItem->setDirty(true);
    parentItem->appendChild(newItem);
    endInsertRows();
    return true;
}

QString SqlTreeModel::getChildTable(const QString& parentTable)
{
    QString previousTable;
    foreach(const QString& table, tables) {
        if (parentTable == previousTable)
            return table;
        previousTable = table;
    }
    return QString();
}

Qt::ItemFlags SqlTreeModel::flags(const QModelIndex &index) const
{
    if (!index.isValid())
        return 0;

    return Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
}

QVariant SqlTreeModel::headerData(int, Qt::Orientation orientation,
                               int role) const
{
    if (orientation != Qt::Horizontal || role != Qt::DisplayRole)
        return QVariant();

    Q_ASSERT(m_rootItem);
    if (!m_rootItem)
        return QVariant();

    return m_rootItem->data();
}

bool SqlTreeModel::submit()
{
    QList<SqlTreeItem*> newCharacters;
    QList<SqlTreeItem*> newConversations;
    QList<SqlTreeItem*> updatedCharacters;
    QList<SqlTreeItem*> updatedConversations;

    foreach (SqlTreeItem* characterItem, m_rootItem->children()) {
        Q_ASSERT(characterItem);
        if (!characterItem)
            return false;

        if (characterItem->data() == "New Item")
            continue;

        if (characterItem->dirty()) {
            if (characterItem->id() == SqlTreeItem::INVALID_ID)
                newCharacters.append(characterItem);
            else
                updatedCharacters.append(characterItem);
        }
        foreach (SqlTreeItem* conversationItem, characterItem->children()) {
            Q_ASSERT(conversationItem);
            if (!conversationItem)
                return false;

            if (conversationItem->data() == "New Item")
                continue;

            if (conversationItem->dirty()) {
                if (conversationItem->id() == SqlTreeItem::INVALID_ID)
                    newConversations.append(conversationItem);
                else
                    updatedConversations.append(conversationItem);
            }
        }
    }

    QSqlQuery query;
    bool querySuccess = false;

    foreach (SqlTreeItem* newCharacter, newCharacters) {
        Q_ASSERT(newCharacter);
        if (!newCharacter)
            return false;

        query.prepare("insert into characters(name) values(:name)");
        query.bindValue(":name", newCharacter->data());
        querySuccess = query.exec();
        if (querySuccess) {
            newCharacter->setId(query.lastInsertId().toInt());
            newCharacter->setDirty(false);
        }
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    foreach (SqlTreeItem* newConversation, newConversations) {
        Q_ASSERT(newConversation);
        if (!newConversation)
            return false;

        query.prepare("insert into conversations(name) values(:name)");
        query.bindValue(":name", newConversation->data());
        querySuccess = query.exec();
        if (querySuccess) {
            newConversation->setId(query.lastInsertId().toInt());
            newConversation->setDirty(false);
        }
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    // TODO: Check we update at least one row

    foreach (SqlTreeItem* updatedCharacter, updatedCharacters) {
        Q_ASSERT(updatedCharacter);
        if (!updatedCharacter)
            return false;

        query.prepare("update characters set name=:name where id=:id");
        query.bindValue(":name", updatedCharacter->data());
        query.bindValue(":id", updatedCharacter->id());
        querySuccess = query.exec();
        if (querySuccess)
            updatedCharacter->setDirty(false);
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    foreach (SqlTreeItem* updatedConversation, updatedConversations) {
        Q_ASSERT(updatedConversation);
        if (!updatedConversation)
            return false;

        query.prepare("update conversations set name=:name where id=:id");
        query.bindValue(":name", updatedConversation->data());
        query.bindValue(":id", updatedConversation->id());
        querySuccess = query.exec();
        if (querySuccess)
            updatedConversation->setDirty(false);
        else
            qWarning() << query.lastQuery() << query.lastError();
    }

    emit submitted();

    return true;
}
<|endoftext|>


<commit_before>//@author A0097630B
#include "stdafx.h"
#include "You-QueryEngine/task_model.h"

#include "exception.h"
#include "query_parser.h"

using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;

namespace You {
namespace NLP {
namespace UnitTests {

using boost::gregorian::date;
using boost::posix_time::ptime;
using boost::posix_time::hours;
using You::NLP::ParserException;

using You::NLP::QueryParser;
using You::NLP::ADD_QUERY;
using You::NLP::EDIT_QUERY;
using You::NLP::QUERY;

TEST_CLASS(QueryParserTests) {
public:
	TEST_METHOD(throwsExceptionOnEmptyString) {
		Assert::ExpectException<ParserException>([]() {
			QueryParser::parse(L"");
		}, L"Throws exception on empty string");
	}

	TEST_METHOD(throwsExceptionWhenParseFails) {
		Assert::ExpectException<ParserException>([]() {
				// "throw" is currently not defined, so this should work.
				QueryParser::parse(L"/throw");
			}, L"Throws exception on syntax error");
	}

	TEST_METHOD(parsesStringAsTask) {
		QUERY q = QueryParser::parse(L"win");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win"
		}), q);

		q = QueryParser::parse(L"win lottery");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery"
		}), q);
	}

	TEST_METHOD(parsesStringWithDeadlineAsTask) {
		QUERY q = QueryParser::parse(L"win by may 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::May, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"win lottery by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesStringWithPriorityAsTask) {
		QUERY q = QueryParser::parse(L"win!");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::HIGH
		}), q);

		q = QueryParser::parse(L"win lottery! by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::HIGH,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesShowQuery) {
		// Boundary case: one filter, zero sort.
		QUERY q = QueryParser::parse(L"/show description='\\\\\\'meh'");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::EQ,
					std::wstring(L"\\\'meh")
				}
			},
			{}
		}), q);

		// Boundary case: more than one filter, zero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high");

		Assert::AreEqual(QUERY(SHOW_QUERY {
				{
					{
						TaskField::DESCRIPTION,
						SHOW_QUERY::Predicate::NOT_EQ,
						std::wstring(L"\\\'meh")
					},
					{
						TaskField::PRIORITY,
						SHOW_QUERY::Predicate::LESS_THAN,
						TaskPriority::HIGH
					}
				},
				{}
		}), q);

		// Boundary case: zero filter, one sort.
		q = QueryParser::parse(L"/show order by description ascending");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{ { TaskField::DESCRIPTION, SHOW_QUERY::Order::ASCENDING } }
		}), q);

		// Boundary case: zero filter, more than one sort.
		q = QueryParser::parse(L"/show order by description descending, "
			L"priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);

		// Boundary case: nonzero filter, nonzero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high, priority>normal, deadline>='3 oct', "
			L"deadline<='7 oct' order by description descending, priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::NOT_EQ,
					std::wstring(L"\\\'meh")
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::LESS_THAN,
					TaskPriority::HIGH
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::GREATER_THAN,
					TaskPriority::NORMAL
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::GREATER_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 3),
						boost::posix_time::hours(0))
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::LESS_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 7),
						boost::posix_time::hours(0))
				}
			},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);
	}

	TEST_METHOD(parsesEditQuery) {
		QUERY q = QueryParser::parse(L"/edit 10 set description='meh'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh"
		}), q);

		q = QueryParser::parse(L"/edit 10 set description='meh with spaces'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh with spaces"
		}), q);

		q = QueryParser::parse(L"/edit 10 set deadline='oct 2014'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			Utils::Option<std::wstring>(),
			Utils::Option<TaskPriority>(),
			ptime(date(2014, boost::gregorian::Oct, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"/edit 10 set complete");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			Utils::Option<std::wstring>(),
			Utils::Option<TaskPriority>(),
			Utils::Option<boost::posix_time::ptime>(),
			true
		}), q);

		q = QueryParser::parse(L"/edit 10 set priority=high");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			Utils::Option<std::wstring>(),
			TaskPriority::HIGH
		}), q);
	}

	TEST_METHOD(parsesEditQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>([]() {
			QueryParser::parse(L"/edit 10 set description='14 oct'");
		});
	}

	TEST_METHOD(parsesDeleteQuery) {
		QUERY q = QueryParser::parse(L"/delete 10");

		Assert::AreEqual(QUERY(DELETE_QUERY {
			10
		}), q);
	}

	TEST_METHOD(parsesUndoQuery) {
		QUERY q = QueryParser::parse(L"/undo");

		Assert::AreEqual(QUERY(UNDO_QUERY {}), q);
	}
};

}  // namespace UnitTests
}  // namespace NLP
}  // namespace You
<commit_msg>Improve code coverage.<commit_after>//@author A0097630B
#include "stdafx.h"
#include "You-QueryEngine/task_model.h"

#include "exception.h"
#include "query_parser.h"

using Assert = Microsoft::VisualStudio::CppUnitTestFramework::Assert;

namespace You {
namespace NLP {
namespace UnitTests {

using boost::gregorian::date;
using boost::posix_time::ptime;
using boost::posix_time::hours;
using You::NLP::ParserException;

using You::NLP::QueryParser;
using You::NLP::ADD_QUERY;
using You::NLP::EDIT_QUERY;
using You::NLP::QUERY;

TEST_CLASS(QueryParserTests) {
public:
	TEST_METHOD(throwsExceptionOnEmptyString) {
		Assert::ExpectException<ParserException>([]() {
			QueryParser::parse(L"");
		}, L"Throws exception on empty string");
	}

	TEST_METHOD(throwsExceptionWhenParseFails) {
		Assert::ExpectException<ParserException>([]() {
				// "throw" is currently not defined, so this should work.
				QueryParser::parse(L"/throw");
			}, L"Throws exception on syntax error");
	}

	TEST_METHOD(parsesStringAsTask) {
		QUERY q = QueryParser::parse(L"win");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win"
		}), q);

		q = QueryParser::parse(L"win lottery");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery"
		}), q);
	}

	TEST_METHOD(parsesStringWithDeadlineAsTask) {
		QUERY q = QueryParser::parse(L"win by may 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::May, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"win lottery by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::NORMAL,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesStringWithPriorityAsTask) {
		QUERY q = QueryParser::parse(L"win!");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win",
			TaskPriority::HIGH
		}), q);

		q = QueryParser::parse(L"win lottery! by dec 2014");

		Assert::AreEqual(QUERY(ADD_QUERY {
			L"win lottery",
			TaskPriority::HIGH,
			ptime(date(2014, boost::gregorian::Dec, 1), hours(0))
		}), q);
	}

	TEST_METHOD(parsesShowQuery) {
		// Boundary case: one filter, zero sort.
		QUERY q = QueryParser::parse(L"/show description='\\\\\\'meh'");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::EQ,
					std::wstring(L"\\\'meh")
				}
			},
			{}
		}), q);

		// Boundary case: more than one filter, zero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high");

		Assert::AreEqual(QUERY(SHOW_QUERY {
				{
					{
						TaskField::DESCRIPTION,
						SHOW_QUERY::Predicate::NOT_EQ,
						std::wstring(L"\\\'meh")
					},
					{
						TaskField::PRIORITY,
						SHOW_QUERY::Predicate::LESS_THAN,
						TaskPriority::HIGH
					}
				},
				{}
		}), q);

		// Boundary case: zero filter, one sort.
		q = QueryParser::parse(L"/show order by description ascending");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{ { TaskField::DESCRIPTION, SHOW_QUERY::Order::ASCENDING } }
		}), q);

		// Boundary case: zero filter, more than one sort.
		q = QueryParser::parse(L"/show order by description descending, "
			L"priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);

		// Boundary case: nonzero filter, nonzero sort.
		q = QueryParser::parse(L"/show description!='\\\\\\'meh', "
			L"priority<high, priority>normal, deadline>='3 oct', "
			L"deadline<='7 oct', complete=true order by description descending, priority");

		Assert::AreEqual(QUERY(SHOW_QUERY {
			{
				{
					TaskField::DESCRIPTION,
					SHOW_QUERY::Predicate::NOT_EQ,
					std::wstring(L"\\\'meh")
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::LESS_THAN,
					TaskPriority::HIGH
				},
				{
					TaskField::PRIORITY,
					SHOW_QUERY::Predicate::GREATER_THAN,
					TaskPriority::NORMAL
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::GREATER_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 3),
						boost::posix_time::hours(0))
				},
				{
					TaskField::DEADLINE,
					SHOW_QUERY::Predicate::LESS_THAN_EQ,
					boost::posix_time::ptime(
						boost::gregorian::date(2015, 10, 7),
						boost::posix_time::hours(0))
				},
				{
					TaskField::COMPLETE,
					SHOW_QUERY::Predicate::EQ,
					true
				}
			},
			{
				{ TaskField::DESCRIPTION, SHOW_QUERY::Order::DESCENDING },
				{ TaskField::PRIORITY, SHOW_QUERY::Order::ASCENDING }
			}
		}), q);
	}

	TEST_METHOD(parsesShowQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>([]() {
			QueryParser::parse(L"/show description=false");
		});
	}

	TEST_METHOD(parsesEditQuery) {
		QUERY q = QueryParser::parse(L"/edit 10 set description='meh'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh"
		}), q);

		q = QueryParser::parse(L"/edit 10 set description='meh with spaces'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			L"meh with spaces"
		}), q);

		q = QueryParser::parse(L"/edit 10 set deadline='oct 2014'");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			Utils::Option<std::wstring>(),
			Utils::Option<TaskPriority>(),
			ptime(date(2014, boost::gregorian::Oct, 1), hours(0))
		}), q);

		q = QueryParser::parse(L"/edit 10 set complete");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			Utils::Option<std::wstring>(),
			Utils::Option<TaskPriority>(),
			Utils::Option<boost::posix_time::ptime>(),
			true
		}), q);

		q = QueryParser::parse(L"/edit 10 set priority=high");

		Assert::AreEqual(QUERY(EDIT_QUERY {
			10,
			Utils::Option<std::wstring>(),
			TaskPriority::HIGH
		}), q);
	}

	TEST_METHOD(parsesEditQueryWithWrongType) {
		Assert::ExpectException<ParserTypeException>([]() {
			QueryParser::parse(L"/edit 10 set description='14 oct'");
		});
	}

	TEST_METHOD(parsesDeleteQuery) {
		QUERY q = QueryParser::parse(L"/delete 10");

		Assert::AreEqual(QUERY(DELETE_QUERY {
			10
		}), q);
	}

	TEST_METHOD(parsesUndoQuery) {
		QUERY q = QueryParser::parse(L"/undo");

		Assert::AreEqual(QUERY(UNDO_QUERY {}), q);
	}
};

}  // namespace UnitTests
}  // namespace NLP
}  // namespace You
<|endoftext|>


<commit_before>#include "TmxScene.h"
using namespace hb;

TmxScene::TmxScene(const std::string& scene_name, const std::string& file_name, std::function<void(const Tmx::Map*)>&& post_init):
Game::Scene(scene_name, [](){}),
m_post_init(std::move(post_init))
{
	HB_CHECK_TMXPARSER_VERSION();
	m_init = [this, file_name]()
	{
		Tmx::Map* map = new Tmx::Map();
		map->ParseFile(file_name);
		if (map->HasError())
		{
			hb_log("error code: " << map->GetErrorCode() << std::endl);
			hb_log("error text: " << map->GetErrorText().c_str() << std::endl);

			std::exit(map->GetErrorCode());
		}

		int last_slash = file_name.find_last_of("/");
		std::string path = file_name.substr(0, last_slash +1);

		std::string c = map->GetBackgroundColor();
		if (c.size() != 0)
		{
			int r = std::stoul(c.substr(1,2), nullptr, 16);
			int g = std::stoul(c.substr(3,2), nullptr, 16);
			int b = std::stoul(c.substr(5,2), nullptr, 16);
			Color color(r, g, b);
			Renderer::setClearColor(color);
		}

		if (map->GetOrientation() == Tmx::TMX_MO_ORTHOGONAL)
		{
			Renderer::getCamera().setAxisX(Vector3d(map->GetTileWidth(), 0, 0));
			Renderer::getCamera().setAxisY(Vector3d(0, map->GetTileHeight(), 0));
			Renderer::getCamera().setAxisZ(Vector3d(0, 0, 1));
		}
		else if (map->GetOrientation() == Tmx::TMX_MO_ISOMETRIC)
		{
			Renderer::getCamera().setAxisX(Vector3d(map->GetTileWidth(), map->GetTileHeight(), map->GetTileHeight()));
			Renderer::getCamera().setAxisY(Vector3d(-map->GetTileWidth(), map->GetTileHeight(), map->GetTileHeight()));
			Renderer::getCamera().setAxisZ(Vector3d(0, 0, 1));
		}

		GameObject::setNextGameObjectId(map->GetNextObjectId());

		// Make Image layers
		for (int i = 0; i < map->GetNumImageLayers(); ++i)
		{
			const Tmx::ImageLayer* layer = map->GetImageLayer(i);
			if (not layer->IsVisible()) continue;

			GameObject *image_layer_go = new GameObject();
			image_layer_go->setName(layer->GetName());
			image_layer_go->setPosition(Vector3d(layer->GetX()/map->GetTileWidth(), layer->GetY()/map->GetTileHeight(), layer->GetZOrder()));

			Texture tex = Texture::loadFromFile(path + layer->GetImage()->GetSource(), Rect(0, 0, layer->GetImage()->GetWidth(), layer->GetImage()->GetHeight()));
			Sprite sprite = Sprite(tex);
			auto sprite_comp = new SpriteComponent(sprite);
			image_layer_go->addComponent(sprite_comp);
		}

		// Make tile layers
		for (int i = 0; i < map->GetNumTileLayers(); ++i)
		{
			const Tmx::TileLayer* layer = map->GetTileLayer(i);
			if (not layer->IsVisible()) continue;

			GameObject *tile_layer_go = new GameObject();
			tile_layer_go->setName(layer->GetName());
			tile_layer_go->setPosition(Vector3d(0, 0, layer->GetZOrder()));
			for (int y = 0; y < layer->GetHeight(); ++y)
			{
				for (int x = 0; x < layer->GetWidth(); ++x)
				{
					if (layer->GetTileTilesetIndex(x, y) == -1) continue;
					int gid = layer->GetTileId(x, y);
					const Tmx::Tileset *tileset = map->FindTileset(gid);
					int lid = gid - tileset->GetFirstGid();
					std::vector<int> anim;
					Time t_anim = Time::seconds(0);
					const Tmx::Tile* tile = tileset->GetTile(lid);
					if (tile->IsAnimated())
					{
						t_anim = Time::milliseconds(tile->GetTotalDuration() / tile->GetFrameCount());
						for (auto anim_frame : tile->GetFrames())
						{
							anim.push_back(anim_frame.GetTileID());
						}
					}
					else
					{
						anim.push_back(lid);
					}
					Texture tex = Texture::loadFromFile(path + tileset->GetImage()->GetSource(), Rect(0, 0, tileset->GetImage()->GetWidth(), tileset->GetImage()->GetHeight()));
					Sprite sprite = Sprite(tex, Vector2d(tileset->GetTileWidth(), tileset->GetTileHeight()), Vector2d(tileset->GetMargin(), tileset->GetMargin()));
					auto sprite_comp = new SpriteComponent(sprite, anim, t_anim);
					sprite_comp->setPosition(Vector3d(x, y, 0));
					tile_layer_go->addComponent(sprite_comp);
				}
			}
		}

		// Make GameObjects
		for (int i = 0; i < map->GetNumObjectGroups(); ++i)
		{
			const Tmx::ObjectGroup* obj_grp = map->GetObjectGroup(i);
			if (not obj_grp->IsVisible()) continue;
			for (int j = 0; j < obj_grp->GetNumObjects(); ++j)
			{
				TmxObjectTypeFactory::makeObject(map, i, j);
			}
		}

		m_post_init(map);

		delete map;
	};
}

TmxScene::TmxScene(const std::string& scene_name, const std::string& file_name, std::function<void(const Tmx::Map*)>& post_init):
TmxScene(scene_name, file_name, std::function<void(const Tmx::Map*)>(post_init))
{

}

TmxScene::~TmxScene()
{

}
<commit_msg>Updated to new tmxparser version<commit_after>#include "TmxScene.h"
using namespace hb;

TmxScene::TmxScene(const std::string& scene_name, const std::string& file_name, std::function<void(const Tmx::Map*)>&& post_init):
Game::Scene(scene_name, [](){}),
m_post_init(std::move(post_init))
{
	HB_CHECK_TMXPARSER_VERSION();
	m_init = [this, file_name]()
	{
		Tmx::Map* map = new Tmx::Map();
		map->ParseFile(file_name);
		if (map->HasError())
		{
			hb_log("error code: " << map->GetErrorCode() << std::endl);
			hb_log("error text: " << map->GetErrorText().c_str() << std::endl);

			std::exit(map->GetErrorCode());
		}

		int last_slash = file_name.find_last_of("/");
		std::string path = file_name.substr(0, last_slash +1);

		std::string c = map->GetBackgroundColor();
		if (c.size() != 0)
		{
			int r = std::stoul(c.substr(1,2), nullptr, 16);
			int g = std::stoul(c.substr(3,2), nullptr, 16);
			int b = std::stoul(c.substr(5,2), nullptr, 16);
			Color color(r, g, b);
			Renderer::setClearColor(color);
		}

		if (map->GetOrientation() == Tmx::TMX_MO_ORTHOGONAL)
		{
			Renderer::getCamera().setAxisX(Vector3d(map->GetTileWidth(), 0, 0));
			Renderer::getCamera().setAxisY(Vector3d(0, map->GetTileHeight(), 0));
			Renderer::getCamera().setAxisZ(Vector3d(0, 0, 1));
		}
		else if (map->GetOrientation() == Tmx::TMX_MO_ISOMETRIC)
		{
			Renderer::getCamera().setAxisX(Vector3d(map->GetTileWidth(), map->GetTileHeight(), map->GetTileHeight()));
			Renderer::getCamera().setAxisY(Vector3d(-map->GetTileWidth(), map->GetTileHeight(), map->GetTileHeight()));
			Renderer::getCamera().setAxisZ(Vector3d(0, 0, 1));
		}

		GameObject::setNextGameObjectId(map->GetNextObjectId());

		// Make Image layers
		for (int i = 0; i < map->GetNumImageLayers(); ++i)
		{
			const Tmx::ImageLayer* layer = map->GetImageLayer(i);
			if (not layer->IsVisible()) continue;

			GameObject *image_layer_go = new GameObject();
			image_layer_go->setName(layer->GetName());
			image_layer_go->setPosition(Vector3d(layer->GetX()/map->GetTileWidth(), layer->GetY()/map->GetTileHeight(), layer->GetZOrder()));

			Texture tex = Texture::loadFromFile(path + layer->GetImage()->GetSource(), Rect(0, 0, layer->GetImage()->GetWidth(), layer->GetImage()->GetHeight()));
			Sprite sprite = Sprite(tex);
			auto sprite_comp = new SpriteComponent(sprite);
			image_layer_go->addComponent(sprite_comp);
		}

		// Make tile layers
		for (int i = 0; i < map->GetNumTileLayers(); ++i)
		{
			const Tmx::TileLayer* layer = map->GetTileLayer(i);
			if (not layer->IsVisible()) continue;

			GameObject *tile_layer_go = new GameObject();
			tile_layer_go->setName(layer->GetName());
			tile_layer_go->setPosition(Vector3d(0, 0, layer->GetZOrder()));
			for (int y = 0; y < layer->GetHeight(); ++y)
			{
				for (int x = 0; x < layer->GetWidth(); ++x)
				{
					if (layer->GetTileTilesetIndex(x, y) == -1) continue;
					int gid = layer->GetTileGid(x, y);
					const Tmx::Tileset *tileset = map->FindTileset(gid);
					int lid = gid - tileset->GetFirstGid();
					std::vector<int> anim;
					Time t_anim = Time::seconds(0);
					const Tmx::Tile* tile = tileset->GetTile(lid);
					if (tile->IsAnimated())
					{
						t_anim = Time::milliseconds(tile->GetTotalDuration() / tile->GetFrameCount());
						for (auto anim_frame : tile->GetFrames())
						{
							anim.push_back(anim_frame.GetTileID());
						}
					}
					else
					{
						anim.push_back(lid);
					}
					Texture tex = Texture::loadFromFile(path + tileset->GetImage()->GetSource(), Rect(0, 0, tileset->GetImage()->GetWidth(), tileset->GetImage()->GetHeight()));
					Sprite sprite = Sprite(tex, Vector2d(tileset->GetTileWidth(), tileset->GetTileHeight()), Vector2d(tileset->GetMargin(), tileset->GetMargin()));
					auto sprite_comp = new SpriteComponent(sprite, anim, t_anim);
					sprite_comp->setPosition(Vector3d(x, y, 0));
					tile_layer_go->addComponent(sprite_comp);
				}
			}
		}

		// Make GameObjects
		for (int i = 0; i < map->GetNumObjectGroups(); ++i)
		{
			const Tmx::ObjectGroup* obj_grp = map->GetObjectGroup(i);
			if (not obj_grp->IsVisible()) continue;
			for (int j = 0; j < obj_grp->GetNumObjects(); ++j)
			{
				TmxObjectTypeFactory::makeObject(map, i, j);
			}
		}

		m_post_init(map);

		delete map;
	};
}

TmxScene::TmxScene(const std::string& scene_name, const std::string& file_name, std::function<void(const Tmx::Map*)>& post_init):
TmxScene(scene_name, file_name, std::function<void(const Tmx::Map*)>(post_init))
{

}

TmxScene::~TmxScene()
{

}
<|endoftext|>


<commit_before>#pragma once
#include <array>
#include <cmath>
#include <string>
#include <memory>
#include <typeinfo>
#include <typeindex>
#include "./debugger.hpp"
namespace darwin {
	enum class status {
	    null,ready,busy,leisure,error
	};
	enum class results {
	    null,success,failure
	};
	enum class colors {
	    white,black,red,green,blue,pink,yellow,cyan
	};
	enum class attris {
	    bright,underline
	};
	enum class commands {
	    echo_on,echo_off,reset_cursor,reset_attri,clrscr
	};
	class pixel final {
		char mChar=' ';
		std::array<bool,2> mAttris= {{false,false}};
		std::array<colors,2> mColors= {{colors::white,colors::black}};
	public:
		pixel()=default;
		pixel(const pixel&)=default;
		pixel(char ch,bool bright,bool underline,colors fcolor,colors bcolor):mChar(ch),mAttris( {
			bright,underline
		}),mColors({fcolor,bcolor}) {}
		~pixel()=default;
		void set_char(char c) {
			mChar=c;
		}
		char get_char() const {
			return mChar;
		}
		void set_front_color(colors c) {
			mColors[0]=c;
		}
		colors get_front_color() const {
			return mColors[0];
		}
		void set_back_color(colors c) {
			mColors[1]=c;
		}
		colors get_back_color() const {
			return mColors[1];
		}
		void set_colors(const std::array<colors,2>& c) {
			mColors=c;
		}
		const std::array<colors,2>& get_colors() const {
			return mColors;
		}
		void set_bright(bool value) {
			mAttris[0]=value;
		}
		bool is_bright() const {
			return mAttris[0];
		}
		void set_underline(bool value) {
			mAttris[1]=value;
		}
		bool is_underline() const {
			return mAttris[1];
		}
		void set_attris(const std::array<bool,2>& a) {
			mAttris=a;
		}
		const std::array<bool,2>& get_attris() const {
			return mAttris;
		}
	};
	class drawable {
	public:
		static double draw_line_precision;
		drawable()=default;
		drawable(const drawable&)=default;
		virtual ~drawable()=default;
		virtual std::type_index get_type() const final {
			return typeid(*this);
		}
		virtual std::shared_ptr<drawable> clone() noexcept {
			return nullptr;
		}
		virtual bool usable() const noexcept=0;
		virtual void clear()=0;
		virtual void fill(const pixel&)=0;
		virtual void resize(std::size_t,std::size_t)=0;
		virtual std::size_t get_width() const=0;
		virtual std::size_t get_height() const=0;
		virtual const pixel& get_pixel(std::size_t,std::size_t) const=0;
		virtual void draw_pixel(int,int,const pixel&)=0;
		virtual void draw_line(int p0x,int p0y,int p1x,int p1y,const pixel& pix) {
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(p0x<0||p0y<0||p1x<0||p1y<0||p0x>this->get_width()-1||p0y>this->get_height()-1||p1x>this->get_width()-1||p1y>this->get_height()-1)
				Darwin_Warning("Out of range.");
			long w(p1x-p0x),h(p1y-p0y);
			double distance(std::sqrt(std::pow(w,2)+std::pow(h,2))*draw_line_precision);
			for(double c=0; c<=1; c+=1.0/distance)
				this->draw_pixel(static_cast<int>(p0x+w*c),static_cast<int>(p0y+h*c),pix);
			this->draw_pixel(p0x,p0y,pix);
			this->draw_pixel(p1x,p1y,pix);
		}
		virtual void draw_rect(int x,int y,int w,int h,const pixel& pix) {
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(x<0||y<0||x>this->get_width()-1||y>this->get_height()-1||x+w>this->get_width()||y+h>this->get_height())
				Darwin_Warning("Out of range.");
			for(int i=0;i<w;w>0?++i:--i)
			{
				this->draw_pixel(x+i,y,pix);
				this->draw_pixel(x+i,y+h-1,pix);
			}
			for(int i=1;i<h-1;h>0?++i:--i)
			{
				this->draw_pixel(x,y+i,pix);
				this->draw_pixel(x+w-1,y+i,pix);
			}
		}
		virtual void fill_rect(int x,int y,int w,int h,const pixel& pix) {
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(x<0||y<0||x>this->get_width()-1||y>this->get_height()-1||x+w>this->get_width()||y+h>this->get_height())
				Darwin_Warning("Out of range.");
			for(int cy=0;cy<h;h>0?++cy:--cy)
				for(int cx=0;cx<w;w>0?++cx:--cx)
					this->draw_pixel(x+cx,y+cy,pix);
		}
		virtual void draw_string(int x,int y,const std::string& str,const pixel& pix)
		{
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(x<0||y<0||x>this->get_width()-1||y>this->get_height()-1||x+str.size()>this->get_width())
				Darwin_Warning("Out of range.");
			pixel p=pix;
			for(int i=0;i<str.size();++i)
			{
				p.set_char(str.at(i));
				this->draw_pixel(x+i,y,p);
			}
		}
		virtual void draw_picture(int col,int row,const drawable& pic) {
			if(!this->usable()||!pic.usable())
				Darwin_Error("Use of not available object.");
			if(col<0||row<0||col>this->get_width()-1||row>this->get_height()-1)
				Darwin_Warning("Out of range.");
			int y0(row>=0?row:0),y1(row>=0?0:-row);
			while(y0<this->get_height()&&y1<pic.get_height()) {
				int x0(col>=0?col:0),x1(col>=0?0:-col);
				while(x0<this->get_width()&&x1<pic.get_width()) {
					this->draw_pixel(x0,y0,pic.get_pixel(x1,y1));
					++x0;
					++x1;
				}
				++y0;
				++y1;
			}
		}
	};
	double drawable::draw_line_precision=1.5;
}<commit_msg>增加三角形填充<commit_after>#pragma once
#include <array>
#include <cmath>
#include <string>
#include <memory>
#include <typeinfo>
#include <typeindex>
#include "./debugger.hpp"
namespace darwin {
	enum class status {
	    null,ready,busy,leisure,error
	};
	enum class results {
	    null,success,failure
	};
	enum class colors {
	    white,black,red,green,blue,pink,yellow,cyan
	};
	enum class attris {
	    bright,underline
	};
	enum class commands {
	    echo_on,echo_off,reset_cursor,reset_attri,clrscr
	};
	class pixel final {
		char mChar=' ';
		std::array<bool,2> mAttris= {{false,false}};
		std::array<colors,2> mColors= {{colors::white,colors::black}};
	public:
		pixel()=default;
		pixel(const pixel&)=default;
		pixel(char ch,bool bright,bool underline,colors fcolor,colors bcolor):mChar(ch),mAttris( {
			bright,underline
		}),mColors({fcolor,bcolor}) {}
		~pixel()=default;
		void set_char(char c) {
			mChar=c;
		}
		char get_char() const {
			return mChar;
		}
		void set_front_color(colors c) {
			mColors[0]=c;
		}
		colors get_front_color() const {
			return mColors[0];
		}
		void set_back_color(colors c) {
			mColors[1]=c;
		}
		colors get_back_color() const {
			return mColors[1];
		}
		void set_colors(const std::array<colors,2>& c) {
			mColors=c;
		}
		const std::array<colors,2>& get_colors() const {
			return mColors;
		}
		void set_bright(bool value) {
			mAttris[0]=value;
		}
		bool is_bright() const {
			return mAttris[0];
		}
		void set_underline(bool value) {
			mAttris[1]=value;
		}
		bool is_underline() const {
			return mAttris[1];
		}
		void set_attris(const std::array<bool,2>& a) {
			mAttris=a;
		}
		const std::array<bool,2>& get_attris() const {
			return mAttris;
		}
	};
	class drawable {
	public:
		static double draw_line_precision;
		drawable()=default;
		drawable(const drawable&)=default;
		virtual ~drawable()=default;
		virtual std::type_index get_type() const final {
			return typeid(*this);
		}
		virtual std::shared_ptr<drawable> clone() noexcept {
			return nullptr;
		}
		virtual bool usable() const noexcept=0;
		virtual void clear()=0;
		virtual void fill(const pixel&)=0;
		virtual void resize(std::size_t,std::size_t)=0;
		virtual std::size_t get_width() const=0;
		virtual std::size_t get_height() const=0;
		virtual const pixel& get_pixel(std::size_t,std::size_t) const=0;
		virtual void draw_pixel(int,int,const pixel&)=0;
		virtual void draw_line(int p0x,int p0y,int p1x,int p1y,const pixel& pix) {
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(p0x<0||p0y<0||p1x<0||p1y<0||p0x>this->get_width()-1||p0y>this->get_height()-1||p1x>this->get_width()-1||p1y>this->get_height()-1)
				Darwin_Warning("Out of range.");
			long w(p1x-p0x),h(p1y-p0y);
			double distance(std::sqrt(std::pow(w,2)+std::pow(h,2))*draw_line_precision);
			for(double c=0; c<=1; c+=1.0/distance)
				this->draw_pixel(static_cast<int>(p0x+w*c),static_cast<int>(p0y+h*c),pix);
			this->draw_pixel(p0x,p0y,pix);
			this->draw_pixel(p1x,p1y,pix);
		}
		virtual void draw_rect(int x,int y,int w,int h,const pixel& pix) {
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(x<0||y<0||x>this->get_width()-1||y>this->get_height()-1||x+w>this->get_width()||y+h>this->get_height())
				Darwin_Warning("Out of range.");
			for(int i=0;i<w;w>0?++i:--i)
			{
				this->draw_pixel(x+i,y,pix);
				this->draw_pixel(x+i,y+h-1,pix);
			}
			for(int i=1;i<h-1;h>0?++i:--i)
			{
				this->draw_pixel(x,y+i,pix);
				this->draw_pixel(x+w-1,y+i,pix);
			}
		}
		virtual void fill_rect(int x,int y,int w,int h,const pixel& pix) {
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(x<0||y<0||x>this->get_width()-1||y>this->get_height()-1||x+w>this->get_width()||y+h>this->get_height())
				Darwin_Warning("Out of range.");
			for(int cy=0;cy<h;h>0?++cy:--cy)
				for(int cx=0;cx<w;w>0?++cx:--cx)
					this->draw_pixel(x+cx,y+cy,pix);
		}
		virtual void fill_triangle(int x1,int y1,int x2,int y2,int x3,int y3,const pixel& pix)
		{
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			int v1x(x2-x1),v1y(y2-y1),v2x(x3-x2),v2y(y3-y2);
			if(v1x*v2y-v2x*v1y==0)
				Darwin_Error("Three points in a line.");
			if(y2<y1)
			{
				std::swap(y1,y2);
				std::swap(x1,x2);
			}
			if(y3<y2)
			{
				std::swap(y2,y3);
				std::swap(x2,x3);
			}
			if(y2<y1)
			{
				std::swap(y1,y2);
				std::swap(x1,x2);
			}
			if(y1==y2)
			{
				double k1(double(x3-x1)/double(y3-y1)),k2(double(x3-x2)/double(y3-y2));
				for(int y=0;y<y3-y2;++y)
					this->draw_line(x1+k1*y,y1+y,x2+k2*y,y2+y,pix);
				this->draw_pixel(x3,y3,pix);
			}else if(y2==y3){
				double k1(double(x3-x1)/double(y3-y1)),k2(double(x2-x1)/double(y2-y1));
				for(int y=1;y<=y2-y1;++y)
					this->draw_line(x1+k1*y,y1+y,x1+k2*y,y1+y,pix);
				this->draw_pixel(x1,y1,pix);
			}else{
				double k1(double(x3-x1)/double(y3-y1)),k2(double(x3-x2)/double(y3-y2)),k3(double(x2-x1)/double(y2-y1));
				for(int y=1;y<y3-y1;++y)
				{
					if(y<y2-y1)
						this->draw_line(x1+k1*y,y1+y,x1+k3*y,y1+y,pix);
					else
						this->draw_line(x1+k1*y,y1+y,x2+k2*(y-y2),y1+y,pix);
				}
				this->draw_pixel(x3,y3,pix);
			}
		}
		virtual void draw_string(int x,int y,const std::string& str,const pixel& pix)
		{
			if(!this->usable())
				Darwin_Error("Use of not available object.");
			if(x<0||y<0||x>this->get_width()-1||y>this->get_height()-1||x+str.size()>this->get_width())
				Darwin_Warning("Out of range.");
			pixel p=pix;
			for(int i=0;i<str.size();++i)
			{
				p.set_char(str.at(i));
				this->draw_pixel(x+i,y,p);
			}
		}
		virtual void draw_picture(int col,int row,const drawable& pic) {
			if(!this->usable()||!pic.usable())
				Darwin_Error("Use of not available object.");
			if(col<0||row<0||col>this->get_width()-1||row>this->get_height()-1)
				Darwin_Warning("Out of range.");
			int y0(row>=0?row:0),y1(row>=0?0:-row);
			while(y0<this->get_height()&&y1<pic.get_height()) {
				int x0(col>=0?col:0),x1(col>=0?0:-col);
				while(x0<this->get_width()&&x1<pic.get_width()) {
					this->draw_pixel(x0,y0,pic.get_pixel(x1,y1));
					++x0;
					++x1;
				}
				++y0;
				++y1;
			}
		}
	};
	double drawable::draw_line_precision=1.5;
}<|endoftext|>