From bb6640369a34a0708548b19b34c70f19db626fd8 Mon Sep 17 00:00:00 2001 From: Manuel Dobusch Date: Mon, 19 May 2014 12:45:44 +0200 Subject: [PATCH] Utility: TextAccess Abstraction for file or text access. review id = 16 fortune cookie message = Good news from someone dear is coming soon. --- bin/data/test_config.xml | 11 -- bin/data/test_text.txt | 7 + src/lib/CMakeLists.txt | 5 +- src/lib/utility/ConfigManager.cpp | 17 ++- src/lib/utility/ConfigManager.h | 7 +- src/lib/utility/text/TextAccess.cpp | 193 ++++++++++++++++++++++++++++ src/lib/utility/text/TextAccess.h | 47 +++++++ src/test/CMakeLists.txt | 1 + src/test/ConfigManagerTestSuite.h | 32 ++++- src/test/TextAccessTestSuite.h | 149 +++++++++++++++++++++ 10 files changed, 440 insertions(+), 29 deletions(-) delete mode 100644 bin/data/test_config.xml create mode 100644 bin/data/test_text.txt create mode 100644 src/lib/utility/text/TextAccess.cpp create mode 100644 src/lib/utility/text/TextAccess.h create mode 100644 src/test/TextAccessTestSuite.h diff --git a/bin/data/test_config.xml b/bin/data/test_config.xml deleted file mode 100644 index 5289f52b..00000000 --- a/bin/data/test_config.xml +++ /dev/null @@ -1,11 +0,0 @@ - - - - - 42 - 1 - 0 - - - - diff --git a/bin/data/test_text.txt b/bin/data/test_text.txt new file mode 100644 index 00000000..31b18034 --- /dev/null +++ b/bin/data/test_text.txt @@ -0,0 +1,7 @@ +"If you're a researcher on this book thing and you were on Earth, you must have been gathering material on it." +"Well, I was able to extend the original entry a bit, yes." +"Let me see what it says in this edition, then. I've got to see it." +... "What? Harmless! Is that all it's got to say? Harmless! One word! ... Well, for God's sake I hope you managed to recitify that a bit." +"Oh yes, well I managed to transmit a new entry off to the editor. He had to trim it a bit, but it's still an improvement." +"And what does it say now?" asked Arthur. +"Mostly harmless," admitted Ford with a slightly embarrassed cough. \ No newline at end of file diff --git a/src/lib/CMakeLists.txt b/src/lib/CMakeLists.txt index 6db84a59..ae58730d 100644 --- a/src/lib/CMakeLists.txt +++ b/src/lib/CMakeLists.txt @@ -111,7 +111,10 @@ add_files( utility/messaging/MessageListenerBase.h utility/messaging/MessageQueue.cpp utility/messaging/MessageQueue.h - + + utility/text/TextAccess.cpp + utility/text/TextAccess.h + utility/ConfigManager.cpp utility/ConfigManager.h utility/FileSystem.cpp diff --git a/src/lib/utility/ConfigManager.cpp b/src/lib/utility/ConfigManager.cpp index 4d87822d..27e613b6 100644 --- a/src/lib/utility/ConfigManager.cpp +++ b/src/lib/utility/ConfigManager.cpp @@ -1,8 +1,10 @@ #include "utility/ConfigManager.h" -std::shared_ptr ConfigManager::createAndLoad(const std::string& filePath) +#include "utility/text/TextAccess.h" + +std::shared_ptr ConfigManager::createAndLoad(const std::shared_ptr textAccess) { - std::shared_ptr configManager = std::shared_ptr(new ConfigManager(filePath)); + std::shared_ptr configManager = std::shared_ptr(new ConfigManager(textAccess)); configManager->load(); return configManager; } @@ -88,10 +90,11 @@ void ConfigManager::setValue(const std::string& key, const bool value) void ConfigManager::load() { - // LOG ("Loading config from file: " + m_filePath) + std::string text = m_textAccess->getText(); - TiXmlDocument doc(m_filePath.c_str()); - if (doc.LoadFile()) + TiXmlDocument doc; + const char* pTest = doc.Parse(text.c_str(), 0, TIXML_ENCODING_UTF8); + if (pTest != NULL) { TiXmlHandle docHandle(&doc); TiXmlNode *rootNode = docHandle.FirstChild("config").ToNode(); @@ -111,8 +114,8 @@ void ConfigManager::save() // LOG ("Saving config to file: " + m_filePath) } -ConfigManager::ConfigManager(const std::string& filePath) - : m_filePath(filePath) +ConfigManager::ConfigManager(const std::shared_ptr textAccess) + : m_textAccess(textAccess) { } diff --git a/src/lib/utility/ConfigManager.h b/src/lib/utility/ConfigManager.h index e4740633..b17d2c90 100644 --- a/src/lib/utility/ConfigManager.h +++ b/src/lib/utility/ConfigManager.h @@ -8,11 +8,12 @@ #include "tinyxml/tinyxml.h" +class TextAccess; class ConfigManager { public: - static std::shared_ptr createAndLoad(const std::string& filePath); + static std::shared_ptr createAndLoad(const std::shared_ptr textAccess); bool getValue(const std::string& key, std::string& value) const; bool getValue(const std::string& key, int& value) const; @@ -28,13 +29,13 @@ public: void save(); private: - ConfigManager(const std::string& filePath); + ConfigManager(const std::shared_ptr textAccess); ConfigManager(const ConfigManager&); ConfigManager operator=(const ConfigManager&); void parseSubtree(TiXmlNode* parentElement, const std::string& currentPath); - const std::string m_filePath; + const std::shared_ptr m_textAccess; std::map m_values; }; diff --git a/src/lib/utility/text/TextAccess.cpp b/src/lib/utility/text/TextAccess.cpp new file mode 100644 index 00000000..9a07cb4d --- /dev/null +++ b/src/lib/utility/text/TextAccess.cpp @@ -0,0 +1,193 @@ +#include "TextAccess.h" + +#include +#include + +#include "utility/logging/logging.h" + +std::shared_ptr TextAccess::createFromFile(const std::string& filePath) +{ + std::shared_ptr result(new TextAccess()); + + result->m_filePath = filePath; + result->m_lines = readFile(filePath); + + return result; +} + +std::shared_ptr TextAccess::createFromString(const std::string& text) +{ + std::shared_ptr result(new TextAccess()); + + result->m_lines = splitStringByLines(text); + + return result; +} + +TextAccess::~TextAccess() +{ +} + +unsigned int TextAccess::getLineCount() const +{ + return m_lines.size(); +} + +std::string TextAccess::getFilePath() const +{ + return m_filePath; +} + +std::string TextAccess::getLine(const unsigned int lineNumber) const +{ + if(lineNumber < 1) + { + std::stringstream message; + message << "Line numbers start with one, is " << lineNumber; + LOG_WARNING(message.str()); + + return ""; + } + + if (checkIndexInRange(lineNumber-1) == false) + { + return ""; + } + + return m_lines[lineNumber-1]; // -1 to correct for use as index +} + +std::vector TextAccess::getLines(const unsigned int firstLineNumber, const unsigned int lastLineNumber) +{ + if(firstLineNumber < 1) + { + std::stringstream message; + message << "Line numbers start with one, is " << firstLineNumber; + LOG_WARNING(message.str()); + + return std::vector(); + } + + if(lastLineNumber < 1) + { + std::stringstream message; + message << "Line numbers start with one, is " << lastLineNumber; + LOG_WARNING(message.str()); + + return std::vector(); + } + + if (checkIndexIntervalInRange(firstLineNumber-1, lastLineNumber-1) == false) + { + return std::vector(); + } + + std::vector::iterator first = m_lines.begin() + firstLineNumber-1; // -1 to correct for use as index + std::vector::iterator last = m_lines.begin() + lastLineNumber; // no correction needed because 'last' needs to point one behind the last included element + return std::vector(first, last); +} + +std::vector TextAccess::getAllLines() const +{ + return m_lines; +} + +std::string TextAccess::getText() const +{ + std::string result = ""; + + for(unsigned int i = 0; i < m_lines.size(); i++) + { + result += m_lines[i]; + } + + return result; +} + +std::vector TextAccess::readFile(const std::string& filePath) +{ + std::vector result; + + std::ifstream srcFile; + srcFile.open(filePath); + + while (!srcFile.eof()) + { + std::string line; + std::getline(srcFile, line); + result.push_back(line + "\n"); + } + srcFile.close(); + + return result; +} + +std::vector TextAccess::splitStringByLines(const std::string& text) +{ + std::vector result; + int prevIndex = 0; + int index = text.find("\n"); + + while (index != std::string::npos) + { + result.push_back(text.substr(prevIndex, index - prevIndex) + "\n"); + + prevIndex = index+1; + index = text.find("\n", prevIndex); + } + + if (prevIndex < text.length()-1) + { + result.push_back(text.substr(prevIndex)); + } + + return result; +} + +TextAccess::TextAccess() + : m_filePath("") +{ +} + +bool TextAccess::checkIndexInRange(const unsigned int index) const +{ + if (index >= m_lines.size()) + { + std::stringstream message; + message << "Tried to access index " << index; + message << ". Maximum index is " << m_lines.size() - 1; + LOG_WARNING(message.str()); + + return false; + } + + return true; +} + +bool TextAccess::checkIndexIntervalInRange( + const unsigned int firstIndex, + const unsigned int lastIndex +) const +{ + if (checkIndexInRange(firstIndex) == false) + { + return false; + } + + if (checkIndexInRange(lastIndex) == false) + { + return false; + } + + if (firstIndex > lastIndex) + { + std::stringstream message; + message << "Index 'firstLine' has to be lower or equal index 'lastLine'"; + message << ", is " << firstIndex << " > " << lastIndex; + LOG_WARNING(message.str()); + + return false; + } + + return true; +} diff --git a/src/lib/utility/text/TextAccess.h b/src/lib/utility/text/TextAccess.h new file mode 100644 index 00000000..8d40cc60 --- /dev/null +++ b/src/lib/utility/text/TextAccess.h @@ -0,0 +1,47 @@ +#ifndef TEXT_ACCESS_H +#define TEXT_ACCESS_H + +#include +#include +#include + +class TextAccess +{ +public: + static std::shared_ptr createFromFile(const std::string& filePath); + static std::shared_ptr createFromString(const std::string& text); + + virtual ~TextAccess(); + + unsigned int getLineCount() const; + + std::string getFilePath() const; + + /** + * @param lineNumber: starts with 1 + */ + std::string getLine(const unsigned int lineNumber) const; + /** + * @param firstLineNumber: starts with 1 + * @param lastLineNumber: starts with 1 + */ + std::vector getLines(const unsigned int firstLineNumber, const unsigned int lastLineNumber); + std::vector getAllLines() const; + std::string getText() const; + +private: + static std::vector readFile(const std::string& filePath); + static std::vector splitStringByLines(const std::string& text); + + TextAccess(); + TextAccess(const TextAccess&); + TextAccess operator=(const TextAccess&); + + bool checkIndexInRange(const unsigned int index) const; + bool checkIndexIntervalInRange(const unsigned int firstIndex, const unsigned int lastIndex) const; + + std::string m_filePath; + std::vector m_lines; +}; + +#endif // TEXT_ACCESS_H diff --git a/src/test/CMakeLists.txt b/src/test/CMakeLists.txt index 138e52ef..52876a2d 100644 --- a/src/test/CMakeLists.txt +++ b/src/test/CMakeLists.txt @@ -8,5 +8,6 @@ add_files( MessageQueueTestSuite.h TestSuiteFixture.cpp TestSuiteFixture.h + TextAccessTestSuite.h Vector2TestSuite.h ) diff --git a/src/test/ConfigManagerTestSuite.h b/src/test/ConfigManagerTestSuite.h index 1eda7653..3b024e16 100644 --- a/src/test/ConfigManagerTestSuite.h +++ b/src/test/ConfigManagerTestSuite.h @@ -1,14 +1,14 @@ #include "cxxtest/TestSuite.h" #include "utility/ConfigManager.h" - +#include "utility/text/TextAccess.h" class ConfigManagerTestSuite: public CxxTest::TestSuite { public: void test_config_manager_returns_true_when_key_is_found() { - std::shared_ptr config = ConfigManager::createAndLoad("data/test_config.xml"); + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); float value; bool success = config->getValue("path/to/single_value", value); @@ -19,7 +19,7 @@ public: void test_config_manager_returns_false_when_key_is_not_found() { - std::shared_ptr config = ConfigManager::createAndLoad("data/test_config.xml"); + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); float value; bool success = config->getValue("path/to/nowhere", value); @@ -30,7 +30,7 @@ public: void test_config_manager_returns_correct_string_for_key() { - std::shared_ptr config = ConfigManager::createAndLoad("data/test_config.xml"); + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); std::string value; config->getValue("path/to/single_value", value); @@ -41,7 +41,7 @@ public: void test_config_manager_returns_correct_float_for_key() { - std::shared_ptr config = ConfigManager::createAndLoad("data/test_config.xml"); + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); float value; config->getValue("path/to/single_value", value); @@ -51,7 +51,7 @@ public: void test_config_manager_returns_correct_bool_for_key_if_value_is_true() { - std::shared_ptr config = ConfigManager::createAndLoad("data/test_config.xml"); + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); float value; bool success(config->getValue("path/to/bool_that_is_true", value)); @@ -62,7 +62,7 @@ public: void test_config_manager_returns_correct_bool_for_key_if_value_is_false() { - std::shared_ptr config = ConfigManager::createAndLoad("data/test_config.xml"); + std::shared_ptr config = ConfigManager::createAndLoad(getConfigTextAccess()); float value; bool success(config->getValue("path/to/bool_that_is_false", value)); @@ -70,4 +70,22 @@ public: TS_ASSERT(success); TS_ASSERT(!value); } + +private: + std::shared_ptr getConfigTextAccess() + { + std::string text = + "\n" + "\n" + "\n" + "\n" + "42\n" + "1\n" + "0\n" + "\n" + "\n" + "\n"; + + return TextAccess::createFromString(text); + } }; diff --git a/src/test/TextAccessTestSuite.h b/src/test/TextAccessTestSuite.h new file mode 100644 index 00000000..3e42e96b --- /dev/null +++ b/src/test/TextAccessTestSuite.h @@ -0,0 +1,149 @@ +#include "cxxtest/TestSuite.h" + +#include +#include + +#include "utility/text/TextAccess.h" + +class TextAccessTestSuite : public CxxTest::TestSuite +{ +public: + void test_textAccessString_constructor() + { + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + + TS_ASSERT(textAccess.get() != NULL); + } + + void test_textAccessString_lines_count() + { + std::string text = getTestText(); + unsigned int lineCount = 8; + + std::shared_ptr textAccess = TextAccess::createFromString(text); + + TS_ASSERT_EQUALS(textAccess->getLineCount(), lineCount); + } + + void test_textAccessString_lines_content() + { + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::vector lines = textAccess->getLines(1, 4); + + TS_ASSERT_EQUALS(lines.size(), 4); + TS_ASSERT_EQUALS(lines[0], "\"But the plans were on display . . .\"\n"); + TS_ASSERT_EQUALS(lines[1], "\"On display? I eventually had to go down to the cellar to find them.\"\n"); + TS_ASSERT_EQUALS(lines[2], "\"That's the display department.\"\n"); + TS_ASSERT_EQUALS(lines[3], "\"With a torch.\"\n"); + } + + void test_textAccessString_lines_content_error_handling() + { + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::vector lines = textAccess->getLines(3, 2); + + TS_ASSERT_EQUALS(lines.size(), 0); + + lines = textAccess->getLines(10, 3); + + TS_ASSERT_EQUALS(lines.size(), 0); + + lines = textAccess->getLines(1, 10); + + TS_ASSERT_EQUALS(lines.size(), 0); + + std::string line = textAccess->getLine(0); + + TS_ASSERT_EQUALS(line, ""); + + lines = textAccess->getLines(0, 2); + + TS_ASSERT_EQUALS(line, ""); + } + + void test_textAccessString_single_line_content() + { + std::string text = getTestText(); + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::string line = textAccess->getLine(6); + + TS_ASSERT_EQUALS(line, "\"So had the stairs.\"\n"); + } + + void test_textAccessString_all_lines() + { + std::string text = getTestText(); + unsigned int lineCount = 8; + + std::shared_ptr textAccess = TextAccess::createFromString(text); + std::vector lines = textAccess->getAllLines(); + + TS_ASSERT_EQUALS(lines.size(), lineCount); + } + + void test_textAccessFile_constructor() + { + std::string filePath = "data/test_text.txt"; + + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + TS_ASSERT(textAccess.get() != NULL); + } + + void test_textAccessFile_lines_count() + { + std::string filePath = "data/test_text.txt"; + unsigned int lineCount = 7; + + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + TS_ASSERT_EQUALS(textAccess->getLineCount(), lineCount); + } + + void test_textAccessFile_lines_content() + { + std::string filePath = "data/test_text.txt"; + + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + std::vector lines = textAccess->getLines(1, 4); + + TS_ASSERT_EQUALS(lines.size(), 4); + TS_ASSERT_EQUALS(lines[0], "\"If you're a researcher on this book thing and you were on Earth, you must have been gathering material on it.\"\n"); + TS_ASSERT_EQUALS(lines[1], "\"Well, I was able to extend the original entry a bit, yes.\"\n"); + TS_ASSERT_EQUALS(lines[2], "\"Let me see what it says in this edition, then. I've got to see it.\"\n"); + TS_ASSERT_EQUALS(lines[3], "... \"What? Harmless! Is that all it's got to say? Harmless! One word! ... Well, for God's sake I hope you managed to recitify that a bit.\"\n"); + } + + void test_textAccessFile_get_filePath() + { + std::string filePath = "data/test_text.txt"; + std::shared_ptr textAccess = TextAccess::createFromFile(filePath); + + TS_ASSERT_EQUALS(textAccess->getFilePath(), filePath); + } + +private: + std::string getTestText() + { + std::string text = + "\"But the plans were on display . . .\"\n" + "\"On display? I eventually had to go down to the cellar to find them.\"\n" + "\"That's the display department.\"\n" + "\"With a torch.\"\n" + "\"Ah, well the lights had probably gone.\"\n" + "\"So had the stairs.\"\n" + "\"But look, you found the notice, didn't you?\"\n" + "\"Yes,\" said Arthur, \"yes I did. It was on display in the bottom of a locked" + " filing cabinet stuck in a disused lavatory with a sign on the door saying" + " Beware of the Leopard.\"\n"; + + return text; + } +};