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 "propertyimport.hxx"
26 #include <sax/tools/converter.hxx>
29 #include <xmloff/xmlimp.hxx>
30 #include <xmloff/xmluconv.hxx>
31 #include <o3tl/temporary.hxx>
32 #include <osl/diagnose.h>
33 #include <sal/log.hxx>
34 #include <comphelper/extract.hxx>
35 #include <xmloff/xmlnamespace.hxx>
36 #include <tools/date.hxx>
37 #include <tools/time.hxx>
38 #include <com/sun/star/util/Date.hpp>
39 #include <com/sun/star/util/Time.hpp>
40 #include <com/sun/star/util/DateTime.hpp>
41 #include <unotools/datetime.hxx>
42 #include <rtl/strbuf.hxx>
44 using namespace ::xmloff::token
;
49 using namespace ::com::sun::star::uno
;
50 using namespace ::com::sun::star::beans
;
51 using namespace ::com::sun::star::xml
;
52 using ::com::sun::star::xml::sax::XFastAttributeList
;
54 // NO using namespace ...util !!!
55 // need a tools Date/Time/DateTime below, which would conflict with the uno types then
59 #define TYPE_DATETIME 3
61 //= PropertyConversion
64 css::util::Time
lcl_getTime(double _nValue
)
66 css::util::Time aTime
;
67 sal_uInt64 nIntValue
= static_cast<sal_uInt64
>(_nValue
* ::tools::Time::nanoSecPerDay
);
68 aTime
.NanoSeconds
= nIntValue
% ::tools::Time::nanoSecPerSec
;
69 nIntValue
/= ::tools::Time::nanoSecPerSec
;
70 aTime
.Seconds
= nIntValue
% ::tools::Time::secondPerMinute
;
71 nIntValue
/= ::tools::Time::secondPerMinute
;
72 aTime
.Minutes
= nIntValue
% ::tools::Time::minutePerHour
;
73 nIntValue
/= ::tools::Time::minutePerHour
;
74 OSL_ENSURE(nIntValue
< 24, "lcl_getTime: more than a day?");
75 aTime
.Hours
= nIntValue
;
80 css::util::Date
lcl_getDate( double _nValue
)
82 Date
aToolsDate(static_cast<sal_uInt32
>(_nValue
));
83 css::util::Date aDate
;
84 ::utl::typeConvert(aToolsDate
, aDate
);
92 Any
convertAsEnum(bool bEnumAsInt
, const css::uno::Type
& _rExpectedType
,
93 std::u16string_view _rReadCharacters
, const SvXMLEnumMapEntry
<sal_uInt16
>* _pEnumMap
)
97 sal_uInt16
nEnumValue(0);
98 bool bSuccess
= SvXMLUnitConverter::convertEnum(nEnumValue
, _rReadCharacters
, _pEnumMap
);
99 OSL_ENSURE(bSuccess
, "PropertyConversion::convertString: could not convert to an enum value!");
103 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
104 aReturn
<<= static_cast<sal_Int16
>(nEnumValue
);
106 aReturn
<<= static_cast<sal_Int32
>(nEnumValue
);
109 aReturn
= ::cppu::int2enum(static_cast<sal_Int32
>(nEnumValue
), _rExpectedType
);
116 Any
PropertyConversion::convertString( const css::uno::Type
& _rExpectedType
,
117 const OUString
& _rReadCharacters
, const SvXMLEnumMapEntry
<sal_uInt16
>* _pEnumMap
, const bool _bInvertBoolean
)
120 switch (_rExpectedType
.getTypeClass())
122 case TypeClass_BOOLEAN
: // sal_Bool
126 ::sax::Converter::convertBool(bValue
, _rReadCharacters
);
128 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
129 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
130 "\" into a boolean!").getStr());
131 aReturn
<<= (_bInvertBoolean
? !bValue
: bValue
);
134 case TypeClass_SHORT
: // sal_Int16
137 { // it's a real int16 property
140 ::sax::Converter::convertNumber(nValue
, _rReadCharacters
, SAL_MIN_INT16
, SAL_MAX_INT16
);
142 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
143 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
144 "\" into a sal_Int16!").getStr());
145 aReturn
<<= static_cast<sal_Int16
>(nValue
);
148 aReturn
= convertAsEnum(true, _rExpectedType
, _rReadCharacters
, _pEnumMap
);
151 case TypeClass_LONG
: // sal_Int32
154 { // it's a real int32 property
157 ::sax::Converter::convertNumber(nValue
, _rReadCharacters
);
159 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
160 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
161 "\" into a sal_Int32!").getStr());
165 aReturn
= convertAsEnum(true, _rExpectedType
, _rReadCharacters
, _pEnumMap
);
170 aReturn
= convertAsEnum(false, _rExpectedType
, _rReadCharacters
, _pEnumMap
);
173 case TypeClass_HYPER
:
175 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
178 case TypeClass_DOUBLE
:
182 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
184 OStringBuffer(OString::Concat("PropertyConversion::convertString: could not convert \"") +
185 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
186 "\" into a double!").getStr());
190 case TypeClass_STRING
:
191 aReturn
<<= _rReadCharacters
;
193 case TypeClass_STRUCT
:
196 if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Date
>::get() ) )
198 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Time
>::get() ) )
200 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::DateTime
>::get() ) )
201 nType
= TYPE_DATETIME
;
205 // first extract the double
208 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
210 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
211 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
212 "\" into a double!").getStr());
214 // then convert it into the target type
219 OSL_ENSURE(std::modf(nValue
, &o3tl::temporary(double())) == 0,
220 "PropertyConversion::convertString: a Date value with a fractional part?");
221 aReturn
<<= lcl_getDate(nValue
);
226 OSL_ENSURE((static_cast<sal_uInt32
>(nValue
)) == 0,
227 "PropertyConversion::convertString: a tools::Time value with more than a fractional part?");
228 aReturn
<<= lcl_getTime(nValue
);
233 css::util::Time aTime
= lcl_getTime(nValue
);
234 css::util::Date aDate
= lcl_getDate(nValue
);
236 css::util::DateTime aDateTime
;
237 aDateTime
.NanoSeconds
= aTime
.NanoSeconds
;
238 aDateTime
.Seconds
= aTime
.Seconds
;
239 aDateTime
.Minutes
= aTime
.Minutes
;
240 aDateTime
.Hours
= aTime
.Hours
;
241 aDateTime
.Day
= aDate
.Day
;
242 aDateTime
.Month
= aDate
.Month
;
243 aDateTime
.Year
= aDate
.Year
;
244 aReturn
<<= aDateTime
;
250 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
254 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
260 Type
PropertyConversion::xmlTypeToUnoType( const OUString
& _rType
)
262 Type
aUnoType( cppu::UnoType
<void>::get() );
264 static std::map
< OUString
, css::uno::Type
> s_aTypeNameMap
266 { token::GetXMLToken( token::XML_BOOLEAN
) , cppu::UnoType
<bool>::get()},
267 // Not a copy paste error, quotation from:
268 // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html
269 // all numeric types (including the UNO double)
270 // consistently map to XML_FLOAT, so taking the extra precision from the
271 // C++ type "float" to "double" makes absolute sense
272 { token::GetXMLToken( token::XML_FLOAT
) , ::cppu::UnoType
<double>::get()},
273 { token::GetXMLToken( token::XML_STRING
) , ::cppu::UnoType
<OUString
>::get()},
274 { token::GetXMLToken( token::XML_VOID
) , cppu::UnoType
<void>::get() },
277 const std::map
< OUString
, css::uno::Type
>::iterator aTypePos
= s_aTypeNameMap
.find( _rType
);
278 OSL_ENSURE( s_aTypeNameMap
.end() != aTypePos
, "PropertyConversion::xmlTypeToUnoType: invalid property name!" );
279 if ( s_aTypeNameMap
.end() != aTypePos
)
280 aUnoType
= aTypePos
->second
;
286 OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl
& _rImport
)
287 :SvXMLImportContext(_rImport
.getGlobalContext())
288 ,m_rContext(_rImport
)
289 ,m_bTrackAttributes(false)
293 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OPropertyImport::createFastChildContext(
295 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& /*xAttrList*/ )
297 if( (nElement
& TOKEN_MASK
) == token::XML_PROPERTIES
)
299 return new OPropertyElementsContext( m_rContext
.getGlobalContext(), this);
302 SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement
));
306 void OPropertyImport::startFastElement(sal_Int32
/*nElement*/, const Reference
< XFastAttributeList
>& xAttrList
)
309 // assume the 'worst' case: all attributes describe properties. This should save our property array
311 m_aValues
.reserve(sax_fastparser::castToFastAttributeList(xAttrList
).size());
313 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
315 handleAttribute(aIter
.getToken(), aIter
.toString());
317 if (m_bTrackAttributes
)
318 m_aEncounteredAttributes
.insert(aIter
.getToken() & TOKEN_MASK
);
321 // TODO: create PropertyValues for all the attributes which were not present, because they were implied
322 // this is necessary as soon as we have properties where the XML default is different from the property
326 bool OPropertyImport::encounteredAttribute(sal_Int32 nAttributeToken
) const
328 OSL_ENSURE(m_bTrackAttributes
, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!");
329 return m_aEncounteredAttributes
.end() != m_aEncounteredAttributes
.find(nAttributeToken
& TOKEN_MASK
);
332 void OPropertyImport::characters(const OUString
& _rChars
)
334 // ignore them (should be whitespace only)
335 OSL_ENSURE(o3tl::trim(_rChars
).empty(), "OPropertyImport::Characters: non-whitespace characters!");
338 bool OPropertyImport::handleAttribute(sal_Int32 nAttributeToken
, const OUString
& _rValue
)
340 const OAttribute2Property::AttributeAssignment
* pProperty
= m_rContext
.getAttributeMap().getAttributeTranslation(nAttributeToken
& TOKEN_MASK
);
343 // create and store a new PropertyValue
344 PropertyValue aNewValue
;
345 aNewValue
.Name
= pProperty
->sPropertyName
;
347 // convert the value string into the target type
348 if ((nAttributeToken
& TOKEN_MASK
) == token::XML_HREF
)
350 aNewValue
.Value
<<= m_rContext
.getGlobalContext().GetAbsoluteReference(_rValue
);
354 aNewValue
.Value
= PropertyConversion::convertString(
355 pProperty
->aPropertyType
, _rValue
, pProperty
->pEnumMap
,
356 pProperty
->bInverseSemantics
);
358 implPushBackPropertyValue( aNewValue
);
361 if ((nAttributeToken
& TOKEN_MASK
) != token::XML_TYPE
) // xlink:type is valid but ignored for <form:form>
363 SAL_WARN( "xmloff", "OPropertyImport::handleAttribute: Can't handle "
364 << SvXMLImport::getPrefixAndNameFromToken(nAttributeToken
) << "=" << _rValue
);
370 //= OPropertyElementsContext
371 OPropertyElementsContext::OPropertyElementsContext(SvXMLImport
& _rImport
,
372 OPropertyImportRef _xPropertyImporter
)
373 :SvXMLImportContext(_rImport
)
374 ,m_xPropertyImporter(std::move(_xPropertyImporter
))
378 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OPropertyElementsContext::createFastChildContext(
379 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
381 if( (nElement
& TOKEN_MASK
) == XML_PROPERTY
)
383 return new OSinglePropertyContext(GetImport(), m_xPropertyImporter
);
385 else if( (nElement
& TOKEN_MASK
) == XML_LIST_PROPERTY
)
387 return new OListPropertyContext( GetImport(), m_xPropertyImporter
);
392 #if OSL_DEBUG_LEVEL > 0
393 void OPropertyElementsContext::startFastElement(
394 sal_Int32
/*nElement*/,
395 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
397 OSL_ENSURE(0 == xAttrList
->getFastAttributes().getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!");
400 void OPropertyElementsContext::characters(const OUString
& _rChars
)
402 OSL_ENSURE(o3tl::trim(_rChars
).empty(), "OPropertyElementsContext::Characters: non-whitespace characters detected!");
406 //= OSinglePropertyContext
407 OSinglePropertyContext::OSinglePropertyContext(SvXMLImport
& _rImport
,
408 OPropertyImportRef _xPropertyImporter
)
409 :SvXMLImportContext(_rImport
)
410 ,m_xPropertyImporter(std::move(_xPropertyImporter
))
414 void OSinglePropertyContext::startFastElement(
415 sal_Int32
/*nElement*/,
416 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
418 css::beans::PropertyValue aPropValue
; // the property the instance imports currently
419 css::uno::Type aPropType
; // the type of the property the instance imports currently
421 OUString sType
, sValue
;
422 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
424 switch (aIter
.getToken())
426 case XML_ELEMENT(FORM
, XML_PROPERTY_NAME
):
427 aPropValue
.Name
= aIter
.toString();
429 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
430 sType
= aIter
.toString();
432 case XML_ELEMENT(OFFICE
, XML_VALUE
):
433 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
434 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
435 sValue
= aIter
.toString();
438 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
442 // the name of the property
443 OSL_ENSURE(!aPropValue
.Name
.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!");
445 // needs to be translated into a css::uno::Type
446 aPropType
= PropertyConversion::xmlTypeToUnoType( sType
);
447 if( TypeClass_VOID
== aPropType
.getTypeClass() )
449 aPropValue
.Value
= Any();
454 PropertyConversion::convertString(aPropType
,
458 // now that we finally have our property value, add it to our parent object
459 if( !aPropValue
.Name
.isEmpty() )
460 m_xPropertyImporter
->implPushBackGenericPropertyValue(aPropValue
);
463 //= OListPropertyContext
464 OListPropertyContext::OListPropertyContext( SvXMLImport
& _rImport
,
465 OPropertyImportRef _rPropertyImporter
)
466 :SvXMLImportContext( _rImport
)
467 ,m_xPropertyImporter(std::move( _rPropertyImporter
))
471 void OListPropertyContext::startFastElement(
472 sal_Int32
/*nElement*/,
473 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
475 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
477 switch (aIter
.getToken())
479 case XML_ELEMENT(FORM
, XML_PROPERTY_NAME
):
480 m_sPropertyName
= aIter
.toString();
482 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
483 m_sPropertyType
= aIter
.toString();
486 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
491 void OListPropertyContext::endFastElement(sal_Int32
)
493 OSL_ENSURE( !m_sPropertyName
.isEmpty() && !m_sPropertyType
.isEmpty(),
494 "OListPropertyContext::EndElement: no property name or type!" );
496 if ( m_sPropertyName
.isEmpty() || m_sPropertyType
.isEmpty() )
499 Sequence
< Any
> aListElements( m_aListValues
.size() );
500 Any
* pListElement
= aListElements
.getArray();
501 css::uno::Type aType
= PropertyConversion::xmlTypeToUnoType( m_sPropertyType
);
502 for ( const auto& rListValue
: m_aListValues
)
504 *pListElement
= PropertyConversion::convertString( aType
, rListValue
);
508 PropertyValue aSequenceValue
;
509 aSequenceValue
.Name
= m_sPropertyName
;
510 aSequenceValue
.Value
<<= aListElements
;
512 m_xPropertyImporter
->implPushBackGenericPropertyValue( aSequenceValue
);
515 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OListPropertyContext::createFastChildContext(
516 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
518 if ( (nElement
& TOKEN_MASK
) == XML_LIST_VALUE
)
520 m_aListValues
.emplace_back();
521 return new OListValueContext( GetImport(), *m_aListValues
.rbegin() );
526 //= OListValueContext
527 OListValueContext::OListValueContext( SvXMLImport
& _rImport
, OUString
& _rListValueHolder
)
528 :SvXMLImportContext( _rImport
)
529 ,m_rListValueHolder( _rListValueHolder
)
533 void OListValueContext::startFastElement(
534 sal_Int32
/*nElement*/,
535 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
537 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
539 switch(aIter
.getToken())
541 case XML_ELEMENT(OFFICE
, XML_VALUE
):
542 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
543 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
544 m_rListValueHolder
= aIter
.toString();
547 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
552 } // namespace xmloff
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */