src/INIWriter.h
branchv_0
changeset 0 1bb084f84eb9
child 2 e753a7f967c8
equal deleted inserted replaced
-1:000000000000 0:1bb084f84eb9
       
     1 /**
       
     2  * Relational pipes
       
     3  * Copyright © 2020 František Kučera (Frantovo.cz, GlobalCode.info)
       
     4  *
       
     5  * This program is free software: you can redistribute it and/or modify
       
     6  * it under the terms of the GNU General Public License as published by
       
     7  * the Free Software Foundation, version 3 of the License.
       
     8  *
       
     9  * This program is distributed in the hope that it will be useful,
       
    10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
       
    12  * GNU General Public License for more details.
       
    13  *
       
    14  * You should have received a copy of the GNU General Public License
       
    15  * along with this program. If not, see <http://www.gnu.org/licenses/>.
       
    16  */
       
    17 #pragma once
       
    18 
       
    19 #include <string>
       
    20 #include <sstream>
       
    21 
       
    22 #include <relpipe/common/type/typedefs.h>
       
    23 #include <relpipe/reader/RelpipeReaderException.h>
       
    24 
       
    25 namespace relpipe {
       
    26 namespace out {
       
    27 namespace ini {
       
    28 
       
    29 class INIWriter {
       
    30 private:
       
    31 	std::ostream& output;
       
    32 	std::wstring_convert<std::codecvt_utf8<wchar_t>> convertor; // TODO: local system encoding or generate INI always in UTF-8 like XML?
       
    33 
       
    34 	std::string keyValueSeparator = " = ";
       
    35 	std::string commentSeparatorForSections = " ; ";
       
    36 	std::string commentSeparatorForEntries = " ; ";
       
    37 	std::string commentSeparatorStandalone = "; ";
       
    38 
       
    39 	enum class TokenType {
       
    40 		SectionName,
       
    41 		SectionTag,
       
    42 		SectionComment,
       
    43 		EntryKey,
       
    44 		EntrySubKey,
       
    45 		EntryValue,
       
    46 		EntryComment,
       
    47 		StandaloneComment,
       
    48 	};
       
    49 
       
    50 	std::string escape(TokenType type, relpipe::common::type::StringX value) {
       
    51 		std::wstringstream result;
       
    52 
       
    53 		for (wchar_t ch : value) {
       
    54 			if (ch == L'\\') result << "\\\\";
       
    55 			else if (ch == L'\n') result << L"\\n";
       
    56 			else if (ch == L'\r') result << L"\\r";
       
    57 			else if (ch == L'\t') result << L"\\t";
       
    58 			else if (ch == L'"') result << "\\\"";
       
    59 			else result.put(ch);
       
    60 		}
       
    61 
       
    62 		// TODO: modular escaping (like unescaping in relpipe-in-ini)
       
    63 		return convertor.to_bytes(result.str());
       
    64 	}
       
    65 
       
    66 public:
       
    67 
       
    68 	class SectionStartEvent {
       
    69 	public:
       
    70 		relpipe::common::type::StringX comment;
       
    71 		relpipe::common::type::StringX name;
       
    72 		relpipe::common::type::StringX tag;
       
    73 	};
       
    74 
       
    75 	class EntryEvent {
       
    76 	public:
       
    77 		relpipe::common::type::StringX comment;
       
    78 		relpipe::common::type::StringX key;
       
    79 		relpipe::common::type::StringX subKey;
       
    80 		relpipe::common::type::StringX value;
       
    81 	};
       
    82 
       
    83 	class CommentEvent {
       
    84 	public:
       
    85 		relpipe::common::type::StringX comment;
       
    86 	};
       
    87 
       
    88 	class WhitespaceEvent {
       
    89 	public:
       
    90 		relpipe::common::type::StringX whitespace;
       
    91 	};
       
    92 
       
    93 	INIWriter(std::ostream& output) : output(output) {
       
    94 	}
       
    95 
       
    96 	virtual ~INIWriter() {
       
    97 	};
       
    98 
       
    99 	void setOption(relpipe::common::type::StringX uri, relpipe::common::type::StringX value) {
       
   100 		// TODO: setOption()
       
   101 		if (uri == L"dialect");
       
   102 		else if (uri == L"comment-separator-for-sections") commentSeparatorForSections = convertor.to_bytes(value);
       
   103 		else if (uri == L"comment-separator-for-entries") commentSeparatorForEntries = convertor.to_bytes(value);
       
   104 		else if (uri == L"comment-separator-standalone") commentSeparatorStandalone = convertor.to_bytes(value);
       
   105 		else if (uri == L"key-value-separator") keyValueSeparator = convertor.to_bytes(value);
       
   106 		else if (uri == L"escape-backspace");
       
   107 		else if (uri == L"escape-basic");
       
   108 		else if (uri == L"escape-java-properties");
       
   109 		else throw relpipe::reader::RelpipeReaderException(L"Unsupported writer option: " + uri);
       
   110 	}
       
   111 
       
   112 	void startDocument() {
       
   113 	}
       
   114 
       
   115 	void endDocument() {
       
   116 		output.flush();
       
   117 	}
       
   118 
       
   119 	void startSection(const SectionStartEvent& event) {
       
   120 		output << "[" << escape(TokenType::SectionName, event.name) << "]";
       
   121 		if (event.tag.size()) output << "[" << escape(TokenType::SectionTag, event.tag) << "]";
       
   122 		if (event.comment.size()) output << commentSeparatorForSections << escape(TokenType::SectionComment, event.comment);
       
   123 		output << std::endl;
       
   124 	}
       
   125 
       
   126 	void endSection() {
       
   127 		output << std::endl;
       
   128 		output.flush();
       
   129 	}
       
   130 
       
   131 	void entry(const EntryEvent& event) {
       
   132 		output << escape(TokenType::EntryKey, event.key);
       
   133 		if (event.subKey.size()) output << "[" << escape(TokenType::EntrySubKey, event.subKey) << "]";
       
   134 		output << keyValueSeparator << escape(TokenType::EntryValue, event.value);
       
   135 		if (event.comment.size()) output << commentSeparatorForEntries << escape(TokenType::EntryComment, event.comment);
       
   136 		output << std::endl;
       
   137 	}
       
   138 
       
   139 	void comment(const CommentEvent& event) {
       
   140 		output << commentSeparatorStandalone << escape(TokenType::StandaloneComment, event.comment);
       
   141 	}
       
   142 
       
   143 	void whitespace(const WhitespaceEvent& event) {
       
   144 		for (wchar_t ch : event.whitespace) {
       
   145 			if (ch == L' ') output << " ";
       
   146 			else if (ch == L'\t') output << "\t";
       
   147 			else if (ch == L'\n') output << "\n";
       
   148 			else if (ch == L'\r'); // TODO: keep CR?
       
   149 			else; // TODO: throw exception if whitespace contains unexpected data? (should not happen)
       
   150 		}
       
   151 	}
       
   152 
       
   153 
       
   154 
       
   155 };
       
   156 
       
   157 }
       
   158 }
       
   159 }