diff options
Diffstat (limited to 'xmloff/source/forms/propertyimport.cxx')
-rw-r--r-- | xmloff/source/forms/propertyimport.cxx | 559 |
1 files changed, 559 insertions, 0 deletions
diff --git a/xmloff/source/forms/propertyimport.cxx b/xmloff/source/forms/propertyimport.cxx new file mode 100644 index 000000000..b048653ca --- /dev/null +++ b/xmloff/source/forms/propertyimport.cxx @@ -0,0 +1,559 @@ +/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */ +/* + * This file is part of the LibreOffice project. + * + * This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * This file incorporates work covered by the following license notice: + * + * 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 . + */ + +#include <sal/config.h> + +#include <cmath> + +#include "propertyimport.hxx" + +#include <sax/tools/converter.hxx> + +#include <xmloff/xmlimp.hxx> +#include <xmloff/xmluconv.hxx> +#include <xmloff/nmspmap.hxx> +#include <o3tl/temporary.hxx> +#include <osl/diagnose.h> +#include <sal/log.hxx> +#include <comphelper/extract.hxx> +#include <xmloff/xmlnmspe.hxx> +#include <tools/date.hxx> +#include <tools/time.hxx> +#include <com/sun/star/util/Date.hpp> +#include <com/sun/star/util/Time.hpp> +#include <com/sun/star/util/DateTime.hpp> +#include <unotools/datetime.hxx> +#include <rtl/strbuf.hxx> + +namespace xmloff +{ + + using namespace ::com::sun::star::uno; + using namespace ::com::sun::star::beans; + using namespace ::com::sun::star::xml; + using ::com::sun::star::xml::sax::XAttributeList; + + // NO using namespace ...util !!! + // need a tools Date/Time/DateTime below, which would conflict with the uno types then + +#define TYPE_DATE 1 +#define TYPE_TIME 2 +#define TYPE_DATETIME 3 + +//= PropertyConversion +namespace +{ + css::util::Time lcl_getTime(double _nValue) + { + css::util::Time aTime; + sal_uInt64 nIntValue = static_cast<sal_uInt64>(_nValue * ::tools::Time::nanoSecPerDay); + aTime.NanoSeconds = nIntValue % ::tools::Time::nanoSecPerSec; + nIntValue /= ::tools::Time::nanoSecPerSec; + aTime.Seconds = nIntValue % ::tools::Time::secondPerMinute; + nIntValue /= ::tools::Time::secondPerMinute; + aTime.Minutes = nIntValue % ::tools::Time::minutePerHour; + nIntValue /= ::tools::Time::minutePerHour; + OSL_ENSURE(nIntValue < 24, "lcl_getTime: more than a day?"); + aTime.Hours = nIntValue; + + return aTime; + } + + css::util::Date lcl_getDate( double _nValue ) + { + Date aToolsDate(static_cast<sal_uInt32>(_nValue)); + css::util::Date aDate; + ::utl::typeConvert(aToolsDate, aDate); + return aDate; + } +} + +Any PropertyConversion::convertString( const css::uno::Type& _rExpectedType, + const OUString& _rReadCharacters, const SvXMLEnumMapEntry<sal_uInt16>* _pEnumMap, const bool _bInvertBoolean ) +{ + Any aReturn; + bool bEnumAsInt = false; + switch (_rExpectedType.getTypeClass()) + { + case TypeClass_BOOLEAN: // sal_Bool + { + bool bValue; + bool bSuccess = + ::sax::Converter::convertBool(bValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \""). + append(OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US)). + append("\" into a boolean!").getStr()); + aReturn <<= (_bInvertBoolean ? !bValue : bValue); + } + break; + case TypeClass_SHORT: // sal_Int16 + case TypeClass_LONG: // sal_Int32 + if (!_pEnumMap) + { // it's a real int32/16 property + sal_Int32 nValue(0); + bool bSuccess = + ::sax::Converter::convertNumber(nValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \""). + append(OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US)). + append("\" into an integer!").getStr()); + if (TypeClass_SHORT == _rExpectedType.getTypeClass()) + aReturn <<= static_cast<sal_Int16>(nValue); + else + aReturn <<= nValue; + break; + } + bEnumAsInt = true; + [[fallthrough]]; + case TypeClass_ENUM: + { + sal_uInt16 nEnumValue(0); + bool bSuccess = SvXMLUnitConverter::convertEnum(nEnumValue, _rReadCharacters, _pEnumMap); + OSL_ENSURE(bSuccess, "PropertyConversion::convertString: could not convert to an enum value!"); + + if (bEnumAsInt) + if (TypeClass_SHORT == _rExpectedType.getTypeClass()) + aReturn <<= static_cast<sal_Int16>(nEnumValue); + else + aReturn <<= static_cast<sal_Int32>(nEnumValue); + else + aReturn = ::cppu::int2enum(static_cast<sal_Int32>(nEnumValue), _rExpectedType); + } + break; + case TypeClass_HYPER: + { + OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!"); + } + break; + case TypeClass_DOUBLE: + { + double nValue; + bool bSuccess = + ::sax::Converter::convertDouble(nValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \""). + append(OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US)). + append("\" into a double!").getStr()); + aReturn <<= nValue; + } + break; + case TypeClass_STRING: + aReturn <<= _rReadCharacters; + break; + case TypeClass_STRUCT: + { + sal_Int32 nType = 0; + if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Date >::get() ) ) + nType = TYPE_DATE; + else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Time >::get() ) ) + nType = TYPE_TIME; + else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::DateTime >::get() ) ) + nType = TYPE_DATETIME; + + if ( nType ) + { + // first extract the double + double nValue = 0; + bool bSuccess = + ::sax::Converter::convertDouble(nValue, _rReadCharacters); + OSL_ENSURE(bSuccess, + OStringBuffer("PropertyConversion::convertString: could not convert \""). + append(OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US)). + append("\" into a double!").getStr()); + + // then convert it into the target type + switch (nType) + { + case TYPE_DATE: + { + OSL_ENSURE(std::modf(nValue, &o3tl::temporary(double())) == 0, + "PropertyConversion::convertString: a Date value with a fractional part?"); + aReturn <<= lcl_getDate(nValue); + } + break; + case TYPE_TIME: + { + OSL_ENSURE((static_cast<sal_uInt32>(nValue)) == 0, + "PropertyConversion::convertString: a tools::Time value with more than a fractional part?"); + aReturn <<= lcl_getTime(nValue); + } + break; + case TYPE_DATETIME: + { + css::util::Time aTime = lcl_getTime(nValue); + css::util::Date aDate = lcl_getDate(nValue); + + css::util::DateTime aDateTime; + aDateTime.NanoSeconds = aTime.NanoSeconds; + aDateTime.Seconds = aTime.Seconds; + aDateTime.Minutes = aTime.Minutes; + aDateTime.Hours = aTime.Hours; + aDateTime.Day = aDate.Day; + aDateTime.Month = aDate.Month; + aDateTime.Year = aDate.Year; + aReturn <<= aDateTime; + } + break; + } + } + else + OSL_FAIL("PropertyConversion::convertString: unsupported property type!"); + } + break; + default: + OSL_FAIL("PropertyConversion::convertString: invalid type class!"); + } + + return aReturn; +} + +Type PropertyConversion::xmlTypeToUnoType( const OUString& _rType ) +{ + Type aUnoType( cppu::UnoType<void>::get() ); + + static std::map< OUString, css::uno::Type > s_aTypeNameMap + { + { token::GetXMLToken( token::XML_BOOLEAN ) , cppu::UnoType<bool>::get()}, + // Not a copy paste error, quotation from: + // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html + // all numeric types (including the UNO double) + // consistently map to XML_FLOAT, so taking the extra precision from the + // C++ type "float" to "double" makes absolute sense + { token::GetXMLToken( token::XML_FLOAT ) , ::cppu::UnoType<double>::get()}, + { token::GetXMLToken( token::XML_STRING ) , ::cppu::UnoType<OUString>::get()}, + { token::GetXMLToken( token::XML_VOID ) , cppu::UnoType<void>::get() }, + }; + + const std::map< OUString, css::uno::Type >::iterator aTypePos = s_aTypeNameMap.find( _rType ); + OSL_ENSURE( s_aTypeNameMap.end() != aTypePos, "PropertyConversion::xmlTypeToUnoType: invalid property name!" ); + if ( s_aTypeNameMap.end() != aTypePos ) + aUnoType = aTypePos->second; + + return aUnoType; +} + +//= OPropertyImport +OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl& _rImport, sal_uInt16 _nPrefix, const OUString& _rName) + :SvXMLImportContext(_rImport.getGlobalContext(), _nPrefix, _rName) + ,m_rContext(_rImport) + ,m_bTrackAttributes(false) +{ +} + +SvXMLImportContextRef OPropertyImport::CreateChildContext(sal_uInt16 _nPrefix, const OUString& _rLocalName, + const Reference< XAttributeList >& /*_rxAttrList*/) +{ + if( token::IsXMLToken( _rLocalName, token::XML_PROPERTIES) ) + { + return new OPropertyElementsContext( m_rContext.getGlobalContext(), + _nPrefix, _rLocalName, this); + } + return nullptr; +} + +void OPropertyImport::StartElement(const Reference< XAttributeList >& _rxAttrList) +{ + OSL_ENSURE(_rxAttrList.is(), "OPropertyImport::StartElement: invalid attribute list!"); + const sal_Int32 nAttributeCount = _rxAttrList->getLength(); + + // assume the 'worst' case: all attributes describe properties. This should save our property array + // some reallocs + m_aValues.reserve(nAttributeCount); + + const SvXMLNamespaceMap& rMap = m_rContext.getGlobalContext().GetNamespaceMap(); + sal_uInt16 nNamespace; + OUString sLocalName; + for (sal_Int32 i=0; i<nAttributeCount; ++i) + { + nNamespace = rMap.GetKeyByAttrName(_rxAttrList->getNameByIndex(i), &sLocalName); + handleAttribute(nNamespace, sLocalName, _rxAttrList->getValueByIndex(i)); + + if (m_bTrackAttributes) + m_aEncounteredAttributes.insert(sLocalName); + } + + // TODO: create PropertyValues for all the attributes which were not present, because they were implied + // this is necessary as soon as we have properties where the XML default is different from the property + // default +} + +bool OPropertyImport::encounteredAttribute(const OUString& _rAttributeName) const +{ + OSL_ENSURE(m_bTrackAttributes, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!"); + return m_aEncounteredAttributes.end() != m_aEncounteredAttributes.find(_rAttributeName); +} + +void OPropertyImport::Characters(const OUString& _rChars ) +{ + // ignore them (should be whitespace only) + OSL_ENSURE(_rChars.trim().isEmpty(), "OPropertyImport::Characters: non-whitespace characters!"); +} + +bool OPropertyImport::handleAttribute(sal_uInt16 /*_nNamespaceKey*/, const OUString& _rLocalName, const OUString& _rValue) +{ + const OAttribute2Property::AttributeAssignment* pProperty = m_rContext.getAttributeMap().getAttributeTranslation(_rLocalName); + if (pProperty) + { + // create and store a new PropertyValue + PropertyValue aNewValue; + aNewValue.Name = pProperty->sPropertyName; + + // convert the value string into the target type + if (token::IsXMLToken(_rLocalName, token::XML_HREF)) + { + aNewValue.Value <<= m_rContext.getGlobalContext().GetAbsoluteReference(_rValue); + } + else + { + aNewValue.Value = PropertyConversion::convertString( + pProperty->aPropertyType, _rValue, pProperty->pEnumMap, + pProperty->bInverseSemantics); + } + implPushBackPropertyValue( aNewValue ); + return true; + } + if (!token::IsXMLToken(_rLocalName, token::XML_TYPE)) // xlink:type is valid but ignored for <form:form> + { + SAL_WARN( "xmloff", "OPropertyImport::handleAttribute: Can't handle the following:\n" + " Attribute name: " << _rLocalName << "\n value: " << _rValue ); + return false; + } + return true; +} + +//= OPropertyElementsContext +OPropertyElementsContext::OPropertyElementsContext(SvXMLImport& _rImport, sal_uInt16 _nPrefix, const OUString& _rName, + const OPropertyImportRef& _rPropertyImporter) + :SvXMLImportContext(_rImport, _nPrefix, _rName) + ,m_xPropertyImporter(_rPropertyImporter) +{ +} + +SvXMLImportContextRef OPropertyElementsContext::CreateChildContext(sal_uInt16 _nPrefix, const OUString& _rLocalName, + const Reference< XAttributeList >&) +{ + if( token::IsXMLToken( _rLocalName, token::XML_PROPERTY ) ) + { + return new OSinglePropertyContext(GetImport(), _nPrefix, _rLocalName, m_xPropertyImporter); + } + else if( token::IsXMLToken( _rLocalName, token::XML_LIST_PROPERTY ) ) + { + return new OListPropertyContext( GetImport(), _nPrefix, _rLocalName, m_xPropertyImporter ); + } + return nullptr; +} + +#if OSL_DEBUG_LEVEL > 0 + void OPropertyElementsContext::StartElement(const Reference< XAttributeList >& _rxAttrList) + { + OSL_ENSURE(0 == _rxAttrList->getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!"); + SvXMLImportContext::StartElement(_rxAttrList); + } + + void OPropertyElementsContext::Characters(const OUString& _rChars) + { + OSL_ENSURE(_rChars.trim().isEmpty(), "OPropertyElementsContext::Characters: non-whitespace characters detected!"); + SvXMLImportContext::Characters(_rChars); + } + +#endif + +//= OSinglePropertyContext +OSinglePropertyContext::OSinglePropertyContext(SvXMLImport& _rImport, sal_uInt16 _nPrefix, const OUString& _rName, + const OPropertyImportRef& _rPropertyImporter) + :SvXMLImportContext(_rImport, _nPrefix, _rName) + ,m_xPropertyImporter(_rPropertyImporter) +{ +} + +SvXMLImportContextRef OSinglePropertyContext::CreateChildContext(sal_uInt16 /*_nPrefix*/, const OUString& /*_rLocalName*/, + const Reference< XAttributeList >&) +{ + return nullptr; +} + +void OSinglePropertyContext::StartElement(const Reference< XAttributeList >& _rxAttrList) +{ + css::beans::PropertyValue aPropValue; // the property the instance imports currently + css::uno::Type aPropType; // the type of the property the instance imports currently + + OUString sType, sValue; + const SvXMLNamespaceMap& rMap = GetImport().GetNamespaceMap(); + const sal_Int16 nAttrCount = _rxAttrList.is() ? _rxAttrList->getLength() : 0; + for( sal_Int16 i=0; i < nAttrCount; i++ ) + { + const OUString& rAttrName = _rxAttrList->getNameByIndex( i ); + + OUString aLocalName; + sal_uInt16 nPrefix = + rMap.GetKeyByAttrName( rAttrName, + &aLocalName ); + if( XML_NAMESPACE_FORM == nPrefix ) + { + if( token::IsXMLToken( aLocalName, token::XML_PROPERTY_NAME ) ) + aPropValue.Name = _rxAttrList->getValueByIndex( i ); + + } + else if( XML_NAMESPACE_OFFICE == nPrefix ) + { + if( token::IsXMLToken( aLocalName, token::XML_VALUE_TYPE ) ) + sType = _rxAttrList->getValueByIndex( i ); + else if( token::IsXMLToken( aLocalName, + token::XML_VALUE ) || + token::IsXMLToken( aLocalName, + token::XML_BOOLEAN_VALUE ) || + token::IsXMLToken( aLocalName, + token::XML_STRING_VALUE ) ) + sValue = _rxAttrList->getValueByIndex( i ); + } + } + + // the name of the property + OSL_ENSURE(!aPropValue.Name.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!"); + + // needs to be translated into a css::uno::Type + aPropType = PropertyConversion::xmlTypeToUnoType( sType ); + if( TypeClass_VOID == aPropType.getTypeClass() ) + { + aPropValue.Value = Any(); + } + else + { + aPropValue.Value = + PropertyConversion::convertString(aPropType, + sValue); + } + + // now that we finally have our property value, add it to our parent object + if( !aPropValue.Name.isEmpty() ) + m_xPropertyImporter->implPushBackGenericPropertyValue(aPropValue); +} + +//= OListPropertyContext +OListPropertyContext::OListPropertyContext( SvXMLImport& _rImport, sal_uInt16 _nPrefix, const OUString& _rName, + const OPropertyImportRef& _rPropertyImporter ) + :SvXMLImportContext( _rImport, _nPrefix, _rName ) + ,m_xPropertyImporter( _rPropertyImporter ) +{ +} + +void OListPropertyContext::StartElement( const Reference< XAttributeList >& _rxAttrList ) +{ + sal_Int32 nAttributeCount = _rxAttrList->getLength(); + + sal_uInt16 nNamespace; + OUString sAttributeName; + const SvXMLNamespaceMap& rMap = GetImport().GetNamespaceMap(); + for ( sal_Int32 i = 0; i < nAttributeCount; ++i ) + { + nNamespace = rMap.GetKeyByAttrName( _rxAttrList->getNameByIndex( i ), &sAttributeName ); + if ( ( XML_NAMESPACE_FORM == nNamespace ) + && ( token::IsXMLToken( sAttributeName, token::XML_PROPERTY_NAME ) ) + ) + { + m_sPropertyName = _rxAttrList->getValueByIndex( i ); + } + else if ( ( XML_NAMESPACE_OFFICE == nNamespace ) + && ( token::IsXMLToken( sAttributeName, token::XML_VALUE_TYPE ) ) + ) + { + m_sPropertyType = _rxAttrList->getValueByIndex( i ); + } + else + { + OSL_FAIL( OStringBuffer( "OListPropertyContext::StartElement: unknown child element (\""). + append(OUStringToOString(sAttributeName, RTL_TEXTENCODING_ASCII_US)). + append("\")!").getStr() ); + } + } +} + +void OListPropertyContext::EndElement() +{ + OSL_ENSURE( !m_sPropertyName.isEmpty() && !m_sPropertyType.isEmpty(), + "OListPropertyContext::EndElement: no property name or type!" ); + + if ( m_sPropertyName.isEmpty() || m_sPropertyType.isEmpty() ) + return; + + Sequence< Any > aListElements( m_aListValues.size() ); + Any* pListElement = aListElements.getArray(); + css::uno::Type aType = PropertyConversion::xmlTypeToUnoType( m_sPropertyType ); + for ( const auto& rListValue : m_aListValues ) + { + *pListElement = PropertyConversion::convertString( aType, rListValue ); + ++pListElement; + } + + PropertyValue aSequenceValue; + aSequenceValue.Name = m_sPropertyName; + aSequenceValue.Value <<= aListElements; + + m_xPropertyImporter->implPushBackGenericPropertyValue( aSequenceValue ); +} + +SvXMLImportContextRef OListPropertyContext::CreateChildContext( sal_uInt16 _nPrefix, const OUString& _rLocalName, const Reference< XAttributeList >& /*_rxAttrList*/ ) +{ + if ( token::IsXMLToken( _rLocalName, token::XML_LIST_VALUE ) ) + { + m_aListValues.emplace_back(); + return new OListValueContext( GetImport(), _nPrefix, _rLocalName, *m_aListValues.rbegin() ); + } + return nullptr; +} + +//= OListValueContext +OListValueContext::OListValueContext( SvXMLImport& _rImport, sal_uInt16 _nPrefix, const OUString& _rName, OUString& _rListValueHolder ) + :SvXMLImportContext( _rImport, _nPrefix, _rName ) + ,m_rListValueHolder( _rListValueHolder ) +{ +} + +void OListValueContext::StartElement( const Reference< XAttributeList >& _rxAttrList ) +{ + const sal_Int32 nAttributeCount = _rxAttrList->getLength(); + + sal_uInt16 nNamespace; + OUString sAttributeName; + const SvXMLNamespaceMap& rMap = GetImport().GetNamespaceMap(); + for ( sal_Int32 i = 0; i < nAttributeCount; ++i ) + { + nNamespace = rMap.GetKeyByAttrName( _rxAttrList->getNameByIndex( i ), &sAttributeName ); + if ( XML_NAMESPACE_OFFICE == nNamespace ) + { + if ( token::IsXMLToken( sAttributeName, token::XML_VALUE ) + || token::IsXMLToken( sAttributeName, token::XML_STRING_VALUE ) + || token::IsXMLToken( sAttributeName, token::XML_BOOLEAN_VALUE ) + ) + { + m_rListValueHolder = _rxAttrList->getValueByIndex( i ); + continue; + } + } + + OSL_FAIL( OStringBuffer( "OListValueContext::StartElement: unknown child element (\""). + append(OUStringToOString(sAttributeName, RTL_TEXTENCODING_ASCII_US)). + append("\")!").getStr() ); + } +} + +} // namespace xmloff + +/* vim:set shiftwidth=4 softtabstop=4 expandtab: */ |