1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
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>
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>
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>
38 #include "localizedvaluenode.hxx"
40 #include "nodemap.hxx"
41 #include "parsemanager.hxx"
42 #include "propertynode.hxx"
44 #include "valueparser.hxx"
50 bool parseHexDigit(char c
, int * value
) {
51 assert(value
!= nullptr);
52 if (c
>= '0' && c
<= '9') {
56 if (c
>= 'A' && c
<= 'F') {
57 *value
= c
- 'A' + 10;
60 if (c
>= 'a' && c
<= 'f') {
61 *value
= c
- 'a' + 10;
67 bool parseValue(xmlreader::Span
const & text
, sal_Bool
* value
) {
68 assert(text
.is() && value
!= nullptr);
69 if (text
== "true" || text
== "1") {
73 if (text
== "false" || text
== "0") {
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;
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);
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
);
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));
120 std::string_view
sView(text
.begin
, text
.length
);
121 *value
= o3tl::toInt32(sView
);
123 //TODO: check valid lexical representation
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
)
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
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
154 bool parseValue(xmlreader::Span
const & text
, OUString
* value
) {
155 assert(text
.is() && value
!= nullptr);
156 *value
= text
.convertFromUtf8();
161 xmlreader::Span
const & text
, css::uno::Sequence
< sal_Int8
> * value
)
163 assert(text
.is() && value
!= nullptr);
164 if ((text
.length
& 1) != 0) {
167 std::vector
< sal_Int8
> seq
;
168 for (sal_Int32 i
= 0; i
!= text
.length
;) {
171 if (!parseHexDigit(text
.begin
[i
++], &n1
) ||
172 !parseHexDigit(text
.begin
[i
++], &n2
))
176 seq
.push_back(static_cast< sal_Int8
>((n1
<< 4) | n2
));
178 *value
= comphelper::containerToSequence(seq
);
182 template< typename T
> css::uno::Any
parseSingleValue(
183 xmlreader::Span
const & text
)
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
;
197 if (separator
.isEmpty()) {
198 sep
= xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" "));
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
);
209 xmlreader::Span(t
.begin
, i
== -1 ? t
.length
: i
), &val
))
211 throw css::uno::RuntimeException(u
"invalid value"_ustr
);
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
)
229 throw css::uno::RuntimeException(u
"invalid value of type any"_ustr
);
231 return parseSingleValue
< sal_Bool
>(text
);
233 return parseSingleValue
< sal_Int16
>(text
);
235 return parseSingleValue
< sal_Int32
>(text
);
237 return parseSingleValue
< sal_Int64
>(text
);
239 return parseSingleValue
< double >(text
);
241 return parseSingleValue
< OUString
>(text
);
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
);
249 return parseListValue
< sal_Int32
>(separator
, text
);
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
> >(
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 {
273 return xmlreader::XmlReader::Text::NONE
;
277 if (!items_
.empty()) {
283 (type_
== TYPE_STRING
|| type_
== TYPE_STRING_LIST
||
284 !separator_
.isEmpty())
285 ? xmlreader::XmlReader::Text::Raw
286 : xmlreader::XmlReader::Text::Normalized
;
290 return xmlreader::XmlReader::Text::NONE
;
293 bool ValueParser::startElement(
294 xmlreader::XmlReader
& reader
, int nsId
, xmlreader::Span
const & name
)
301 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&& name
== "it" &&
302 isListType(type_
) && separator_
.isEmpty())
305 // before first <it>, characters are not ignored; assume they
306 // are only whitespace
312 if (nsId
== xmlreader::XmlReader::NAMESPACE_NONE
&&
314 (type_
== TYPE_STRING
|| type_
== TYPE_STRING_LIST
))
316 sal_Int32 scalar
= -1;
319 xmlreader::Span attrLn
;
320 if (!reader
.nextAttribute(&attrNsId
, &attrLn
)) {
323 if (attrNsId
== ParseManager::NAMESPACE_OOR
&&
326 if (!parseValue(reader
.getAttributeValue(true), &scalar
)) {
332 if (scalar
>= 0 && scalar
< 0x20 && scalar
!= 0x09 &&
333 scalar
!= 0x0A && scalar
!= 0x0D)
335 char c
= static_cast< char >(scalar
);
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"));
342 throw css::uno::RuntimeException(
343 "bad unicode scalar attribute in " + reader
.getUrl());
345 state_
= state_
== State::Text
? State::TextUnicode
: State::ITUnicode
;
352 throw css::uno::RuntimeException(
353 "bad member <" + name
.convertFromUtf8() + "> in " + reader
.getUrl());
356 bool ValueParser::endElement() {
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
);
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
;
375 pLVNode
= new LocalizedValueNode(layer_
);
378 pLVNode
= static_cast< LocalizedValueNode
* >(i
->second
.get());
380 pValue
= pLVNode
->getValuePtr(layer_
, layer_
== Data::NO_LAYER
);
384 assert(false); // this cannot happen
388 if (items_
.empty()) {
389 *pValue
= parseValue(separator_
, pad_
.get(), type_
);
393 case TYPE_BOOLEAN_LIST
:
394 *pValue
= convertItems
< sal_Bool
>();
396 case TYPE_SHORT_LIST
:
397 *pValue
= convertItems
< sal_Int16
>();
400 *pValue
= convertItems
< sal_Int32
>();
403 *pValue
= convertItems
< sal_Int64
>();
405 case TYPE_DOUBLE_LIST
:
406 *pValue
= convertItems
< double >();
408 case TYPE_STRING_LIST
:
409 *pValue
= convertItems
< OUString
>();
411 case TYPE_HEXBINARY_LIST
:
412 *pValue
= convertItems
< css::uno::Sequence
< sal_Int8
> >();
415 assert(false); // this cannot happen
424 case State::TextUnicode
:
425 state_
= State::Text
;
427 case State::ITUnicode
:
432 parseValue(OString(), pad_
.get(), elementType(type_
)));
434 state_
= State::Text
;
440 void ValueParser::characters(xmlreader::Span
const & text
) {
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());
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
]);
463 (void) ok
; // avoid warnings
465 return css::uno::Any(seq
);
470 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */