diff --git a/Jupiter/GenericCommand.cpp b/Jupiter/GenericCommand.cpp index 12b3a4b..0fcb4c1 100644 --- a/Jupiter/GenericCommand.cpp +++ b/Jupiter/GenericCommand.cpp @@ -19,14 +19,19 @@ #include "GenericCommand.h" #include "Plugin.h" -Jupiter::ArrayList o_genericCommands; -Jupiter::ArrayList *Jupiter::g_generic_commands = &o_genericCommands; +using namespace Jupiter::literals; + +constexpr const char GENERIC_COMMAND_WORD_DELIMITER = ' '; +constexpr const char *GENERIC_COMMAND_WORD_DELIMITER_CS = " "; + +Jupiter::GenericCommandNamespace o_generic_commands; +Jupiter::GenericCommandNamespace &Jupiter::g_generic_commands = o_generic_commands; /** GenericCommand */ Jupiter::GenericCommand::GenericCommand() { - o_genericCommands.add(this); + Jupiter::GenericCommand::setNamespace(o_generic_commands); for (size_t index = 0; index != Jupiter::plugins->size(); ++index) Jupiter::plugins->get(index)->OnGenericCommandAdd(*this); @@ -34,18 +39,48 @@ Jupiter::GenericCommand::GenericCommand() Jupiter::GenericCommand::~GenericCommand() { - size_t count = o_genericCommands.size(); - while (count != 0) - if (o_genericCommands.get(--count) == this) - { - o_genericCommands.remove(count); - break; - } + // Inform the parent + if (GenericCommand::m_parent == nullptr) + GenericCommand::m_parent->removeCommand(*this); + // Notify plugins for (size_t index = 0; index != Jupiter::plugins->size(); ++index) Jupiter::plugins->get(index)->OnGenericCommandRemove(*this); } +bool Jupiter::GenericCommand::isNamespace() const +{ + return false; +} + +void Jupiter::GenericCommand::setNamespace(const Jupiter::ReadableString &in_namespace) +{ + if (in_namespace.wordCount(GENERIC_COMMAND_WORD_DELIMITER_CS) == 0) + return; // We're already here + + if (Jupiter::GenericCommand::m_parent == nullptr) + return; // We have no parent to start from + + Jupiter::GenericCommand *command = Jupiter::GenericCommand::m_parent->getCommand(in_namespace); + if (command != nullptr && command->isNamespace()) + Jupiter::GenericCommand::setNamespace(*reinterpret_cast(command)); +} + +void Jupiter::GenericCommand::setNamespace(Jupiter::GenericCommandNamespace &in_namespace) +{ + if (Jupiter::GenericCommand::m_parent != nullptr) // Remove from previous parent + Jupiter::GenericCommand::m_parent->removeCommand(*this); + + // Add to new parent + Jupiter::GenericCommand::m_parent = &in_namespace; + in_namespace.addCommand(*this); +} + +Jupiter::GenericCommandNamespace *Jupiter::GenericCommand::getNamespace() const +{ + return Jupiter::GenericCommand::m_parent; +} + /** GenericCommand::ResponseLine */ Jupiter::GenericCommand::ResponseLine::ResponseLine(const Jupiter::ReadableString &response_, GenericCommand::DisplayType type_) @@ -61,14 +96,169 @@ Jupiter::GenericCommand::ResponseLine *Jupiter::GenericCommand::ResponseLine::se return this; } -Jupiter::GenericCommand *Jupiter::getGenericCommand(const Jupiter::ReadableString &trigger) +/** GenericCommandNamespace */ + +Jupiter::GenericCommandNamespace::~GenericCommandNamespace() +{ + // Murder the children + Jupiter::GenericCommandNamespace::m_commands.emptyAndDelete(); +} + +Jupiter::GenericCommand::ResponseLine *Jupiter::GenericCommandNamespace::trigger(const Jupiter::ReadableString &in_input) +{ + GenericCommand *command; + Jupiter::ReferenceString input(in_input); + + if (input.wordCount(GENERIC_COMMAND_WORD_DELIMITER_CS) == 0) // No parameters; list commands + return new Jupiter::GenericCommand::ResponseLine(Jupiter::GenericCommandNamespace::m_help, Jupiter::GenericCommand::DisplayType::PrivateSuccess); + + command = Jupiter::GenericCommandNamespace::getCommand(input.getWord(0, GENERIC_COMMAND_WORD_DELIMITER_CS)); + if (command != nullptr) + return command->trigger(input.gotoWord(1, GENERIC_COMMAND_WORD_DELIMITER_CS)); + + return new Jupiter::GenericCommand::ResponseLine(Jupiter::ReferenceString::empty, Jupiter::GenericCommand::DisplayType::PrivateError); +} + +const Jupiter::ReadableString &Jupiter::GenericCommandNamespace::getHelp(const Jupiter::ReadableString ¶meters) +{ + static Jupiter::ReferenceString not_found = "Error: Command not found"_jrs; + + if (parameters.wordCount(GENERIC_COMMAND_WORD_DELIMITER_CS) == 0) // No parameters; list commands + return Jupiter::GenericCommandNamespace::m_help; + + Jupiter::ReferenceString input(parameters); + GenericCommand *command; + + // Search for command + command = Jupiter::GenericCommandNamespace::getCommand(input.getWord(0, GENERIC_COMMAND_WORD_DELIMITER_CS)); + if (command != nullptr) + return command->getHelp(input.gotoWord(1, GENERIC_COMMAND_WORD_DELIMITER_CS)); + + // Command not found + return not_found; +} + +bool Jupiter::GenericCommandNamespace::isNamespace() const +{ + return true; +} + +bool Jupiter::GenericCommandNamespace::isUsing() const +{ + return m_using; +} + +void Jupiter::GenericCommandNamespace::setUsing(bool in_value) +{ + m_using = in_value; + + if (Jupiter::GenericCommand::m_parent != nullptr) + Jupiter::GenericCommand::m_parent->updateHelp(); +} + +Jupiter::ArrayList Jupiter::GenericCommandNamespace::getCommands() const { - size_t count = o_genericCommands.size(); - while (count != 0) + Jupiter::ArrayList result(Jupiter::GenericCommandNamespace::m_commands.size()); + Jupiter::GenericCommand *command; + size_t tmp_index; + + for (size_t command_index = 0; command_index != Jupiter::GenericCommandNamespace::m_commands.size(); ++command_index) { - Jupiter::GenericCommand *cmd = o_genericCommands.get(--count); - if (cmd->matches(trigger)) - return cmd; + command = Jupiter::GenericCommandNamespace::m_commands.get(command_index); + result.add(command); + + if (command->isNamespace() && reinterpret_cast(command)->isUsing()) + { + // Add commands from namespace + Jupiter::ArrayList tmp = reinterpret_cast(command)->getCommands(); + + for (tmp_index = 0; tmp_index != tmp.size(); ++tmp_index) + result.add(tmp.get(tmp_index)); + } } + + return result; +} + +Jupiter::GenericCommand *Jupiter::GenericCommandNamespace::getCommand(const Jupiter::ReadableString &in_command) const +{ + Jupiter::GenericCommand *command; + size_t command_index, namespaces = 0; + + /** This is broken into 2 loops in order to insure that exact matches are ALWAYS prioritized over inexact matches */ + + // Search commands + for (command_index = 0; command_index != Jupiter::GenericCommandNamespace::m_commands.size(); ++command_index) + { + command = Jupiter::GenericCommandNamespace::m_commands.get(command_index); + + if (command->matches(in_command)) // Command found + return command; + else if (command->isNamespace() && reinterpret_cast(command)->isUsing()) // Search namespace for matches + ++namespaces; + } + + // None found; check namespaces + for (command_index = 0; namespaces != 0; ++command_index) + { + command = Jupiter::GenericCommandNamespace::m_commands.get(command_index); + + if (command->isNamespace() && reinterpret_cast(command)->isUsing()) + { + command = reinterpret_cast(command)->getCommand(in_command); + if (command != nullptr) // match found + return command; + + --namespaces; + } + } + return nullptr; } + +void Jupiter::GenericCommandNamespace::addCommand(Jupiter::GenericCommand &in_command) +{ + if (in_command.getNamespace() != this) + in_command.setNamespace(*this); + else + { + Jupiter::GenericCommandNamespace::m_commands.add(&in_command); + Jupiter::GenericCommandNamespace::updateHelp(); + } +} + +void Jupiter::GenericCommandNamespace::removeCommand(Jupiter::GenericCommand &in_command) +{ + for (size_t index = 0; index != Jupiter::GenericCommandNamespace::m_commands.size(); ++index) + if (Jupiter::GenericCommandNamespace::m_commands.get(index) == &in_command) + { + Jupiter::GenericCommandNamespace::m_commands.remove(index); + Jupiter::GenericCommandNamespace::updateHelp(); + return; + } +} + +void Jupiter::GenericCommandNamespace::removeCommand(const Jupiter::ReadableString &in_command) +{ + for (size_t index = 0; index != Jupiter::GenericCommandNamespace::m_commands.size(); ++index) + if (Jupiter::GenericCommandNamespace::m_commands.get(index)->matches(in_command)) + { + Jupiter::GenericCommandNamespace::m_commands.remove(index); + Jupiter::GenericCommandNamespace::updateHelp(); + return; + } +} + +void Jupiter::GenericCommandNamespace::updateHelp() +{ + Jupiter::ArrayList commands = Jupiter::GenericCommandNamespace::getCommands(); + Jupiter::StringL tmp_help(commands.size() * 8); + + for (size_t index = 0; index != commands.size(); ++index) + { + tmp_help += commands.get(index)->getTrigger(); + tmp_help += ' '; + } + + Jupiter::GenericCommandNamespace::m_help = tmp_help; +} diff --git a/Jupiter/GenericCommand.h b/Jupiter/GenericCommand.h index f1ecf39..26f11ff 100644 --- a/Jupiter/GenericCommand.h +++ b/Jupiter/GenericCommand.h @@ -26,6 +26,7 @@ #include "Command.h" #include "String.h" +#include "ArrayList.h" /** DLL Linkage Nagging */ #if defined _MSC_VER @@ -35,6 +36,8 @@ namespace Jupiter { + class GenericCommandNamespace; + /** * @brief Provides the base for generic commands. */ @@ -55,6 +58,7 @@ namespace Jupiter { Jupiter::StringS response; GenericCommand::DisplayType type; + uint8_t error = 0; // 0 = no error; 1 = command not found ResponseLine *next = nullptr; /** @@ -65,8 +69,10 @@ namespace Jupiter * @return This. */ ResponseLine *set(const Jupiter::ReadableString &response, GenericCommand::DisplayType type); + ResponseLine *set(uint8_t in_error); ResponseLine() = default; ResponseLine(const Jupiter::ReadableString &response, GenericCommand::DisplayType type); + ResponseLine(uint8_t in_error); }; /** @@ -76,6 +82,35 @@ namespace Jupiter */ virtual ResponseLine *trigger(const Jupiter::ReadableString &input) = 0; + /** + * @brief Checks if this command is a namespace + * + * @return True if the command is a namespace, false otherwise + */ + virtual bool isNamespace() const; + + /** + * @brief Places this command in a namespace, creating new namespaces as necessary + * Note: This moves the command into a namespace relative to its current namespace + * + * @param in_namespace Name of the namespace to move the command into + */ + void setNamespace(const Jupiter::ReadableString &in_namespace); + + /** + * @brief Places this command in a namespace + * + * @param in_namespace Namespace to move the command into + */ + void setNamespace(GenericCommandNamespace &in_namespace); + + /** + * @brief Fetches the namespace this command is in + * + * @return Namespace this command is in, or nullptr if there is none. + */ + GenericCommandNamespace *getNamespace() const; + /** * @brief Default constructor for the generic command class. */ @@ -85,18 +120,48 @@ namespace Jupiter * @brief Destructor for the generic command class. */ virtual ~GenericCommand(); + + protected: + GenericCommandNamespace *m_parent = nullptr; }; /** - * @brief Fetches a generic command based on its trigger. - * - * @param trigger Trigger of the command to fetch. - * @return A generic command if it exists, nullptr otherwise. + * @brief Provides the base for generic command namespaces */ - JUPITER_API extern GenericCommand *getGenericCommand(const Jupiter::ReadableString &trigger); + class JUPITER_API GenericCommandNamespace : public Jupiter::GenericCommand + { + public: // Jupiter::Command + ResponseLine *trigger(const Jupiter::ReadableString &input) override; + const Jupiter::ReadableString &getHelp(const Jupiter::ReadableString ¶meters) override; + + public: // Jupiter::GenericCommand + bool isNamespace() const override; + + public: // Jupiter::GenericCommandNamespace + bool isUsing() const; + void setUsing(bool in_value); + + Jupiter::ArrayList getCommands() const; // differs from m_commands in that it checks if it's using a namespace + GenericCommand *getCommand(const Jupiter::ReadableString &in_command) const; + + void addCommand(GenericCommand &in_command); + void removeCommand(GenericCommand &in_command); + void removeCommand(const Jupiter::ReadableString &in_command); + + void updateHelp(); + + virtual ~GenericCommandNamespace(); + + protected: + bool m_using; + Jupiter::ArrayList m_commands; + + private: + Jupiter::StringS m_help; + }; /** Generic command list */ - JUPITER_API extern Jupiter::ArrayList *g_generic_commands; + JUPITER_API extern GenericCommandNamespace &g_generic_commands; } /** Generic Command Macros */ @@ -105,8 +170,8 @@ namespace Jupiter #define BASE_GENERIC_COMMAND(CLASS) \ public: \ CLASS(); \ - Jupiter::GenericCommand::ResponseLine *trigger(const Jupiter::ReadableString ¶meters); \ - const Jupiter::ReadableString &getHelp(const Jupiter::ReadableString ¶meters); \ + Jupiter::GenericCommand::ResponseLine *trigger(const Jupiter::ReadableString ¶meters) override; \ + const Jupiter::ReadableString &getHelp(const Jupiter::ReadableString ¶meters) override; \ static CLASS instance; /** Expands to become the entire declaration for a generic command. In most cases, this will be sufficient. */ diff --git a/Jupiter/IRC.cpp b/Jupiter/IRC.cpp deleted file mode 100644 index c6aa744..0000000 --- a/Jupiter/IRC.cpp +++ /dev/null @@ -1,27 +0,0 @@ -/** - * Copyright (C) 2013-2015 Jessica James. - * - * Permission to use, copy, modify, and/or distribute this software for any - * purpose with or without fee is hereby granted, provided that the above - * copyright notice and this permission notice appear in all copies. - * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY - * SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION - * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN - * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. - * - * Written by Jessica James - */ - -#include "IRC.h" - -const char Jupiter::IRC::CTCP = IRCCTCP[0]; /** IRC CTCP character */ -const char Jupiter::IRC::bold = IRCBOLD[0]; /** IRC bold character */ -const char Jupiter::IRC::color = IRCCOLOR[0]; /** IRC color character */ -const char Jupiter::IRC::normal = IRCNORMAL[0]; /** IRC normal character */ -const char Jupiter::IRC::reverse = IRCREVERSE[0]; /** IRC reverse character */ -const char Jupiter::IRC::italicize = IRCITALICIZE[0]; /** IRC italicize character */ -const char Jupiter::IRC::underline = IRCUNDERLINE[0]; /** IRC underline character */ \ No newline at end of file diff --git a/Jupiter/IRC.h b/Jupiter/IRC.h index ed34aa8..9c31789 100644 --- a/Jupiter/IRC.h +++ b/Jupiter/IRC.h @@ -1,5 +1,5 @@ /** - * Copyright (C) 2013-2015 Jessica James. + * Copyright (C) 2013-2016 Jessica James. * * Permission to use, copy, modify, and/or distribute this software for any * purpose with or without fee is hereby granted, provided that the above @@ -42,13 +42,19 @@ namespace Jupiter { namespace IRC { - JUPITER_API extern const char CTCP; /** IRC CTCP character */ - JUPITER_API extern const char bold; /** IRC bold character */ - JUPITER_API extern const char color; /** IRC color character */ - JUPITER_API extern const char normal; /** IRC normal character */ - JUPITER_API extern const char reverse; /** IRC reverse character */ - JUPITER_API extern const char italicize; /** IRC italicize character */ - JUPITER_API extern const char underline; /** IRC underline character */ + constexpr const char CTCP = 0x01; /** IRC CTCP character */ + constexpr const char bold = 0x02; /** IRC bold character */ + constexpr const char BOLD = Jupiter::IRC::bold; + constexpr const char color = 0x03; /** IRC color character */ + constexpr const char COLOR = Jupiter::IRC::color; + constexpr const char normal = 0x0F; /** IRC normal character */ + constexpr const char NORMAL = Jupiter::IRC::normal; + constexpr const char reverse = 0x16; /** IRC reverse character */ + constexpr const char REVERSE = Jupiter::IRC::reverse; + constexpr const char italicize = 0x1D; /** IRC italicize character */ + constexpr const char ITALICIZE = Jupiter::IRC::italicize; + constexpr const char underline = 0x1F; /** IRC underline character */ + constexpr const char UNDERLINE = Jupiter::IRC::underline; } } diff --git a/Jupiter/Jupiter.vcxproj b/Jupiter/Jupiter.vcxproj index 879f469..195d8d9 100644 --- a/Jupiter/Jupiter.vcxproj +++ b/Jupiter/Jupiter.vcxproj @@ -186,7 +186,6 @@ - diff --git a/Jupiter/Jupiter.vcxproj.filters b/Jupiter/Jupiter.vcxproj.filters index fb9aad8..2221399 100644 --- a/Jupiter/Jupiter.vcxproj.filters +++ b/Jupiter/Jupiter.vcxproj.filters @@ -63,9 +63,6 @@ Source Files - - Source Files\IRC - Source Files\IRC @@ -87,9 +84,6 @@ Source Files\Files - - Source Files - Source Files\Files @@ -129,6 +123,9 @@ Source Files\Object Extensions + + Source Files\Object Extensions + @@ -188,9 +185,6 @@ Header Files\Files - - Header Files - Header Files\Files @@ -266,6 +260,9 @@ Header Files\Object Extensions + + Header Files\Object Extensions + diff --git a/Release/Jupiter.lib b/Release/Jupiter.lib index c6114f2..551e1a5 100644 Binary files a/Release/Jupiter.lib and b/Release/Jupiter.lib differ