OSDN Git Service

まだコンパイルも通らないけどとりあえず最初のコミット…
authorcaprice <caprice@users.sourceforge.jp>
Tue, 31 Dec 2013 05:10:46 +0000 (14:10 +0900)
committercaprice <caprice@users.sourceforge.jp>
Tue, 31 Dec 2013 05:10:46 +0000 (14:10 +0900)
13 files changed:
.gitignore [new file with mode: 0644]
doc/README.txt [new file with mode: 0644]
include/GikoMona.hpp [new file with mode: 0644]
include/config.hpp [new file with mode: 0644]
include/database.hpp [new file with mode: 0644]
include/extension.hpp [new file with mode: 0644]
include/model.hpp [new file with mode: 0644]
include/query.hpp [new file with mode: 0644]
include/string.hpp [new file with mode: 0644]
src/GikoMona.cpp [new file with mode: 0644]
src/model.cpp [new file with mode: 0644]
test/query.cpp [new file with mode: 0644]
test/test.cpp [new file with mode: 0644]

diff --git a/.gitignore b/.gitignore
new file mode 100644 (file)
index 0000000..3428565
--- /dev/null
@@ -0,0 +1,240 @@
+# Created by http://gitignore.io
+
+### Linux ###
+.*
+!.gitignore
+!.git*
+*~
+
+### OSX ###
+.DS_Store
+.AppleDouble
+.LSOverride
+Icon
+
+# Thumbnails
+._*
+
+# Files that might appear on external disk
+.Spotlight-V100
+.Trashes
+
+### Windows ###
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+### C++ ###
+# Compiled Object files
+*.slo
+*.lo
+*.o
+
+# Compiled Dynamic libraries
+*.so
+*.dylib
+
+# Compiled Static libraries
+*.lai
+*.la
+*.a
+
+### Xcode ###
+*.xcodeproj/*
+
+### Eclipse ###
+*.pydevproject
+.project
+.metadata
+bin/**
+tmp/**
+tmp/**/*
+*.tmp
+*.bak
+*.swp
+*~.nib
+local.properties
+.classpath
+.settings/
+.loadpath
+
+# External tool builders
+.externalToolBuilders/
+
+# Locally stored "Eclipse launch configurations"
+*.launch
+
+# CDT-specific
+.cproject
+
+# PDT-specific
+.buildpath
+
+### VisualStudio ###
+## Ignore Visual Studio temporary files, build results, and
+## files generated by popular Visual Studio add-ons.
+
+# User-specific files
+*.suo
+*.user
+*.sln.docstates
+
+# Build results
+
+[Dd]ebug/
+[Dd]ebugPublic/
+[Rr]elease/
+x64/
+#build/
+[Bb]in/
+[Oo]bj/
+
+# MSTest test Results
+[Tt]est[Rr]esult*/
+[Bb]uild[Ll]og.*
+
+*_i.c
+*_p.c
+*.ilk
+*.meta
+*.obj
+*.pch
+*.pdb
+*.pgc
+*.pgd
+*.rsp
+*.sbr
+*.tlb
+*.tli
+*.tlh
+*.tmp
+*.tmp_proj
+*.log
+*.vspscc
+*.vssscc
+.builds
+*.pidb
+*.log
+*.scc
+
+# Visual C++ cache files
+ipch/
+*.aps
+*.ncb
+*.opensdf
+*.sdf
+*.cachefile
+
+# Visual Studio profiler
+*.psess
+*.vsp
+*.vspx
+
+# Guidance Automation Toolkit
+*.gpState
+
+# ReSharper is a .NET coding add-in
+_ReSharper*/
+*.[Rr]e[Ss]harper
+
+# TeamCity is a build add-in
+_TeamCity*
+
+# DotCover is a Code Coverage Tool
+*.dotCover
+
+# NCrunch
+*.ncrunch*
+.*crunch*.local.xml
+
+# Installshield output folder
+[Ee]xpress/
+
+# DocProject is a documentation generator add-in
+DocProject/buildhelp/
+DocProject/Help/*.HxT
+DocProject/Help/*.HxC
+DocProject/Help/*.hhc
+DocProject/Help/*.hhk
+DocProject/Help/*.hhp
+DocProject/Help/Html2
+DocProject/Help/html
+
+# Click-Once directory
+publish/
+
+# Publish Web Output
+*.Publish.xml
+*.pubxml
+
+# NuGet Packages Directory
+## TODO: If you have NuGet Package Restore enabled, uncomment the next line
+#packages/
+
+# Windows Azure Build Output
+csx
+*.build.csdef
+
+# Windows Store app package directory
+AppPackages/
+
+# Others
+sql/
+*.Cache
+ClientBin/
+[Ss]tyle[Cc]op.*
+~$*
+*~
+*.dbmdl
+*.[Pp]ublish.xml
+*.pfx
+*.publishsettings
+
+# RIA/Silverlight projects
+Generated_Code/
+
+# Backup & report files from converting an old project file to a newer
+# Visual Studio version. Backup files are not needed, because we have git ;-)
+_UpgradeReport_Files/
+Backup*/
+UpgradeLog*.XML
+UpgradeLog*.htm
+
+# SQL Server files
+App_Data/*.mdf
+App_Data/*.ldf
+
+# Business Intelligence projects
+*.rdl.data
+*.bim.layout
+*.bim_*.settings
+
+# =========================
+# Windows detritus
+# =========================
+
+# Windows image file caches
+Thumbs.db
+ehthumbs.db
+
+# Folder config file
+Desktop.ini
+
+# Recycle Bin used on file shares
+$RECYCLE.BIN/
+
+# Mac crap
+.DS_Store
+
+### CMake ###
+CMakeCache.txt
+CMakeFiles
+Makefile
+cmake_install.cmake
+install_manifest.txt
diff --git a/doc/README.txt b/doc/README.txt
new file mode 100644 (file)
index 0000000..928b8f5
--- /dev/null
@@ -0,0 +1,64 @@
+*ビルド方法
+libcore単体でビルドする場合は、ターミナル上で、
+    cd $(LIBCORE_ROOT)
+    make standalone
+    
+pnuts/gikomonaとともにビルドする場合は、
+$(GIKOMONA_ROOT)/build/HOW-TO-BUILD.txt を参照してください。
+なお、このビルドによって生成されるライブラリは必ず静的ライブラリ (*.a, *.lib) になります。
+動的ライブラリ (*.so, *.dylib, *.dll) の生成法は用意されていません。
+
+*テスト方法
+libcore単体でテストするときは、ターミナル上で、
+    cd $(LIBCORE_ROOT)
+    make test
+
+pnuts/gikomonaとともにテストする際には、自動でlibcoreのテストも行われるので、
+いちいち指定してテストする必要はありません。
+
+*ビルド後の pnuts/gikomona のフォルダの構造
+
+@ Windows {
+$(GIKOMONA_ROOT)/
+    resource/
+        icon/
+            ...
+        pnuts.xrc   (pnuts only)
+        gikomona.xrc(gikomona only)
+
+    doc/
+        README.html
+        LICENSE.html
+
+    pnuts.exe       (pnuts only)
+    gikomona.exe    (gikomona only)
+    update-utility.exe
+
+    thread/
+    board-list/
+
+    history.db
+    config.xml
+    favorite.xml
+}
+
+@ Linux {
+$(GIKOMONA_ROOT)/
+    resource/
+    doc/
+    
+}
+
+@ Mac OS X {
+$(GIKOMONA_ROOT)/
+    Contents/
+        Frameworks/
+        Resources/
+        MacOS/
+            pnuts
+            gikomona
+            update-utility
+        Info.plist
+        version.plist
+        PkgInfo
+}
diff --git a/include/GikoMona.hpp b/include/GikoMona.hpp
new file mode 100644 (file)
index 0000000..377c047
--- /dev/null
@@ -0,0 +1,31 @@
+#ifndef GIKOMONA_CORE_GIKOMONA_HPP
+#define GIKOMONA_CORE_GIKOMONA_HPP
+
+#include <type_traits>
+
+#include <boost/filesystem/path.hpp>
+
+#include "string.hpp"
+
+namespace monazilla { namespace GikoMona {
+
+namespace core {
+
+bool init_core();
+void done_core() noexcept ;
+void optimize_database(const boost::filesystem::path& db_path);
+
+extern void *enabler;
+
+}
+
+} }
+
+/// @breif libbbs で string に関わる typedef を抑制する。@link libbbs/settings.hpp @endlink を見よ。
+#define LIBBBS_PRAGMA_USED_BY_GIKOMONA
+#define LIBSQLITEXX_PRAGMA_USED_BY_GIKOMONA
+
+#define PROJECT_NAME "GikoMona project, powered by monazilla.org."
+#define PROJECT_URL "http://sourceforge.jp/projects/gikomona/"
+
+#endif
diff --git a/include/config.hpp b/include/config.hpp
new file mode 100644 (file)
index 0000000..efe9338
--- /dev/null
@@ -0,0 +1,70 @@
+#ifndef GIKOMONA_CORE_CONFIG_HPP
+#define GIKOMONA_CORE_CONFIG_HPP
+
+#include <unordered_map>
+
+#include <boost/fusion/include/vector.hpp>
+#include <boost/mpl/bool.hpp>
+#include <boost/any.hpp>
+
+#include "GikoMona.hpp"
+#include "query.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+class config final : public query {
+public:
+    typedef config self_type;
+    typedef query base_type;
+
+    config() noexcept {}
+    ~config() {}
+
+    /**
+     *  @breif query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @retval true insert 操作が成功した
+     *          false insert 操作に失敗した
+     *  @param[in] into どのテーブルのどの要素に対し insertion query を実行するかを記述する。
+     *                  記述の仕方は次の通り:(テーブル名)/[(サブテーブル名)/]*(要素名)
+     *  @param[in] value テーブルに対して代入する値を記述する。
+     *  @note さらに、最大限 multi-threading な環境を考慮しなければならない。
+     */
+    template <typename T, typename U, typename ...ValueType>
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T, U, ValueType...>& value) {}
+    
+    template <typename T>
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T>& value) {}
+    
+    template <typename T>
+    bool insert(const mona_string& into,
+                const boost::any& value,
+                base_type::enable_if_T_is_U<T, boost::any>*& = enabler) {}
+    
+    /**
+     *  @brief query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @return 引数で指定したテーブルの要素から、テンプレートで指定した方に変換された値が返される。
+     */
+    template <typename T>
+    T select(const mona_string& column,
+             const mona_string& from) const noexcept {}
+    
+    template <typename T>
+    boost::any select(const mona_string& column,
+                      const mona_string& from,
+                      base_type::enable_if_T_is_U<T, boost::any>*& = enabler) const noexcept {}
+    
+    template <typename ...ValueType>
+    boost::fusion::vector<ValueType...>
+    select_all(const mona_string& from) const noexcept {}
+private:
+    std::unordered_map<mona_string, boost::any> map;
+};
+
+template <>
+struct is_satisfied_with_query_concept<config> : public boost::mpl::true_ {};
+
+} } }
+
+#endif
diff --git a/include/database.hpp b/include/database.hpp
new file mode 100644 (file)
index 0000000..161cd5a
--- /dev/null
@@ -0,0 +1,200 @@
+#ifndef GIKOMONA_CORE_DATABASE_HPP
+#define GIKOMONA_CORE_DATABASE_HPP
+
+#include <memory>
+
+#include <sqlite3.h>
+
+#include <boost/system/system_error.hpp>
+#include <boost/filesystem.hpp>
+#include <boost/fusion/include/vector.hpp>
+
+#include "GikoMona.hpp"
+#include "query.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+class database;
+
+template <typename ReturnTypeList, typename ArgTypeList>
+class sql_executer final {
+    friend database;
+    
+    typedef ReturnTypeList result_type;
+    typedef ArgTypeList arg_type;
+    typedef ::sqlite3 *db_object_type;
+    typedef ::sqlite3_stmt *sql_stmt_type;
+
+    const ::sqlite3_stmt *stmt;
+        
+    sql_executer(db_object_type db,
+                 const sql_stmt_type stmt,
+                 boost::system::error_code& ec) noexcept {}
+    ~sql_executer() {}
+        
+public:
+    typename <typename T>
+    void bind(const mona_string& param_name, const T& val) {}
+    result_type operator()() {}
+};
+
+class database final : public query {
+public:
+    typedef ::sqlite3 *db_object_type;
+    typedef database self_type;
+    typedef query base_type;
+
+    database() : database("") {}
+    
+    /// @brief すでに存在するデータベースファイルを開く。まだファイルが存在しない場合は、新たに作成する。
+    database(const boost::filesystem::path& db_path) {
+        if(db_path.empty()) {
+            is_opened_db = false;
+        } else {
+            if(!boost::filesystem::exists(db_path)) {
+                create(db_path);
+            } else {
+                open(db_path);
+            }
+        }
+    }
+    
+    database(const self_type& other) = delete;
+    
+    ~database() { close(); }
+    
+    /// @brief 受け取った SQL を実行する。ただし、実行できる文は引数と返り値が共に無いものに限られる。
+    /**
+     * @return SQL が正常に実行された場合は true、それ以外の場合は false を返す。
+     * @note この関数は SQL の実行結果を返さない。したがって、実行結果が必要な場合は、
+     *       compile_sql() を用いるべきである。
+     */
+    bool run_sql(const mona_string& sql) {
+        return (::sqlite3_exec(db, sql.c_str(), NULL, NULL, NULL) != SQLITE_OK);
+    }
+    
+    /// @brief 受け取った SQL を sql_executer へと変換して返す。
+    /**
+     * @note 返された sql_executer を実行することで、ここで渡した SQL の実行結果を得ることができる。
+     *       実行した SQL の実行結果が不要である (あるいはそもそも実行結果が無い) 場合は、
+     *       run_sql() を用いるべきである。
+     */
+    template <typename ...T>
+    sql_executer<T...> compile_sql(const mona_string& sql) {
+        /**
+         *  @note sqlite3_prepare(db,zSql,nByte,ppStmtpzTail) で nByte に "sql.length() + 1" を
+         *        渡すのはなぜか?詳しくは次を参照 : http://www.sqlite.org/c3ref/prepare.html
+         *        ページ中程、"If the nByte argument is less than zero, ~" の先、関係あるところを
+         *        かいつまんで訳すと「もしも関数を呼び出した側が、zSql が NULL-terminated な文字列
+         *        (\0、\u0 が終端であるような文字列) であることを知っている場合、nByte に文字列のバイト長
+         *        (ただしここで言うバイト長には終端文字である\0または\u0も「含んだ」ものである) を渡すことで、
+         *        若干パフォーマンスが上がる」という記述がある。C++のstd::string::length()/size()は文字列の
+         *        終端文字を除いた長さを返すので、わざわざ1を加えている。
+         */
+        int result = ::sqlite3_prepare_v2(db.get_sqlite3_obj(),
+                                          sql.c_str(),
+                                          sql.length() + 1,
+                                          &stmt,
+                                          NULL);
+        if (result != ::SQLITE_OK && !stmt) {
+            
+        } else {
+            return sql_executer<T...>(sql);
+        }
+    }
+    
+    /**
+     *  @brief
+     *  @retval true insert 操作が成功した
+     *          false insert 操作に失敗した
+     *  @param[in] into どのテーブルのどの要素に対し insertion query を実行するかを記述する。
+     *                  記述の仕方は次の通り:(テーブル名)/[(サブテーブル名)/]*(要素名)
+     *  @param[in] value テーブルに対して代入する値を記述する。
+     *  @note さらに、最大限 multi-threading な環境を考慮しなければならない。
+     */
+    template <typename T, typename U, typename ...ValueType>
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T, U, ValueType...>& value) {}
+    
+    template <typename T>
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T>& value) {}
+    
+    template <typename T>
+    bool insert(const mona_string& into,
+                const boost::any& value,
+                base_type::enable_if_T_is_U<T, boost::any>*& = enabler) {}
+    
+    /**
+     *  @brief query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @return 引数で指定したテーブルの要素から、テンプレートで指定した方に変換された値が返される。
+     */
+    template <typename T>
+    T select(const mona_string& column,
+             const mona_string& from) const noexcept {}
+    
+    template <typename T>
+    boost::any select(const mona_string& column,
+                      const mona_string& from,
+                      base_type::enable_if_T_is_U<T, boost::any>*& = enabler) const noexcept {}
+    
+    template <typename ...ValueType>
+    boost::fusion::vector<ValueType...>
+    select_all(const mona_string& from) const noexcept {}
+
+    /// @brief データベースの最適化を行う。
+    /**
+     * @note 内部では以下のコマンドが実行され、データベースの不要データの圧縮と再構築が行われる。
+     *       VACUUM; REINDEX;
+     */
+    void optimize() {
+        run_sql("VACUUM;");
+        run_sql("REINDEX;");
+    }
+    
+    /// @brief すでに存在するデータベースファイルを開く。
+    bool open(const boost::filesystem::path& db_path) {
+        if (is_opened_db) {
+            close();
+        } else if (!boost::filesystem::exists(db_path)) {
+            is_opened_db = false;
+            return is_opened_db;
+        }
+        
+        is_opened_db = (::sqlite3_open(db_path.c_str(), &db) != SQLITE_OK);
+        
+        return is_opened_db;
+    }
+    
+    /// @brief まだ存在しないデータベースファイルを新たに作成する。
+    /**
+     * @retval true ファイルが存在せず、かつ新たにデータベースファイルを作成することに成功した
+     *         false ファイルが存在する、あるいは存在しないが新規データベースファイルの作成に失敗した
+     */
+    bool create(const boost::filesystem::path& db_path) {
+        if(boost::filesystem::exists(db_path)) {
+            return false;
+        }
+        
+        return open(db_path);
+    }
+    
+    /// @brief 開いていたデータベースファイルを閉じる。
+    void close() noexcept {
+        ::sqlite3_close(db);
+    }
+
+private:
+    db_object_type db;
+    bool is_opened_db;
+    
+    void begin_sql_statement() { run_sql("BEGIN;"); }
+    void end_sql_statement() { run_sql("END;"); }
+};
+
+template <>
+struct is_responsible_to_query<database> : public boost::mpl::true_ {};
+
+} } }
+
+#endif
diff --git a/include/extension.hpp b/include/extension.hpp
new file mode 100644 (file)
index 0000000..b5443d1
--- /dev/null
@@ -0,0 +1,69 @@
+#ifndef GIKOMONA_CORE_EXTENSION_HPP
+#define GIKOMONA_CORE_EXTENSION_HPP
+
+#include <unordered_map>
+#include <functional>
+
+#include <boost/filesystem.hpp>
+
+#include <lua.hpp>
+
+#include "GikoMona.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+class extension final {
+public:
+    typedef mona_string extension_id_type;
+    const static extension_id_type inavailable_id = "";
+    typedef extension self_type;
+    typedef ::lua_State* vm_type;
+    
+    typedef std::function<void (const vm_type*)> initialize_handler_type;
+    typedef std::function<void () noexcept> finalize_handler_type;
+    
+    struct information {
+        const mona_string name;
+        const vm_type lua_obj;
+        bool is_packaged;
+    };
+    
+    extension() {
+        if(boost::filesystem::exists(unpacked_extension_path)) {
+            boost::filesystem::create_directories(unpacked_extension_path);
+        }
+        instance = this;
+    }
+    
+    ~extension() {
+        boost::system::error_code dummy;
+        boost::filesystem::remove_all(unpacked_extension_path, dummy);
+    }
+    
+    static extension *get_instance() { return instance; }
+    void add_initialize_handler(const initialize_handler_type& handler) { init_func = handler; }
+    void add_finalize_handler(const finalize_handler_type& handler) { fin_func = handler; }
+    
+    extension_id_type load(const boost::filesystem::path& ext_path) {
+        if(ext_path.get_ext() == "gep") {
+            // エクステンションはパッケージ化されている
+            
+            information ext_info = {"", luaL_newstate(), true};
+            
+            extension_table[ext_info.name] = ext_info;
+        } else if(ext_path.get_ext() == "gex") {
+            // エクステンションは単体のファイルである
+        } else { return inavailable_id; }
+    }
+private:
+    std::unordered_map<extension_id_type, information> extension_table;
+    const boost::filesystem::path unpacked_extension_path = ".tmp/unpacked-ext";
+    static self_type *instance;
+    
+    initialize_handler_type init_func;
+    finalize_handler_type fin_func;
+};
+
+} } }
+
+#endif
diff --git a/include/model.hpp b/include/model.hpp
new file mode 100644 (file)
index 0000000..432c6d2
--- /dev/null
@@ -0,0 +1,101 @@
+#ifndef GIKOMONA_CORE_MODEL_HPP
+#define GIKOMONA_CORE_MODEL_HPP
+
+#include <unistd.h>
+#include <memory>
+
+#include <boost/fusion/include/vector.hpp>
+#include <boost/any.hpp>
+#include <boost/lockfree/queue.hpp>
+#include <boost/filesystem/path.hpp>
+
+#include "GikoMona.hpp"
+#include "query.hpp"
+#include "database.hpp"
+#include "config.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+class model : public query {
+public:
+    typedef model self_type;
+    typedef query base_type;
+    typedef boost::fusion::vector<mona_string, boost::any> inserted_value_type;
+    typedef inserted_value_type *inserted_value_triv_copyable_type;
+    
+    model() noexcept;
+    ~model();
+    
+    static
+    model *get_instance() {
+        return instance;
+    }
+
+    /**
+     *  @breif query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @retval true insert 操作が成功した
+     *          false insert 操作に失敗した
+     *  @param[in] into どのテーブルのどの要素に対し insertion query を実行するかを記述する。
+     *                  記述の仕方は次の通り:(テーブル名)/[(サブテーブル名)/]*(要素名)
+     *  @param[in] value テーブルに対して代入する値を記述する。
+     *  @note さらに、最大限 multi-threading な環境を考慮しなければならない。
+     */
+    template <typename T, typename U, typename ...ValueType>
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T, U, ValueType...>& value) {}
+    
+    template <typename T>
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T>& value) {}
+    
+    template <typename T>
+    virtual
+    bool insert(const mona_string& into,
+                const boost::any& value,
+                base_type::enable_if_T_is_U<T, boost::any>*& = enabler) {}
+    
+    /**
+     *  @brief query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @return 引数で指定したテーブルの要素から、テンプレートで指定した方に変換された値が返される。
+     */
+    template <typename T>
+    T select(const mona_string& column,
+             const mona_string& from) const noexcept {}
+    
+    template <typename T>
+    boost::any select(const mona_string& column,
+                      const mona_string& from,
+                      base_type::enable_if_T_is_U<T, boost::any>*& = enabler) const noexcept {}
+    
+    template <typename ...ValueType>
+    boost::fusion::vector<ValueType...>
+    select_all(const mona_string& from) const noexcept {}
+
+    void exec_inserted_query();
+    bool load_file(const boost::filesystem::path& file_path,
+                   const mona_string& loaded_table_name);
+    bool save_to_file(const boost::filesystem::path& file_path,
+                      const mona_string& saved_table_name);
+
+private:
+    void get_object_expressing_into_path_in_query(const mona_string& src,
+                                                  const boost::any& value);
+    void analyze_into_path_in_query(const mona_string& into_path_in_query);
+    
+    static self_type *instance;
+    struct model_pimpl;
+    
+    std::shared_ptr<model_pimpl> pimpl;
+    boost::lockfree::queue<inserted_value_triv_copyable_type> query_queue;
+};
+
+template <>
+struct is_satisfied_with_query_concept<model> : public boost::mpl::true_ {};
+
+void exec_insert_query() {
+    model::get_instance()->exec_inserted_query();
+}
+
+} } }
+
+#endif 
diff --git a/include/query.hpp b/include/query.hpp
new file mode 100644 (file)
index 0000000..b2762e4
--- /dev/null
@@ -0,0 +1,78 @@
+#ifndef GIKOMONA_CORE_QUERY_HPP_INCLUDED
+#define GIKOMONA_CORE_QUERY_HPP_INCLUDED
+
+#include <boost/fusion/include/vector.hpp>
+#include <boost/mpl/bool.hpp>
+
+#include "GikoMona.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+   
+template <typename T>
+struct is_satisfied_with_query_concept : public boost::mpl::false_ {};
+
+/**
+ *  @brief このクラスは query_concept を満たす全てのクラスが継承しなければならない。
+ */
+class query {
+public:
+    /// query_concept を満たすクラスは自分自身の型を self_type として表明しなければならない。
+    typedef query self_type;
+    
+    template <typename T, typename U>
+    using enable_if_T_is_U = typename std::enable_if<std::is_same<T, U>::value>::type;
+    
+    /// query_concept を満たすクラスは外部から構築可能でなければならない。
+    query() {}
+    /// query_concept を満たすクラスは外部から解体可能でなければならない。
+    ~query() {}
+    
+    /**
+     *  @breif query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @retval true insert 操作が成功した
+     *          false insert 操作に失敗した
+     *  @param[in] into どのテーブルのどの要素に対し insertion query を実行するかを記述する。
+     *                  記述の仕方は次の通り:(テーブル名)/[(サブテーブル名)/]*(要素名)
+     *  @param[in] value テーブルに対して代入する値を記述する。
+     *  @note さらに、最大限 multi-threading な環境を考慮しなければならない。
+     */
+    template <typename T, typename U, typename ...ValueType>
+    virtual
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T, U, ValueType...>& value) override {}
+    
+    template <typename T>
+    virtual
+    bool insert(const mona_string& into,
+                const boost::fusion::vector<T>& value) override {}
+    
+    template <typename T>
+    virtual
+    bool insert(const mona_string& into,
+                const boost::any& value,
+                enable_if_T_is_U<T, boost::any>*& = enabler) override {}
+    
+    /**
+     *  @brief query_concept を満たすクラスはこの関数と同じ型、名前を持つ関数を持っていなければならない。
+     *  @return 引数で指定したテーブルの要素から、テンプレートで指定した方に変換された値が返される。
+     */
+    template <typename T>
+    virtual
+    T select(const mona_string& column,
+             const mona_string& from) const noexcept override {}
+    
+    template <typename T>
+    virtual
+    boost::any select(const mona_string& column,
+                      const mona_string& from,
+                      enable_if_T_is_U<T, boost::any>*& = enabler) const noexcept override {}
+    
+    template <typename ...ValueType>
+    virtual
+    boost::fusion::vector<ValueType...>
+    select_all(const mona_string& from) const noexcept override {}
+};
+
+} } }
+
+#endif
\ No newline at end of file
diff --git a/include/string.hpp b/include/string.hpp
new file mode 100644 (file)
index 0000000..d76fb56
--- /dev/null
@@ -0,0 +1,115 @@
+//
+//  string.hpp
+//  core
+//
+//  Created by caprice on 2013/08/05.
+//
+
+#ifndef GIKOMONA_CORE_STRING_HPP
+#define GIKOMONA_CORE_STRING_HPP
+
+#include <string>
+
+#include <boost/utility/string_ref.hpp>
+
+#include <wx/string.h>
+
+// このコンパイラの char16_t、char32_t を UTF-16/32 を表現するか?
+#if !(__STDC_UTF_16__)
+    #error "[GikoMona.libcore @pp-time] : sorry, this compiler does not support utf-16."
+#endif
+
+#if !(__STDC_UTF_32__)
+    #error "[GikoMona.libcore @pp-time] : sorry, this compiler does not support utf-32."
+#endif
+
+namespace monazilla { namespace GikoMona {
+    
+template <typename CharT>
+using basic_string_ref = boost::basic_string_ref<CharT, std::char_traits<CharT>>;
+    
+typedef char u8_char;
+typedef std::basic_string<u8_char> u8_string;
+typedef basic_string_ref<u8_char> u8_string_ref;
+    
+typedef u8_string mona_string;
+typedef basic_string_ref<u8_char> mona_string_ref;
+
+typedef char sjis_char;
+typedef std::basic_string<sjis_char> sjis_string;
+typedef basic_string_ref<sjis_char> sjis_string_ref;
+
+// to...
+u8_string convert_to_u8(const mona_string_ref& src);
+sjis_string convert_to_sjis(const mona_string_ref& src);
+
+std::u16string convert_to_u16(const mona_string_ref& src);
+std::u32string convert_to_u32(const mona_string_ref& src);
+std::string convert_to_std_str(const mona_string_ref& src);
+std::wstring convert_to_std_wstr(const mona_string_ref& src);
+
+wxString convert_to_wx(const mona_string_ref& src) {
+    return wxString(src.data());
+}
+
+// from...
+mona_string convert_from(const u8_string_ref& src);
+mona_string convert_from(const sjis_string_ref& src);
+    
+mona_string convert_from(const basic_string_ref<char16_t>& src);
+mona_string convert_from(const basic_string_ref<char32_t>& src);
+mona_string convert_from(const basic_string_ref<std::string::value_type>& src);
+mona_string convert_from(const basic_string_ref<std::wstring::value_type>& src);
+    
+mona_string convert_from(const wxString& src);
+
+// converter
+template <typename Other>
+class converter_impl {
+public:
+    typedef typename Other::value_type char_type;
+    typedef basic_string_ref<char_type> string_ref_type;
+    
+    mona_string from(const string_ref_type& src) const { return convert_from(src); }
+};
+
+template <typename Other, bool U8_is_Sjis = std::is_same<u8_string, sjis_string>::value>
+class converter {};
+
+template <>
+class converter <u8_string, false>  : public converter_impl<u8_string> {
+public:
+    char_type to(const mona_string_ref& src) const { return convert_to_u8(src); }
+};
+
+template <>
+class converter <sjis_string, false> : public converter_impl<sjis_string> {
+public:
+    char_type to(const mona_string_ref& src) const { return convert_to_sjis(src); }
+};
+
+template <>
+class converter <u8_string, true> : public converter_impl<u8_string> {
+public:
+    char_type to_u8(const mona_string_ref& src) const { return convert_to_u8(src); }
+    char_type to_sjis(const mona_string_ref& src) const { return convert_to_sjis(src); }
+    char_type to(const mona_string_ref&) const {
+        static_assert(false, "std::is_same<u8_string, sjis_string>::value == true.");
+    }
+};
+
+template <bool Value>
+class converter <std::u16string, Value> : public converter_impl<std::u16string> {
+public:
+    char_type to(const mona_string_ref& src) const { return convert_to_u16(src); }
+};
+
+template <bool Value>
+class converter <std::u32string, Value> : public converter_impl<std::u32string> {
+public:
+    char_type to(const mona_string_ref& src) const { return convert_to_u32(src); }
+};
+
+}
+}
+#endif
diff --git a/src/GikoMona.cpp b/src/GikoMona.cpp
new file mode 100644 (file)
index 0000000..74d450c
--- /dev/null
@@ -0,0 +1,58 @@
+#include <wx/xrc/xmlres.h>
+#include <exception>
+
+#include <boost/filesystem.hpp>
+
+#include <wx/msgdlg.h>
+
+#include "GikoMona.hpp"
+#include "database.hpp"
+#include "model.hpp"
+
+#include "extension.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+namespace {
+
+model *app_model;
+extension *app_extension;
+
+}
+
+bool init_core() {
+    wxXmlResource::Get()->InitAllHandlers();
+    wxXmlResource::Get()->LoadAllFiles(wxT("resouce"));
+    
+    boost::system::error_code reason;
+    if(!boost::filesystem::create_directory(".tmp", reason) || reason) {
+        // log
+        std::terminate();
+    }
+    
+    app_model = new model;
+    app_extension = new extension;
+    
+    return true;
+}
+
+void done_core() noexcept {
+    delete app_extension;
+    delete app_model;
+
+    optimize_database("./history.db");
+}
+
+void optimize_database(const boost::filesystem::path& db_path) {
+    database db(db_path);
+    /*
+    auto value = db.compile(u8"SELECT * FROM file-information;");
+    value.execute();
+    value.get_result<int>();
+    */
+    if(db.select<int>("count-of-deleting-values", "file-infomation") >= 1000) {
+        db.optimize();
+    }
+}
+
+} } }
diff --git a/src/model.cpp b/src/model.cpp
new file mode 100644 (file)
index 0000000..de24846
--- /dev/null
@@ -0,0 +1,72 @@
+#include <boost/filesystem/path.hpp>
+
+#include "model.hpp"
+
+namespace monazilla { namespace GikoMona { namespace core {
+
+struct model::model_pimpl {
+    // session/tab-window
+    /* 構造
+     * bbs-name(TEXT)|board-name(TEXT)|thread-id(TEXT)|thread-name(TEXT)|is_fixed(INTEGAR)
+     */
+    database tab_db;
+    // session/history
+    /* 構造
+     * date(TEXT)|bbs-name(TEXT)|board-name(TEXT)|thread-id(TEXT)|thread-name(TEXT)
+     */
+    database history_db;
+    // application
+    config app_config;
+};
+
+model::model() noexcept {
+    instance = this;
+    pimpl = std::make_shared<model_pimpl>();
+    
+    auto config_path = pimpl->app_config.select<boost::filesystem::path>("config", "file-path");
+    
+    pimpl->tab_db.create(config_path);
+}
+
+model::~model() {}
+
+bool model::load_file(const boost::filesystem::path& file_path,
+                      const mona_string& loaded_table_name) {
+    if(!boost::filesystem::exists(file_path)) {
+        return false;
+    }
+    
+    return true;
+}
+
+void model::exec_inserted_query()  {
+    inserted_value_triv_copyable_type q;
+    if(query_queue.empty()) {
+        return;
+    } else {
+        query_queue.pop(q);
+    }
+    
+    mona_string into;
+    boost::any value;
+    std::tie(*q, into, value);
+    
+    analyze_into_path_in_query(into);
+
+    if(into.find_first_of("application", 0, into.length()) != mona_string::npos) {
+        // application/*
+    } else if (into.find_first_of("session", 0, into.length()) != mona_string::npos) {
+        // session/*
+        if(into.find("history", 0, into.length()) != mona_string::npos) {
+            ;
+        } else if(into.find("tab-window") != mona_string::npos != mona_string::npos) {
+            pimpl->tab_db.insert(into, tup);
+        }
+    } else if(into.find_first_of("extension", 0, into.length()) != mona_string::npos) {
+        // extension/*
+    } else {
+        // ?
+    }
+}
+
+} } }
diff --git a/test/query.cpp b/test/query.cpp
new file mode 100644 (file)
index 0000000..d0a8acb
--- /dev/null
@@ -0,0 +1,18 @@
+#ifndef GIKOMONA_CORE_TEST_QUERY_HPP_INCLUDED
+#define GIKOMONA_CORE_TEST_QUERY_HPP_INCLUDED
+
+#include <type_traits>
+
+#include <boost/test/test_unit.hpp>
+
+#include "../include/query.hpp"
+
+BOOST_AUTO_TEST_SUITE(CORE)
+BOOST_AUTO_TEST_SUITE(query)
+
+namespace gm_core = monazilla::GikoMona::core;
+
+BOOST_AUTO_TEST_SUITE_END // query
+BOOST_AUTO_TEST_SUITE_END // CORE
+
+#endif // GIKOMONA_CORE_TEST_QUERY_HPP_INCLUDED
diff --git a/test/test.cpp b/test/test.cpp
new file mode 100644 (file)
index 0000000..789e125
--- /dev/null
@@ -0,0 +1,11 @@
+#define BOOST_TEST_MODULE gikomona_core_test_module
+#include <boost/test/unit_test.hpp>
+
+/**
+ * @note ここでunittestを行う。
+ *       テストされる項目は以下の通り:
+ *          class query : 
+ *              trivially-copyable な型であるかどうか
+ *              query は selecter の呼び出し順序に従って、query-tree を形成できているか
+ *
+ */