src/INIWriter.h
branchv_0
changeset 3 ae8775e0bc7a
parent 2 e753a7f967c8
child 4 372b161669e4
equal deleted inserted replaced
2:e753a7f967c8 3:ae8775e0bc7a
    20 #include <sstream>
    20 #include <sstream>
    21 
    21 
    22 #include <relpipe/common/type/typedefs.h>
    22 #include <relpipe/common/type/typedefs.h>
    23 #include <relpipe/reader/RelpipeReaderException.h>
    23 #include <relpipe/reader/RelpipeReaderException.h>
    24 
    24 
       
    25 #include "uri.h"
       
    26 #include "Dialect.h"
       
    27 #include "EscapingProcessor.h"
       
    28 
    25 namespace relpipe {
    29 namespace relpipe {
    26 namespace out {
    30 namespace out {
    27 namespace ini {
    31 namespace ini {
    28 
    32 
    29 class INIWriter {
    33 class INIWriter {
    33 
    37 
    34 	std::string keyValueSeparator = " = ";
    38 	std::string keyValueSeparator = " = ";
    35 	std::string commentSeparatorForSections = " ; ";
    39 	std::string commentSeparatorForSections = " ; ";
    36 	std::string commentSeparatorForEntries = " ; ";
    40 	std::string commentSeparatorForEntries = " ; ";
    37 	std::string commentSeparatorStandalone = "; ";
    41 	std::string commentSeparatorStandalone = "; ";
    38 	
    42 
    39 	bool hasContent = false;
    43 	bool hasContent = false;
    40 
    44 
    41 	enum class TokenType {
    45 	/**
    42 		SectionName,
    46 	 * TODO: use a common method
    43 		SectionTag,
    47 	 */
    44 		SectionComment,
    48 	bool parseBoolean(const relpipe::common::type::StringX& value) {
    45 		EntryKey,
    49 		if (value == L"true") return true;
    46 		EntrySubKey,
    50 		else if (value == L"false") return false;
    47 		EntryValue,
    51 		else throw relpipe::reader::RelpipeReaderException(L"Unable to parse boolean value: " + value + L" (expecting true or false)");
    48 		EntryComment,
    52 	}
    49 		StandaloneComment,
    53 
    50 	};
    54 	std::string escape(const relpipe::common::type::StringX& value, EscapingProcessor::TextType type) {
    51 
    55 		relpipe::common::type::StringX result = value;
    52 	std::string escape(TokenType type, relpipe::common::type::StringX value) {
    56 		EscapingProcessor::QuotingType quotingType = EscapingProcessor::QuotingType::None;
    53 		std::wstringstream result;
    57 		for (ConfiguredEscapingProcessor p : escapingProcessors) if (p.enbaled) result = p.processor->escape(result, type, quotingType);
    54 
    58 		return convertor.to_bytes(result);
    55 		for (wchar_t ch : value) {
    59 	}
    56 			if (ch == L'\\') result << "\\\\";
    60 
    57 			else if (ch == L'\n') result << L"\\n";
    61 	class ConfiguredEscapingProcessor {
    58 			else if (ch == L'\r') result << L"\\r";
    62 	public:
    59 			else if (ch == L'\t') result << L"\\t";
    63 		std::shared_ptr<EscapingProcessor> processor;
    60 			else if (ch == L'"') result << "\\\"";
    64 		const relpipe::common::type::StringX uri;
    61 			else result.put(ch);
    65 		bool enbaled;
    62 		}
    66 
    63 
    67 		ConfiguredEscapingProcessor(std::shared_ptr<EscapingProcessor> processor, const relpipe::common::type::StringX uri, bool enbaled) : processor(processor), uri(uri), enbaled(enbaled) {
    64 		// TODO: modular escaping (like unescaping in relpipe-in-ini)
    68 		}
    65 		return convertor.to_bytes(result.str());
    69 
       
    70 	};
       
    71 
       
    72 	std::vector<ConfiguredEscapingProcessor> escapingProcessors;
       
    73 
       
    74 	bool setEscaping(const relpipe::common::type::StringX& uri, const relpipe::common::type::StringX& value) {
       
    75 		for (ConfiguredEscapingProcessor& p : escapingProcessors) {
       
    76 			if (p.uri == uri) {
       
    77 				p.enbaled = parseBoolean(value);
       
    78 				return true;
       
    79 			}
       
    80 		}
       
    81 		return false;
       
    82 	}
       
    83 
       
    84 	class ConfiguredDialect {
       
    85 	public:
       
    86 		std::shared_ptr<Dialect> dialect;
       
    87 		const relpipe::common::type::StringX uri;
       
    88 
       
    89 		ConfiguredDialect(std::shared_ptr<Dialect> dialect, const relpipe::common::type::StringX uri) : dialect(dialect), uri(uri) {
       
    90 		}
       
    91 
       
    92 	};
       
    93 
       
    94 	std::vector<ConfiguredDialect> dialects;
       
    95 
       
    96 	void setDialect(const relpipe::common::type::StringX& uri) {
       
    97 		for (ConfiguredDialect& d : dialects) {
       
    98 			if (d.uri == uri) {
       
    99 				d.dialect->apply(*this);
       
   100 				return;
       
   101 			}
       
   102 		}
       
   103 		throw relpipe::reader::RelpipeReaderException(L"Unsupported INI dialect: " + uri);
    66 	}
   104 	}
    67 
   105 
    68 public:
   106 public:
    69 
   107 
    70 	class SectionStartEvent {
   108 	class SectionStartEvent {
    97 
   135 
    98 	virtual ~INIWriter() {
   136 	virtual ~INIWriter() {
    99 	};
   137 	};
   100 
   138 
   101 	void setOption(relpipe::common::type::StringX uri, relpipe::common::type::StringX value) {
   139 	void setOption(relpipe::common::type::StringX uri, relpipe::common::type::StringX value) {
   102 		// TODO: setOption()
   140 		// TODO: setOption(): escaping, quotes, allow-sections
   103 		if (uri == L"dialect");
   141 		if (uri == option::Dialect) setDialect(value);
   104 		else if (uri == L"comment-separator-for-sections") commentSeparatorForSections = convertor.to_bytes(value);
   142 		else if (uri == option::CommentSeparatorForSections) commentSeparatorForSections = convertor.to_bytes(value);
   105 		else if (uri == L"comment-separator-for-entries") commentSeparatorForEntries = convertor.to_bytes(value);
   143 		else if (uri == option::CommentSeparatorForEntries) commentSeparatorForEntries = convertor.to_bytes(value);
   106 		else if (uri == L"comment-separator-standalone") commentSeparatorStandalone = convertor.to_bytes(value);
   144 		else if (uri == option::CommentSeparatorStandalone) commentSeparatorStandalone = convertor.to_bytes(value);
   107 		else if (uri == L"key-value-separator") keyValueSeparator = convertor.to_bytes(value);
   145 		else if (uri == option::KeyValueSeparator) keyValueSeparator = convertor.to_bytes(value);
   108 		else if (uri == L"escape-backspace");
   146 		else if (uri == option::AllowSections);
   109 		else if (uri == L"escape-basic");
   147 		else if (uri == option::Quotes);
   110 		else if (uri == L"escape-java-properties");
   148 		else if (setEscaping(uri, value));
   111 		else throw relpipe::reader::RelpipeReaderException(L"Unsupported writer option: " + uri);
   149 		else throw relpipe::reader::RelpipeReaderException(L"Unsupported writer option: " + uri);
       
   150 	}
       
   151 
       
   152 	void addDialect(std::shared_ptr<Dialect> dialect, const relpipe::common::type::StringX uri, bool enabledByDefault) {
       
   153 		dialects.push_back({dialect, uri});
       
   154 		if (enabledByDefault) dialect->apply(*this);
       
   155 	}
       
   156 
       
   157 	void addEscapingProcessor(std::shared_ptr<EscapingProcessor> processor, const relpipe::common::type::StringX uri, bool enabledByDefault) {
       
   158 		escapingProcessors.push_back({processor, uri, enabledByDefault});
   112 	}
   159 	}
   113 
   160 
   114 	void startDocument() {
   161 	void startDocument() {
   115 	}
   162 	}
   116 
   163 
   118 		output.flush();
   165 		output.flush();
   119 	}
   166 	}
   120 
   167 
   121 	void startSection(const SectionStartEvent& event) {
   168 	void startSection(const SectionStartEvent& event) {
   122 		if (hasContent) output << std::endl;
   169 		if (hasContent) output << std::endl;
   123 		output << "[" << escape(TokenType::SectionName, event.name) << "]";
   170 		output << "[" << escape(event.name, EscapingProcessor::TextType::SectionName) << "]";
   124 		if (event.tag.size()) output << "[" << escape(TokenType::SectionTag, event.tag) << "]";
   171 		if (event.tag.size()) output << "[" << escape(event.tag, EscapingProcessor::TextType::SectionTag) << "]";
   125 		if (event.comment.size()) output << commentSeparatorForSections << escape(TokenType::SectionComment, event.comment);
   172 		if (event.comment.size()) output << commentSeparatorForSections << escape(event.comment, EscapingProcessor::TextType::SectionComment);
   126 		output << std::endl;
   173 		output << std::endl;
   127 		hasContent = true;
   174 		hasContent = true;
   128 	}
   175 	}
   129 
   176 
   130 	void endSection() {
   177 	void endSection() {
   131 		output.flush();
   178 		output.flush();
   132 	}
   179 	}
   133 
   180 
   134 	void entry(const EntryEvent& event) {
   181 	void entry(const EntryEvent& event) {
   135 		output << escape(TokenType::EntryKey, event.key);
   182 		output << escape(event.key, EscapingProcessor::TextType::EntryKey);
   136 		if (event.subKey.size()) output << "[" << escape(TokenType::EntrySubKey, event.subKey) << "]";
   183 		if (event.subKey.size()) output << "[" << escape(event.subKey, EscapingProcessor::TextType::EntrySubKey) << "]";
   137 		output << keyValueSeparator << escape(TokenType::EntryValue, event.value);
   184 		output << keyValueSeparator << escape(event.value, EscapingProcessor::TextType::EntryValue);
   138 		if (event.comment.size()) output << commentSeparatorForEntries << escape(TokenType::EntryComment, event.comment);
   185 		if (event.comment.size()) output << commentSeparatorForEntries << escape(event.comment, EscapingProcessor::TextType::EntryComment);
   139 		output << std::endl;
   186 		output << std::endl;
   140 		hasContent = true;
   187 		hasContent = true;
   141 	}
   188 	}
   142 
   189 
   143 	void comment(const CommentEvent& event) {
   190 	void comment(const CommentEvent& event) {
   144 		output << commentSeparatorStandalone << escape(TokenType::StandaloneComment, event.comment);
   191 		output << commentSeparatorStandalone << escape(event.comment, EscapingProcessor::TextType::StandaloneComment);
   145 		output << std::endl;
   192 		output << std::endl;
   146 		hasContent = true;
   193 		hasContent = true;
   147 	}
   194 	}
   148 
   195 
   149 	void whitespace(const WhitespaceEvent& event) {
   196 	void whitespace(const WhitespaceEvent& event) {