CppunitTest_sc_tiledrendering2: move to tiledrendering folder
[LibreOffice.git] / configmgr / source / valueparser.cxx
blobedf39add25e58a4218bbb5845b6f98181e9d8b0a
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 <o3tl/string_view.hxx>
29 #include <rtl/math.h>
30 #include <rtl/string.h>
31 #include <rtl/string.hxx>
32 #include <rtl/ustring.hxx>
33 #include <sal/types.h>
34 #include <xmlreader/span.hxx>
35 #include <xmlreader/xmlreader.hxx>
37 #include "data.hxx"
38 #include "localizedvaluenode.hxx"
39 #include "node.hxx"
40 #include "nodemap.hxx"
41 #include "parsemanager.hxx"
42 #include "propertynode.hxx"
43 #include "type.hxx"
44 #include "valueparser.hxx"
46 namespace configmgr {
48 namespace {
50 bool parseHexDigit(char c, int * value) {
51 assert(value != nullptr);
52 if (c >= '0' && c <= '9') {
53 *value = c - '0';
54 return true;
56 if (c >= 'A' && c <= 'F') {
57 *value = c - 'A' + 10;
58 return true;
60 if (c >= 'a' && c <= 'f') {
61 *value = c - 'a' + 10;
62 return true;
64 return false;
67 bool parseValue(xmlreader::Span const & text, sal_Bool * value) {
68 assert(text.is() && value != nullptr);
69 if (text == "true" || text == "1") {
70 *value = true;
71 return true;
73 if (text == "false" || text == "0") {
74 *value = false;
75 return true;
77 return false;
80 bool parseValue(xmlreader::Span const & text, sal_Int16 * value) {
81 assert(text.is() && value != nullptr);
82 // For backwards compatibility, support hexadecimal values:
83 bool bStartWithHexPrefix =
84 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
85 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
86 RTL_CONSTASCII_LENGTH("0X")) == 0;
87 sal_Int32 n;
88 if (bStartWithHexPrefix)
90 std::string_view sView(
91 text.begin + RTL_CONSTASCII_LENGTH("0X"),
92 text.length - RTL_CONSTASCII_LENGTH("0X"));
93 n = o3tl::toUInt32(sView, 16);
95 else
96 n = o3tl::toInt32(std::string_view(text.begin, text.length));
97 //TODO: check valid lexical representation
98 if (n >= SAL_MIN_INT16 && n <= SAL_MAX_INT16) {
99 *value = static_cast< sal_Int16 >(n);
100 return true;
102 return false;
105 bool parseValue(xmlreader::Span const & text, sal_Int32 * value) {
106 assert(text.is() && value != nullptr);
107 // For backwards compatibility, support hexadecimal values:
108 bool bStartWithHexPrefix = rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
109 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
110 RTL_CONSTASCII_LENGTH("0X")) == 0;
112 if (bStartWithHexPrefix)
114 std::string_view sView(text.begin + RTL_CONSTASCII_LENGTH("0X"),
115 text.length - RTL_CONSTASCII_LENGTH("0X"));
116 *value = static_cast< sal_Int32 >(o3tl::toUInt32(sView, 16));
118 else
120 std::string_view sView(text.begin, text.length);
121 *value = o3tl::toInt32(sView);
123 //TODO: check valid lexical representation
124 return true;
127 bool parseValue(xmlreader::Span const & text, sal_Int64 * value) {
128 assert(text.is() && value != nullptr);
129 // For backwards compatibility, support hexadecimal values:
130 bool bStartWithHexPrefix =
131 rtl_str_shortenedCompareIgnoreAsciiCase_WithLength(
132 text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"),
133 RTL_CONSTASCII_LENGTH("0X")) == 0;
134 if (bStartWithHexPrefix)
136 OString sSuffix(
137 text.begin + RTL_CONSTASCII_LENGTH("0X"),
138 text.length - RTL_CONSTASCII_LENGTH("0X"));
139 *value = static_cast< sal_Int64 >(sSuffix.toUInt64(16));
141 else *value = o3tl::toInt64(std::string_view(text.begin, text.length));
142 //TODO: check valid lexical representation
143 return true;
146 bool parseValue(xmlreader::Span const & text, double * value) {
147 assert(text.is() && value != nullptr);
148 *value = rtl_math_stringToDouble(
149 text.begin, text.begin + text.length, '.', 0, nullptr, nullptr);
150 //TODO: check valid lexical representation
151 return true;
154 bool parseValue(xmlreader::Span const & text, OUString * value) {
155 assert(text.is() && value != nullptr);
156 *value = text.convertFromUtf8();
157 return true;
160 bool parseValue(
161 xmlreader::Span const & text, css::uno::Sequence< sal_Int8 > * value)
163 assert(text.is() && value != nullptr);
164 if ((text.length & 1) != 0) {
165 return false;
167 std::vector< sal_Int8 > seq;
168 for (sal_Int32 i = 0; i != text.length;) {
169 int n1;
170 int n2;
171 if (!parseHexDigit(text.begin[i++], &n1) ||
172 !parseHexDigit(text.begin[i++], &n2))
174 return false;
176 seq.push_back(static_cast< sal_Int8 >((n1 << 4) | n2));
178 *value = comphelper::containerToSequence(seq);
179 return true;
182 template< typename T > css::uno::Any parseSingleValue(
183 xmlreader::Span const & text)
185 T val;
186 if (!parseValue(text, &val)) {
187 throw css::uno::RuntimeException(u"invalid value"_ustr);
189 return css::uno::Any(val);
192 template< typename T > css::uno::Any parseListValue(
193 OString const & separator, xmlreader::Span const & text)
195 std::vector< T > seq;
196 xmlreader::Span sep;
197 if (separator.isEmpty()) {
198 sep = xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" "));
199 } else {
200 sep = xmlreader::Span(separator.getStr(), separator.getLength());
202 if (text.length != 0) {
203 for (xmlreader::Span t(text);;) {
204 // coverity[ tainted_data_return : FALSE ] version 2023.12.2
205 sal_Int32 i = rtl_str_indexOfStr_WithLength(
206 t.begin, t.length, sep.begin, sep.length);
207 T val;
208 if (!parseValue(
209 xmlreader::Span(t.begin, i == -1 ? t.length : i), &val))
211 throw css::uno::RuntimeException(u"invalid value"_ustr);
213 seq.push_back(val);
214 if (i < 0) {
215 break;
217 t.begin += i + sep.length;
218 t.length -= i + sep.length;
221 return css::uno::Any(comphelper::containerToSequence(seq));
224 css::uno::Any parseValue(
225 OString const & separator, xmlreader::Span const & text, Type type)
227 switch (type) {
228 case TYPE_ANY:
229 throw css::uno::RuntimeException(u"invalid value of type any"_ustr);
230 case TYPE_BOOLEAN:
231 return parseSingleValue< sal_Bool >(text);
232 case TYPE_SHORT:
233 return parseSingleValue< sal_Int16 >(text);
234 case TYPE_INT:
235 return parseSingleValue< sal_Int32 >(text);
236 case TYPE_LONG:
237 return parseSingleValue< sal_Int64 >(text);
238 case TYPE_DOUBLE:
239 return parseSingleValue< double >(text);
240 case TYPE_STRING:
241 return parseSingleValue< OUString >(text);
242 case TYPE_HEXBINARY:
243 return parseSingleValue< css::uno::Sequence< sal_Int8 > >(text);
244 case TYPE_BOOLEAN_LIST:
245 return parseListValue< sal_Bool >(separator, text);
246 case TYPE_SHORT_LIST:
247 return parseListValue< sal_Int16 >(separator, text);
248 case TYPE_INT_LIST:
249 return parseListValue< sal_Int32 >(separator, text);
250 case TYPE_LONG_LIST:
251 return parseListValue< sal_Int64 >(separator, text);
252 case TYPE_DOUBLE_LIST:
253 return parseListValue< double >(separator, text);
254 case TYPE_STRING_LIST:
255 return parseListValue< OUString >(separator, text);
256 case TYPE_HEXBINARY_LIST:
257 return parseListValue< css::uno::Sequence< sal_Int8 > >(
258 separator, text);
259 default:
260 assert(false);
261 throw css::uno::RuntimeException(u"this cannot happen"_ustr);
267 ValueParser::ValueParser(int layer): type_(TYPE_ERROR), layer_(layer), state_() {}
269 ValueParser::~ValueParser() {}
271 xmlreader::XmlReader::Text ValueParser::getTextMode() const {
272 if (!node_)
273 return xmlreader::XmlReader::Text::NONE;
275 switch (state_) {
276 case State::Text:
277 if (!items_.empty()) {
278 break;
280 [[fallthrough]];
281 case State::IT:
282 return
283 (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST ||
284 !separator_.isEmpty())
285 ? xmlreader::XmlReader::Text::Raw
286 : xmlreader::XmlReader::Text::Normalized;
287 default:
288 break;
290 return xmlreader::XmlReader::Text::NONE;
293 bool ValueParser::startElement(
294 xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name)
296 if (!node_.is()) {
297 return false;
299 switch (state_) {
300 case State::Text:
301 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && name == "it" &&
302 isListType(type_) && separator_.isEmpty())
304 pad_.clear();
305 // before first <it>, characters are not ignored; assume they
306 // are only whitespace
307 state_ = State::IT;
308 return true;
310 [[fallthrough]];
311 case State::IT:
312 if (nsId == xmlreader::XmlReader::NAMESPACE_NONE &&
313 name == "unicode" &&
314 (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST))
316 sal_Int32 scalar = -1;
317 for (;;) {
318 int attrNsId;
319 xmlreader::Span attrLn;
320 if (!reader.nextAttribute(&attrNsId, &attrLn)) {
321 break;
323 if (attrNsId == ParseManager::NAMESPACE_OOR &&
324 attrLn == "scalar")
326 if (!parseValue(reader.getAttributeValue(true), &scalar)) {
327 scalar = -1;
329 break;
332 if (scalar >= 0 && scalar < 0x20 && scalar != 0x09 &&
333 scalar != 0x0A && scalar != 0x0D)
335 char c = static_cast< char >(scalar);
336 pad_.add(&c, 1);
337 } else if (scalar == 0xFFFE) {
338 pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBE"));
339 } else if (scalar == 0xFFFF) {
340 pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBF"));
341 } else {
342 throw css::uno::RuntimeException(
343 "bad unicode scalar attribute in " + reader.getUrl());
345 state_ = state_ == State::Text ? State::TextUnicode : State::ITUnicode;
346 return true;
348 break;
349 default:
350 break;
352 throw css::uno::RuntimeException(
353 "bad member <" + name.convertFromUtf8() + "> in " + reader.getUrl());
356 bool ValueParser::endElement() {
357 if (!node_.is()) {
358 return false;
360 switch (state_) {
361 case State::Text:
363 css::uno::Any *pValue = nullptr;
365 switch (node_->kind()) {
366 case Node::KIND_PROPERTY:
367 pValue = static_cast< PropertyNode * >(node_.get())->getValuePtr(layer_, layer_ == Data::NO_LAYER);
368 break;
369 case Node::KIND_LOCALIZED_PROPERTY:
371 NodeMap & members = node_->getMembers();
372 auto [i, bInserted] = members.insert(NodeMap::value_type(localizedName_, nullptr));
373 LocalizedValueNode *pLVNode;
374 if (bInserted) {
375 pLVNode = new LocalizedValueNode(layer_);
376 i->second = pLVNode;
377 } else {
378 pLVNode = static_cast< LocalizedValueNode * >(i->second.get());
380 pValue = pLVNode->getValuePtr(layer_, layer_ == Data::NO_LAYER);
382 break;
383 default:
384 assert(false); // this cannot happen
385 return false;
388 if (items_.empty()) {
389 *pValue = parseValue(separator_, pad_.get(), type_);
390 pad_.clear();
391 } else {
392 switch (type_) {
393 case TYPE_BOOLEAN_LIST:
394 *pValue = convertItems< sal_Bool >();
395 break;
396 case TYPE_SHORT_LIST:
397 *pValue = convertItems< sal_Int16 >();
398 break;
399 case TYPE_INT_LIST:
400 *pValue = convertItems< sal_Int32 >();
401 break;
402 case TYPE_LONG_LIST:
403 *pValue = convertItems< sal_Int64 >();
404 break;
405 case TYPE_DOUBLE_LIST:
406 *pValue = convertItems< double >();
407 break;
408 case TYPE_STRING_LIST:
409 *pValue = convertItems< OUString >();
410 break;
411 case TYPE_HEXBINARY_LIST:
412 *pValue = convertItems< css::uno::Sequence< sal_Int8 > >();
413 break;
414 default:
415 assert(false); // this cannot happen
416 break;
418 items_.clear();
420 separator_.clear();
421 node_.clear();
423 break;
424 case State::TextUnicode:
425 state_ = State::Text;
426 break;
427 case State::ITUnicode:
428 state_ = State::IT;
429 break;
430 case State::IT:
431 items_.push_back(
432 parseValue(OString(), pad_.get(), elementType(type_)));
433 pad_.clear();
434 state_ = State::Text;
435 break;
437 return true;
440 void ValueParser::characters(xmlreader::Span const & text) {
441 if (node_.is()) {
442 assert(state_ == State::Text || state_ == State::IT);
443 pad_.add(text.begin, text.length);
447 void ValueParser::start(
448 rtl::Reference< Node > const & node, OUString const & localizedName)
450 assert(node.is() && !node_.is());
451 node_ = node;
452 localizedName_ = localizedName;
453 state_ = State::Text;
457 template< typename T > css::uno::Any ValueParser::convertItems() {
458 css::uno::Sequence< T > seq(items_.size());
459 auto seqRange = asNonConstRange(seq);
460 for (sal_Int32 i = 0; i < seq.getLength(); ++i) {
461 bool ok = (items_[i] >>= seqRange[i]);
462 assert(ok);
463 (void) ok; // avoid warnings
465 return css::uno::Any(seq);
470 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */