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::XFastAttributeList
;
55 // NO using namespace ...util !!!
56 // need a tools Date/Time/DateTime below, which would conflict with the uno types then
60 #define TYPE_DATETIME 3
62 //= PropertyConversion
65 css::util::Time
lcl_getTime(double _nValue
)
67 css::util::Time aTime
;
68 sal_uInt64 nIntValue
= static_cast<sal_uInt64
>(_nValue
* ::tools::Time::nanoSecPerDay
);
69 aTime
.NanoSeconds
= nIntValue
% ::tools::Time::nanoSecPerSec
;
70 nIntValue
/= ::tools::Time::nanoSecPerSec
;
71 aTime
.Seconds
= nIntValue
% ::tools::Time::secondPerMinute
;
72 nIntValue
/= ::tools::Time::secondPerMinute
;
73 aTime
.Minutes
= nIntValue
% ::tools::Time::minutePerHour
;
74 nIntValue
/= ::tools::Time::minutePerHour
;
75 OSL_ENSURE(nIntValue
< 24, "lcl_getTime: more than a day?");
76 aTime
.Hours
= nIntValue
;
81 css::util::Date
lcl_getDate( double _nValue
)
83 Date
aToolsDate(static_cast<sal_uInt32
>(_nValue
));
84 css::util::Date aDate
;
85 ::utl::typeConvert(aToolsDate
, aDate
);
90 Any
PropertyConversion::convertString( const css::uno::Type
& _rExpectedType
,
91 const OUString
& _rReadCharacters
, const SvXMLEnumMapEntry
<sal_uInt16
>* _pEnumMap
, const bool _bInvertBoolean
)
94 bool bEnumAsInt
= false;
95 switch (_rExpectedType
.getTypeClass())
97 case TypeClass_BOOLEAN
: // sal_Bool
101 ::sax::Converter::convertBool(bValue
, _rReadCharacters
);
103 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
104 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
105 "\" into a boolean!").getStr());
106 aReturn
<<= (_bInvertBoolean
? !bValue
: bValue
);
109 case TypeClass_SHORT
: // sal_Int16
110 case TypeClass_LONG
: // sal_Int32
112 { // it's a real int32/16 property
115 ::sax::Converter::convertNumber(nValue
, _rReadCharacters
);
117 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
118 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
119 "\" into an integer!").getStr());
120 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
121 aReturn
<<= static_cast<sal_Int16
>(nValue
);
130 sal_uInt16
nEnumValue(0);
131 bool bSuccess
= SvXMLUnitConverter::convertEnum(nEnumValue
, _rReadCharacters
, _pEnumMap
);
132 OSL_ENSURE(bSuccess
, "PropertyConversion::convertString: could not convert to an enum value!");
135 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
136 aReturn
<<= static_cast<sal_Int16
>(nEnumValue
);
138 aReturn
<<= static_cast<sal_Int32
>(nEnumValue
);
140 aReturn
= ::cppu::int2enum(static_cast<sal_Int32
>(nEnumValue
), _rExpectedType
);
143 case TypeClass_HYPER
:
145 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
148 case TypeClass_DOUBLE
:
152 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
154 OStringBuffer(OString::Concat("PropertyConversion::convertString: could not convert \"") +
155 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
156 "\" into a double!").getStr());
160 case TypeClass_STRING
:
161 aReturn
<<= _rReadCharacters
;
163 case TypeClass_STRUCT
:
166 if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Date
>::get() ) )
168 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Time
>::get() ) )
170 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::DateTime
>::get() ) )
171 nType
= TYPE_DATETIME
;
175 // first extract the double
178 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
180 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
181 OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
) +
182 "\" into a double!").getStr());
184 // then convert it into the target type
189 OSL_ENSURE(std::modf(nValue
, &o3tl::temporary(double())) == 0,
190 "PropertyConversion::convertString: a Date value with a fractional part?");
191 aReturn
<<= lcl_getDate(nValue
);
196 OSL_ENSURE((static_cast<sal_uInt32
>(nValue
)) == 0,
197 "PropertyConversion::convertString: a tools::Time value with more than a fractional part?");
198 aReturn
<<= lcl_getTime(nValue
);
203 css::util::Time aTime
= lcl_getTime(nValue
);
204 css::util::Date aDate
= lcl_getDate(nValue
);
206 css::util::DateTime aDateTime
;
207 aDateTime
.NanoSeconds
= aTime
.NanoSeconds
;
208 aDateTime
.Seconds
= aTime
.Seconds
;
209 aDateTime
.Minutes
= aTime
.Minutes
;
210 aDateTime
.Hours
= aTime
.Hours
;
211 aDateTime
.Day
= aDate
.Day
;
212 aDateTime
.Month
= aDate
.Month
;
213 aDateTime
.Year
= aDate
.Year
;
214 aReturn
<<= aDateTime
;
220 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
224 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
230 Type
PropertyConversion::xmlTypeToUnoType( const OUString
& _rType
)
232 Type
aUnoType( cppu::UnoType
<void>::get() );
234 static std::map
< OUString
, css::uno::Type
> s_aTypeNameMap
236 { token::GetXMLToken( token::XML_BOOLEAN
) , cppu::UnoType
<bool>::get()},
237 // Not a copy paste error, quotation from:
238 // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html
239 // all numeric types (including the UNO double)
240 // consistently map to XML_FLOAT, so taking the extra precision from the
241 // C++ type "float" to "double" makes absolute sense
242 { token::GetXMLToken( token::XML_FLOAT
) , ::cppu::UnoType
<double>::get()},
243 { token::GetXMLToken( token::XML_STRING
) , ::cppu::UnoType
<OUString
>::get()},
244 { token::GetXMLToken( token::XML_VOID
) , cppu::UnoType
<void>::get() },
247 const std::map
< OUString
, css::uno::Type
>::iterator aTypePos
= s_aTypeNameMap
.find( _rType
);
248 OSL_ENSURE( s_aTypeNameMap
.end() != aTypePos
, "PropertyConversion::xmlTypeToUnoType: invalid property name!" );
249 if ( s_aTypeNameMap
.end() != aTypePos
)
250 aUnoType
= aTypePos
->second
;
256 OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl
& _rImport
)
257 :SvXMLImportContext(_rImport
.getGlobalContext())
258 ,m_rContext(_rImport
)
259 ,m_bTrackAttributes(false)
263 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OPropertyImport::createFastChildContext(
265 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& /*xAttrList*/ )
267 if( (nElement
& TOKEN_MASK
) == token::XML_PROPERTIES
)
269 return new OPropertyElementsContext( m_rContext
.getGlobalContext(), this);
272 SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement
));
276 void OPropertyImport::startFastElement(sal_Int32
/*nElement*/, const Reference
< XFastAttributeList
>& xAttrList
)
279 // assume the 'worst' case: all attributes describe properties. This should save our property array
281 m_aValues
.reserve(sax_fastparser::castToFastAttributeList(xAttrList
).size());
283 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
285 handleAttribute(aIter
.getToken(), aIter
.toString());
287 if (m_bTrackAttributes
)
288 m_aEncounteredAttributes
.insert(aIter
.getToken() & TOKEN_MASK
);
291 // TODO: create PropertyValues for all the attributes which were not present, because they were implied
292 // this is necessary as soon as we have properties where the XML default is different from the property
296 bool OPropertyImport::encounteredAttribute(sal_Int32 nAttributeToken
) const
298 OSL_ENSURE(m_bTrackAttributes
, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!");
299 return m_aEncounteredAttributes
.end() != m_aEncounteredAttributes
.find(nAttributeToken
& TOKEN_MASK
);
302 void OPropertyImport::characters(const OUString
& _rChars
)
304 // ignore them (should be whitespace only)
305 OSL_ENSURE(o3tl::trim(_rChars
).empty(), "OPropertyImport::Characters: non-whitespace characters!");
308 bool OPropertyImport::handleAttribute(sal_Int32 nAttributeToken
, const OUString
& _rValue
)
310 const OAttribute2Property::AttributeAssignment
* pProperty
= m_rContext
.getAttributeMap().getAttributeTranslation(nAttributeToken
& TOKEN_MASK
);
313 // create and store a new PropertyValue
314 PropertyValue aNewValue
;
315 aNewValue
.Name
= pProperty
->sPropertyName
;
317 // convert the value string into the target type
318 if ((nAttributeToken
& TOKEN_MASK
) == token::XML_HREF
)
320 aNewValue
.Value
<<= m_rContext
.getGlobalContext().GetAbsoluteReference(_rValue
);
324 aNewValue
.Value
= PropertyConversion::convertString(
325 pProperty
->aPropertyType
, _rValue
, pProperty
->pEnumMap
,
326 pProperty
->bInverseSemantics
);
328 implPushBackPropertyValue( aNewValue
);
331 if ((nAttributeToken
& TOKEN_MASK
) != token::XML_TYPE
) // xlink:type is valid but ignored for <form:form>
333 SAL_WARN( "xmloff", "OPropertyImport::handleAttribute: Can't handle "
334 << SvXMLImport::getPrefixAndNameFromToken(nAttributeToken
) << "=" << _rValue
);
340 //= OPropertyElementsContext
341 OPropertyElementsContext::OPropertyElementsContext(SvXMLImport
& _rImport
,
342 OPropertyImportRef _xPropertyImporter
)
343 :SvXMLImportContext(_rImport
)
344 ,m_xPropertyImporter(std::move(_xPropertyImporter
))
348 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OPropertyElementsContext::createFastChildContext(
349 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
351 if( (nElement
& TOKEN_MASK
) == XML_PROPERTY
)
353 return new OSinglePropertyContext(GetImport(), m_xPropertyImporter
);
355 else if( (nElement
& TOKEN_MASK
) == XML_LIST_PROPERTY
)
357 return new OListPropertyContext( GetImport(), m_xPropertyImporter
);
362 #if OSL_DEBUG_LEVEL > 0
363 void OPropertyElementsContext::startFastElement(
364 sal_Int32
/*nElement*/,
365 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
367 OSL_ENSURE(0 == xAttrList
->getFastAttributes().getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!");
370 void OPropertyElementsContext::characters(const OUString
& _rChars
)
372 OSL_ENSURE(o3tl::trim(_rChars
).empty(), "OPropertyElementsContext::Characters: non-whitespace characters detected!");
376 //= OSinglePropertyContext
377 OSinglePropertyContext::OSinglePropertyContext(SvXMLImport
& _rImport
,
378 OPropertyImportRef _xPropertyImporter
)
379 :SvXMLImportContext(_rImport
)
380 ,m_xPropertyImporter(std::move(_xPropertyImporter
))
384 void OSinglePropertyContext::startFastElement(
385 sal_Int32
/*nElement*/,
386 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
388 css::beans::PropertyValue aPropValue
; // the property the instance imports currently
389 css::uno::Type aPropType
; // the type of the property the instance imports currently
391 OUString sType
, sValue
;
392 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
394 switch (aIter
.getToken())
396 case XML_ELEMENT(FORM
, XML_PROPERTY_NAME
):
397 aPropValue
.Name
= aIter
.toString();
399 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
400 sType
= aIter
.toString();
402 case XML_ELEMENT(OFFICE
, XML_VALUE
):
403 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
404 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
405 sValue
= aIter
.toString();
408 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
412 // the name of the property
413 OSL_ENSURE(!aPropValue
.Name
.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!");
415 // needs to be translated into a css::uno::Type
416 aPropType
= PropertyConversion::xmlTypeToUnoType( sType
);
417 if( TypeClass_VOID
== aPropType
.getTypeClass() )
419 aPropValue
.Value
= Any();
424 PropertyConversion::convertString(aPropType
,
428 // now that we finally have our property value, add it to our parent object
429 if( !aPropValue
.Name
.isEmpty() )
430 m_xPropertyImporter
->implPushBackGenericPropertyValue(aPropValue
);
433 //= OListPropertyContext
434 OListPropertyContext::OListPropertyContext( SvXMLImport
& _rImport
,
435 OPropertyImportRef _rPropertyImporter
)
436 :SvXMLImportContext( _rImport
)
437 ,m_xPropertyImporter(std::move( _rPropertyImporter
))
441 void OListPropertyContext::startFastElement(
442 sal_Int32
/*nElement*/,
443 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
445 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
447 switch (aIter
.getToken())
449 case XML_ELEMENT(FORM
, XML_PROPERTY_NAME
):
450 m_sPropertyName
= aIter
.toString();
452 case XML_ELEMENT(OFFICE
, XML_VALUE_TYPE
):
453 m_sPropertyType
= aIter
.toString();
456 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
461 void OListPropertyContext::endFastElement(sal_Int32
)
463 OSL_ENSURE( !m_sPropertyName
.isEmpty() && !m_sPropertyType
.isEmpty(),
464 "OListPropertyContext::EndElement: no property name or type!" );
466 if ( m_sPropertyName
.isEmpty() || m_sPropertyType
.isEmpty() )
469 Sequence
< Any
> aListElements( m_aListValues
.size() );
470 Any
* pListElement
= aListElements
.getArray();
471 css::uno::Type aType
= PropertyConversion::xmlTypeToUnoType( m_sPropertyType
);
472 for ( const auto& rListValue
: m_aListValues
)
474 *pListElement
= PropertyConversion::convertString( aType
, rListValue
);
478 PropertyValue aSequenceValue
;
479 aSequenceValue
.Name
= m_sPropertyName
;
480 aSequenceValue
.Value
<<= aListElements
;
482 m_xPropertyImporter
->implPushBackGenericPropertyValue( aSequenceValue
);
485 css::uno::Reference
< css::xml::sax::XFastContextHandler
> OListPropertyContext::createFastChildContext(
486 sal_Int32 nElement
, const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& )
488 if ( (nElement
& TOKEN_MASK
) == XML_LIST_VALUE
)
490 m_aListValues
.emplace_back();
491 return new OListValueContext( GetImport(), *m_aListValues
.rbegin() );
496 //= OListValueContext
497 OListValueContext::OListValueContext( SvXMLImport
& _rImport
, OUString
& _rListValueHolder
)
498 :SvXMLImportContext( _rImport
)
499 ,m_rListValueHolder( _rListValueHolder
)
503 void OListValueContext::startFastElement(
504 sal_Int32
/*nElement*/,
505 const css::uno::Reference
< css::xml::sax::XFastAttributeList
>& xAttrList
)
507 for( auto& aIter
: sax_fastparser::castToFastAttributeList(xAttrList
) )
509 switch(aIter
.getToken())
511 case XML_ELEMENT(OFFICE
, XML_VALUE
):
512 case XML_ELEMENT(OFFICE
, XML_STRING_VALUE
):
513 case XML_ELEMENT(OFFICE
, XML_BOOLEAN_VALUE
):
514 m_rListValueHolder
= aIter
.toString();
517 XMLOFF_WARN_UNKNOWN("xmloff", aIter
);
522 } // namespace xmloff
524 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */