From a3dde8da4168c8ade095b2a7a4a1a22c78b79eb4 Mon Sep 17 00:00:00 2001 From: Alexander Bock Date: Tue, 13 Sep 2016 15:30:11 +0200 Subject: [PATCH] First working implementation of automatic documentation generation --- include/openspace/util/documentation.h | 361 +++++++++++++++++++++++++ src/CMakeLists.txt | 1 + 2 files changed, 362 insertions(+) create mode 100644 include/openspace/util/documentation.h diff --git a/include/openspace/util/documentation.h b/include/openspace/util/documentation.h new file mode 100644 index 0000000000..5cec2aee9a --- /dev/null +++ b/include/openspace/util/documentation.h @@ -0,0 +1,361 @@ +/***************************************************************************************** + * * + * OpenSpace * + * * + * Copyright (c) 2014-2016 * + * * + * Permission is hereby granted, free of charge, to any person obtaining a copy of this * + * software and associated documentation files (the "Software"), to deal in the Software * + * without restriction, including without limitation the rights to use, copy, modify, * + * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * + * permit persons to whom the Software is furnished to do so, subject to the following * + * conditions: * + * * + * The above copyright notice and this permission notice shall be included in all copies * + * or substantial portions of the Software. * + * * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, * + * INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A * + * PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT * + * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF * + * CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE * + * OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. * + ****************************************************************************************/ + +#ifndef __DOCUMENTATION_H__ +#define __DOCUMENTATION_H__ + +#include +#include + +#include +#include +#include +#include +#include + +namespace openspace { +namespace documentation { + +struct AbstractVerifier { + using Success = bool; + using Offender = std::vector; + using TestResult = std::tuple; + + virtual TestResult operator()(const ghoul::Dictionary& dict, const std::string& key) const { + bool testSuccess = test(dict, key); + if (testSuccess) { + return { testSuccess, {} }; + } + else { + return { testSuccess, {key} }; + } + } + + virtual bool test(const ghoul::Dictionary& dict, const std::string& key) const { + return false; + }; + + virtual std::string documentation() const = 0; +}; + + +struct DocumentationEntry { + DocumentationEntry(std::string key, AbstractVerifier* t, bool optional = false, std::string doc = "") + : key(std::move(key)) + , tester(std::move(t)) + , optional(optional) + , documentation(std::move(doc)) {} + + std::string key; + std::unique_ptr tester; + bool optional; + std::string documentation; +}; + +using Documentation = std::vector; + + +std::tuple> testSpecification(const Documentation& d, const ghoul::Dictionary& dictionary) { + bool success = true; + std::vector offenders; + + for (const auto& p : d) { + AbstractVerifier& verifier = *(p.tester); + AbstractVerifier::TestResult res = verifier(dictionary, p.key); + if (!std::get<0>(res)) { + success = false; + offenders.insert( + offenders.end(), + std::get<1>(res).begin(), + std::get<1>(res).end() + ); + } + } + return { success, offenders }; +} + +std::string generateDocumentation(const Documentation& d) { + using namespace std::string_literals; + std::string result; + + for (const auto& p : d) { + result += p.key + '\n'; + result += "Optional: "s + (p.optional ? "true" : "false") + '\n'; + result += p.tester->documentation() + '\n'; + result += '\n'; + result += p.documentation + '\n'; + } + + return result; +} + +template +struct TemplateVerifier : public AbstractVerifier { + using Type = T; +}; + +struct BoolVerifier : public TemplateVerifier { + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return dict.hasKeyAndValue(key); + } + + std::string documentation() const override { + return "Type: Boolean"; + } +}; + +struct DoubleVerifier : public TemplateVerifier { + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return dict.hasKeyAndValue(key); + } + + std::string documentation() const override { + return "Type: Double"; + } +}; + +struct IntVerifier : public TemplateVerifier { + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + if (dict.hasKeyAndValue(key)) { + return true; + } + else { + if (dict.hasKeyAndValue(key)) { + // If we have a double value, we need to check if it is integer + double value = dict.value(key); + double intPart; + return modf(value, &intPart) == 0.0; + } + else { + // If we don't have a double value, we cannot have an int value + return false; + } + } + } + + std::string documentation() const override { + return "Type: Integer"; + } +}; + +struct StringVerifier : public TemplateVerifier { + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return dict.hasKeyAndValue(key); + } + + std::string documentation() const override { + return "Type: String"; + } +}; + +struct TableVerifier : public TemplateVerifier { + TableVerifier(Documentation d) : doc(std::move(d)) {} + + TestResult operator()(const ghoul::Dictionary& dict, const std::string& key) const override { + if (dict.hasKeyAndValue(key)) { + ghoul::Dictionary d = dict.value(key); + AbstractVerifier::TestResult res = testSpecification(doc, d); + + for (std::string& s : std::get<1>(res)) { + s = key + "." + s; + } + + return res; + } + return{ dict.hasKeyAndValue(key), {} }; + } + + std::string documentation() const override { + return "Type: Table" + '\n' + generateDocumentation(doc); + } + + Documentation doc; +}; + +template +struct LessVerifier : public T { + LessVerifier(typename T::Type value) : value(std::move(value)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return T::test(dict, key) && dict.value(key) < value; + } + + std::string documentation() const override { + return T::documentation() + '\n' + "Less than: " + std::to_string(value); + } + + typename T::Type value; +}; + +template +struct LessEqualVerifier : public T { + LessEqualVerifier(typename T::Type value) : value(std::move(value)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return T::test(dict, key) && dict.value(key) <= value; + } + + std::string documentation() const override { + return T::documentation() + '\n' + "Less or equal to: " + std::to_string(value); + } + + typename T::Type value; +}; + +template +struct GreaterVerifier : public T { + GreaterVerifier(typename T::Type value) : value(std::move(value)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return T::test(dict, key) && dict.value(key) > value; + } + + std::string documentation() const override { + return T::documentation() + '\n' + "Greater than: " + std::to_string(value); + } + + typename T::Type value; +}; + +template +struct GreaterEqualVerifier : public T { + GreaterEqualVerifier(typename T::Type value) : value(std::move(value)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return T::test(dict, key) && dict.value(key) >= value; + } + + std::string documentation() const override { + return T::documentation() + '\n' + "Greater or equal to: " + std::to_string(value); + } + + typename T::Type value; +}; + +template +struct EqualVerifier : public T { + EqualVerifier(typename T::Type value) : value(std::move(value)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return T::test(dict, key) && dict.value(key) == value; + } + + std::string documentation() const override { + return T::documentation() + '\n' + "Equal to: " + std::to_string(value); + } + + typename T::Type value; +}; + +template +struct UnequalVerifier : public T { + UnequalVerifier(typename T::Type value) : value(std::move(value)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + return T::test(dict, key) && dict.value(key) != value; + } + + std::string documentation() const override { + return T::documentation() + '\n' + "Unequal to: " + std::to_string(value); + } + + typename T::Type value; +}; + +template +struct InListVerifier : public T { + InListVerifier(std::vector values) : values(std::move(values)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + if (T::test(dict, key)) { + typename T::Type value = dict.value(key); + + auto it = std::find(values.begin(), values.end(), value); + return it != values.end(); + } + else { + return false; + } + } + + std::string documentation() const override { + std::string result = T::documentation() + '\n' + "In list {"; + + std::stringstream s; + std::copy(values.begin(), values.end(), std::ostream_iterator(s, ",")); + + std::string joined = s.str(); + // We need to remove a trailing ',' at the end of the string + result += joined.substr(0, joined.size() - 1); + + result += "}"; + return result; + } + + std::vector values; +}; + +template +struct NotInListVerifier : public T { + NotInListVerifier(std::vector values) : values(std::move(values)) {} + + bool test(const ghoul::Dictionary& dict, const std::string& key) const override { + if (T::test(dict, key)) { + typename T::Type value = dict.value(key); + + auto it = std::find(values.begin(), values.end(), value); + return it == values.end(); + } + else { + return false; + } + } + + std::string documentation() const override { + std::string result = T::documentation() + '\n' + "Not in list {"; + + std::stringstream s; + std::copy(values.begin(), values.end(), std::ostream_iterator(s, ",")); + + std::string joined = s.str(); + // We need to remove a trailing ',' at the end of the string + result += joined.substr(0, joined.size() - 1); + + + result += "}"; + return result; + } + + std::vector values; +}; + + + + + + +} // namespace documentation +} // namespace openspace + +#endif // __DOCUMENTATION_H__ diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 718429a4e2..9b0a640699 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -172,6 +172,7 @@ set(OPENSPACE_HEADER ${OPENSPACE_BASE_DIR}/include/openspace/util/blockplaneintersectiongeometry.h ${OPENSPACE_BASE_DIR}/include/openspace/util/boxgeometry.h ${OPENSPACE_BASE_DIR}/include/openspace/util/camera.h + ${OPENSPACE_BASE_DIR}/include/openspace/util/documentation.h ${OPENSPACE_BASE_DIR}/include/openspace/util/factorymanager.h ${OPENSPACE_BASE_DIR}/include/openspace/util/factorymanager.inl ${OPENSPACE_BASE_DIR}/include/openspace/util/keys.h