diff -r 000000000000 -r 1bb084f84eb9 src/INIWriter.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/INIWriter.h Wed Dec 09 17:55:03 2020 +0100 @@ -0,0 +1,159 @@ +/** + * Relational pipes + * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info) + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, version 3 of the License. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +#pragma once + +#include +#include + +#include +#include + +namespace relpipe { +namespace out { +namespace ini { + +class INIWriter { +private: + std::ostream& output; + std::wstring_convert> convertor; // TODO: local system encoding or generate INI always in UTF-8 like XML? + + std::string keyValueSeparator = " = "; + std::string commentSeparatorForSections = " ; "; + std::string commentSeparatorForEntries = " ; "; + std::string commentSeparatorStandalone = "; "; + + enum class TokenType { + SectionName, + SectionTag, + SectionComment, + EntryKey, + EntrySubKey, + EntryValue, + EntryComment, + StandaloneComment, + }; + + std::string escape(TokenType type, relpipe::common::type::StringX value) { + std::wstringstream result; + + for (wchar_t ch : value) { + if (ch == L'\\') result << "\\\\"; + else if (ch == L'\n') result << L"\\n"; + else if (ch == L'\r') result << L"\\r"; + else if (ch == L'\t') result << L"\\t"; + else if (ch == L'"') result << "\\\""; + else result.put(ch); + } + + // TODO: modular escaping (like unescaping in relpipe-in-ini) + return convertor.to_bytes(result.str()); + } + +public: + + class SectionStartEvent { + public: + relpipe::common::type::StringX comment; + relpipe::common::type::StringX name; + relpipe::common::type::StringX tag; + }; + + class EntryEvent { + public: + relpipe::common::type::StringX comment; + relpipe::common::type::StringX key; + relpipe::common::type::StringX subKey; + relpipe::common::type::StringX value; + }; + + class CommentEvent { + public: + relpipe::common::type::StringX comment; + }; + + class WhitespaceEvent { + public: + relpipe::common::type::StringX whitespace; + }; + + INIWriter(std::ostream& output) : output(output) { + } + + virtual ~INIWriter() { + }; + + void setOption(relpipe::common::type::StringX uri, relpipe::common::type::StringX value) { + // TODO: setOption() + if (uri == L"dialect"); + else if (uri == L"comment-separator-for-sections") commentSeparatorForSections = convertor.to_bytes(value); + else if (uri == L"comment-separator-for-entries") commentSeparatorForEntries = convertor.to_bytes(value); + else if (uri == L"comment-separator-standalone") commentSeparatorStandalone = convertor.to_bytes(value); + else if (uri == L"key-value-separator") keyValueSeparator = convertor.to_bytes(value); + else if (uri == L"escape-backspace"); + else if (uri == L"escape-basic"); + else if (uri == L"escape-java-properties"); + else throw relpipe::reader::RelpipeReaderException(L"Unsupported writer option: " + uri); + } + + void startDocument() { + } + + void endDocument() { + output.flush(); + } + + void startSection(const SectionStartEvent& event) { + output << "[" << escape(TokenType::SectionName, event.name) << "]"; + if (event.tag.size()) output << "[" << escape(TokenType::SectionTag, event.tag) << "]"; + if (event.comment.size()) output << commentSeparatorForSections << escape(TokenType::SectionComment, event.comment); + output << std::endl; + } + + void endSection() { + output << std::endl; + output.flush(); + } + + void entry(const EntryEvent& event) { + output << escape(TokenType::EntryKey, event.key); + if (event.subKey.size()) output << "[" << escape(TokenType::EntrySubKey, event.subKey) << "]"; + output << keyValueSeparator << escape(TokenType::EntryValue, event.value); + if (event.comment.size()) output << commentSeparatorForEntries << escape(TokenType::EntryComment, event.comment); + output << std::endl; + } + + void comment(const CommentEvent& event) { + output << commentSeparatorStandalone << escape(TokenType::StandaloneComment, event.comment); + } + + void whitespace(const WhitespaceEvent& event) { + for (wchar_t ch : event.whitespace) { + if (ch == L' ') output << " "; + else if (ch == L'\t') output << "\t"; + else if (ch == L'\n') output << "\n"; + else if (ch == L'\r'); // TODO: keep CR? + else; // TODO: throw exception if whitespace contains unexpected data? (should not happen) + } + } + + + +}; + +} +} +}