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 "propertyimport.hxx"
22 #include <sax/tools/converter.hxx>
24 #include <xmloff/xmlimp.hxx>
25 #include <xmloff/xmluconv.hxx>
26 #include <xmloff/nmspmap.hxx>
27 #include <osl/diagnose.h>
28 #include <comphelper/extract.hxx>
29 #include "callbacks.hxx"
30 #include <xmloff/xmlnmspe.hxx>
31 #include <tools/date.hxx>
32 #include <tools/time.hxx>
33 #include <com/sun/star/util/Date.hpp>
34 #include <com/sun/star/util/Time.hpp>
35 #include <com/sun/star/util/DateTime.hpp>
36 #include <unotools/datetime.hxx>
37 #include <rtl/strbuf.hxx>
39 #if OSL_DEBUG_LEVEL > 0
40 #include <osl/thread.h>
46 using namespace ::com::sun::star::uno
;
47 using namespace ::com::sun::star::beans
;
48 using namespace ::com::sun::star::xml
;
49 using ::com::sun::star::xml::sax::XAttributeList
;
51 // NO using namespace ...util !!!
52 // need a tools Date/Time/DateTime below, which would conflict with the uno types then
56 #define TYPE_DATETIME 3
58 //= PropertyConversion
61 css::util::Time
lcl_getTime(double _nValue
)
63 css::util::Time aTime
;
64 sal_uInt64 nIntValue
= static_cast<sal_uInt64
>(_nValue
* ::tools::Time::nanoSecPerDay
);
65 aTime
.NanoSeconds
= nIntValue
% ::tools::Time::nanoSecPerSec
;
66 nIntValue
/= ::tools::Time::nanoSecPerSec
;
67 aTime
.Seconds
= nIntValue
% ::tools::Time::secondPerMinute
;
68 nIntValue
/= ::tools::Time::secondPerMinute
;
69 aTime
.Minutes
= nIntValue
% ::tools::Time::minutePerHour
;
70 nIntValue
/= ::tools::Time::minutePerHour
;
71 OSL_ENSURE(nIntValue
< 24, "lcl_getTime: more than a day?");
72 aTime
.Hours
= nIntValue
;
77 css::util::Date
lcl_getDate( double _nValue
)
79 Date
aToolsDate((sal_uInt32
)_nValue
);
80 css::util::Date aDate
;
81 ::utl::typeConvert(aToolsDate
, aDate
);
86 Any
PropertyConversion::convertString( const css::uno::Type
& _rExpectedType
,
87 const OUString
& _rReadCharacters
, const SvXMLEnumMapEntry
* _pEnumMap
, const bool _bInvertBoolean
)
90 bool bEnumAsInt
= false;
91 switch (_rExpectedType
.getTypeClass())
93 case TypeClass_BOOLEAN
: // sal_Bool
97 ::sax::Converter::convertBool(bValue
, _rReadCharacters
);
99 OStringBuffer("PropertyConversion::convertString: could not convert \"").
100 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
101 append("\" into a boolean!").getStr());
102 aReturn
<<= (_bInvertBoolean
? !bValue
: bValue
);
105 case TypeClass_SHORT
: // sal_Int16
106 case TypeClass_LONG
: // sal_Int32
108 { // it's a real int32/16 property
111 ::sax::Converter::convertNumber(nValue
, _rReadCharacters
);
113 OStringBuffer("PropertyConversion::convertString: could not convert \"").
114 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
115 append("\" into an integer!").getStr());
116 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
117 aReturn
<<= (sal_Int16
)nValue
;
119 aReturn
<<= (sal_Int32
)nValue
;
126 sal_uInt16
nEnumValue(0);
127 bool bSuccess
= SvXMLUnitConverter::convertEnum(nEnumValue
, _rReadCharacters
, _pEnumMap
);
128 OSL_ENSURE(bSuccess
, "PropertyConversion::convertString: could not convert to an enum value!");
132 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
133 aReturn
<<= (sal_Int16
)nEnumValue
;
135 aReturn
<<= (sal_Int32
)nEnumValue
;
137 aReturn
= ::cppu::int2enum((sal_Int32
)nEnumValue
, _rExpectedType
);
140 case TypeClass_HYPER
:
142 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
145 case TypeClass_DOUBLE
:
149 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
151 OStringBuffer("PropertyConversion::convertString: could not convert \"").
152 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
153 append("\" into a double!").getStr());
154 aReturn
<<= (double)nValue
;
157 case TypeClass_STRING
:
158 aReturn
<<= _rReadCharacters
;
160 case TypeClass_STRUCT
:
163 if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Date
>::get() ) )
165 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::Time
>::get() ) )
167 else if ( _rExpectedType
.equals( ::cppu::UnoType
< css::util::DateTime
>::get() ) )
168 nType
= TYPE_DATETIME
;
172 // first extract the double
175 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
177 OStringBuffer("PropertyConversion::convertString: could not convert \"").
178 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
179 append("\" into a double!").getStr());
181 // then convert it into the target type
186 OSL_ENSURE(((sal_uInt32
)nValue
) - nValue
== 0,
187 "PropertyConversion::convertString: a Date value with a fractional part?");
188 aReturn
<<= lcl_getDate(nValue
);
193 OSL_ENSURE(((sal_uInt32
)nValue
) == 0,
194 "PropertyConversion::convertString: a tools::Time value with more than a fractional part?");
195 aReturn
<<= lcl_getTime(nValue
);
200 css::util::Time aTime
= lcl_getTime(nValue
);
201 css::util::Date aDate
= lcl_getDate(nValue
);
203 css::util::DateTime aDateTime
;
204 aDateTime
.NanoSeconds
= aTime
.NanoSeconds
;
205 aDateTime
.Seconds
= aTime
.Seconds
;
206 aDateTime
.Minutes
= aTime
.Minutes
;
207 aDateTime
.Hours
= aTime
.Hours
;
208 aDateTime
.Day
= aDate
.Day
;
209 aDateTime
.Month
= aDate
.Month
;
210 aDateTime
.Year
= aDate
.Year
;
211 aReturn
<<= aDateTime
;
217 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
221 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
227 Type
PropertyConversion::xmlTypeToUnoType( const OUString
& _rType
)
229 Type
aUnoType( cppu::UnoType
<void>::get() );
231 static std::map
< OUString
, css::uno::Type
> s_aTypeNameMap
;
232 if ( s_aTypeNameMap
.empty() )
234 s_aTypeNameMap
[ token::GetXMLToken( token::XML_BOOLEAN
) ] = cppu::UnoType
<bool>::get();
235 // Not a copy paste error, quotation from:
236 // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html
237 // all numeric types (including the UNO double)
238 // consistently map to XML_FLOAT, so taking the extra precision from the
239 // C++ type "float" to "double" makes absolute sense
240 s_aTypeNameMap
[ token::GetXMLToken( token::XML_FLOAT
) ] = ::cppu::UnoType
<double>::get();
241 s_aTypeNameMap
[ token::GetXMLToken( token::XML_STRING
) ] = ::cppu::UnoType
<OUString
>::get();
242 s_aTypeNameMap
[ token::GetXMLToken( token::XML_VOID
) ] = cppu::UnoType
<void>::get();
245 const std::map
< OUString
, css::uno::Type
>::iterator aTypePos
= s_aTypeNameMap
.find( _rType
);
246 OSL_ENSURE( s_aTypeNameMap
.end() != aTypePos
, "PropertyConversion::xmlTypeToUnoType: invalid property name!" );
247 if ( s_aTypeNameMap
.end() != aTypePos
)
248 aUnoType
= aTypePos
->second
;
254 OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
)
255 :SvXMLImportContext(_rImport
.getGlobalContext(), _nPrefix
, _rName
)
256 ,m_rContext(_rImport
)
257 ,m_bTrackAttributes(false)
261 SvXMLImportContext
* OPropertyImport::CreateChildContext(sal_uInt16 _nPrefix
, const OUString
& _rLocalName
,
262 const Reference
< XAttributeList
>& _rxAttrList
)
264 if( token::IsXMLToken( _rLocalName
, token::XML_PROPERTIES
) )
266 return new OPropertyElementsContext( m_rContext
.getGlobalContext(),
267 _nPrefix
, _rLocalName
, this);
271 OSL_FAIL(OStringBuffer("OPropertyImport::CreateChildContext: unknown sub element (only \"properties\" is recognized, but it is ").
272 append(OUStringToOString(_rLocalName
, RTL_TEXTENCODING_ASCII_US
)).
273 append(")!").getStr());
274 return SvXMLImportContext::CreateChildContext(_nPrefix
, _rLocalName
, _rxAttrList
);
278 void OPropertyImport::StartElement(const Reference
< XAttributeList
>& _rxAttrList
)
280 OSL_ENSURE(_rxAttrList
.is(), "OPropertyImport::StartElement: invalid attribute list!");
281 const sal_Int32 nAttributeCount
= _rxAttrList
->getLength();
283 // assume the 'worst' case: all attributes describe properties. This should save our property array
285 m_aValues
.reserve(nAttributeCount
);
287 const SvXMLNamespaceMap
& rMap
= m_rContext
.getGlobalContext().GetNamespaceMap();
288 sal_uInt16 nNamespace
;
290 for (sal_Int32 i
=0; i
<nAttributeCount
; ++i
)
292 nNamespace
= rMap
.GetKeyByAttrName(_rxAttrList
->getNameByIndex(i
), &sLocalName
);
293 handleAttribute(nNamespace
, sLocalName
, _rxAttrList
->getValueByIndex(i
));
295 if (m_bTrackAttributes
)
296 m_aEncounteredAttributes
.insert(sLocalName
);
299 // TODO: create PropertyValues for all the attributes which were not present, because they were implied
300 // this is necessary as soon as we have properties where the XML default is different from the property
304 bool OPropertyImport::encounteredAttribute(const OUString
& _rAttributeName
) const
306 OSL_ENSURE(m_bTrackAttributes
, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!");
307 return m_aEncounteredAttributes
.end() != m_aEncounteredAttributes
.find(_rAttributeName
);
310 void OPropertyImport::Characters(const OUString
& _rChars
)
312 // ignore them (should be whitespaces only)
313 OSL_ENSURE(_rChars
.trim().isEmpty(), "OPropertyImport::Characters: non-whitespace characters!");
316 bool OPropertyImport::handleAttribute(sal_uInt16
/*_nNamespaceKey*/, const OUString
& _rLocalName
, const OUString
& _rValue
)
318 const OAttribute2Property::AttributeAssignment
* pProperty
= m_rContext
.getAttributeMap().getAttributeTranslation(_rLocalName
);
321 // create and store a new PropertyValue
322 PropertyValue aNewValue
;
323 aNewValue
.Name
= pProperty
->sPropertyName
;
325 // convert the value string into the target type
326 if (token::IsXMLToken(_rLocalName
, token::XML_HREF
))
328 aNewValue
.Value
<<= m_rContext
.getGlobalContext().GetAbsoluteReference(_rValue
);
332 aNewValue
.Value
= PropertyConversion::convertString(
333 pProperty
->aPropertyType
, _rValue
, pProperty
->pEnumMap
,
334 pProperty
->bInverseSemantics
);
336 implPushBackPropertyValue( aNewValue
);
339 if (!token::IsXMLToken(_rLocalName
, token::XML_TYPE
)) // xlink:type is valid but ignored for <form:form>
341 #if OSL_DEBUG_LEVEL > 0
342 OString
sMessage( "OPropertyImport::handleAttribute: Can't handle the following:\n" );
343 sMessage
+= OString( " Attribute name: " );
344 sMessage
+= OString( _rLocalName
.getStr(), _rLocalName
.getLength(), osl_getThreadTextEncoding() );
345 sMessage
+= OString( "\n value: " );
346 sMessage
+= OString( _rValue
.getStr(), _rValue
.getLength(), osl_getThreadTextEncoding() );
347 OSL_FAIL( sMessage
.getStr() );
354 //= OPropertyElementsContext
355 OPropertyElementsContext::OPropertyElementsContext(SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
,
356 const OPropertyImportRef
& _rPropertyImporter
)
357 :SvXMLImportContext(_rImport
, _nPrefix
, _rName
)
358 ,m_xPropertyImporter(_rPropertyImporter
)
362 SvXMLImportContext
* OPropertyElementsContext::CreateChildContext(sal_uInt16 _nPrefix
, const OUString
& _rLocalName
,
363 const Reference
< XAttributeList
>&)
365 if( token::IsXMLToken( _rLocalName
, token::XML_PROPERTY
) )
367 return new OSinglePropertyContext(GetImport(), _nPrefix
, _rLocalName
, m_xPropertyImporter
);
369 else if( token::IsXMLToken( _rLocalName
, token::XML_LIST_PROPERTY
) )
371 return new OListPropertyContext( GetImport(), _nPrefix
, _rLocalName
, m_xPropertyImporter
);
375 OSL_FAIL(OStringBuffer("OPropertyElementsContext::CreateChildContext: unknown child element (\"").
376 append(OUStringToOString(_rLocalName
, RTL_TEXTENCODING_ASCII_US
)).
377 append("\")!").getStr());
378 return new SvXMLImportContext(GetImport(), _nPrefix
, _rLocalName
);
382 #if OSL_DEBUG_LEVEL > 0
383 void OPropertyElementsContext::StartElement(const Reference
< XAttributeList
>& _rxAttrList
)
385 OSL_ENSURE(0 == _rxAttrList
->getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!");
386 SvXMLImportContext::StartElement(_rxAttrList
);
389 void OPropertyElementsContext::Characters(const OUString
& _rChars
)
391 OSL_ENSURE(nullptr == _rChars
.trim(), "OPropertyElementsContext::Characters: non-whitespace characters detected!");
392 SvXMLImportContext::Characters(_rChars
);
397 //= OSinglePropertyContext
398 OSinglePropertyContext::OSinglePropertyContext(SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
,
399 const OPropertyImportRef
& _rPropertyImporter
)
400 :SvXMLImportContext(_rImport
, _nPrefix
, _rName
)
401 ,m_xPropertyImporter(_rPropertyImporter
)
405 SvXMLImportContext
* OSinglePropertyContext::CreateChildContext(sal_uInt16 _nPrefix
, const OUString
& _rLocalName
,
406 const Reference
< XAttributeList
>&)
408 OSL_FAIL(OStringBuffer("OSinglePropertyContext::CreateChildContext: unknown child element (\"").
409 append(OUStringToOString(_rLocalName
, RTL_TEXTENCODING_ASCII_US
)).
410 append("\")!").getStr());
411 return new SvXMLImportContext(GetImport(), _nPrefix
, _rLocalName
);
414 void OSinglePropertyContext::StartElement(const Reference
< XAttributeList
>& _rxAttrList
)
416 css::beans::PropertyValue aPropValue
; // the property the instance imports currently
417 css::uno::Type aPropType
; // the type of the property the instance imports currently
419 OUString sType
, sValue
;
420 const SvXMLNamespaceMap
& rMap
= GetImport().GetNamespaceMap();
421 const sal_Int16 nAttrCount
= _rxAttrList
.is() ? _rxAttrList
->getLength() : 0;
422 for( sal_Int16 i
=0; i
< nAttrCount
; i
++ )
424 const OUString
& rAttrName
= _rxAttrList
->getNameByIndex( i
);
428 rMap
.GetKeyByAttrName( rAttrName
,
430 if( XML_NAMESPACE_FORM
== nPrefix
)
432 if( token::IsXMLToken( aLocalName
, token::XML_PROPERTY_NAME
) )
433 aPropValue
.Name
= _rxAttrList
->getValueByIndex( i
);
436 else if( XML_NAMESPACE_OFFICE
== nPrefix
)
438 if( token::IsXMLToken( aLocalName
, token::XML_VALUE_TYPE
) )
439 sType
= _rxAttrList
->getValueByIndex( i
);
440 else if( token::IsXMLToken( aLocalName
,
441 token::XML_VALUE
) ||
442 token::IsXMLToken( aLocalName
,
443 token::XML_BOOLEAN_VALUE
) ||
444 token::IsXMLToken( aLocalName
,
445 token::XML_STRING_VALUE
) )
446 sValue
= _rxAttrList
->getValueByIndex( i
);
450 // the name of the property
451 OSL_ENSURE(!aPropValue
.Name
.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!");
453 // needs to be translated into a css::uno::Type
454 aPropType
= PropertyConversion::xmlTypeToUnoType( sType
);
455 if( TypeClass_VOID
== aPropType
.getTypeClass() )
457 aPropValue
.Value
= Any();
462 PropertyConversion::convertString(aPropType
,
466 // now that we finally have our property value, add it to our parent object
467 if( !aPropValue
.Name
.isEmpty() )
468 m_xPropertyImporter
->implPushBackGenericPropertyValue(aPropValue
);
471 //= OListPropertyContext
472 OListPropertyContext::OListPropertyContext( SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
,
473 const OPropertyImportRef
& _rPropertyImporter
)
474 :SvXMLImportContext( _rImport
, _nPrefix
, _rName
)
475 ,m_xPropertyImporter( _rPropertyImporter
)
479 void OListPropertyContext::StartElement( const Reference
< XAttributeList
>& _rxAttrList
)
481 sal_Int32 nAttributeCount
= _rxAttrList
->getLength();
483 sal_uInt16 nNamespace
;
484 OUString sAttributeName
;
485 const SvXMLNamespaceMap
& rMap
= GetImport().GetNamespaceMap();
486 for ( sal_Int32 i
= 0; i
< nAttributeCount
; ++i
)
488 nNamespace
= rMap
.GetKeyByAttrName( _rxAttrList
->getNameByIndex( i
), &sAttributeName
);
489 if ( ( XML_NAMESPACE_FORM
== nNamespace
)
490 && ( token::IsXMLToken( sAttributeName
, token::XML_PROPERTY_NAME
) )
493 m_sPropertyName
= _rxAttrList
->getValueByIndex( i
);
495 else if ( ( XML_NAMESPACE_OFFICE
== nNamespace
)
496 && ( token::IsXMLToken( sAttributeName
, token::XML_VALUE_TYPE
) )
499 m_sPropertyType
= _rxAttrList
->getValueByIndex( i
);
503 OSL_FAIL( OStringBuffer( "OListPropertyContext::StartElement: unknown child element (\"").
504 append(OUStringToOString(sAttributeName
, RTL_TEXTENCODING_ASCII_US
)).
505 append("\")!").getStr() );
510 void OListPropertyContext::EndElement()
512 OSL_ENSURE( !m_sPropertyName
.isEmpty() && !m_sPropertyType
.isEmpty(),
513 "OListPropertyContext::EndElement: no property name or type!" );
515 if ( m_sPropertyName
.isEmpty() || m_sPropertyType
.isEmpty() )
518 Sequence
< Any
> aListElements( m_aListValues
.size() );
519 Any
* pListElement
= aListElements
.getArray();
520 css::uno::Type aType
= PropertyConversion::xmlTypeToUnoType( m_sPropertyType
);
521 for ( ::std::vector
< OUString
>::const_iterator values
= m_aListValues
.begin();
522 values
!= m_aListValues
.end();
523 ++values
, ++pListElement
526 *pListElement
= PropertyConversion::convertString( aType
, *values
);
529 PropertyValue aSequenceValue
;
530 aSequenceValue
.Name
= m_sPropertyName
;
531 aSequenceValue
.Value
<<= aListElements
;
533 m_xPropertyImporter
->implPushBackGenericPropertyValue( aSequenceValue
);
536 SvXMLImportContext
* OListPropertyContext::CreateChildContext( sal_uInt16 _nPrefix
, const OUString
& _rLocalName
, const Reference
< XAttributeList
>& /*_rxAttrList*/ )
538 if ( token::IsXMLToken( _rLocalName
, token::XML_LIST_VALUE
) )
540 m_aListValues
.resize( m_aListValues
.size() + 1 );
541 return new OListValueContext( GetImport(), _nPrefix
, _rLocalName
, *m_aListValues
.rbegin() );
545 OSL_FAIL( OStringBuffer("OListPropertyContext::CreateChildContext: unknown child element (\"").
546 append(OUStringToOString(_rLocalName
.getStr(), RTL_TEXTENCODING_ASCII_US
)).
547 append("\")!").getStr() );
548 return new SvXMLImportContext( GetImport(), _nPrefix
, _rLocalName
);
552 //= OListValueContext
553 OListValueContext::OListValueContext( SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
, OUString
& _rListValueHolder
)
554 :SvXMLImportContext( _rImport
, _nPrefix
, _rName
)
555 ,m_rListValueHolder( _rListValueHolder
)
559 void OListValueContext::StartElement( const Reference
< XAttributeList
>& _rxAttrList
)
561 const sal_Int32 nAttributeCount
= _rxAttrList
->getLength();
563 sal_uInt16 nNamespace
;
564 OUString sAttributeName
;
565 const SvXMLNamespaceMap
& rMap
= GetImport().GetNamespaceMap();
566 for ( sal_Int32 i
= 0; i
< nAttributeCount
; ++i
)
568 nNamespace
= rMap
.GetKeyByAttrName( _rxAttrList
->getNameByIndex( i
), &sAttributeName
);
569 if ( XML_NAMESPACE_OFFICE
== nNamespace
)
571 if ( token::IsXMLToken( sAttributeName
, token::XML_VALUE
)
572 || token::IsXMLToken( sAttributeName
, token::XML_STRING_VALUE
)
573 || token::IsXMLToken( sAttributeName
, token::XML_BOOLEAN_VALUE
)
576 m_rListValueHolder
= _rxAttrList
->getValueByIndex( i
);
581 OSL_FAIL( OStringBuffer( "OListValueContext::StartElement: unknown child element (\"").
582 append(OUStringToOString(sAttributeName
, RTL_TEXTENCODING_ASCII_US
)).
583 append("\")!").getStr() );
587 } // namespace xmloff
589 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */