1 | // |
2 | // PropertyFileConfiguration.cpp |
3 | // |
4 | // Library: Util |
5 | // Package: Configuration |
6 | // Module: PropertyFileConfiguration |
7 | // |
8 | // Copyright (c) 2004-2009, Applied Informatics Software Engineering GmbH. |
9 | // and Contributors. |
10 | // |
11 | // SPDX-License-Identifier: BSL-1.0 |
12 | // |
13 | |
14 | |
15 | #include "Poco/Util/PropertyFileConfiguration.h" |
16 | #include "Poco/Exception.h" |
17 | #include "Poco/String.h" |
18 | #include "Poco/Path.h" |
19 | #include "Poco/FileStream.h" |
20 | #include "Poco/LineEndingConverter.h" |
21 | #include "Poco/Ascii.h" |
22 | |
23 | |
24 | using Poco::trim; |
25 | using Poco::Path; |
26 | |
27 | |
28 | namespace Poco { |
29 | namespace Util { |
30 | |
31 | |
32 | PropertyFileConfiguration::PropertyFileConfiguration() : |
33 | _preserveComment(false) |
34 | { |
35 | } |
36 | |
37 | |
38 | PropertyFileConfiguration::PropertyFileConfiguration(std::istream& istr, bool ) : |
39 | _preserveComment(preserveComment) |
40 | { |
41 | load(istr, preserveComment); |
42 | } |
43 | |
44 | |
45 | PropertyFileConfiguration::PropertyFileConfiguration(const std::string& path, bool ) : |
46 | _preserveComment(preserveComment) |
47 | { |
48 | load(path, preserveComment); |
49 | } |
50 | |
51 | |
52 | PropertyFileConfiguration::~PropertyFileConfiguration() |
53 | { |
54 | } |
55 | |
56 | |
57 | void PropertyFileConfiguration::load(std::istream& istr, bool ) |
58 | { |
59 | _preserveComment = preserveComment; |
60 | clear(); |
61 | _fileContent.clear(); |
62 | _keyFileContentItMap.clear(); |
63 | |
64 | while (!istr.eof()) parseLine(istr); |
65 | } |
66 | |
67 | |
68 | void PropertyFileConfiguration::load(const std::string& path, bool ) |
69 | { |
70 | Poco::FileInputStream istr(path); |
71 | if (istr.good()) |
72 | load(istr, preserveComment); |
73 | else |
74 | throw Poco::OpenFileException(path); |
75 | } |
76 | |
77 | |
78 | void PropertyFileConfiguration::save(std::ostream& ostr) const |
79 | { |
80 | if (_preserveComment) |
81 | { |
82 | for (FileContent::const_iterator it = _fileContent.begin(); it != _fileContent.end(); ++it) |
83 | ostr << *it; |
84 | } |
85 | else |
86 | { |
87 | for (MapConfiguration::iterator it = begin(); it != end(); ++it) |
88 | ostr << composeOneLine(it->first, it->second); |
89 | } |
90 | } |
91 | |
92 | |
93 | void PropertyFileConfiguration::save(const std::string& path) const |
94 | { |
95 | Poco::FileOutputStream ostr(path); |
96 | if (ostr.good()) |
97 | { |
98 | Poco::OutputLineEndingConverter lec(ostr); |
99 | save(lec); |
100 | lec.flush(); |
101 | ostr.flush(); |
102 | if (!ostr.good()) throw Poco::WriteFileException(path); |
103 | } |
104 | else throw Poco::CreateFileException(path); |
105 | } |
106 | |
107 | |
108 | // If _preserveComment is true, not only save key-value into map |
109 | // but also save the entire file into _fileContent. |
110 | // Otherwise, only save key-value into map. |
111 | void PropertyFileConfiguration::parseLine(std::istream& istr) |
112 | { |
113 | skipSpace(istr); |
114 | |
115 | if (!istr.eof()) |
116 | { |
117 | if (isComment(istr.peek())) |
118 | { |
119 | if (_preserveComment) saveComment(istr); |
120 | else skipLine(istr); |
121 | } |
122 | else saveKeyValue(istr); |
123 | } |
124 | } |
125 | |
126 | |
127 | void PropertyFileConfiguration::skipSpace(std::istream& istr) const |
128 | { |
129 | while (!istr.eof() && Poco::Ascii::isSpace(istr.peek())) istr.get(); |
130 | } |
131 | |
132 | |
133 | int PropertyFileConfiguration::readChar(std::istream& istr) |
134 | { |
135 | for (;;) |
136 | { |
137 | int c = istr.get(); |
138 | if (c == '\\') |
139 | { |
140 | c = istr.get(); |
141 | switch (c) |
142 | { |
143 | case 't': |
144 | return '\t'; |
145 | case 'r': |
146 | return '\r'; |
147 | case 'n': |
148 | return '\n'; |
149 | case 'f': |
150 | return '\f'; |
151 | case '\r': |
152 | if (istr.peek() == '\n') |
153 | istr.get(); |
154 | continue; |
155 | case '\n': |
156 | continue; |
157 | default: |
158 | return c; |
159 | } |
160 | } |
161 | else if (c == '\n' || c == '\r') |
162 | return 0; |
163 | else |
164 | return c; |
165 | } |
166 | } |
167 | |
168 | |
169 | void PropertyFileConfiguration::setRaw(const std::string& key, const std::string& value) |
170 | { |
171 | MapConfiguration::setRaw(key, value); |
172 | if (_preserveComment) |
173 | { |
174 | // Insert the key-value to the end of _fileContent and update _keyFileContentItMap. |
175 | if (_keyFileContentItMap.count(key) == 0) |
176 | { |
177 | FileContent::iterator fit = _fileContent.insert(_fileContent.end(), composeOneLine(key, value)); |
178 | _keyFileContentItMap[key] = fit; |
179 | } |
180 | // Update the key-value in _fileContent. |
181 | else |
182 | { |
183 | FileContent::iterator fit = _keyFileContentItMap[key]; |
184 | *fit = composeOneLine(key, value); |
185 | } |
186 | } |
187 | } |
188 | |
189 | |
190 | void PropertyFileConfiguration::removeRaw(const std::string& key) |
191 | { |
192 | MapConfiguration::removeRaw(key); |
193 | // remove the key from _fileContent and _keyFileContentItMap. |
194 | if (_preserveComment) |
195 | { |
196 | _fileContent.erase(_keyFileContentItMap[key]); |
197 | _keyFileContentItMap.erase(key); |
198 | } |
199 | } |
200 | |
201 | bool PropertyFileConfiguration::(int c) const |
202 | { |
203 | return c == '#' || c == '!'; |
204 | } |
205 | |
206 | |
207 | void PropertyFileConfiguration::(std::istream& istr) |
208 | { |
209 | std::string ; |
210 | |
211 | int c = istr.get(); |
212 | while (!isNewLine(c)) |
213 | { |
214 | comment += (char) c; |
215 | c = istr.get(); |
216 | } |
217 | comment += (char) c; |
218 | |
219 | _fileContent.push_back(comment); |
220 | } |
221 | |
222 | |
223 | void PropertyFileConfiguration::skipLine(std::istream& istr) const |
224 | { |
225 | int c = istr.get(); |
226 | while (!isNewLine(c)) c = istr.get(); |
227 | } |
228 | |
229 | |
230 | void PropertyFileConfiguration::saveKeyValue(std::istream& istr) |
231 | { |
232 | int c = istr.get(); |
233 | |
234 | std::string key; |
235 | while (!isNewLine(c) && !isKeyValueSeparator(c)) |
236 | { |
237 | key += (char) c; |
238 | c = istr.get(); |
239 | } |
240 | |
241 | std::string value; |
242 | if (isKeyValueSeparator(c)) |
243 | { |
244 | c = readChar(istr); |
245 | while (!istr.eof() && c) |
246 | { |
247 | value += (char) c; |
248 | c = readChar(istr); |
249 | } |
250 | } |
251 | |
252 | setRaw(trim(key), trim(value)); |
253 | } |
254 | |
255 | |
256 | bool PropertyFileConfiguration::isNewLine(int c) const |
257 | { |
258 | return c == std::char_traits<char>::eof() || c == '\n' || c == '\r'; |
259 | } |
260 | |
261 | |
262 | std::string PropertyFileConfiguration::composeOneLine(const std::string& key, const std::string& value) const |
263 | { |
264 | std::string result = key + ": " ; |
265 | |
266 | for (std::string::const_iterator its = value.begin(); its != value.end(); ++its) |
267 | { |
268 | switch (*its) |
269 | { |
270 | case '\t': |
271 | result += "\\t" ; |
272 | break; |
273 | case '\r': |
274 | result += "\\r" ; |
275 | break; |
276 | case '\n': |
277 | result += "\\n" ; |
278 | break; |
279 | case '\f': |
280 | result += "\\f" ; |
281 | break; |
282 | case '\\': |
283 | result += "\\\\" ; |
284 | break; |
285 | default: |
286 | result += *its; |
287 | break; |
288 | } |
289 | } |
290 | |
291 | return result += "\n" ; |
292 | } |
293 | |
294 | |
295 | bool PropertyFileConfiguration::isKeyValueSeparator(int c) const |
296 | { |
297 | return c == '=' || c == ':'; |
298 | } |
299 | |
300 | |
301 | } } // namespace Poco::Util |
302 | |