/***************************************************************************************** * * * OpenSpace * * * * Copyright (c) 2014-2020 * * * * 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. * ****************************************************************************************/ #include #include #include #include #include #include #include #include namespace { constexpr const char* HandlebarsFilename = "${WEB}/documentation/handlebars-v4.0.5.js"; constexpr const char* BootstrapFilename = "${WEB}/common/bootstrap.min.css"; constexpr const char* CssFilename = "${WEB}/documentation/style.css"; constexpr const char* JsFilename = "${WEB}/documentation/script.js"; } // namespace namespace openspace::documentation { DocumentationEngine* DocumentationEngine::_instance = nullptr; DocumentationEngine::DuplicateDocumentationException::DuplicateDocumentationException( Documentation documentation) : ghoul::RuntimeError(fmt::format( "Duplicate Documentation with name '{}' and id '{}'", documentation.name, documentation.id )) , documentation(std::move(documentation)) {} DocumentationEngine::DocumentationEngine() : DocumentationGenerator( "Top Level", "toplevel", { { "toplevelTemplate", "${WEB}/documentation/toplevel.hbs" }, } ) {} void DocumentationEngine::initialize() { ghoul_assert(!isInitialized(), "DocumentationEngine is already initialized"); _instance = new DocumentationEngine; } void DocumentationEngine::deinitialize() { ghoul_assert(isInitialized(), "DocumentationEngine is not initialized"); delete _instance; _instance = nullptr; } bool DocumentationEngine::isInitialized() { return _instance != nullptr; } DocumentationEngine& DocumentationEngine::ref() { if (_instance == nullptr) { _instance = new DocumentationEngine; registerCoreClasses(*_instance); } return *_instance; } std::string generateJsonDocumentation(const Documentation& d) { std::stringstream result; result << "{"; result << R"("name": ")" << d.name << "\","; result << R"("id": ")" << d.id << "\","; result << R"("entries": [)"; for (const DocumentationEntry& p : d.entries) { result << '{'; result << R"("key": ")" << p.key << "\","; result << R"("optional": )" << (p.optional ? "true" : "false") << ','; result << R"("type": ")" << p.verifier->type() << "\","; result << R"("documentation": ")" << escapedJson(p.documentation) << "\","; TableVerifier* tv = dynamic_cast(p.verifier.get()); ReferencingVerifier* rv = dynamic_cast(p.verifier.get()); if (rv) { const std::vector& documentations = DocEng.documentations(); auto it = std::find_if( documentations.begin(), documentations.end(), [rv](const Documentation& doc) { return doc.id == rv->identifier; } ); if (it == documentations.end()) { result << R"("reference": { "found": false })"; } else { result << R"("reference": {)" << R"("found": true,)" << R"("name": ")" << it->name << "\"," << R"("identifier": ")" << rv->identifier << '\"' << '}'; } } else if (tv) { std::string json = generateJsonDocumentation({ "", "", tv->documentations }); // We have a TableVerifier, so we need to recurse result << R"("restrictions": )" << json; } else { result << R"("description": ")" << p.verifier->documentation() << '\"'; } result << '}'; if (&p != &d.entries.back()) { result << ", "; } } result << ']'; result << '}'; return result.str(); } std::string DocumentationEngine::generateJson() const { std::stringstream json; json << "["; for (const Documentation& d : _documentations) { json << generateJsonDocumentation(d); if (&d != &_documentations.back()) { json << ", "; } } json << "]"; return json.str(); } void DocumentationEngine::addDocumentation(Documentation documentation) { if (documentation.id.empty()) { _documentations.push_back(std::move(documentation)); } else { auto it = std::find_if( _documentations.begin(), _documentations.end(), [documentation](const Documentation& d) { return documentation.id == d.id; } ); if (it != _documentations.end()) { throw DuplicateDocumentationException(std::move(documentation)); } else { _documentations.push_back(std::move(documentation)); } } } void DocumentationEngine::addHandlebarTemplates(std::vector templates) { _handlebarTemplates.insert( std::end(_handlebarTemplates), std::begin(templates), std::end(templates) ); } std::vector DocumentationEngine::documentations() const { return _documentations; } void DocumentationEngine::writeDocumentationHtml(const std::string& path, std::string data) { ZoneScoped std::ifstream handlebarsInput; handlebarsInput.exceptions(~std::ofstream::goodbit); handlebarsInput.open(absPath(HandlebarsFilename)); const std::string handlebarsContent = std::string( std::istreambuf_iterator(handlebarsInput), std::istreambuf_iterator() ); std::ifstream jsInput; jsInput.exceptions(~std::ofstream::goodbit); jsInput.open(absPath(JsFilename)); const std::string jsContent = std::string( std::istreambuf_iterator(jsInput), std::istreambuf_iterator() ); std::ifstream bootstrapInput; bootstrapInput.exceptions(~std::ofstream::goodbit); bootstrapInput.open(absPath(BootstrapFilename)); const std::string bootstrapContent = std::string( std::istreambuf_iterator(bootstrapInput), std::istreambuf_iterator() ); std::ifstream cssInput; cssInput.exceptions(~std::ofstream::goodbit); cssInput.open(absPath(CssFilename)); const std::string cssContent = std::string( std::istreambuf_iterator(cssInput), std::istreambuf_iterator() ); std::string filename = path + ("index.html"); std::ofstream file; file.exceptions(~std::ofstream::goodbit); file.open(filename); // We probably should escape backslashes here? file << "" << '\n' << "" << '\n' << " " << "" << '\n'; //write handlebar templates to htmlpage as script elements (as per hb) for (const HandlebarTemplate& t : _handlebarTemplates) { const char* Type = "text/x-handlebars-template"; file << " " << '\n'; } //write main template file << " " << '\n'; //write scripte to register templates dynamically file << " " << '\n'; const std::string DataId = "data"; const std::string Version = "[" + std::to_string(OPENSPACE_VERSION_MAJOR) + "," + std::to_string(OPENSPACE_VERSION_MINOR) + "," + std::to_string(OPENSPACE_VERSION_PATCH) + "]"; file << " " << "" << '\n'; file << " " << "" << '\n' << " " << "" << '\n' << " " << "OpenSpace Documentation" << '\n' << " " << "" << '\n' << " " << "" << '\n' << " " << "" << '\n' << "" << '\n'; } } // namespace openspace::documentation