diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_string.cpp b/modules/j/base/test/src/ut_string.cpp new file mode 100644 index 0000000..6882524 --- /dev/null +++ b/modules/j/base/test/src/ut_string.cpp @@ -0,0 +1,318 @@ +#include +#include +#include + +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class StringTest : public TestCase +{ +public: + StringTest() {} + ~StringTest() {} + void setUp() {} + void tearDown() {} + + void testOpPlusEq() + { + String str = "Hello"; + str += " World!"; + assertEquals("Hello World!", str); + } + + void testCharAt() + { + // 0123456789 + String str = "Hello World!"; + + char c0 = str.charAt(0); + assertEquals('H', c0); + + char c3 = str.charAt(3); + assertEquals('l', c3); + + char c5 = str.charAt(5); + assertEquals(' ', c5); + + try + { + str.charAt(30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + + try + { + str.charAt(-1); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + } + + void testSubString() + { + // 0123456789 + String str = "Hello World!"; + String subStr = str.substring(2, 5); + assertEquals("llo", subStr); + + try + { + str.substring(-1, 5); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + + try + { + str.substring(5, 3); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("5") >= 0); + } + + try + { + str.substring(5, 30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + } + + void testContains() + { + // 0123456789 + String str = "Hello World!"; + bool isContains = str.contains("World"); + assertTrue(isContains); + + isContains = str.contains("xyz"); + assertFalse(isContains); + } + + void testReplace() + { + // 0123456789 + String str = "Hello World!"; + + String str2 = str.replace('l', 'x'); + assertEquals("Hexxo Worxd!", str2); + + String str3 = str.replace("l", "x"); + assertEquals("Hexlo World!", str3); + + String strX = "Tonarino Kyakuha Yoku Kakikuu Kyakuda!"; + String str4 = strX.replace("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu Kyakuda!", str4); + + String str5 = strX.replaceAll("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu AAAAAAAda!", str5); + + // 対象なし + String strNo1 = str.replace('z', 'v'); + assertEquals("Hello World!", strNo1); + + String strNo2 = str.replace("ZZA", "XYZ"); + assertEquals("Hello World!", strNo2); + } + + void testSplit() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + std::vector result = str.split("::"); + const char *exp[] = { + "fe00", + "1234", + "xxq3", + "dg353", + "dab3"}; + for (int idx = 0; idx < static_cast(result.size()); idx++) + { + assertEquals(exp[idx], result[idx]); + } + } + + void testStartsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.startsWith("fe00::")); + assertFalse(str.startsWith("abcd")); + + // over + assertFalse(str.startsWith("fe00::1234::xxq3::dg353::dab3::def")); + } + + void testEndsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.endsWith(":dab3")); + assertFalse(str.endsWith("abcde")); + + // over + assertFalse(str.endsWith("abc::fe00::1234::xxq3::dg353::dab3")); + } + + void testToLowerCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("abcdefghijklmn", str.toLowerCase()); + } + + void testToUpperCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("ABCDEFGHIJKLMN", str.toUpperCase()); + } + + void testTrim() + { + // 0123456789 + String str = "\t AbcdeFghijkLMN as "; + assertEquals("AbcdeFghijkLMN as", str.trim()); + + String str2 = "abcd"; + assertEquals("abcd", str2.trim()); + + String str3 = " abcd"; + assertEquals("abcd", str3.trim()); + + String str4 = "abcd "; + assertEquals("abcd", str4.trim()); + } + + void testIndexOf() + { + // 012345678901234567898 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(0, str.indexOf("")); + assertEquals(1, str.indexOf("bcd")); + assertEquals(14, str.indexOf("bcd", 5)); + } + + void testLastIndexOf() + { + // 10 20 + // 012345678901234567890 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(21, str.lastIndexOf("")); + assertEquals(14, str.lastIndexOf("bcd")); + assertEquals(1, str.lastIndexOf("bcd", 12)); + assertEquals(-1, str.lastIndexOf("XYZ", 12)); + assertEquals(-1, str.lastIndexOf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + + assertEquals(16, str.lastIndexOf('d')); + assertEquals(-1, str.lastIndexOf('=')); + assertEquals(3, str.lastIndexOf('d', 12)); + assertEquals(-1, str.lastIndexOf('=', 12)); + } + + void testEq() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + + assertTrue(str1 == str2); + assertTrue(str1 != str3); + assertTrue(str2 != str3); + + IndexOutOfBoundsException e; + assertFalse(str1.equals(e)); + } + + void testToString() + { + String str1 = "Abc"; + String str3 = "Def"; + assertEquals("Abc", str1.toString()); + assertEquals("Def", str3.toString()); + } + + void testHashCode() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + int code1 = str1.hashCode(); + int code2 = str2.hashCode(); + int code3 = str3.hashCode(); + + assertTrue(code1 == code2); + assertTrue(code1 != code3); + assertTrue(code2 != code3); + } + + void testCat() + { + String str1 = "Abc"; + String str2 = str1 + 123; + assertEquals("Abc123", str2); + + String str3 = "ABC" + str2; + assertEquals("ABCAbc123", str3); + + String str4 = 123 + str2; + assertEquals("123Abc123", str4); + + String str5 = str1 + str2; + assertEquals("AbcAbc123", str5); + } + + void testIn() + { + std::istringstream iss("ABCDEF"); + String str; + iss >> str; + assertEquals("ABCDEF", str); + } + + void suite() + { + RUN_TEST("operator+=", testOpPlusEq); + RUN_TEST("charAt", testCharAt); + RUN_TEST("substring", testSubString); + RUN_TEST("contains", testContains); + RUN_TEST("replace", testReplace); + RUN_TEST("split", testSplit); + RUN_TEST("startsWith", testStartsWith); + RUN_TEST("endsWith", testEndsWith); + RUN_TEST("toLowerCase", testToLowerCase); + RUN_TEST("toUpperCase", testToUpperCase); + RUN_TEST("trim", testTrim); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testLastIndexOf); + RUN_TEST("opEq, opNotEq", testEq); + RUN_TEST("toString", testToString); + RUN_TEST("hashCode", testHashCode); + RUN_TEST("cat", testCat); + RUN_TEST("in stream", testIn); + } +}; diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_string.cpp b/modules/j/base/test/src/ut_string.cpp new file mode 100644 index 0000000..6882524 --- /dev/null +++ b/modules/j/base/test/src/ut_string.cpp @@ -0,0 +1,318 @@ +#include +#include +#include + +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class StringTest : public TestCase +{ +public: + StringTest() {} + ~StringTest() {} + void setUp() {} + void tearDown() {} + + void testOpPlusEq() + { + String str = "Hello"; + str += " World!"; + assertEquals("Hello World!", str); + } + + void testCharAt() + { + // 0123456789 + String str = "Hello World!"; + + char c0 = str.charAt(0); + assertEquals('H', c0); + + char c3 = str.charAt(3); + assertEquals('l', c3); + + char c5 = str.charAt(5); + assertEquals(' ', c5); + + try + { + str.charAt(30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + + try + { + str.charAt(-1); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + } + + void testSubString() + { + // 0123456789 + String str = "Hello World!"; + String subStr = str.substring(2, 5); + assertEquals("llo", subStr); + + try + { + str.substring(-1, 5); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + + try + { + str.substring(5, 3); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("5") >= 0); + } + + try + { + str.substring(5, 30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + } + + void testContains() + { + // 0123456789 + String str = "Hello World!"; + bool isContains = str.contains("World"); + assertTrue(isContains); + + isContains = str.contains("xyz"); + assertFalse(isContains); + } + + void testReplace() + { + // 0123456789 + String str = "Hello World!"; + + String str2 = str.replace('l', 'x'); + assertEquals("Hexxo Worxd!", str2); + + String str3 = str.replace("l", "x"); + assertEquals("Hexlo World!", str3); + + String strX = "Tonarino Kyakuha Yoku Kakikuu Kyakuda!"; + String str4 = strX.replace("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu Kyakuda!", str4); + + String str5 = strX.replaceAll("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu AAAAAAAda!", str5); + + // 対象なし + String strNo1 = str.replace('z', 'v'); + assertEquals("Hello World!", strNo1); + + String strNo2 = str.replace("ZZA", "XYZ"); + assertEquals("Hello World!", strNo2); + } + + void testSplit() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + std::vector result = str.split("::"); + const char *exp[] = { + "fe00", + "1234", + "xxq3", + "dg353", + "dab3"}; + for (int idx = 0; idx < static_cast(result.size()); idx++) + { + assertEquals(exp[idx], result[idx]); + } + } + + void testStartsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.startsWith("fe00::")); + assertFalse(str.startsWith("abcd")); + + // over + assertFalse(str.startsWith("fe00::1234::xxq3::dg353::dab3::def")); + } + + void testEndsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.endsWith(":dab3")); + assertFalse(str.endsWith("abcde")); + + // over + assertFalse(str.endsWith("abc::fe00::1234::xxq3::dg353::dab3")); + } + + void testToLowerCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("abcdefghijklmn", str.toLowerCase()); + } + + void testToUpperCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("ABCDEFGHIJKLMN", str.toUpperCase()); + } + + void testTrim() + { + // 0123456789 + String str = "\t AbcdeFghijkLMN as "; + assertEquals("AbcdeFghijkLMN as", str.trim()); + + String str2 = "abcd"; + assertEquals("abcd", str2.trim()); + + String str3 = " abcd"; + assertEquals("abcd", str3.trim()); + + String str4 = "abcd "; + assertEquals("abcd", str4.trim()); + } + + void testIndexOf() + { + // 012345678901234567898 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(0, str.indexOf("")); + assertEquals(1, str.indexOf("bcd")); + assertEquals(14, str.indexOf("bcd", 5)); + } + + void testLastIndexOf() + { + // 10 20 + // 012345678901234567890 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(21, str.lastIndexOf("")); + assertEquals(14, str.lastIndexOf("bcd")); + assertEquals(1, str.lastIndexOf("bcd", 12)); + assertEquals(-1, str.lastIndexOf("XYZ", 12)); + assertEquals(-1, str.lastIndexOf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + + assertEquals(16, str.lastIndexOf('d')); + assertEquals(-1, str.lastIndexOf('=')); + assertEquals(3, str.lastIndexOf('d', 12)); + assertEquals(-1, str.lastIndexOf('=', 12)); + } + + void testEq() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + + assertTrue(str1 == str2); + assertTrue(str1 != str3); + assertTrue(str2 != str3); + + IndexOutOfBoundsException e; + assertFalse(str1.equals(e)); + } + + void testToString() + { + String str1 = "Abc"; + String str3 = "Def"; + assertEquals("Abc", str1.toString()); + assertEquals("Def", str3.toString()); + } + + void testHashCode() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + int code1 = str1.hashCode(); + int code2 = str2.hashCode(); + int code3 = str3.hashCode(); + + assertTrue(code1 == code2); + assertTrue(code1 != code3); + assertTrue(code2 != code3); + } + + void testCat() + { + String str1 = "Abc"; + String str2 = str1 + 123; + assertEquals("Abc123", str2); + + String str3 = "ABC" + str2; + assertEquals("ABCAbc123", str3); + + String str4 = 123 + str2; + assertEquals("123Abc123", str4); + + String str5 = str1 + str2; + assertEquals("AbcAbc123", str5); + } + + void testIn() + { + std::istringstream iss("ABCDEF"); + String str; + iss >> str; + assertEquals("ABCDEF", str); + } + + void suite() + { + RUN_TEST("operator+=", testOpPlusEq); + RUN_TEST("charAt", testCharAt); + RUN_TEST("substring", testSubString); + RUN_TEST("contains", testContains); + RUN_TEST("replace", testReplace); + RUN_TEST("split", testSplit); + RUN_TEST("startsWith", testStartsWith); + RUN_TEST("endsWith", testEndsWith); + RUN_TEST("toLowerCase", testToLowerCase); + RUN_TEST("toUpperCase", testToUpperCase); + RUN_TEST("trim", testTrim); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testLastIndexOf); + RUN_TEST("opEq, opNotEq", testEq); + RUN_TEST("toString", testToString); + RUN_TEST("hashCode", testHashCode); + RUN_TEST("cat", testCat); + RUN_TEST("in stream", testIn); + } +}; diff --git a/modules/j/base/test/src/ut_thread.cpp b/modules/j/base/test/src/ut_thread.cpp new file mode 100644 index 0000000..ef24e09 --- /dev/null +++ b/modules/j/base/test/src/ut_thread.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class TestThread : public Thread +{ +public: + TestThread(const String &name) : passRun(false), threadName(name) {} + virtual ~TestThread() {} + void run() + { + Thread::sleep(500); + passRun = true; + } + + bool passRun; + String threadName; +}; + +class TmpRunnable : public Runnable +{ +public: + TmpRunnable() : passRun(false) {} + virtual ~TmpRunnable() {} + void run() override + { + Thread::yield(); + Thread::sleep(500); + passRun = true; + } + bool passRun; +}; + +class ThreadTest : public TestCase +{ +public: + ThreadTest() {} + ~ThreadTest() {} + void setUp() {} + void tearDown() {} + + void testThread() + { + TestThread t1("Thread1"); + TestThread t2("Thread2"); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + assertTrue(t1.passRun); + assertTrue(t2.passRun); + } + + void testRunnable() + { + TmpRunnable runnable; + Thread t1(&runnable); + Thread t2(&runnable); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + } + + void testMove() + { + TmpRunnable runnable; + Thread t1(&runnable); + + // 開始 + + // t2 に移動 + Thread t2(std::move(t1)); + t2.start(); + Thread::sleep(10); + bool alive = t2.isAlive(); + assertTrue(alive); + t2.join(); + alive = t2.isAlive(); + assertFalse(alive); + + // t3 に移動 + Thread t3; + t3 = std::move(t2); + t3.start(); + Thread::sleep(10); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertTrue(alive); + t3.join(); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertFalse(alive); + } + + void suite() + { + RUN_TEST("Thread", testThread); + RUN_TEST("Runnable", testRunnable); + RUN_TEST("Move", testMove); + } +}; diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_string.cpp b/modules/j/base/test/src/ut_string.cpp new file mode 100644 index 0000000..6882524 --- /dev/null +++ b/modules/j/base/test/src/ut_string.cpp @@ -0,0 +1,318 @@ +#include +#include +#include + +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class StringTest : public TestCase +{ +public: + StringTest() {} + ~StringTest() {} + void setUp() {} + void tearDown() {} + + void testOpPlusEq() + { + String str = "Hello"; + str += " World!"; + assertEquals("Hello World!", str); + } + + void testCharAt() + { + // 0123456789 + String str = "Hello World!"; + + char c0 = str.charAt(0); + assertEquals('H', c0); + + char c3 = str.charAt(3); + assertEquals('l', c3); + + char c5 = str.charAt(5); + assertEquals(' ', c5); + + try + { + str.charAt(30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + + try + { + str.charAt(-1); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + } + + void testSubString() + { + // 0123456789 + String str = "Hello World!"; + String subStr = str.substring(2, 5); + assertEquals("llo", subStr); + + try + { + str.substring(-1, 5); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + + try + { + str.substring(5, 3); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("5") >= 0); + } + + try + { + str.substring(5, 30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + } + + void testContains() + { + // 0123456789 + String str = "Hello World!"; + bool isContains = str.contains("World"); + assertTrue(isContains); + + isContains = str.contains("xyz"); + assertFalse(isContains); + } + + void testReplace() + { + // 0123456789 + String str = "Hello World!"; + + String str2 = str.replace('l', 'x'); + assertEquals("Hexxo Worxd!", str2); + + String str3 = str.replace("l", "x"); + assertEquals("Hexlo World!", str3); + + String strX = "Tonarino Kyakuha Yoku Kakikuu Kyakuda!"; + String str4 = strX.replace("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu Kyakuda!", str4); + + String str5 = strX.replaceAll("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu AAAAAAAda!", str5); + + // 対象なし + String strNo1 = str.replace('z', 'v'); + assertEquals("Hello World!", strNo1); + + String strNo2 = str.replace("ZZA", "XYZ"); + assertEquals("Hello World!", strNo2); + } + + void testSplit() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + std::vector result = str.split("::"); + const char *exp[] = { + "fe00", + "1234", + "xxq3", + "dg353", + "dab3"}; + for (int idx = 0; idx < static_cast(result.size()); idx++) + { + assertEquals(exp[idx], result[idx]); + } + } + + void testStartsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.startsWith("fe00::")); + assertFalse(str.startsWith("abcd")); + + // over + assertFalse(str.startsWith("fe00::1234::xxq3::dg353::dab3::def")); + } + + void testEndsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.endsWith(":dab3")); + assertFalse(str.endsWith("abcde")); + + // over + assertFalse(str.endsWith("abc::fe00::1234::xxq3::dg353::dab3")); + } + + void testToLowerCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("abcdefghijklmn", str.toLowerCase()); + } + + void testToUpperCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("ABCDEFGHIJKLMN", str.toUpperCase()); + } + + void testTrim() + { + // 0123456789 + String str = "\t AbcdeFghijkLMN as "; + assertEquals("AbcdeFghijkLMN as", str.trim()); + + String str2 = "abcd"; + assertEquals("abcd", str2.trim()); + + String str3 = " abcd"; + assertEquals("abcd", str3.trim()); + + String str4 = "abcd "; + assertEquals("abcd", str4.trim()); + } + + void testIndexOf() + { + // 012345678901234567898 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(0, str.indexOf("")); + assertEquals(1, str.indexOf("bcd")); + assertEquals(14, str.indexOf("bcd", 5)); + } + + void testLastIndexOf() + { + // 10 20 + // 012345678901234567890 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(21, str.lastIndexOf("")); + assertEquals(14, str.lastIndexOf("bcd")); + assertEquals(1, str.lastIndexOf("bcd", 12)); + assertEquals(-1, str.lastIndexOf("XYZ", 12)); + assertEquals(-1, str.lastIndexOf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + + assertEquals(16, str.lastIndexOf('d')); + assertEquals(-1, str.lastIndexOf('=')); + assertEquals(3, str.lastIndexOf('d', 12)); + assertEquals(-1, str.lastIndexOf('=', 12)); + } + + void testEq() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + + assertTrue(str1 == str2); + assertTrue(str1 != str3); + assertTrue(str2 != str3); + + IndexOutOfBoundsException e; + assertFalse(str1.equals(e)); + } + + void testToString() + { + String str1 = "Abc"; + String str3 = "Def"; + assertEquals("Abc", str1.toString()); + assertEquals("Def", str3.toString()); + } + + void testHashCode() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + int code1 = str1.hashCode(); + int code2 = str2.hashCode(); + int code3 = str3.hashCode(); + + assertTrue(code1 == code2); + assertTrue(code1 != code3); + assertTrue(code2 != code3); + } + + void testCat() + { + String str1 = "Abc"; + String str2 = str1 + 123; + assertEquals("Abc123", str2); + + String str3 = "ABC" + str2; + assertEquals("ABCAbc123", str3); + + String str4 = 123 + str2; + assertEquals("123Abc123", str4); + + String str5 = str1 + str2; + assertEquals("AbcAbc123", str5); + } + + void testIn() + { + std::istringstream iss("ABCDEF"); + String str; + iss >> str; + assertEquals("ABCDEF", str); + } + + void suite() + { + RUN_TEST("operator+=", testOpPlusEq); + RUN_TEST("charAt", testCharAt); + RUN_TEST("substring", testSubString); + RUN_TEST("contains", testContains); + RUN_TEST("replace", testReplace); + RUN_TEST("split", testSplit); + RUN_TEST("startsWith", testStartsWith); + RUN_TEST("endsWith", testEndsWith); + RUN_TEST("toLowerCase", testToLowerCase); + RUN_TEST("toUpperCase", testToUpperCase); + RUN_TEST("trim", testTrim); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testLastIndexOf); + RUN_TEST("opEq, opNotEq", testEq); + RUN_TEST("toString", testToString); + RUN_TEST("hashCode", testHashCode); + RUN_TEST("cat", testCat); + RUN_TEST("in stream", testIn); + } +}; diff --git a/modules/j/base/test/src/ut_thread.cpp b/modules/j/base/test/src/ut_thread.cpp new file mode 100644 index 0000000..ef24e09 --- /dev/null +++ b/modules/j/base/test/src/ut_thread.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class TestThread : public Thread +{ +public: + TestThread(const String &name) : passRun(false), threadName(name) {} + virtual ~TestThread() {} + void run() + { + Thread::sleep(500); + passRun = true; + } + + bool passRun; + String threadName; +}; + +class TmpRunnable : public Runnable +{ +public: + TmpRunnable() : passRun(false) {} + virtual ~TmpRunnable() {} + void run() override + { + Thread::yield(); + Thread::sleep(500); + passRun = true; + } + bool passRun; +}; + +class ThreadTest : public TestCase +{ +public: + ThreadTest() {} + ~ThreadTest() {} + void setUp() {} + void tearDown() {} + + void testThread() + { + TestThread t1("Thread1"); + TestThread t2("Thread2"); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + assertTrue(t1.passRun); + assertTrue(t2.passRun); + } + + void testRunnable() + { + TmpRunnable runnable; + Thread t1(&runnable); + Thread t2(&runnable); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + } + + void testMove() + { + TmpRunnable runnable; + Thread t1(&runnable); + + // 開始 + + // t2 に移動 + Thread t2(std::move(t1)); + t2.start(); + Thread::sleep(10); + bool alive = t2.isAlive(); + assertTrue(alive); + t2.join(); + alive = t2.isAlive(); + assertFalse(alive); + + // t3 に移動 + Thread t3; + t3 = std::move(t2); + t3.start(); + Thread::sleep(10); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertTrue(alive); + t3.join(); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertFalse(alive); + } + + void suite() + { + RUN_TEST("Thread", testThread); + RUN_TEST("Runnable", testRunnable); + RUN_TEST("Move", testMove); + } +}; diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile deleted file mode 100644 index 1c6c98f..0000000 --- a/modules/j/cppunit/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj-cppunit -TARGET = $(NAME).so -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib -ljbase - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_string.cpp b/modules/j/base/test/src/ut_string.cpp new file mode 100644 index 0000000..6882524 --- /dev/null +++ b/modules/j/base/test/src/ut_string.cpp @@ -0,0 +1,318 @@ +#include +#include +#include + +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class StringTest : public TestCase +{ +public: + StringTest() {} + ~StringTest() {} + void setUp() {} + void tearDown() {} + + void testOpPlusEq() + { + String str = "Hello"; + str += " World!"; + assertEquals("Hello World!", str); + } + + void testCharAt() + { + // 0123456789 + String str = "Hello World!"; + + char c0 = str.charAt(0); + assertEquals('H', c0); + + char c3 = str.charAt(3); + assertEquals('l', c3); + + char c5 = str.charAt(5); + assertEquals(' ', c5); + + try + { + str.charAt(30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + + try + { + str.charAt(-1); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + } + + void testSubString() + { + // 0123456789 + String str = "Hello World!"; + String subStr = str.substring(2, 5); + assertEquals("llo", subStr); + + try + { + str.substring(-1, 5); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + + try + { + str.substring(5, 3); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("5") >= 0); + } + + try + { + str.substring(5, 30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + } + + void testContains() + { + // 0123456789 + String str = "Hello World!"; + bool isContains = str.contains("World"); + assertTrue(isContains); + + isContains = str.contains("xyz"); + assertFalse(isContains); + } + + void testReplace() + { + // 0123456789 + String str = "Hello World!"; + + String str2 = str.replace('l', 'x'); + assertEquals("Hexxo Worxd!", str2); + + String str3 = str.replace("l", "x"); + assertEquals("Hexlo World!", str3); + + String strX = "Tonarino Kyakuha Yoku Kakikuu Kyakuda!"; + String str4 = strX.replace("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu Kyakuda!", str4); + + String str5 = strX.replaceAll("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu AAAAAAAda!", str5); + + // 対象なし + String strNo1 = str.replace('z', 'v'); + assertEquals("Hello World!", strNo1); + + String strNo2 = str.replace("ZZA", "XYZ"); + assertEquals("Hello World!", strNo2); + } + + void testSplit() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + std::vector result = str.split("::"); + const char *exp[] = { + "fe00", + "1234", + "xxq3", + "dg353", + "dab3"}; + for (int idx = 0; idx < static_cast(result.size()); idx++) + { + assertEquals(exp[idx], result[idx]); + } + } + + void testStartsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.startsWith("fe00::")); + assertFalse(str.startsWith("abcd")); + + // over + assertFalse(str.startsWith("fe00::1234::xxq3::dg353::dab3::def")); + } + + void testEndsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.endsWith(":dab3")); + assertFalse(str.endsWith("abcde")); + + // over + assertFalse(str.endsWith("abc::fe00::1234::xxq3::dg353::dab3")); + } + + void testToLowerCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("abcdefghijklmn", str.toLowerCase()); + } + + void testToUpperCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("ABCDEFGHIJKLMN", str.toUpperCase()); + } + + void testTrim() + { + // 0123456789 + String str = "\t AbcdeFghijkLMN as "; + assertEquals("AbcdeFghijkLMN as", str.trim()); + + String str2 = "abcd"; + assertEquals("abcd", str2.trim()); + + String str3 = " abcd"; + assertEquals("abcd", str3.trim()); + + String str4 = "abcd "; + assertEquals("abcd", str4.trim()); + } + + void testIndexOf() + { + // 012345678901234567898 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(0, str.indexOf("")); + assertEquals(1, str.indexOf("bcd")); + assertEquals(14, str.indexOf("bcd", 5)); + } + + void testLastIndexOf() + { + // 10 20 + // 012345678901234567890 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(21, str.lastIndexOf("")); + assertEquals(14, str.lastIndexOf("bcd")); + assertEquals(1, str.lastIndexOf("bcd", 12)); + assertEquals(-1, str.lastIndexOf("XYZ", 12)); + assertEquals(-1, str.lastIndexOf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + + assertEquals(16, str.lastIndexOf('d')); + assertEquals(-1, str.lastIndexOf('=')); + assertEquals(3, str.lastIndexOf('d', 12)); + assertEquals(-1, str.lastIndexOf('=', 12)); + } + + void testEq() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + + assertTrue(str1 == str2); + assertTrue(str1 != str3); + assertTrue(str2 != str3); + + IndexOutOfBoundsException e; + assertFalse(str1.equals(e)); + } + + void testToString() + { + String str1 = "Abc"; + String str3 = "Def"; + assertEquals("Abc", str1.toString()); + assertEquals("Def", str3.toString()); + } + + void testHashCode() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + int code1 = str1.hashCode(); + int code2 = str2.hashCode(); + int code3 = str3.hashCode(); + + assertTrue(code1 == code2); + assertTrue(code1 != code3); + assertTrue(code2 != code3); + } + + void testCat() + { + String str1 = "Abc"; + String str2 = str1 + 123; + assertEquals("Abc123", str2); + + String str3 = "ABC" + str2; + assertEquals("ABCAbc123", str3); + + String str4 = 123 + str2; + assertEquals("123Abc123", str4); + + String str5 = str1 + str2; + assertEquals("AbcAbc123", str5); + } + + void testIn() + { + std::istringstream iss("ABCDEF"); + String str; + iss >> str; + assertEquals("ABCDEF", str); + } + + void suite() + { + RUN_TEST("operator+=", testOpPlusEq); + RUN_TEST("charAt", testCharAt); + RUN_TEST("substring", testSubString); + RUN_TEST("contains", testContains); + RUN_TEST("replace", testReplace); + RUN_TEST("split", testSplit); + RUN_TEST("startsWith", testStartsWith); + RUN_TEST("endsWith", testEndsWith); + RUN_TEST("toLowerCase", testToLowerCase); + RUN_TEST("toUpperCase", testToUpperCase); + RUN_TEST("trim", testTrim); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testLastIndexOf); + RUN_TEST("opEq, opNotEq", testEq); + RUN_TEST("toString", testToString); + RUN_TEST("hashCode", testHashCode); + RUN_TEST("cat", testCat); + RUN_TEST("in stream", testIn); + } +}; diff --git a/modules/j/base/test/src/ut_thread.cpp b/modules/j/base/test/src/ut_thread.cpp new file mode 100644 index 0000000..ef24e09 --- /dev/null +++ b/modules/j/base/test/src/ut_thread.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class TestThread : public Thread +{ +public: + TestThread(const String &name) : passRun(false), threadName(name) {} + virtual ~TestThread() {} + void run() + { + Thread::sleep(500); + passRun = true; + } + + bool passRun; + String threadName; +}; + +class TmpRunnable : public Runnable +{ +public: + TmpRunnable() : passRun(false) {} + virtual ~TmpRunnable() {} + void run() override + { + Thread::yield(); + Thread::sleep(500); + passRun = true; + } + bool passRun; +}; + +class ThreadTest : public TestCase +{ +public: + ThreadTest() {} + ~ThreadTest() {} + void setUp() {} + void tearDown() {} + + void testThread() + { + TestThread t1("Thread1"); + TestThread t2("Thread2"); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + assertTrue(t1.passRun); + assertTrue(t2.passRun); + } + + void testRunnable() + { + TmpRunnable runnable; + Thread t1(&runnable); + Thread t2(&runnable); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + } + + void testMove() + { + TmpRunnable runnable; + Thread t1(&runnable); + + // 開始 + + // t2 に移動 + Thread t2(std::move(t1)); + t2.start(); + Thread::sleep(10); + bool alive = t2.isAlive(); + assertTrue(alive); + t2.join(); + alive = t2.isAlive(); + assertFalse(alive); + + // t3 に移動 + Thread t3; + t3 = std::move(t2); + t3.start(); + Thread::sleep(10); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertTrue(alive); + t3.join(); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertFalse(alive); + } + + void suite() + { + RUN_TEST("Thread", testThread); + RUN_TEST("Runnable", testRunnable); + RUN_TEST("Move", testMove); + } +}; diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile deleted file mode 100644 index 1c6c98f..0000000 --- a/modules/j/cppunit/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj-cppunit -TARGET = $(NAME).so -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib -ljbase - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md deleted file mode 100644 index dd82708..0000000 --- a/modules/j/cppunit/README.md +++ /dev/null @@ -1,8 +0,0 @@ -* Model : j.cppunit - -** includes -- j/cppunit - -** depends -- j.base - diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_string.cpp b/modules/j/base/test/src/ut_string.cpp new file mode 100644 index 0000000..6882524 --- /dev/null +++ b/modules/j/base/test/src/ut_string.cpp @@ -0,0 +1,318 @@ +#include +#include +#include + +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class StringTest : public TestCase +{ +public: + StringTest() {} + ~StringTest() {} + void setUp() {} + void tearDown() {} + + void testOpPlusEq() + { + String str = "Hello"; + str += " World!"; + assertEquals("Hello World!", str); + } + + void testCharAt() + { + // 0123456789 + String str = "Hello World!"; + + char c0 = str.charAt(0); + assertEquals('H', c0); + + char c3 = str.charAt(3); + assertEquals('l', c3); + + char c5 = str.charAt(5); + assertEquals(' ', c5); + + try + { + str.charAt(30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + + try + { + str.charAt(-1); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + } + + void testSubString() + { + // 0123456789 + String str = "Hello World!"; + String subStr = str.substring(2, 5); + assertEquals("llo", subStr); + + try + { + str.substring(-1, 5); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + + try + { + str.substring(5, 3); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("5") >= 0); + } + + try + { + str.substring(5, 30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + } + + void testContains() + { + // 0123456789 + String str = "Hello World!"; + bool isContains = str.contains("World"); + assertTrue(isContains); + + isContains = str.contains("xyz"); + assertFalse(isContains); + } + + void testReplace() + { + // 0123456789 + String str = "Hello World!"; + + String str2 = str.replace('l', 'x'); + assertEquals("Hexxo Worxd!", str2); + + String str3 = str.replace("l", "x"); + assertEquals("Hexlo World!", str3); + + String strX = "Tonarino Kyakuha Yoku Kakikuu Kyakuda!"; + String str4 = strX.replace("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu Kyakuda!", str4); + + String str5 = strX.replaceAll("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu AAAAAAAda!", str5); + + // 対象なし + String strNo1 = str.replace('z', 'v'); + assertEquals("Hello World!", strNo1); + + String strNo2 = str.replace("ZZA", "XYZ"); + assertEquals("Hello World!", strNo2); + } + + void testSplit() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + std::vector result = str.split("::"); + const char *exp[] = { + "fe00", + "1234", + "xxq3", + "dg353", + "dab3"}; + for (int idx = 0; idx < static_cast(result.size()); idx++) + { + assertEquals(exp[idx], result[idx]); + } + } + + void testStartsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.startsWith("fe00::")); + assertFalse(str.startsWith("abcd")); + + // over + assertFalse(str.startsWith("fe00::1234::xxq3::dg353::dab3::def")); + } + + void testEndsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.endsWith(":dab3")); + assertFalse(str.endsWith("abcde")); + + // over + assertFalse(str.endsWith("abc::fe00::1234::xxq3::dg353::dab3")); + } + + void testToLowerCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("abcdefghijklmn", str.toLowerCase()); + } + + void testToUpperCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("ABCDEFGHIJKLMN", str.toUpperCase()); + } + + void testTrim() + { + // 0123456789 + String str = "\t AbcdeFghijkLMN as "; + assertEquals("AbcdeFghijkLMN as", str.trim()); + + String str2 = "abcd"; + assertEquals("abcd", str2.trim()); + + String str3 = " abcd"; + assertEquals("abcd", str3.trim()); + + String str4 = "abcd "; + assertEquals("abcd", str4.trim()); + } + + void testIndexOf() + { + // 012345678901234567898 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(0, str.indexOf("")); + assertEquals(1, str.indexOf("bcd")); + assertEquals(14, str.indexOf("bcd", 5)); + } + + void testLastIndexOf() + { + // 10 20 + // 012345678901234567890 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(21, str.lastIndexOf("")); + assertEquals(14, str.lastIndexOf("bcd")); + assertEquals(1, str.lastIndexOf("bcd", 12)); + assertEquals(-1, str.lastIndexOf("XYZ", 12)); + assertEquals(-1, str.lastIndexOf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + + assertEquals(16, str.lastIndexOf('d')); + assertEquals(-1, str.lastIndexOf('=')); + assertEquals(3, str.lastIndexOf('d', 12)); + assertEquals(-1, str.lastIndexOf('=', 12)); + } + + void testEq() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + + assertTrue(str1 == str2); + assertTrue(str1 != str3); + assertTrue(str2 != str3); + + IndexOutOfBoundsException e; + assertFalse(str1.equals(e)); + } + + void testToString() + { + String str1 = "Abc"; + String str3 = "Def"; + assertEquals("Abc", str1.toString()); + assertEquals("Def", str3.toString()); + } + + void testHashCode() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + int code1 = str1.hashCode(); + int code2 = str2.hashCode(); + int code3 = str3.hashCode(); + + assertTrue(code1 == code2); + assertTrue(code1 != code3); + assertTrue(code2 != code3); + } + + void testCat() + { + String str1 = "Abc"; + String str2 = str1 + 123; + assertEquals("Abc123", str2); + + String str3 = "ABC" + str2; + assertEquals("ABCAbc123", str3); + + String str4 = 123 + str2; + assertEquals("123Abc123", str4); + + String str5 = str1 + str2; + assertEquals("AbcAbc123", str5); + } + + void testIn() + { + std::istringstream iss("ABCDEF"); + String str; + iss >> str; + assertEquals("ABCDEF", str); + } + + void suite() + { + RUN_TEST("operator+=", testOpPlusEq); + RUN_TEST("charAt", testCharAt); + RUN_TEST("substring", testSubString); + RUN_TEST("contains", testContains); + RUN_TEST("replace", testReplace); + RUN_TEST("split", testSplit); + RUN_TEST("startsWith", testStartsWith); + RUN_TEST("endsWith", testEndsWith); + RUN_TEST("toLowerCase", testToLowerCase); + RUN_TEST("toUpperCase", testToUpperCase); + RUN_TEST("trim", testTrim); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testLastIndexOf); + RUN_TEST("opEq, opNotEq", testEq); + RUN_TEST("toString", testToString); + RUN_TEST("hashCode", testHashCode); + RUN_TEST("cat", testCat); + RUN_TEST("in stream", testIn); + } +}; diff --git a/modules/j/base/test/src/ut_thread.cpp b/modules/j/base/test/src/ut_thread.cpp new file mode 100644 index 0000000..ef24e09 --- /dev/null +++ b/modules/j/base/test/src/ut_thread.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class TestThread : public Thread +{ +public: + TestThread(const String &name) : passRun(false), threadName(name) {} + virtual ~TestThread() {} + void run() + { + Thread::sleep(500); + passRun = true; + } + + bool passRun; + String threadName; +}; + +class TmpRunnable : public Runnable +{ +public: + TmpRunnable() : passRun(false) {} + virtual ~TmpRunnable() {} + void run() override + { + Thread::yield(); + Thread::sleep(500); + passRun = true; + } + bool passRun; +}; + +class ThreadTest : public TestCase +{ +public: + ThreadTest() {} + ~ThreadTest() {} + void setUp() {} + void tearDown() {} + + void testThread() + { + TestThread t1("Thread1"); + TestThread t2("Thread2"); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + assertTrue(t1.passRun); + assertTrue(t2.passRun); + } + + void testRunnable() + { + TmpRunnable runnable; + Thread t1(&runnable); + Thread t2(&runnable); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + } + + void testMove() + { + TmpRunnable runnable; + Thread t1(&runnable); + + // 開始 + + // t2 に移動 + Thread t2(std::move(t1)); + t2.start(); + Thread::sleep(10); + bool alive = t2.isAlive(); + assertTrue(alive); + t2.join(); + alive = t2.isAlive(); + assertFalse(alive); + + // t3 に移動 + Thread t3; + t3 = std::move(t2); + t3.start(); + Thread::sleep(10); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertTrue(alive); + t3.join(); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertFalse(alive); + } + + void suite() + { + RUN_TEST("Thread", testThread); + RUN_TEST("Runnable", testRunnable); + RUN_TEST("Move", testMove); + } +}; diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile deleted file mode 100644 index 1c6c98f..0000000 --- a/modules/j/cppunit/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj-cppunit -TARGET = $(NAME).so -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib -ljbase - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md deleted file mode 100644 index dd82708..0000000 --- a/modules/j/cppunit/README.md +++ /dev/null @@ -1,8 +0,0 @@ -* Model : j.cppunit - -** includes -- j/cppunit - -** depends -- j.base - diff --git a/modules/j/cppunit/src/assert.cpp b/modules/j/cppunit/src/assert.cpp deleted file mode 100644 index 9ba67f9..0000000 --- a/modules/j/cppunit/src/assert.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include -#include - -namespace j -{ - namespace cppunit - { - namespace Assert - { - - // オーバーロード: const char* - void _assertEqualsImpl(const char *expected, const char *actual, - const char *file, const char *func, int line) - { - if (std::strcmp(expected, actual) != 0) - { - std::ostringstream msg; - msg << "expected <" << expected << "> but was: <" << actual << ">"; - throw lang::AssertionError(msg.str(), file, func, line); - } - } - - // float, double 用 - void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, - const char *file, const char *func, int line) - { - bool isSuccess = (std::fabs(expected - actual) < delta); - if (!isSuccess) - { - std::ostringstream msg; - msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; - throw lang::AssertionError(msg.str(), file, func, line); - } - } - - void _assertTrueImpl(bool condition, const char *file, const char *func, int line) - { - if (!condition) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - - void _assertFalseImpl(bool condition, const char *file, const char *func, int line) - { - if (condition) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - void _assertNullImpl(void *obj, const char *file, const char *func, int line) - { - if (obj != nullptr) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - - void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) - { - if (obj == nullptr) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - - void _failImpl(const char *file, const char *func, int line) - { - throw lang::AssertionError("fail()", file, func, line); - } - - } // namespace Assert - } // namespace cppunit -} // namespace j diff --git a/include/j/lang/iterable.hpp b/include/j/lang/iterable.hpp new file mode 100644 index 0000000..a2b50c1 --- /dev/null +++ b/include/j/lang/iterable.hpp @@ -0,0 +1,32 @@ +/** + * @file iterable.hpp + * @brief J Library Iterable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_ITERABLE_HPP +#define J_LANG_ITERABLE_HPP + +#include + +#include +#include + +namespace j +{ + namespace lang + { + + template + class Iterable + { + public: + virtual ~Iterable() = default; + virtual std::unique_ptr> iterator() const = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_ITERABLE_HPP diff --git a/include/j/lang/runnable.hpp b/include/j/lang/runnable.hpp new file mode 100644 index 0000000..54a697b --- /dev/null +++ b/include/j/lang/runnable.hpp @@ -0,0 +1,29 @@ +/** + * @file runnable.hpp + * @brief J Library Runnable ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + */ +#ifndef J_LANG_RUNNABLE_HPP +#define J_LANG_RUNNABLE_HPP + +#include + +namespace j +{ + namespace lang + { + + class Runnable + { + public: + // デストラクタ + virtual ~Runnable() noexcept = default; + virtual void run() = 0; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_RUNNABLE_HPP diff --git a/include/j/lang/string.hpp b/include/j/lang/string.hpp index 9c35f6b..b745531 100644 --- a/include/j/lang/string.hpp +++ b/include/j/lang/string.hpp @@ -11,7 +11,9 @@ #include #include #include +#include #include +#include #include @@ -47,6 +49,21 @@ // 文字列結合用 String &operator+=(const String &str) noexcept; + // 数値型との結合 + template + String operator+(T number) const + { + std::ostringstream oss; + oss << &value[0] << number; + return String(oss.str()); + } + + // 比較演算子 + bool operator==(const String &str) const noexcept; + + // 比較演算子 + bool operator!=(const String &str) const noexcept; + // C言語文字列表現 operator const char *() const; @@ -57,6 +74,9 @@ char charAt(int index) const; // 部分文字列を返す。 + String substring(int beginIndex) const; + + // 部分文字列を返す。 String substring(int beginIndex, int endIndex) const; // 指定文字列が含まれるかい否かを返す。 @@ -72,7 +92,7 @@ String replaceAll(const String ®ex, const String &replacement) const; // 分割 - std::unique_ptr split(const String ®ex) const noexcept; + std::vector split(const String ®ex) const noexcept; // 先頭の文字列が一致するか bool startsWith(const String &prefix) const noexcept; @@ -115,6 +135,15 @@ // 入力用 friend std::istream &operator>>(std::istream &is, String &str); + // 数値型との結合(数値が左辺) + template + friend String operator+(T number, const String &str) noexcept + { + std::ostringstream oss; + oss << number << &str.value[0]; + return String(oss.str()); + } + protected: // クローン std::unique_ptr clone() const noexcept override; diff --git a/include/j/lang/thread.hpp b/include/j/lang/thread.hpp new file mode 100644 index 0000000..070321c --- /dev/null +++ b/include/j/lang/thread.hpp @@ -0,0 +1,79 @@ +/** + * @file thread.hpp + * @brief J Library Thread ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/object.hpp + * j/lang/string.hpp + */ +#ifndef J_LANG_THREAD_HPP +#define J_LANG_THREAD_HPP + +#include + +#include +#include +#include + +namespace j +{ + namespace lang + { + + class Thread : public Runnable, public Object + { + public: + // デフォルトコンストラクタ + Thread() noexcept; + + // コンストラクタ + Thread(Runnable *r) noexcept; + + // コピーコンストラクタ + Thread(const Thread &t) noexcept = delete; + + // ムーブコンストラクタ + Thread(Thread &&t) noexcept; + + // デストラクタ + virtual ~Thread() noexcept; + + // コピー代入演算子 + Thread &operator=(const Thread &t) noexcept = delete; + + // ムーブ代入演算子 + Thread &operator=(Thread &&t) noexcept; + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool isAlive() const; + + virtual void run() override; + + // スレッドが終了するのを待機します。 + void join(); + + // スレッドを開始します。 + void start(); + + // スレッドのID + std::thread::id getId(); + + // 指定された ms 間現在のスレッドをスリープさせる。 + static void sleep(long time); + + static void yield(); + + protected: + virtual void execRun(); + Runnable *target; + std::thread tid; + bool aliveState; + }; + + } // namespace lang +} // namespace j + +#endif // J_LANG_THREAD_HPP diff --git a/include/j/util/array_list.hpp b/include/j/util/array_list.hpp new file mode 100644 index 0000000..6f4a949 --- /dev/null +++ b/include/j/util/array_list.hpp @@ -0,0 +1,253 @@ +/** + * @file array_list.hpp + * @brief J Library ArrayList ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_ARRAY_LIST_HPP +#define J_UTIL_ARRAY_LIST_HPP + +#include +#include +#include +#include +#include + +#include +#include +#include + +namespace j +{ + namespace util + { + + template + class ArrayList : public List, public j::lang::Object + { + public: + ArrayList() = default; + virtual ~ArrayList() = default; + + /** + * このリスト内にある要素の数を返します。 + * + * @return このリスト内の要素数 + */ + virtual int size() const override + { + return static_cast(items.size()); + } + + /** + * このリストに要素がない場合に true を返します。 + * + * @return このリストに要素が含まれていない場合は true + */ + virtual bool isEmpty() const override + { + return items.empty(); + } + + /** + * 指定の要素がこのリストに含まれている場合に true を返します。 + * + * @param このリスト内にあるかどうか判定される要素 + * @return 指定された要素がこのリスト内にある場合は true + */ + virtual bool contains(const T &t) const override + { + return (std::find(items.begin(), items.end(), t) != items.end()); + } + + /** + * 指定された要素がこのリスト内で最初に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最初に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int indexOf(const T &t) const override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + return std::distance(items.begin(), it); + } + return -1; + } + + /** + * 指定された要素がこのリスト内で最後に検出された位置のインデックスを返します。 + * 指定された要素がこのリストにない場合は -1 を返します。 + * + * @param t 検索する要素 + * @return 指定された要素がリスト内で最後に検出された位置のインデックス。その要素がリストにない場合は -1。 + */ + virtual int lastIndexOf(const T &t) const override + { + auto it = std::find(items.rbegin(), items.rend(), t); + if (it != items.rend()) + { + return std::distance(items.begin(), it.base()) - 1; + } + return -1; + } + + /** + * このリスト内の指定された位置にある要素を返します。 + * + * @param index 返される要素のインデックス + * @return このリスト内の指定された位置にある要素 + */ + virtual T &get(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + return items[index]; + } + + /** + * このリストの指定された位置にある要素を、指定された要素で置き換えます。 + * + * @param index 置換される要素のインデックス + * @param t 指定された位置に格納される要素 + * @return 指定された位置に以前あった要素 + */ + virtual T set(int index, const T &t) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T oldItem = std::move(items[index]); + items[index] = t; + return oldItem; + } + + /** + * このリストの最後に、指定された要素を追加します。 + * + * @param t このリストに追加される要素 + * @return true + */ + virtual bool add(const T &t) override + { + items.push_back(t); + return true; + } + + /** + * このリスト内の指定された位置に指定された要素を挿入します。 + * その位置とそれ以降に要素があればそれらを右に移動させ、各要素のインデックスに1を加えます。 + * + * @param index 指定の要素が挿入される位置のインデックス + * @param t 挿入される要素 + */ + virtual void add(int index, const T &t) override + { + if ((index < 0) || (index > size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + items.insert(items.begin() + index, t); + } + + /** + * このリストの指定された位置にある要素を削除します。 + * 後続の要素は左に移動します(インデックス値から1を減算)。 + * + * @param index 削除される要素のインデックス + * @return リストから削除した要素 + */ + virtual T remove(int index) override + { + if ((index < 0) || (index >= size())) + { + std::ostringstream ss; + ss << "index out of range: " << index; + throw j::lang::IndexOutOfBoundsException(ss.str()); + } + T removedItem = items[index]; + items.erase(items.begin() + index); + return removedItem; + } + + /** + * 指定された要素がこのリストにあれば、その最初のものをリストから削除します。 + * その要素がリストになり場合、変更はありません。 + * 指定された要素がこのリストに含まれていた場合は true を返します。 + * + * @param t このリストから削除される要素 + * @return 指定された要素がこのリストに含まれていた場合は true + */ + virtual bool remove(const T &t) override + { + auto it = std::find(items.begin(), items.end(), t); + if (it != items.end()) + { + items.erase(it); + return true; + } + return false; + } + + /** + * このリストからすべての要素を削除します。 + * この呼出しが戻ると、このリストは空になります。 + */ + virtual void clear() override + { + items.clear(); + } + + std::unique_ptr> iterator() const override + { + return std::make_unique>(items.begin(), items.end()); + } + + Iterator begin() + { + return Iterator(items.begin(), items.end()); + } + + Iterator end() + { + return Iterator(items.end(), items.end()); + } + + virtual j::lang::String toString() + { + std::stringstream oss; + bool isFirst = true; + oss << "["; + for (const auto &e : items) + { + if (!isFirst) + { + oss << ", "; + } + oss << e; + isFirst = false; + } + oss << "]"; + return j::lang::String(oss.str()); + } + + private: + std::vector items; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_ARRAY_LIST_HPP diff --git a/include/j/util/blocking_queue.hpp b/include/j/util/blocking_queue.hpp new file mode 100644 index 0000000..c81d0ba --- /dev/null +++ b/include/j/util/blocking_queue.hpp @@ -0,0 +1,29 @@ +/** + * @file blocking_queue.hpp + * @brief J Library BlockingQueue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_BLOCKING_QUEUE_HPP +#define J_UTIL_BLOCKING_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class BlockingQueue : public Queue + { + virtual ~BlockingQueue() = default; + virtual void put(const T &t); + virtual T take(); + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_BLOCKING_QUEUE_HPP diff --git a/include/j/util/collection.hpp b/include/j/util/collection.hpp new file mode 100644 index 0000000..183bbf5 --- /dev/null +++ b/include/j/util/collection.hpp @@ -0,0 +1,34 @@ +/** + * @file collection.hpp + * @brief J Library Collection ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_COLLECTION_HPP +#define J_UTIL_COLLECTION_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Collection : public j::lang::Iterable + { + public: + virtual ~Collection() = default; + virtual int size() const = 0; + virtual bool isEmpty() const = 0; + virtual bool contains(const T &t) const = 0; + virtual bool add(const T &t) = 0; + virtual void clear() = 0; + virtual bool remove(const T &t) = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/iterator.hpp b/include/j/util/iterator.hpp new file mode 100644 index 0000000..46ec93f --- /dev/null +++ b/include/j/util/iterator.hpp @@ -0,0 +1,94 @@ +/** + * @file iterator.hpp + * @brief J Library Iterator ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j.hpp + */ +#ifndef J_UTIL_ITERATOR_HPP +#define J_UTIL_ITERATOR_HPP + +#include +#include + +#include + +namespace j +{ + namespace util + { + + template + class Iterator + { + private: + typename std::vector::const_iterator current; + typename std::vector::const_iterator end; + + public: + Iterator(typename std::vector::const_iterator start, typename std::vector::const_iterator end) : current(start), end(end) {} + virtual ~Iterator() = default; + + /** + * 反復処理でさらに要素がある場合に true を返します。 + * + * @return 反復処理でさらに要素がある場合は true + */ + virtual bool hasNext() const + { + return (current != end); + } + + /** + * 反復処理で次の要素を返します。 + * + * @return 反復処理での次の要素 + */ + virtual T &next() + { + if (!hasNext()) + { + // TODO + } + return const_cast(*current++); + } + + T &operator*() const + { + return const_cast(*current); + } + + Iterator &operator++() + { + current++; + return *this; + } + + Iterator operator++(int) + { + Iterator tmp = *this; + current++; + return tmp; + } + + bool operator==(const Iterator &ite) const + { + return (current == ite.current); + } + + bool operator!=(const Iterator &ite) const + { + return (current != ite.current); + } + + using iterator_category = std::input_iterator_tag; + using value_type = T; + using difference_type = std::ptrdiff_t; + using pointer = T *; + using reference = T &; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_COLLECTION_HPP diff --git a/include/j/util/list.hpp b/include/j/util/list.hpp new file mode 100644 index 0000000..adcdeab --- /dev/null +++ b/include/j/util/list.hpp @@ -0,0 +1,34 @@ +/** + * @file list.hpp + * @brief J Library List ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/iterable.hpp + */ +#ifndef J_UTIL_LIST_HPP +#define J_UTIL_LIST_HPP + +#include + +namespace j +{ + namespace util + { + + template + class List : public Collection + { + public: + virtual ~List() = default; + virtual void add(int index, const T &t) = 0; + virtual T remove(int index) = 0; + virtual T &get(int index) = 0; + virtual T set(int index, const T &t) = 0; + virtual int indexOf(const T &t) const = 0; + virtual int lastIndexOf(const T &t) const = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_LIST_HPP diff --git a/include/j/util/no_such_element_exception.hpp b/include/j/util/no_such_element_exception.hpp new file mode 100644 index 0000000..f65c08c --- /dev/null +++ b/include/j/util/no_such_element_exception.hpp @@ -0,0 +1,41 @@ +/** + * @file no_such_element_exception.hpp + * @brief J Library NoSuchElementException ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/throwable.hpp + */ +#ifndef J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP +#define J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP + +#include +#include + +namespace j +{ + namespace util + { + + class NoSuchElementException : public j::lang::Throwable + { + public: + // デフォルトコンストラクタ + NoSuchElementException() noexcept; + + // コンストラクタ + NoSuchElementException(const j::lang::String &msg) noexcept; + + // コピーコンストラクタ + NoSuchElementException(const NoSuchElementException &t) noexcept; + + // ムーブコンストラクタ + NoSuchElementException(NoSuchElementException &&t) noexcept; + + // デストラクタ + ~NoSuchElementException() noexcept; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_NO_SUCH_ELEMENT_EXCEPTION_HPP diff --git a/include/j/util/queue.hpp b/include/j/util/queue.hpp new file mode 100644 index 0000000..e9fe6f7 --- /dev/null +++ b/include/j/util/queue.hpp @@ -0,0 +1,33 @@ +/** + * @file queue.hpp + * @brief J Library Queue ヘッダファイル。 + * @copyright 2001 - 2024 Nomura Kei + * @depends + * j/lang/collection.hpp + */ +#ifndef J_UTIL_QUEUE_HPP +#define J_UTIL_QUEUE_HPP + +#include + +namespace j +{ + namespace util + { + + template + class Queue : public Collection + { + public: + virtual ~Queue() = default; + virtual T &element() const = 0; + virtual bool offer(const T &t) = 0; + virtual T &peek() const = 0; + virtual T poll() = 0; + virtual T remove() = 0; + }; + + } // namespace util +} // namespace j + +#endif // J_UTIL_QUEUE_HPP diff --git a/modules/j/base/src/no_such_element_exception.cpp b/modules/j/base/src/no_such_element_exception.cpp new file mode 100644 index 0000000..2e91c65 --- /dev/null +++ b/modules/j/base/src/no_such_element_exception.cpp @@ -0,0 +1,56 @@ +#include + +using namespace j::lang; + +namespace j +{ + namespace util + { + /** + * NoSuchElementException を構築します。 + */ + NoSuchElementException::NoSuchElementException() noexcept : Throwable() + { + // NOP + } + + /** + * NoSuchElementException を構築します。 + * + * @param msg メッセージ + */ + NoSuchElementException::NoSuchElementException(const String &msg) noexcept : Throwable(msg) + { + // NOP + } + + /** + * NoSuchElementException のコピーコンストラクタ。 + * + * @param t コピー元 NoSuchElementException + */ + NoSuchElementException::NoSuchElementException(const NoSuchElementException &t) noexcept : Throwable(t) + { + // NOP + } + + /** + * NoSuchElementException のムーブコンストラクタ。 + * + * @param str ムーブ元 String + */ + NoSuchElementException::NoSuchElementException(NoSuchElementException &&t) noexcept : Throwable(std::move(t)) + { + // NOP + } + + /** + * デストラクタ。 + */ + NoSuchElementException::~NoSuchElementException() noexcept + { + // NOP + } + + } // namespace lang +} // namespace j diff --git a/modules/j/base/src/string.cpp b/modules/j/base/src/string.cpp index cf3d8de..cdea9d7 100644 --- a/modules/j/base/src/string.cpp +++ b/modules/j/base/src/string.cpp @@ -1,7 +1,10 @@ +#include #include #include +#include #include +#include // 入力ストリーム用バッファサイズ static constexpr int MAX_ISTREAM_BUFFER_SIZE = 4096; @@ -88,7 +91,7 @@ * @return 本オブジェクトへの参照 */ String &String::operator=(String &&str) noexcept - { // 特に移すものはない。 + { if (this != &str) { Object::operator=(std::move(str)); @@ -115,6 +118,18 @@ return *this; } + // 比較演算子 + bool String::operator==(const String &str) const noexcept + { + return this->equals(str); + } + + // 比較演算子 + bool String::operator!=(const String &str) const noexcept + { + return !this->equals(str); + } + /** * const char* 型に変換します。 */ @@ -143,7 +158,9 @@ { if ((index < 0) || (index >= len)) { - // TODO: IndexOutOfBoundsException + std::ostringstream ss; + ss << "invalid index: " << index; + throw IndexOutOfBoundsException(ss.str()); } return value[index]; } @@ -152,6 +169,17 @@ * 指定された部分文字列を返します。 * * @param beginIndex 開始位置 + * @return 部分文字列 + */ + String String::substring(int beginIndex) const + { + return substring(beginIndex, len); + } + + /** + * 指定された部分文字列を返します。 + * + * @param beginIndex 開始位置 * @param endIndex 終了位置 * @return 部分文字列 */ @@ -168,8 +196,9 @@ } else { - // TODO: IndexOutOfBoundsException - return nullptr; + std::ostringstream ss; + ss << "invalid index: " << beginIndex << " - " << endIndex; + throw IndexOutOfBoundsException(ss.str()); } } @@ -204,17 +233,31 @@ return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、先頭の文字列のみ置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replace(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); std::string res = std::regex_replace( - &value[0], re, &replacement.value[0], std::regex_constants::match_continuous); + &value[0], re, &replacement.value[0], std::regex_constants::format_first_only); String str(res.c_str()); return str; } - // 文字列置換 + /** + * 指定された文字列を置換します。 + * 置換する文字列が複数存在する場合、すべて置換されます。 + * + * @param regex 置換する文字列(正規表現) + * @param replacement 置換後文字列 + * @return 置換された文字列 + */ String String::replaceAll(const String ®ex, const String &replacement) const { std::regex re(®ex.value[0]); @@ -224,13 +267,38 @@ return str; } - // 分割 - std::unique_ptr String::split(const String &) const noexcept + /** + * 指定された文字列で分割します。 + * + * @param delimiter 分割文字 + * @return 分割後の文字列配列 + */ + std::vector String::split(const String &delimiter) const noexcept { - return nullptr; + std::vector result; + int startIndex = 0; + int endIndex = 0; + + // 文字列を操作し、区切り文字で分割する。 + endIndex = this->indexOf(delimiter, startIndex); + while (endIndex >= 0) + { + result.emplace_back(this->substring(startIndex, endIndex)); + startIndex = endIndex + delimiter.len; + endIndex = this->indexOf(delimiter, startIndex); + } + + // 最後の要素を追加 + result.emplace_back(this->substring(startIndex)); + return result; } - // 先頭の文字列が一致するか + /** + * 文字列が指定されたプレフィックスで始まるか否かを返します。 + * + * @param prefix プレフィックス + * @return true/false (始まる/始まらない) + */ bool String::startsWith(const String &prefix) const noexcept { if (prefix.len > len) @@ -247,7 +315,12 @@ return true; } - // 末尾の文字列が一致するか + /** + * 文字列が指定されたサフィックスで終わるか否かを返します。 + * + * @param suffix サフィックス + * @return true/false (終わる/終わらない) + */ bool String::endsWith(const String &suffix) const noexcept { if (suffix.len > len) @@ -266,7 +339,11 @@ return true; } - // 小文字変換 + /** + * 文字列を小文字に変換します。 + * + * @return 小文字に変換された文字列 + */ String String::toLowerCase() const noexcept { String str(*this); @@ -277,7 +354,11 @@ return str; } - // 大文字変換 + /** + * 文字列を大文字に変換します。 + * + * @return 大文字に変換された文字列 + */ String String::toUpperCase() const noexcept { String str(*this); @@ -288,7 +369,11 @@ return str; } - // trim + /** + * 前後の空白を取り除きます。 + * + * @return 前後の空白を取り除いた文字列 + */ String String::trim() const noexcept { int beginIndex = 0; @@ -299,7 +384,8 @@ break; } } - int endIndex = len; + + int endIndex = len - 1; for (; endIndex >= beginIndex; endIndex--) { if (value[endIndex] > 0x20) @@ -307,7 +393,7 @@ break; } } - int trimedLen = endIndex - beginIndex; + int trimedLen = endIndex - beginIndex + 1; std::unique_ptr trimedStr = std::make_unique(trimedLen + 1); std::strncpy(&trimedStr[0], &value[beginIndex], trimedLen); trimedStr[trimedLen] = '\0'; diff --git a/modules/j/base/src/thread.cpp b/modules/j/base/src/thread.cpp new file mode 100644 index 0000000..9f42958 --- /dev/null +++ b/modules/j/base/src/thread.cpp @@ -0,0 +1,137 @@ +#include + +#include +#include + +namespace j +{ + namespace lang + { + /** + * 新しい Thread オブジェクトを割り当てます。 + */ + Thread::Thread() noexcept : target(nullptr), aliveState(false) + { + // NOP + } + + /** + * 新しい Thread オブジェクトを割り当てます。 + * + * @param target このスレッドの起動時に呼び出される run メソッドを含むオブジェクト + */ + Thread::Thread(Runnable *r) noexcept : target(r), aliveState(false) + { + // NOP + } + + // ムーブコンストラクタ + Thread::Thread(Thread &&t) noexcept : Object(std::move(t)), target(std::move(t.target)), tid(std::move(t.tid)), aliveState(std::move(t.aliveState)) + { + // NOP + } + + // デストラクタ + Thread::~Thread() noexcept + { + // NOP + } + + // ムーブ代入演算子 + Thread &Thread::operator=(Thread &&t) noexcept + { + if (this != &t) + { + Object::operator=(std::move(t)); + tid = std::move(t.tid); + target = std::move(t.target); + } + return *this; + } + + // スレッドに割り込みます。 + // void interrupt(); + + // スレッドが生存しているか判定します。 + bool Thread::isAlive() const + { + return aliveState; + } + + void Thread::run() + { + // NOP + } + + /** + * スレッドが終了するのを待機します。 + */ + void Thread::join() + { + if (tid.joinable()) + { + tid.join(); + } + } + + /** + * スレッドを開始します。 + */ + void Thread::start() + { + tid = std::thread(&Thread::execRun, this); + } + + /** + * スレッドのIDを返します。 + * + * @return スレッドのID + */ + std::thread::id Thread::getId() + { + return tid.get_id(); + } + + /** + * 指定された ms 間現在のスレッドをスリープさせます。 + * + * @param time スリープするミリ秒数 + */ + void Thread::sleep(long time) + { + std::this_thread::sleep_for(std::chrono::milliseconds(time)); + } + + /** + * 現在のスレッドが現在のプロセッサ使用料を譲る用意があることを示すヒントを与える。 + */ + void Thread::yield() + { + std::this_thread::yield(); + } + + /** + * start 実施時に内部的に呼び出されるメソッド。 + */ + void Thread::execRun() + { + aliveState = true; + try + { + if (target) + { + target->run(); + } + else + { + this->run(); + } + } + catch (...) + { + // NOP + } + aliveState = false; + } + } // namespace lang +} // namespace j diff --git a/modules/j/base/test/src/ut.cpp b/modules/j/base/test/src/ut.cpp index 21015f2..cd9d6bf 100644 --- a/modules/j/base/test/src/ut.cpp +++ b/modules/j/base/test/src/ut.cpp @@ -1,6 +1,7 @@ #include #include +#include "ut_array_list.cpp" #include "ut_assertion_error.cpp" #include "ut_dl.cpp" #include "ut_env.cpp" @@ -9,7 +10,9 @@ #include "ut_illegal_argument_exception.cpp" #include "ut_index_out_of_bounds_exception.cpp" #include "ut_runtime_exception.cpp" +#include "ut_string.cpp" #include "ut_term.cpp" +#include "ut_thread.cpp" #include "ut_throwable.cpp" #include "ut_unsupported_operation_exception.cpp" @@ -18,6 +21,7 @@ int main() { + { ArrayListTest tc; tc.suite(); } { AssertionErrorTest tc; tc.suite(); } { DlTest tc; tc.suite(); } { EnvTest tc; tc.suite(); } @@ -26,7 +30,9 @@ { IllegalArgumentExceptionTest tc; tc.suite(); } { IndexOutOfBoundsExceptionTest tc; tc.suite(); } { RuntimeExceptionTest tc; tc.suite(); } + { StringTest tc; tc.suite(); } { TermTest tc; tc.suite(); } + { ThreadTest tc; tc.suite(); } { ThrowableTest tc; tc.suite(); } { UnsupportedOperationExceptionTest tc; tc.suite(); } testManager.printResult(); diff --git a/modules/j/base/test/src/ut_array_list.cpp b/modules/j/base/test/src/ut_array_list.cpp new file mode 100644 index 0000000..c8c5a07 --- /dev/null +++ b/modules/j/base/test/src/ut_array_list.cpp @@ -0,0 +1,311 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::util; +using namespace j::cppunit; + +class ArrayListTest : public TestCase +{ +public: + ArrayListTest() {} + ~ArrayListTest() {} + void setUp() {} + void tearDown() {} + + void testArrayList() + { + List *list = new ArrayList(); + int size = list->size(); + assertEquals(0, size); + + ArrayList intList; + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + + int val = intList.get(2); + assertEquals(30, val); + } + + void testIsEmpty() + { + ArrayList intList; + assertTrue(intList.isEmpty()); + + intList.add(10); + intList.add(20); + intList.add(30); + intList.add(40); + assertFalse(intList.isEmpty()); + } + + void testContains() + { + ArrayList strList; + assertFalse(strList.contains("ABC")); + + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + assertTrue(strList.contains("ABC")); + } + + void testIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(-1, strList.indexOf("ZZZ")); + assertEquals(0, strList.indexOf("DEF")); + assertEquals(1, strList.indexOf("ABC")); + assertEquals(2, strList.indexOf("XYZ")); + assertEquals(4, strList.indexOf("BB")); + } + + void testlastIndexOf() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals(5, strList.lastIndexOf("ABC")); + assertEquals(0, strList.lastIndexOf("DEF")); + assertEquals(3, strList.lastIndexOf("AAAAAA")); + assertEquals(-1, strList.lastIndexOf("===")); + } + + void testGet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.get(0)); + assertEquals("ABC", strList.get(1)); + assertEquals("ABC", strList.get(5)); + assertEquals("DDD", strList.get(6)); + try + { + strList.get(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + + assertTrue(true); + } + try + { + strList.get(7); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testSet() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + assertEquals("DEF", strList.set(0, "XXX")); + assertEquals("ABC", strList.set(1, "YYY")); + assertEquals("ABC", strList.set(5, "ZZZ")); + assertEquals("DDD", strList.set(6, "===")); + + assertEquals("XXX", strList.get(0)); + assertEquals("YYY", strList.get(1)); + assertEquals("XYZ", strList.get(2)); + assertEquals("ZZZ", strList.get(5)); + assertEquals("===", strList.get(6)); + + try + { + strList.set(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.set(7, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testAdd() + { + ArrayList strList; + strList.add(0, "DEF"); + strList.add(0, "ABC"); // ABC, DEF + strList.add(2, "GHIJK"); // ABC, DEF, GHIJK + + assertEquals("ABC", strList.get(0)); + assertEquals("DEF", strList.get(1)); + assertEquals("GHIJK", strList.get(2)); + + try + { + strList.add(-1, "A"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.add(4, "X"); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testRemove() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + assertEquals(7, strList.size()); + assertEquals("DEF", strList.remove(0)); + assertEquals(6, strList.size()); + assertEquals("ABC", strList.get(0)); + + assertTrue(strList.remove("ABC")); + assertEquals(5, strList.size()); + assertEquals("XYZ", strList.get(0)); + + assertFalse(strList.remove("XXX")); + + try + { + strList.remove(-1); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + try + { + strList.remove(10); + fail(); + } + catch (const IndexOutOfBoundsException &) + { + assertTrue(true); + } + } + + void testClear() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + strList.clear(); + assertEquals(0, strList.size()); + } + + void testIterator() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + std::unique_ptr> ite = strList.iterator(); + + const char *results[] = {"DEF", "ABC", "XYZ", "AAAAAA", "BB", "ABC", "DDD"}; + int idx = 0; + while (ite->hasNext()) + { + String data = ite->next(); + assertEquals(results[idx], data); + idx++; + } + + assertEquals("BB", strList.get(4)); + } + + void testToString() + { + ArrayList strList; + strList.add("DEF"); + strList.add("ABC"); + strList.add("XYZ"); + strList.add("AAAAAA"); + strList.add("BB"); + strList.add("ABC"); + strList.add("DDD"); + + String str = strList.toString(); + assertEquals("[DEF, ABC, XYZ, AAAAAA, BB, ABC, DDD]", str); + } + + void suite() + { + RUN_TEST("new ArrayList()", testArrayList); + RUN_TEST("isEmpty", testIsEmpty); + RUN_TEST("contains", testContains); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testlastIndexOf); + RUN_TEST("get", testGet); + RUN_TEST("set", testSet); + RUN_TEST("add", testAdd); + RUN_TEST("remove", testRemove); + RUN_TEST("clear", testClear); + RUN_TEST("iterator", testIterator); + RUN_TEST("toString", testToString); + } +}; diff --git a/modules/j/base/test/src/ut_string.cpp b/modules/j/base/test/src/ut_string.cpp new file mode 100644 index 0000000..6882524 --- /dev/null +++ b/modules/j/base/test/src/ut_string.cpp @@ -0,0 +1,318 @@ +#include +#include +#include + +#include +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class StringTest : public TestCase +{ +public: + StringTest() {} + ~StringTest() {} + void setUp() {} + void tearDown() {} + + void testOpPlusEq() + { + String str = "Hello"; + str += " World!"; + assertEquals("Hello World!", str); + } + + void testCharAt() + { + // 0123456789 + String str = "Hello World!"; + + char c0 = str.charAt(0); + assertEquals('H', c0); + + char c3 = str.charAt(3); + assertEquals('l', c3); + + char c5 = str.charAt(5); + assertEquals(' ', c5); + + try + { + str.charAt(30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + + try + { + str.charAt(-1); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + } + + void testSubString() + { + // 0123456789 + String str = "Hello World!"; + String subStr = str.substring(2, 5); + assertEquals("llo", subStr); + + try + { + str.substring(-1, 5); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("-1") >= 0); + } + + try + { + str.substring(5, 3); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("5") >= 0); + } + + try + { + str.substring(5, 30); + fail(); + } + catch (const IndexOutOfBoundsException &e) + { + String msg = e.getMessage(); + assertTrue(msg.indexOf("30") >= 0); + } + } + + void testContains() + { + // 0123456789 + String str = "Hello World!"; + bool isContains = str.contains("World"); + assertTrue(isContains); + + isContains = str.contains("xyz"); + assertFalse(isContains); + } + + void testReplace() + { + // 0123456789 + String str = "Hello World!"; + + String str2 = str.replace('l', 'x'); + assertEquals("Hexxo Worxd!", str2); + + String str3 = str.replace("l", "x"); + assertEquals("Hexlo World!", str3); + + String strX = "Tonarino Kyakuha Yoku Kakikuu Kyakuda!"; + String str4 = strX.replace("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu Kyakuda!", str4); + + String str5 = strX.replaceAll("Kyaku", "AAAAAAA"); + assertEquals("Tonarino AAAAAAAha Yoku Kakikuu AAAAAAAda!", str5); + + // 対象なし + String strNo1 = str.replace('z', 'v'); + assertEquals("Hello World!", strNo1); + + String strNo2 = str.replace("ZZA", "XYZ"); + assertEquals("Hello World!", strNo2); + } + + void testSplit() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + std::vector result = str.split("::"); + const char *exp[] = { + "fe00", + "1234", + "xxq3", + "dg353", + "dab3"}; + for (int idx = 0; idx < static_cast(result.size()); idx++) + { + assertEquals(exp[idx], result[idx]); + } + } + + void testStartsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.startsWith("fe00::")); + assertFalse(str.startsWith("abcd")); + + // over + assertFalse(str.startsWith("fe00::1234::xxq3::dg353::dab3::def")); + } + + void testEndsWith() + { + // 0123456789 + String str = "fe00::1234::xxq3::dg353::dab3"; + assertTrue(str.endsWith(":dab3")); + assertFalse(str.endsWith("abcde")); + + // over + assertFalse(str.endsWith("abc::fe00::1234::xxq3::dg353::dab3")); + } + + void testToLowerCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("abcdefghijklmn", str.toLowerCase()); + } + + void testToUpperCase() + { + // 0123456789 + String str = "AbcdeFghijkLMN"; + assertEquals("ABCDEFGHIJKLMN", str.toUpperCase()); + } + + void testTrim() + { + // 0123456789 + String str = "\t AbcdeFghijkLMN as "; + assertEquals("AbcdeFghijkLMN as", str.trim()); + + String str2 = "abcd"; + assertEquals("abcd", str2.trim()); + + String str3 = " abcd"; + assertEquals("abcd", str3.trim()); + + String str4 = "abcd "; + assertEquals("abcd", str4.trim()); + } + + void testIndexOf() + { + // 012345678901234567898 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(0, str.indexOf("")); + assertEquals(1, str.indexOf("bcd")); + assertEquals(14, str.indexOf("bcd", 5)); + } + + void testLastIndexOf() + { + // 10 20 + // 012345678901234567890 + String str = "AbcdeFghijkLMNbcdAxce"; + assertEquals(21, str.lastIndexOf("")); + assertEquals(14, str.lastIndexOf("bcd")); + assertEquals(1, str.lastIndexOf("bcd", 12)); + assertEquals(-1, str.lastIndexOf("XYZ", 12)); + assertEquals(-1, str.lastIndexOf("aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa")); + + assertEquals(16, str.lastIndexOf('d')); + assertEquals(-1, str.lastIndexOf('=')); + assertEquals(3, str.lastIndexOf('d', 12)); + assertEquals(-1, str.lastIndexOf('=', 12)); + } + + void testEq() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + + assertTrue(str1 == str2); + assertTrue(str1 != str3); + assertTrue(str2 != str3); + + IndexOutOfBoundsException e; + assertFalse(str1.equals(e)); + } + + void testToString() + { + String str1 = "Abc"; + String str3 = "Def"; + assertEquals("Abc", str1.toString()); + assertEquals("Def", str3.toString()); + } + + void testHashCode() + { + String str1 = "Abc"; + String str2 = "Abc"; + String str3 = "Def"; + int code1 = str1.hashCode(); + int code2 = str2.hashCode(); + int code3 = str3.hashCode(); + + assertTrue(code1 == code2); + assertTrue(code1 != code3); + assertTrue(code2 != code3); + } + + void testCat() + { + String str1 = "Abc"; + String str2 = str1 + 123; + assertEquals("Abc123", str2); + + String str3 = "ABC" + str2; + assertEquals("ABCAbc123", str3); + + String str4 = 123 + str2; + assertEquals("123Abc123", str4); + + String str5 = str1 + str2; + assertEquals("AbcAbc123", str5); + } + + void testIn() + { + std::istringstream iss("ABCDEF"); + String str; + iss >> str; + assertEquals("ABCDEF", str); + } + + void suite() + { + RUN_TEST("operator+=", testOpPlusEq); + RUN_TEST("charAt", testCharAt); + RUN_TEST("substring", testSubString); + RUN_TEST("contains", testContains); + RUN_TEST("replace", testReplace); + RUN_TEST("split", testSplit); + RUN_TEST("startsWith", testStartsWith); + RUN_TEST("endsWith", testEndsWith); + RUN_TEST("toLowerCase", testToLowerCase); + RUN_TEST("toUpperCase", testToUpperCase); + RUN_TEST("trim", testTrim); + RUN_TEST("indexOf", testIndexOf); + RUN_TEST("lastIndexOf", testLastIndexOf); + RUN_TEST("opEq, opNotEq", testEq); + RUN_TEST("toString", testToString); + RUN_TEST("hashCode", testHashCode); + RUN_TEST("cat", testCat); + RUN_TEST("in stream", testIn); + } +}; diff --git a/modules/j/base/test/src/ut_thread.cpp b/modules/j/base/test/src/ut_thread.cpp new file mode 100644 index 0000000..ef24e09 --- /dev/null +++ b/modules/j/base/test/src/ut_thread.cpp @@ -0,0 +1,129 @@ +#include +#include +#include +#include + +#include + +using namespace j; +using namespace j::lang; +using namespace j::cppunit; + +class TestThread : public Thread +{ +public: + TestThread(const String &name) : passRun(false), threadName(name) {} + virtual ~TestThread() {} + void run() + { + Thread::sleep(500); + passRun = true; + } + + bool passRun; + String threadName; +}; + +class TmpRunnable : public Runnable +{ +public: + TmpRunnable() : passRun(false) {} + virtual ~TmpRunnable() {} + void run() override + { + Thread::yield(); + Thread::sleep(500); + passRun = true; + } + bool passRun; +}; + +class ThreadTest : public TestCase +{ +public: + ThreadTest() {} + ~ThreadTest() {} + void setUp() {} + void tearDown() {} + + void testThread() + { + TestThread t1("Thread1"); + TestThread t2("Thread2"); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + assertTrue(t1.passRun); + assertTrue(t2.passRun); + } + + void testRunnable() + { + TmpRunnable runnable; + Thread t1(&runnable); + Thread t2(&runnable); + t1.start(); + t2.start(); + Thread::sleep(10); + bool alive1 = t1.isAlive(); + bool alive2 = t2.isAlive(); + assertTrue(t1.getId() != t2.getId()); + assertTrue(alive1); + assertTrue(alive2); + t1.join(); + t2.join(); + alive1 = t1.isAlive(); + alive2 = t2.isAlive(); + assertFalse(alive1); + assertFalse(alive2); + } + + void testMove() + { + TmpRunnable runnable; + Thread t1(&runnable); + + // 開始 + + // t2 に移動 + Thread t2(std::move(t1)); + t2.start(); + Thread::sleep(10); + bool alive = t2.isAlive(); + assertTrue(alive); + t2.join(); + alive = t2.isAlive(); + assertFalse(alive); + + // t3 に移動 + Thread t3; + t3 = std::move(t2); + t3.start(); + Thread::sleep(10); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertTrue(alive); + t3.join(); + alive = t3.isAlive(); + std::cout << "t3 alive = " << alive << std::endl; + assertFalse(alive); + } + + void suite() + { + RUN_TEST("Thread", testThread); + RUN_TEST("Runnable", testRunnable); + RUN_TEST("Move", testMove); + } +}; diff --git a/modules/j/cppunit/Makefile b/modules/j/cppunit/Makefile deleted file mode 100644 index 1c6c98f..0000000 --- a/modules/j/cppunit/Makefile +++ /dev/null @@ -1,48 +0,0 @@ -# ============================================================================== -# Makefile -# ============================================================================== -# -# TOPDIR : トップディレクトリ -# RULEDIR : Meke のルール一式が格納されているディレクトリ -# NAME : モジュール名 (拡張子を含めないこと) -# TARGET : モジュールファイル名 (拡張子を含めること) -# SUBDIR : サブディレクトリ (処理したい順に空白区切りで記述すること) -# -TOPDIR ?= ../../.. -RULEDIR ?= $(TOPDIR)/mk -NAME = libj-cppunit -TARGET = $(NAME).so -SUBDIRS = -USE_SO_VERSION = - -# ------------------------------------------------------------------------------ -# *-cmd.mk : コマンド -# *-conf.mk : 設定 -# *-auto.mk : 自動設定 -# ------------------------------------------------------------------------------ -include $(TOPDIR)/config.mk -include $(RULEDIR)/*-cmd.mk -include $(RULEDIR)/*-conf.mk -include $(RULEDIR)/*-auto.mk -# ------------------------------------------------------------------------------ -# -# 以下、オプションを適宜変更してください。 -# - -INCLUDES += -I$(TOPDIR)/include -CFLAGS += -CXXFLAGS += -LDFLAGS += -LIBS += -L$(TOPDIR)/lib -ljbase - -CLEAN_FILES += -CLEAN_DIRS += - -.DEFAULT_GOAL := all - -# ------------------------------------------------------------------------------ -# *-rule : ルール -# ------------------------------------------------------------------------------ -include $(RULEDIR)/*-rule.mk -# ------------------------------------------------------------------------------ - diff --git a/modules/j/cppunit/README.md b/modules/j/cppunit/README.md deleted file mode 100644 index dd82708..0000000 --- a/modules/j/cppunit/README.md +++ /dev/null @@ -1,8 +0,0 @@ -* Model : j.cppunit - -** includes -- j/cppunit - -** depends -- j.base - diff --git a/modules/j/cppunit/src/assert.cpp b/modules/j/cppunit/src/assert.cpp deleted file mode 100644 index 9ba67f9..0000000 --- a/modules/j/cppunit/src/assert.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include -#include -#include -#include -#include - -namespace j -{ - namespace cppunit - { - namespace Assert - { - - // オーバーロード: const char* - void _assertEqualsImpl(const char *expected, const char *actual, - const char *file, const char *func, int line) - { - if (std::strcmp(expected, actual) != 0) - { - std::ostringstream msg; - msg << "expected <" << expected << "> but was: <" << actual << ">"; - throw lang::AssertionError(msg.str(), file, func, line); - } - } - - // float, double 用 - void _assertEqualsFloatImpl(const double &expected, const double &actual, const double &delta, - const char *file, const char *func, int line) - { - bool isSuccess = (std::fabs(expected - actual) < delta); - if (!isSuccess) - { - std::ostringstream msg; - msg << "expected <" << expected << "> but was: <" << actual << "> (delta = " << delta << ")"; - throw lang::AssertionError(msg.str(), file, func, line); - } - } - - void _assertTrueImpl(bool condition, const char *file, const char *func, int line) - { - if (!condition) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - - void _assertFalseImpl(bool condition, const char *file, const char *func, int line) - { - if (condition) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - void _assertNullImpl(void *obj, const char *file, const char *func, int line) - { - if (obj != nullptr) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - - void _assertNotNullImpl(void *obj, const char *file, const char *func, int line) - { - if (obj == nullptr) - { - throw lang::AssertionError("expected but was: ", file, func, line); - } - } - - void _failImpl(const char *file, const char *func, int line) - { - throw lang::AssertionError("fail()", file, func, line); - } - - } // namespace Assert - } // namespace cppunit -} // namespace j diff --git a/modules/j/cppunit/src/cppunit.cpp b/modules/j/cppunit/src/cppunit.cpp deleted file mode 100644 index 7359c93..0000000 --- a/modules/j/cppunit/src/cppunit.cpp +++ /dev/null @@ -1,109 +0,0 @@ -#include -#include -#include -#include - -#include -#include - -using namespace j::io::Term; - -namespace j -{ - namespace cppunit - { - - //////////////////////////////////////////////////////////////////////// - // - // TestCase - // - - /** - * TestCase を構築します。 - */ - TestCase::TestCase() noexcept { /* NOP */ } - - /** - * TestCase を破棄します。 - */ - TestCase::~TestCase() noexcept { /* NOP */ } - - /** - * 各テストケース実施前に実行されます。 - */ - void TestCase::setUp() { /* NOP */ } - - /** - * 各テストケース実施後に実行されます。 - */ - void TestCase::tearDown() { /* NOP */ } - - //////////////////////////////////////////////////////////////////////// - // - // TestManager - // - - /** - * TestManager を構築します。 - */ - TestManager::TestManager() : okCount(0), ngCount(0) - { - // NOP - } - - /** - * TestManager を破棄します。 - * 破棄するタイミングで、テスト結果を出力します。 - */ - TestManager::~TestManager() - { - // NOP - } - - /** - * テスト結果を追加します。 - * - * @param file ファイル - */ - void TestManager::addTestResult(const char *title, const char *func, const char *file, int line, bool result, const j::lang::AssertionError *e) - { - std::ostringstream msg; - msg << title << " [" << func << "] "; - std::cout << BLD << Color::CYN << std::setw(5) << std::setfill('0') << std::right << (okCount + ngCount + 1) << " "; - std::cout << BLD << Color::CYN << std::setw(84) << std::setfill(' ') << std::left << msg.str() << Color::DEF << CLR; - - if (result) - { // テスト成功 - std::cout << BLD << Color::CYN << "[ " << Color::GRN << "OK" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; - okCount++; - } - else - { // テスト失敗 - std::cout << BLD << Color::CYN << "[ " << Color::RED << "NG" << Color::CYN << " ]" << Color::DEF << CLR << std::endl; - ngCount++; - if (e) - { - std::cerr << BLD << Color::YEL << e->toString() << Color::DEF << CLR << std::endl; - std::cerr << BLD << Color::YEL << "\tat " << file << ":" << line << " [RUN_TEST]" << Color::DEF << CLR << std::endl; - } - } - } - - /** - * テスト結果を出力します。 - */ - void TestManager::printResult() - { - std::string line(80, '-'); - std::cout << BLD << Color::CYN << line << Color::DEF << CLR << std::endl; - std::cout << BLD << Color::GRN << " Success : " << okCount << Color::DEF << CLR << std::endl; - std::cout << BLD << Color::RED << " Failure : " << ngCount << Color::DEF << CLR << std::endl; - std::cout << BLD << Color::CYN << " Total : " << (okCount + ngCount) << Color::DEF << CLR << std::endl; - std::cout << std::endl; - } - - // テスト実施用インスタンス - TestManager testManager; - - } // namespace cppunit -} // namespace j