| /************************************************************** |
| * |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| * |
| *************************************************************/ |
| |
| |
| |
| #include "precompiled_configmgr.hxx" |
| #include "sal/config.h" |
| |
| #include "com/sun/star/uno/Any.hxx" |
| #include "com/sun/star/uno/Reference.hxx" |
| #include "com/sun/star/uno/RuntimeException.hpp" |
| #include "com/sun/star/uno/Sequence.hxx" |
| #include "com/sun/star/uno/XInterface.hpp" |
| #include "comphelper/sequenceasvector.hxx" |
| #include "osl/diagnose.h" |
| #include "rtl/string.h" |
| #include "rtl/string.hxx" |
| #include "rtl/ustring.h" |
| #include "rtl/ustring.hxx" |
| #include "sal/types.h" |
| #include "xmlreader/span.hxx" |
| #include "xmlreader/xmlreader.hxx" |
| |
| #include "localizedvaluenode.hxx" |
| #include "node.hxx" |
| #include "nodemap.hxx" |
| #include "parsemanager.hxx" |
| #include "propertynode.hxx" |
| #include "type.hxx" |
| #include "valueparser.hxx" |
| #include "xmldata.hxx" |
| |
| namespace configmgr { |
| |
| namespace { |
| |
| namespace css = com::sun::star; |
| |
| bool parseHexDigit(char c, int * value) { |
| OSL_ASSERT(value != 0); |
| if (c >= '0' && c <= '9') { |
| *value = c - '0'; |
| return true; |
| } |
| if (c >= 'A' && c <= 'F') { |
| *value = c - 'A' + 10; |
| return true; |
| } |
| if (c >= 'a' && c <= 'f') { |
| *value = c - 'a' + 10; |
| return true; |
| } |
| return false; |
| } |
| |
| bool parseValue(xmlreader::Span const & text, sal_Bool * value) { |
| OSL_ASSERT(text.is() && value != 0); |
| if (text.equals(RTL_CONSTASCII_STRINGPARAM("true")) || |
| text.equals(RTL_CONSTASCII_STRINGPARAM("1"))) |
| { |
| *value = true; |
| return true; |
| } |
| if (text.equals(RTL_CONSTASCII_STRINGPARAM("false")) || |
| text.equals(RTL_CONSTASCII_STRINGPARAM("0"))) |
| { |
| *value = false; |
| return true; |
| } |
| return false; |
| } |
| |
| bool parseValue(xmlreader::Span const & text, sal_Int16 * value) { |
| OSL_ASSERT(text.is() && value != 0); |
| // For backwards compatibility, support hexadecimal values: |
| sal_Int32 n = |
| rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( |
| text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"), |
| RTL_CONSTASCII_LENGTH("0X")) == 0 ? |
| rtl::OString( |
| text.begin + RTL_CONSTASCII_LENGTH("0X"), |
| text.length - RTL_CONSTASCII_LENGTH("0X")).toInt32(16) : |
| rtl::OString(text.begin, text.length).toInt32(); |
| //TODO: check valid lexical representation |
| if (n >= SAL_MIN_INT16 && n <= SAL_MAX_INT16) { |
| *value = static_cast< sal_Int16 >(n); |
| return true; |
| } |
| return false; |
| } |
| |
| bool parseValue(xmlreader::Span const & text, sal_Int32 * value) { |
| OSL_ASSERT(text.is() && value != 0); |
| // For backwards compatibility, support hexadecimal values: |
| *value = |
| rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( |
| text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"), |
| RTL_CONSTASCII_LENGTH("0X")) == 0 ? |
| rtl::OString( |
| text.begin + RTL_CONSTASCII_LENGTH("0X"), |
| text.length - RTL_CONSTASCII_LENGTH("0X")).toInt32(16) : |
| rtl::OString(text.begin, text.length).toInt32(); |
| //TODO: check valid lexical representation |
| return true; |
| } |
| |
| bool parseValue(xmlreader::Span const & text, sal_Int64 * value) { |
| OSL_ASSERT(text.is() && value != 0); |
| // For backwards compatibility, support hexadecimal values: |
| *value = |
| rtl_str_shortenedCompareIgnoreAsciiCase_WithLength( |
| text.begin, text.length, RTL_CONSTASCII_STRINGPARAM("0X"), |
| RTL_CONSTASCII_LENGTH("0X")) == 0 ? |
| rtl::OString( |
| text.begin + RTL_CONSTASCII_LENGTH("0X"), |
| text.length - RTL_CONSTASCII_LENGTH("0X")).toInt64(16) : |
| rtl::OString(text.begin, text.length).toInt64(); |
| //TODO: check valid lexical representation |
| return true; |
| } |
| |
| bool parseValue(xmlreader::Span const & text, double * value) { |
| OSL_ASSERT(text.is() && value != 0); |
| *value = rtl::OString(text.begin, text.length).toDouble(); |
| //TODO: check valid lexical representation |
| return true; |
| } |
| |
| bool parseValue(xmlreader::Span const & text, rtl::OUString * value) { |
| OSL_ASSERT(text.is() && value != 0); |
| *value = text.convertFromUtf8(); |
| return true; |
| } |
| |
| bool parseValue( |
| xmlreader::Span const & text, css::uno::Sequence< sal_Int8 > * value) |
| { |
| OSL_ASSERT(text.is() && value != 0); |
| if ((text.length & 1) != 0) { |
| return false; |
| } |
| comphelper::SequenceAsVector< sal_Int8 > seq; |
| for (sal_Int32 i = 0; i != text.length;) { |
| int n1; |
| int n2; |
| if (!parseHexDigit(text.begin[i++], &n1) || |
| !parseHexDigit(text.begin[i++], &n2)) |
| { |
| return false; |
| } |
| seq.push_back(static_cast< sal_Int8 >((n1 << 4) | n2)); |
| } |
| *value = seq.getAsConstList(); |
| return true; |
| } |
| |
| template< typename T > css::uno::Any parseSingleValue( |
| xmlreader::Span const & text) |
| { |
| T val; |
| if (!parseValue(text, &val)) { |
| throw css::uno::RuntimeException( |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("invalid value")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| return css::uno::makeAny(val); |
| } |
| |
| template< typename T > css::uno::Any parseListValue( |
| rtl::OString const & separator, xmlreader::Span const & text) |
| { |
| comphelper::SequenceAsVector< T > seq; |
| xmlreader::Span sep; |
| if (separator.getLength() == 0) { |
| sep = xmlreader::Span(RTL_CONSTASCII_STRINGPARAM(" ")); |
| } else { |
| sep = xmlreader::Span(separator.getStr(), separator.getLength()); |
| } |
| if (text.length != 0) { |
| for (xmlreader::Span t(text);;) { |
| sal_Int32 i = rtl_str_indexOfStr_WithLength( |
| t.begin, t.length, sep.begin, sep.length); |
| T val; |
| if (!parseValue( |
| xmlreader::Span(t.begin, i == -1 ? t.length : i), &val)) |
| { |
| throw css::uno::RuntimeException( |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("invalid value")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| seq.push_back(val); |
| if (i < 0) { |
| break; |
| } |
| t.begin += i + sep.length; |
| t.length -= i + sep.length; |
| } |
| } |
| return css::uno::makeAny(seq.getAsConstList()); |
| } |
| |
| css::uno::Any parseValue( |
| rtl::OString const & separator, xmlreader::Span const & text, Type type) |
| { |
| switch (type) { |
| case TYPE_ANY: |
| throw css::uno::RuntimeException( |
| rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM("invalid value of type any")), |
| css::uno::Reference< css::uno::XInterface >()); |
| case TYPE_BOOLEAN: |
| return parseSingleValue< sal_Bool >(text); |
| case TYPE_SHORT: |
| return parseSingleValue< sal_Int16 >(text); |
| case TYPE_INT: |
| return parseSingleValue< sal_Int32 >(text); |
| case TYPE_LONG: |
| return parseSingleValue< sal_Int64 >(text); |
| case TYPE_DOUBLE: |
| return parseSingleValue< double >(text); |
| case TYPE_STRING: |
| return parseSingleValue< rtl::OUString >(text); |
| case TYPE_HEXBINARY: |
| return parseSingleValue< css::uno::Sequence< sal_Int8 > >(text); |
| case TYPE_BOOLEAN_LIST: |
| return parseListValue< sal_Bool >(separator, text); |
| case TYPE_SHORT_LIST: |
| return parseListValue< sal_Int16 >(separator, text); |
| case TYPE_INT_LIST: |
| return parseListValue< sal_Int32 >(separator, text); |
| case TYPE_LONG_LIST: |
| return parseListValue< sal_Int64 >(separator, text); |
| case TYPE_DOUBLE_LIST: |
| return parseListValue< double >(separator, text); |
| case TYPE_STRING_LIST: |
| return parseListValue< rtl::OUString >(separator, text); |
| case TYPE_HEXBINARY_LIST: |
| return parseListValue< css::uno::Sequence< sal_Int8 > >( |
| separator, text); |
| default: |
| OSL_ASSERT(false); |
| throw css::uno::RuntimeException( |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("this cannot happen")), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| } |
| |
| } |
| |
| ValueParser::ValueParser(int layer): layer_(layer) {} |
| |
| ValueParser::~ValueParser() {} |
| |
| xmlreader::XmlReader::Text ValueParser::getTextMode() const { |
| if (node_.is()) { |
| switch (state_) { |
| case STATE_TEXT: |
| if (!items_.empty()) { |
| break; |
| } |
| // fall through |
| case STATE_IT: |
| return |
| (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST || |
| separator_.getLength() != 0) |
| ? xmlreader::XmlReader::TEXT_RAW |
| : xmlreader::XmlReader::TEXT_NORMALIZED; |
| default: |
| break; |
| } |
| } |
| return xmlreader::XmlReader::TEXT_NONE; |
| } |
| |
| bool ValueParser::startElement( |
| xmlreader::XmlReader & reader, int nsId, xmlreader::Span const & name) |
| { |
| if (!node_.is()) { |
| return false; |
| } |
| switch (state_) { |
| case STATE_TEXT: |
| if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && |
| name.equals(RTL_CONSTASCII_STRINGPARAM("it")) && |
| isListType(type_) && separator_.getLength() == 0) |
| { |
| pad_.clear(); |
| // before first <it>, characters are not ignored; assume they |
| // are only whitespace |
| state_ = STATE_IT; |
| return true; |
| } |
| // fall through |
| case STATE_IT: |
| if (nsId == xmlreader::XmlReader::NAMESPACE_NONE && |
| name.equals(RTL_CONSTASCII_STRINGPARAM("unicode")) && |
| (type_ == TYPE_STRING || type_ == TYPE_STRING_LIST)) |
| { |
| sal_Int32 scalar = -1; |
| for (;;) { |
| int attrNsId; |
| xmlreader::Span attrLn; |
| if (!reader.nextAttribute(&attrNsId, &attrLn)) { |
| break; |
| } |
| if (attrNsId == ParseManager::NAMESPACE_OOR && |
| attrLn.equals(RTL_CONSTASCII_STRINGPARAM("scalar"))) |
| { |
| if (!parseValue(reader.getAttributeValue(true), &scalar)) { |
| scalar = -1; |
| } |
| break; |
| } |
| } |
| if (scalar >= 0 && scalar < 0x20 && scalar != 0x09 && |
| scalar != 0x0A && scalar != 0x0D) |
| { |
| char c = static_cast< char >(scalar); |
| pad_.add(&c, 1); |
| } else if (scalar == 0xFFFE) { |
| pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBE")); |
| } else if (scalar == 0xFFFF) { |
| pad_.add(RTL_CONSTASCII_STRINGPARAM("\xEF\xBF\xBF")); |
| } else { |
| throw css::uno::RuntimeException( |
| (rtl::OUString( |
| RTL_CONSTASCII_USTRINGPARAM( |
| "bad unicode scalar attribute in ")) + |
| reader.getUrl()), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| state_ = State(state_ + 1); |
| return true; |
| } |
| break; |
| default: |
| break; |
| } |
| throw css::uno::RuntimeException( |
| (rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("bad member <")) + |
| name.convertFromUtf8() + |
| rtl::OUString(RTL_CONSTASCII_USTRINGPARAM("> in ")) + reader.getUrl()), |
| css::uno::Reference< css::uno::XInterface >()); |
| } |
| |
| bool ValueParser::endElement() { |
| if (!node_.is()) { |
| return false; |
| } |
| switch (state_) { |
| case STATE_TEXT: |
| { |
| css::uno::Any value; |
| if (items_.empty()) { |
| value = parseValue(separator_, pad_.get(), type_); |
| pad_.clear(); |
| } else { |
| switch (type_) { |
| case TYPE_BOOLEAN_LIST: |
| value = convertItems< sal_Bool >(); |
| break; |
| case TYPE_SHORT_LIST: |
| value = convertItems< sal_Int16 >(); |
| break; |
| case TYPE_INT_LIST: |
| value = convertItems< sal_Int32 >(); |
| break; |
| case TYPE_LONG_LIST: |
| value = convertItems< sal_Int64 >(); |
| break; |
| case TYPE_DOUBLE_LIST: |
| value = convertItems< double >(); |
| break; |
| case TYPE_STRING_LIST: |
| value = convertItems< rtl::OUString >(); |
| break; |
| case TYPE_HEXBINARY_LIST: |
| value = convertItems< css::uno::Sequence< sal_Int8 > >(); |
| break; |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| items_.clear(); |
| } |
| switch (node_->kind()) { |
| case Node::KIND_PROPERTY: |
| dynamic_cast< PropertyNode * >(node_.get())->setValue( |
| layer_, value); |
| break; |
| case Node::KIND_LOCALIZED_PROPERTY: |
| { |
| NodeMap::iterator i( |
| node_->getMembers().find(localizedName_)); |
| if (i == node_->getMembers().end()) { |
| node_->getMembers().insert( |
| NodeMap::value_type( |
| localizedName_, |
| new LocalizedValueNode(layer_, value))); |
| } else { |
| dynamic_cast< LocalizedValueNode * >(i->second.get())-> |
| setValue(layer_, value); |
| } |
| } |
| break; |
| default: |
| OSL_ASSERT(false); // this cannot happen |
| break; |
| } |
| separator_ = rtl::OString(); |
| node_.clear(); |
| } |
| break; |
| case STATE_TEXT_UNICODE: |
| case STATE_IT_UNICODE: |
| state_ = State(state_ - 1); |
| break; |
| case STATE_IT: |
| items_.push_back( |
| parseValue(rtl::OString(), pad_.get(), elementType(type_))); |
| pad_.clear(); |
| state_ = STATE_TEXT; |
| break; |
| } |
| return true; |
| } |
| |
| void ValueParser::characters(xmlreader::Span const & text) { |
| if (node_.is()) { |
| OSL_ASSERT(state_ == STATE_TEXT || state_ == STATE_IT); |
| pad_.add(text.begin, text.length); |
| } |
| } |
| |
| void ValueParser::start( |
| rtl::Reference< Node > const & node, rtl::OUString const & localizedName) |
| { |
| OSL_ASSERT(node.is() && !node_.is()); |
| node_ = node; |
| localizedName_ = localizedName; |
| state_ = STATE_TEXT; |
| } |
| |
| int ValueParser::getLayer() const { |
| return layer_; |
| } |
| |
| template< typename T > css::uno::Any ValueParser::convertItems() { |
| css::uno::Sequence< T > seq(items_.size()); |
| for (sal_Int32 i = 0; i < seq.getLength(); ++i) { |
| OSL_VERIFY(items_[i] >>= seq[i]); |
| } |
| return css::uno::makeAny(seq); |
| } |
| |
| } |