Version 6.4.0.3, tag libreoffice-6.4.0.3
[LibreOffice.git] / configmgr / source / valueparser.cxx
blob49ddba36fc273a2065833ba971bf8ad0f139d4b6
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <sal/config.h>
22 #include <cassert>
24 #include <com/sun/star/uno/Any.hxx>
25 #include <com/sun/star/uno/RuntimeException.hpp>
26 #include <com/sun/star/uno/Sequence.hxx>
27 #include <comphelper/sequence.hxx>
28 #include <rtl/string.h>
29 #include <rtl/string.hxx>
30 #include <rtl/ustring.hxx>
31 #include <sal/types.h>
32 #include <xmlreader/span.hxx>
33 #include <xmlreader/xmlreader.hxx>
35 #include "localizedvaluenode.hxx"
36 #include "node.hxx"
37 #include "nodemap.hxx"
38 #include "parsemanager.hxx"
39 #include "propertynode.hxx"
40 #include "type.hxx"
41 #include "valueparser.hxx"
43 namespace configmgr {
45 namespace {
47 bool parseHexDigit(char c, int * value) {
48 assert(value != nullptr);
49 if (c >= '0' && c <= '9') {
50 *value = c - '0';
51 return true;
53 if (c >= 'A' && c <= 'F') {
54 *value = c - 'A' + 10;
55 return true;
57 if (c >= 'a' && c <= 'f') {
58 *value = c - 'a' + 10;
59 return true;
61 return false;
64 bool parseValue(xmlreader::Span const & text, sal_Bool * value) {
65 assert(text.is() && value != nullptr);
66 if (text == "true" || text == "1") {
67 *value = true;
68 return true;
70 if (text == "false" || text == "0") {
71 *value = false;
72 return true;
74 return false;
77 bool parseValue(xmlreader::Span const & text, sal_Int16 * value) {
78 assert(text.is() && value != nullptr);
79 // For backwards compatibility, support hexadecimal values:
80 sal_Int32 n =
81 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
82 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
83 RTL_CONSTASCII_LENGTH("0X")) == 0 ?
84 static_cast< sal_Int32 >(
85 OString(
86 text.begin + RTL_CONSTASCII_LENGTH("0X"),
87 text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt32(16)) :
88 OString(text.begin, text.length).toInt32();
89 //TODO: check valid lexical representation
90 if (n >= SAL_MIN_INT16 && n <= SAL_MAX_INT16) {
91 *value = static_cast< sal_Int16 >(n);
92 return true;
94 return false;
97 bool parseValue(xmlreader::Span const & text, sal_Int32 * value) {
98 assert(text.is() && value != nullptr);
99 // For backwards compatibility, support hexadecimal values:
100 *value =
101 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
102 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
103 RTL_CONSTASCII_LENGTH("0X")) == 0 ?
104 static_cast< sal_Int32 >(
105 OString(
106 text.begin + RTL_CONSTASCII_LENGTH("0X"),
107 text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt32(16)) :
108 OString(text.begin, text.length).toInt32();
109 //TODO: check valid lexical representation
110 return true;
113 bool parseValue(xmlreader::Span const & text, sal_Int64 * value) {
114 assert(text.is() && value != nullptr);
115 // For backwards compatibility, support hexadecimal values:
116 *value =
117 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
118 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
119 RTL_CONSTASCII_LENGTH("0X")) == 0 ?
120 static_cast< sal_Int64 >(
121 OString(
122 text.begin + RTL_CONSTASCII_LENGTH("0X"),
123 text.length - RTL_CONSTASCII_LENGTH("0X")).toUInt64(16)) :
124 OString(text.begin, text.length).toInt64();
125 //TODO: check valid lexical representation
126 return true;
129 bool parseValue(xmlreader::Span const & text, double * value) {
130 assert(text.is() && value != nullptr);
131 *value = OString(text.begin, text.length).toDouble();
132 //TODO: check valid lexical representation
133 return true;
136 bool parseValue(xmlreader::Span const & text, OUString * value) {
137 assert(text.is() && value != nullptr);
138 *value = text.convertFromUtf8();
139 return true;
142 bool parseValue(
143 xmlreader::Span const & text, css::uno::Sequence< sal_Int8 > * value)
145 assert(text.is() && value != nullptr);
146 if ((text.length & 1) != 0) {
147 return false;
149 std::vector< sal_Int8 > seq;
150 for (sal_Int32 i = 0; i != text.length;) {
151 int n1;
152 int n2;
153 if (!parseHexDigit(text.begin[i++], &n1) ||
154 !parseHexDigit(text.begin[i++], &n2))
156 return false;
158 seq.push_back(static_cast< sal_Int8 >((n1 << 4) | n2));
160 *value = comphelper::containerToSequence(seq);
161 return true;
164 template< typename T > css::uno::Any parseSingleValue(
165 xmlreader::Span const & text)
167 T val;
168 if (!parseValue(text, &val)) {
169 throw css::uno::RuntimeException("invalid value");
171 return css::uno::Any(val);
174 template< typename T > css::uno::Any parseListValue(
175 OString const & separator, xmlreader::Span const & text)
177 std::vector< T > seq;
178 xmlreader::Span sep;
179 if (separator.isEmpty()) {
180 sep = xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" "));
181 } else {
182 sep = xmlreader::Span(separator.getStr(), separator.getLength());
184 if (text.length != 0) {
185 for (xmlreader::Span t(text);;) {
186 sal_Int32 i = rtl_str_indexOfStr_WithLength(
187 t.begin, t.length, sep.begin, sep.length);
188 T val;
189 if (!parseValue(
190 xmlreader::Span(t.begin, i == -1 ? t.length : i), &val))
192 throw css::uno::RuntimeException("invalid value");
194 seq.push_back(val);
195 if (i < 0) {
196 break;
198 t.begin += i + sep.length;
199 t.length -= i + sep.length;
202 return css::uno::Any(comphelper::containerToSequence(seq));
205 css::uno::Any parseValue(
206 OString const & separator, xmlreader::Span const & text, Type type)
208 switch (type) {
209 case TYPE_ANY:
210 throw css::uno::RuntimeException("invalid value of type any");
211 case TYPE_BOOLEAN:
212 return parseSingleValue< sal_Bool >(text);
213 case TYPE_SHORT:
214 return parseSingleValue< sal_Int16 >(text);
215 case TYPE_INT:
216 return parseSingleValue< sal_Int32 >(text);
217 case TYPE_LONG:
218 return parseSingleValue< sal_Int64 >(text);
219 case TYPE_DOUBLE:
220 return parseSingleValue< double >(text);
221 case TYPE_STRING:
222 return parseSingleValue< OUString >(text);
223 case TYPE_HEXBINARY:
224 return parseSingleValue< css::uno::Sequence< sal_Int8 > >(text);
225 case TYPE_BOOLEAN_LIST:
226 return parseListValue< sal_Bool >(separator, text);
227 case TYPE_SHORT_LIST:
228 return parseListValue< sal_Int16 >(separator, text);
229 case TYPE_INT_LIST:
230 return parseListValue< sal_Int32 >(separator, text);
231 case TYPE_LONG_LIST:
232 return parseListValue< sal_Int64 >(separator, text);
233 case TYPE_DOUBLE_LIST:
234 return parseListValue< double >(separator, text);
235 case TYPE_STRING_LIST:
236 return parseListValue< OUString >(separator, text);
237 case TYPE_HEXBINARY_LIST:
238 return parseListValue< css::uno::Sequence< sal_Int8 > >(
239 separator, text);
240 default:
241 assert(false);
242 throw css::uno::RuntimeException("this cannot happen");
248 ValueParser::ValueParser(int layer): type_(TYPE_ERROR), layer_(layer), state_() {}
250 ValueParser::~ValueParser() {}
252 xmlreader::XmlReader::Text ValueParser::getTextMode() const {
253 if (node_.is()) {
254 switch (state_) {
255 case State::Text:
256 if (!items_.empty()) {
257 break;
259 [[fallthrough]];
260 case State::IT:
261 return
262 (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST ||
263 !separator_.isEmpty())
264 ? xmlreader::XmlReader::Text::Raw
265 : xmlreader::XmlReader::Text::Normalized;
266 default:
267 break;
270 return xmlreader::XmlReader::Text::NONE;
273 bool ValueParser::startElement(
274 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
276 if (!node_.is()) {
277 return false;
279 switch (state_) {
280 case State::Text:
281 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && name == "it" &&
282 isListType(type_) && separator_.isEmpty())
284 pad_.clear();
285 // before first <it>, characters are not ignored; assume they
286 // are only whitespace
287 state_ = State::IT;
288 return true;
290 [[fallthrough]];
291 case State::IT:
292 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
293 name == "unicode" &&
294 (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST))
296 sal_Int32 scalar = -1;
297 for (;;) {
298 int attrNsId;
299 xmlreader::Span attrLn;
300 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
301 break;
303 if (attrNsId == ParseManager::NAMESPACE_OOR &&
304 attrLn == "scalar")
306 if (!parseValue(reader.getAttributeValue(true), &scalar)) {
307 scalar = -1;
309 break;
312 if (scalar >= 0 && scalar < 0x20 && scalar != 0x09 &&
313 scalar != 0x0A && scalar != 0x0D)
315 char c = static_cast< char >(scalar);
316 pad_.add(&c, 1);
317 } else if (scalar == 0xFFFE) {
318 pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBE"));
319 } else if (scalar == 0xFFFF) {
320 pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBF"));
321 } else {
322 throw css::uno::RuntimeException(
323 "bad unicode scalar attribute in " + reader.getUrl());
325 state_ = state_ == State::Text ? State::TextUnicode : State::ITUnicode;
326 return true;
328 break;
329 default:
330 break;
332 throw css::uno::RuntimeException(
333 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
336 bool ValueParser::endElement() {
337 if (!node_.is()) {
338 return false;
340 switch (state_) {
341 case State::Text:
343 css::uno::Any *pValue = nullptr;
345 switch (node_->kind()) {
346 case Node::KIND_PROPERTY:
347 pValue = static_cast< PropertyNode * >(node_.get())->getValuePtr(layer_);
348 break;
349 case Node::KIND_LOCALIZED_PROPERTY:
351 NodeMap & members = node_->getMembers();
352 NodeMap::iterator i(members.find(localizedName_));
353 LocalizedValueNode *pLVNode;
354 if (i == members.end()) {
355 pLVNode = new LocalizedValueNode(layer_);
356 members.insert(
357 NodeMap::value_type(localizedName_, pLVNode ));
358 } else {
359 pLVNode = static_cast< LocalizedValueNode * >(i->second.get());
361 pValue = pLVNode->getValuePtr(layer_);
363 break;
364 default:
365 assert(false); // this cannot happen
366 return false;
369 if (items_.empty()) {
370 *pValue = parseValue(separator_, pad_.get(), type_);
371 pad_.clear();
372 } else {
373 switch (type_) {
374 case TYPE_BOOLEAN_LIST:
375 *pValue = convertItems< sal_Bool >();
376 break;
377 case TYPE_SHORT_LIST:
378 *pValue = convertItems< sal_Int16 >();
379 break;
380 case TYPE_INT_LIST:
381 *pValue = convertItems< sal_Int32 >();
382 break;
383 case TYPE_LONG_LIST:
384 *pValue = convertItems< sal_Int64 >();
385 break;
386 case TYPE_DOUBLE_LIST:
387 *pValue = convertItems< double >();
388 break;
389 case TYPE_STRING_LIST:
390 *pValue = convertItems< OUString >();
391 break;
392 case TYPE_HEXBINARY_LIST:
393 *pValue = convertItems< css::uno::Sequence< sal_Int8 > >();
394 break;
395 default:
396 assert(false); // this cannot happen
397 break;
399 items_.clear();
401 separator_.clear();
402 node_.clear();
404 break;
405 case State::TextUnicode:
406 state_ = State::Text;
407 break;
408 case State::ITUnicode:
409 state_ = State::IT;
410 break;
411 case State::IT:
412 items_.push_back(
413 parseValue(OString(), pad_.get(), elementType(type_)));
414 pad_.clear();
415 state_ = State::Text;
416 break;
418 return true;
421 void ValueParser::characters(xmlreader::Span const & text) {
422 if (node_.is()) {
423 assert(state_ == State::Text || state_ == State::IT);
424 pad_.add(text.begin, text.length);
428 void ValueParser::start(
429 rtl::Reference< Node > const & node, OUString const & localizedName)
431 assert(node.is() && !node_.is());
432 node_ = node;
433 localizedName_ = localizedName;
434 state_ = State::Text;
438 template< typename T > css::uno::Any ValueParser::convertItems() {
439 css::uno::Sequence< T > seq(items_.size());
440 for (sal_Int32 i = 0; i < seq.getLength(); ++i) {
441 bool ok = (items_[i] >>= seq[i]);
442 assert(ok);
443 (void) ok; // avoid warnings
445 return css::uno::Any(seq);
450 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */