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 <xmloff/namespacemap.hxx>
32 #include <o3tl/temporary.hxx>
33 #include <osl/diagnose.h>
34 #include <sal/log.hxx>
35 #include <comphelper/extract.hxx>
36 #include <xmloff/xmlnamespace.hxx>
37 #include <tools/date.hxx>
38 #include <tools/time.hxx>
39 #include <com/sun/star/util/Date.hpp>
40 #include <com/sun/star/util/Time.hpp>
41 #include <com/sun/star/util/DateTime.hpp>
42 #include <unotools/datetime.hxx>
43 #include <rtl/strbuf.hxx>
45 using namespace ::xmloff::token
;
50 using namespace ::com::sun::star::uno
;
51 using namespace ::com::sun::star::beans
;
52 using namespace ::com::sun::star::xml
;
53 using ::com::sun::star::xml::sax::XAttributeList
;
54 using ::com::sun::star::xml::sax::XFastAttributeList
;
56 // NO using namespace ...util !!!
57 // need a tools Date/Time/DateTime below, which would conflict with the uno types then
61 #define TYPE_DATETIME 3
63 //= PropertyConversion
66 css::util::Time
lcl_getTime(double _nValue
)
68 css::util::Time aTime
;
69 sal_uInt64 nIntValue
= static_cast<sal_uInt64
>(_nValue
* ::tools::Time::nanoSecPerDay
);
70 aTime
.NanoSeconds
= nIntValue
% ::tools::Time::nanoSecPerSec
;
71 nIntValue
/= ::tools::Time::nanoSecPerSec
;
72 aTime
.Seconds
= nIntValue
% ::tools::Time::secondPerMinute
;
73 nIntValue
/= ::tools::Time::secondPerMinute
;
74 aTime
.Minutes
= nIntValue
% ::tools::Time::minutePerHour
;
75 nIntValue
/= ::tools::Time::minutePerHour
;
76 OSL_ENSURE(nIntValue
< 24, "lcl_getTime: more than a day?");
77 aTime
.Hours
= nIntValue
;
82 css::util::Date
lcl_getDate( double _nValue
)
84 Date
aToolsDate(static_cast<sal_uInt32
>(_nValue
));
85 css::util::Date aDate
;
86 ::utl::typeConvert(aToolsDate
, aDate
);
91 Any
PropertyConversion::convertString( const css::uno::Type
& _rExpectedType
,
92 const OUString
& _rReadCharacters
, const SvXMLEnumMapEntry
<sal_uInt16
>* _pEnumMap
, const bool _bInvertBoolean
)
95 bool bEnumAsInt
= false;
96 switch (_rExpectedType
.getTypeClass())
98 case TypeClass_BOOLEAN
: // sal_Bool
102 ::sax::Converter::convertBool(bValue
, _rReadCharacters
);
104 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
105 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
106 "\" into a boolean!").getStr());
107 aReturn
<<= (_bInvertBoolean
? !bValue
: bValue
);
110 case TypeClass_SHORT
: // sal_Int16
111 case TypeClass_LONG
: // sal_Int32
113 { // it's a real int32/16 property
116 ::sax::Converter::convertNumber(nValue
, _rReadCharacters
);
118 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
119 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
120 "\" into an integer!").getStr());
121 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
122 aReturn
<<= static_cast<sal_Int16
>(nValue
);
131 sal_uInt16
nEnumValue(0);
132 bool bSuccess
= SvXMLUnitConverter::convertEnum(nEnumValue
, _rReadCharacters
, _pEnumMap
);
133 OSL_ENSURE(bSuccess
, "PropertyConversion::convertString: could not convert to an enum value!");
136 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
137 aReturn
<<= static_cast<sal_Int16
>(nEnumValue
);
139 aReturn
<<= static_cast<sal_Int32
>(nEnumValue
);
141 aReturn
= ::cppu::int2enum(static_cast<sal_Int32
>(nEnumValue
), _rExpectedType
);
144 case TypeClass_HYPER
:
146 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
149 case TypeClass_DOUBLE
:
153 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
155 OStringBuffer(OString::Concat("PropertyConversion::convertString: could not convert \"") +
156 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
157 "\" into a double!").getStr());
161 case TypeClass_STRING
:
162 aReturn
<<= _rReadCharacters
;
164 case TypeClass_STRUCT
:
167 if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Date
>::get() ) )
169 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Time
>::get() ) )
171 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::DateTime
>::get() ) )
172 nType
= TYPE_DATETIME
;
176 // first extract the double
179 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
181 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
182 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
183 "\" into a double!").getStr());
185 // then convert it into the target type
190 OSL_ENSURE(std::modf(nValue
, &o3tl::temporary(double())) == 0,
191 "PropertyConversion::convertString: a Date value with a fractional part?");
192 aReturn
<<= lcl_getDate(nValue
);
197 OSL_ENSURE((static_cast<sal_uInt32
>(nValue
)) == 0,
198 "PropertyConversion::convertString: a tools::Time value with more than a fractional part?");
199 aReturn
<<= lcl_getTime(nValue
);
204 css::util::Time aTime
= lcl_getTime(nValue
);
205 css::util::Date aDate
= lcl_getDate(nValue
);
207 css::util::DateTime aDateTime
;
208 aDateTime
.NanoSeconds
= aTime
.NanoSeconds
;
209 aDateTime
.Seconds
= aTime
.Seconds
;
210 aDateTime
.Minutes
= aTime
.Minutes
;
211 aDateTime
.Hours
= aTime
.Hours
;
212 aDateTime
.Day
= aDate
.Day
;
213 aDateTime
.Month
= aDate
.Month
;
214 aDateTime
.Year
= aDate
.Year
;
215 aReturn
<<= aDateTime
;
221 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
225 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
231 Type
PropertyConversion::xmlTypeToUnoType( const OUString
& _rType
)
233 Type
aUnoType( cppu::UnoType
<void>::get() );
235 static std::map
< OUString
, css::uno::Type
> s_aTypeNameMap
237 { token::GetXMLToken( token::XML_BOOLEAN
) , cppu::UnoType
<bool>::get()},
238 // Not a copy paste error, quotation from:
239 // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html
240 // all numeric types (including the UNO double)
241 // consistently map to XML_FLOAT, so taking the extra precision from the
242 // C++ type "float" to "double" makes absolute sense
243 { token::GetXMLToken( token::XML_FLOAT
) , ::cppu::UnoType
<double>::get()},
244 { token::GetXMLToken( token::XML_STRING
) , ::cppu::UnoType
<OUString
>::get()},
245 { token::GetXMLToken( token::XML_VOID
) , cppu::UnoType
<void>::get() },
248 const std::map
< OUString
, css::uno::Type
>::iterator aTypePos
= s_aTypeNameMap
.find( _rType
);
249 OSL_ENSURE( s_aTypeNameMap
.end() != aTypePos
, "PropertyConversion::xmlTypeToUnoType: invalid property name!" );
250 if ( s_aTypeNameMap
.end() != aTypePos
)
251 aUnoType
= aTypePos
->second
;
257 OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl
& _rImport
)
258 :SvXMLImportContext(_rImport
.getGlobalContext())
259 ,m_rContext(_rImport
)
260 ,m_bTrackAttributes(false)
264 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OPropertyImport::createFastChildContext(
266 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& /*xAttrList*/ )
268 if( (nElement
& TOKEN_MASK
) == token::XML_PROPERTIES
)
270 return new OPropertyElementsContext( m_rContext
.getGlobalContext(), this);
273 SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement
));
277 void OPropertyImport::startFastElement(sal_Int32
/*nElement*/, const Reference
< XFastAttributeList
>& xAttrList
)
280 // assume the 'worst' case: all attributes describe properties. This should save our property array
282 m_aValues
.reserve(sax_fastparser::castToFastAttributeList(xAttrList
).size());
284 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
286 handleAttribute(aIter
.getToken(), aIter
.toString());
288 if (m_bTrackAttributes
)
289 m_aEncounteredAttributes
.insert(aIter
.getToken() & TOKEN_MASK
);
292 // TODO: create PropertyValues for all the attributes which were not present, because they were implied
293 // this is necessary as soon as we have properties where the XML default is different from the property
297 bool OPropertyImport::encounteredAttribute(sal_Int32 nAttributeToken
) const
299 OSL_ENSURE(m_bTrackAttributes
, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!");
300 return m_aEncounteredAttributes
.end() != m_aEncounteredAttributes
.find(nAttributeToken
& TOKEN_MASK
);
303 void OPropertyImport::characters(const OUString
& _rChars
)
305 // ignore them (should be whitespace only)
306 OSL_ENSURE(_rChars
.trim().isEmpty(), "OPropertyImport::Characters: non-whitespace characters!");
309 bool OPropertyImport::handleAttribute(sal_Int32 nAttributeToken
, const OUString
& _rValue
)
311 const OAttribute2Property::AttributeAssignment
* pProperty
= m_rContext
.getAttributeMap().getAttributeTranslation(nAttributeToken
& TOKEN_MASK
);
314 // create and store a new PropertyValue
315 PropertyValue aNewValue
;
316 aNewValue
.Name
= pProperty
->sPropertyName
;
318 // convert the value string into the target type
319 if ((nAttributeToken
& TOKEN_MASK
) == token::XML_HREF
)
321 aNewValue
.Value
<<= m_rContext
.getGlobalContext().GetAbsoluteReference(_rValue
);
325 aNewValue
.Value
= PropertyConversion::convertString(
326 pProperty
->aPropertyType
, _rValue
, pProperty
->pEnumMap
,
327 pProperty
->bInverseSemantics
);
329 implPushBackPropertyValue( aNewValue
);
332 if ((nAttributeToken
& TOKEN_MASK
) != token::XML_TYPE
) // xlink:type is valid but ignored for <form:form>
334 SAL_WARN( "xmloff", "OPropertyImport::handleAttribute: Can't handle "
335 << SvXMLImport::getPrefixAndNameFromToken(nAttributeToken
) << "=" << _rValue
);
341 //= OPropertyElementsContext
342 OPropertyElementsContext::OPropertyElementsContext(SvXMLImport
& _rImport
,
343 OPropertyImportRef _xPropertyImporter
)
344 :SvXMLImportContext(_rImport
)
345 ,m_xPropertyImporter(std::move(_xPropertyImporter
))
349 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OPropertyElementsContext::createFastChildContext(
350 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
352 if( (nElement
& TOKEN_MASK
) == XML_PROPERTY
)
354 return new OSinglePropertyContext(GetImport(), m_xPropertyImporter
);
356 else if( (nElement
& TOKEN_MASK
) == XML_LIST_PROPERTY
)
358 return new OListPropertyContext( GetImport(), m_xPropertyImporter
);
363 #if OSL_DEBUG_LEVEL > 0
364 void OPropertyElementsContext::startFastElement(
365 sal_Int32
/*nElement*/,
366 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
368 OSL_ENSURE(0 == xAttrList
->getFastAttributes().getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!");
371 void OPropertyElementsContext::characters(const OUString
& _rChars
)
373 OSL_ENSURE(_rChars
.trim().isEmpty(), "OPropertyElementsContext::Characters: non-whitespace characters detected!");
377 //= OSinglePropertyContext
378 OSinglePropertyContext::OSinglePropertyContext(SvXMLImport
& _rImport
,
379 OPropertyImportRef _xPropertyImporter
)
380 :SvXMLImportContext(_rImport
)
381 ,m_xPropertyImporter(std::move(_xPropertyImporter
))
385 void OSinglePropertyContext::startFastElement(
386 sal_Int32
/*nElement*/,
387 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
389 css::beans::PropertyValue aPropValue
; // the property the instance imports currently
390 css::uno::Type aPropType
; // the type of the property the instance imports currently
392 OUString sType
, sValue
;
393 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
395 switch (aIter
.getToken())
397 case XML_ELEMENT(FORM
, XML_PROPERTY_NAME
):
398 aPropValue
.Name
= aIter
.toString();
400 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
401 sType
= aIter
.toString();
403 case XML_ELEMENT(OFFICE
, XML_VALUE
):
404 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
405 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
406 sValue
= aIter
.toString();
409 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
413 // the name of the property
414 OSL_ENSURE(!aPropValue
.Name
.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!");
416 // needs to be translated into a css::uno::Type
417 aPropType
= PropertyConversion::xmlTypeToUnoType( sType
);
418 if( TypeClass_VOID
== aPropType
.getTypeClass() )
420 aPropValue
.Value
= Any();
425 PropertyConversion::convertString(aPropType
,
429 // now that we finally have our property value, add it to our parent object
430 if( !aPropValue
.Name
.isEmpty() )
431 m_xPropertyImporter
->implPushBackGenericPropertyValue(aPropValue
);
434 //= OListPropertyContext
435 OListPropertyContext::OListPropertyContext( SvXMLImport
& _rImport
,
436 OPropertyImportRef _rPropertyImporter
)
437 :SvXMLImportContext( _rImport
)
438 ,m_xPropertyImporter(std::move( _rPropertyImporter
))
442 void OListPropertyContext::startFastElement(
443 sal_Int32
/*nElement*/,
444 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
446 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
448 switch (aIter
.getToken())
450 case XML_ELEMENT(FORM
, XML_PROPERTY_NAME
):
451 m_sPropertyName
= aIter
.toString();
453 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
454 m_sPropertyType
= aIter
.toString();
457 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
462 void OListPropertyContext::endFastElement(sal_Int32
)
464 OSL_ENSURE( !m_sPropertyName
.isEmpty() && !m_sPropertyType
.isEmpty(),
465 "OListPropertyContext::EndElement: no property name or type!" );
467 if ( m_sPropertyName
.isEmpty() || m_sPropertyType
.isEmpty() )
470 Sequence
< Any
> aListElements( m_aListValues
.size() );
471 Any
* pListElement
= aListElements
.getArray();
472 css::uno::Type aType
= PropertyConversion::xmlTypeToUnoType( m_sPropertyType
);
473 for ( const auto& rListValue
: m_aListValues
)
475 *pListElement
= PropertyConversion::convertString( aType
, rListValue
);
479 PropertyValue aSequenceValue
;
480 aSequenceValue
.Name
= m_sPropertyName
;
481 aSequenceValue
.Value
<<= aListElements
;
483 m_xPropertyImporter
->implPushBackGenericPropertyValue( aSequenceValue
);
486 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OListPropertyContext::createFastChildContext(
487 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
489 if ( (nElement
& TOKEN_MASK
) == XML_LIST_VALUE
)
491 m_aListValues
.emplace_back();
492 return new OListValueContext( GetImport(), *m_aListValues
.rbegin() );
497 //= OListValueContext
498 OListValueContext::OListValueContext( SvXMLImport
& _rImport
, OUString
& _rListValueHolder
)
499 :SvXMLImportContext( _rImport
)
500 ,m_rListValueHolder( _rListValueHolder
)
504 void OListValueContext::startFastElement(
505 sal_Int32
/*nElement*/,
506 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
508 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
510 switch(aIter
.getToken())
512 case XML_ELEMENT(OFFICE
, XML_VALUE
):
513 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
514 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
515 m_rListValueHolder
= aIter
.toString();
518 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
523 } // namespace xmloff
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */