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 ::com::sun::star::util::Time
lcl_getTime(double _nValue
)
63 ::com::sun::star::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 static ::com::sun::star::util::Date
lcl_getDate( double _nValue
)
79 Date
aToolsDate((sal_uInt32
)_nValue
);
80 ::com::sun::star::util::Date aDate
;
81 ::utl::typeConvert(aToolsDate
, aDate
);
86 Any
PropertyConversion::convertString( const ::com::sun::star::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
96 #if OSL_DEBUG_LEVEL > 0
99 ::sax::Converter::convertBool(bValue
, _rReadCharacters
);
101 OStringBuffer("PropertyConversion::convertString: could not convert \"").
102 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
103 append("\" into a boolean!").getStr());
104 aReturn
<<= (_bInvertBoolean
? !bValue
: bValue
);
107 case TypeClass_SHORT
: // sal_Int16
108 case TypeClass_LONG
: // sal_Int32
110 { // it's a real int32/16 property
112 #if OSL_DEBUG_LEVEL > 0
115 ::sax::Converter::convertNumber(nValue
, _rReadCharacters
);
117 OStringBuffer("PropertyConversion::convertString: could not convert \"").
118 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
119 append("\" into an integer!").getStr());
120 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
121 aReturn
<<= (sal_Int16
)nValue
;
123 aReturn
<<= (sal_Int32
)nValue
;
127 // NO BREAK! handle it as enum
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!");
136 if (TypeClass_SHORT
== _rExpectedType
.getTypeClass())
137 aReturn
<<= (sal_Int16
)nEnumValue
;
139 aReturn
<<= (sal_Int32
)nEnumValue
;
141 aReturn
= ::cppu::int2enum((sal_Int32
)nEnumValue
, _rExpectedType
);
144 case TypeClass_HYPER
:
146 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
149 case TypeClass_DOUBLE
:
152 #if OSL_DEBUG_LEVEL > 0
155 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
157 OStringBuffer("PropertyConversion::convertString: could not convert \"").
158 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
159 append("\" into a double!").getStr());
160 aReturn
<<= (double)nValue
;
163 case TypeClass_STRING
:
164 aReturn
<<= _rReadCharacters
;
166 case TypeClass_STRUCT
:
169 if ( _rExpectedType
.equals( ::cppu::UnoType
< ::com::sun::star::util::Date
>::get() ) )
171 else if ( _rExpectedType
.equals( ::cppu::UnoType
< ::com::sun::star::util::Time
>::get() ) )
173 else if ( _rExpectedType
.equals( ::cppu::UnoType
< ::com::sun::star::util::DateTime
>::get() ) )
174 nType
= TYPE_DATETIME
;
178 // first extract the double
180 #if OSL_DEBUG_LEVEL > 0
183 ::sax::Converter::convertDouble(nValue
, _rReadCharacters
);
185 OStringBuffer("PropertyConversion::convertString: could not convert \"").
186 append(OUStringToOString(_rReadCharacters
, RTL_TEXTENCODING_ASCII_US
)).
187 append("\" into a double!").getStr());
189 // then convert it into the target type
194 OSL_ENSURE(((sal_uInt32
)nValue
) - nValue
== 0,
195 "PropertyConversion::convertString: a Date value with a fractional part?");
196 aReturn
<<= lcl_getDate(nValue
);
201 OSL_ENSURE(((sal_uInt32
)nValue
) == 0,
202 "PropertyConversion::convertString: a tools::Time value with more than a fractional part?");
203 aReturn
<<= lcl_getTime(nValue
);
208 ::com::sun::star::util::Time aTime
= lcl_getTime(nValue
);
209 ::com::sun::star::util::Date aDate
= lcl_getDate(nValue
);
211 ::com::sun::star::util::DateTime aDateTime
;
212 aDateTime
.NanoSeconds
= aTime
.NanoSeconds
;
213 aDateTime
.Seconds
= aTime
.Seconds
;
214 aDateTime
.Minutes
= aTime
.Minutes
;
215 aDateTime
.Hours
= aTime
.Hours
;
216 aDateTime
.Day
= aDate
.Day
;
217 aDateTime
.Month
= aDate
.Month
;
218 aDateTime
.Year
= aDate
.Year
;
219 aReturn
<<= aDateTime
;
225 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
229 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
235 Type
PropertyConversion::xmlTypeToUnoType( const OUString
& _rType
)
237 Type
aUnoType( cppu::UnoType
<void>::get() );
239 static std::map
< OUString
, css::uno::Type
> s_aTypeNameMap
;
240 if ( s_aTypeNameMap
.empty() )
242 s_aTypeNameMap
[ token::GetXMLToken( token::XML_BOOLEAN
) ] = cppu::UnoType
<bool>::get();
243 // Not a copy paste error, quotation from:
244 // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html
245 // all numeric types (including the UNO double)
246 // consistently map to XML_FLOAT, so taking the extra precision from the
247 // C++ type "float" to "double" makes absolute sense
248 s_aTypeNameMap
[ token::GetXMLToken( token::XML_FLOAT
) ] = ::cppu::UnoType
<double>::get();
249 s_aTypeNameMap
[ token::GetXMLToken( token::XML_STRING
) ] = ::cppu::UnoType
<OUString
>::get();
250 s_aTypeNameMap
[ token::GetXMLToken( token::XML_VOID
) ] = cppu::UnoType
<void>::get();
253 const std::map
< OUString
, css::uno::Type
>::iterator aTypePos
= s_aTypeNameMap
.find( _rType
);
254 OSL_ENSURE( s_aTypeNameMap
.end() != aTypePos
, "PropertyConversion::xmlTypeToUnoType: invalid property name!" );
255 if ( s_aTypeNameMap
.end() != aTypePos
)
256 aUnoType
= aTypePos
->second
;
262 OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
)
263 :SvXMLImportContext(_rImport
.getGlobalContext(), _nPrefix
, _rName
)
264 ,m_rContext(_rImport
)
265 ,m_bTrackAttributes(false)
269 SvXMLImportContext
* OPropertyImport::CreateChildContext(sal_uInt16 _nPrefix
, const OUString
& _rLocalName
,
270 const Reference
< XAttributeList
>& _rxAttrList
)
272 if( token::IsXMLToken( _rLocalName
, token::XML_PROPERTIES
) )
274 return new OPropertyElementsContext( m_rContext
.getGlobalContext(),
275 _nPrefix
, _rLocalName
, this);
279 OSL_FAIL(OStringBuffer("OPropertyImport::CreateChildContext: unknown sub element (only \"properties\" is recognized, but it is ").
280 append(OUStringToOString(_rLocalName
, RTL_TEXTENCODING_ASCII_US
)).
281 append(")!").getStr());
282 return SvXMLImportContext::CreateChildContext(_nPrefix
, _rLocalName
, _rxAttrList
);
286 void OPropertyImport::StartElement(const Reference
< XAttributeList
>& _rxAttrList
)
288 OSL_ENSURE(_rxAttrList
.is(), "OPropertyImport::StartElement: invalid attribute list!");
289 const sal_Int32 nAttributeCount
= _rxAttrList
->getLength();
291 // assume the 'worst' case: all attributes describe properties. This should save our property array
293 m_aValues
.reserve(nAttributeCount
);
295 const SvXMLNamespaceMap
& rMap
= m_rContext
.getGlobalContext().GetNamespaceMap();
296 sal_uInt16 nNamespace
;
298 for (sal_Int16 i
=0; i
<nAttributeCount
; ++i
)
300 nNamespace
= rMap
.GetKeyByAttrName(_rxAttrList
->getNameByIndex(i
), &sLocalName
);
301 handleAttribute(nNamespace
, sLocalName
, _rxAttrList
->getValueByIndex(i
));
303 if (m_bTrackAttributes
)
304 m_aEncounteredAttributes
.insert(sLocalName
);
307 // TODO: create PropertyValues for all the attributes which were not present, because they were implied
308 // this is necessary as soon as we have properties where the XML default is different from the property
312 bool OPropertyImport::encounteredAttribute(const OUString
& _rAttributeName
) const
314 OSL_ENSURE(m_bTrackAttributes
, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!");
315 return m_aEncounteredAttributes
.end() != m_aEncounteredAttributes
.find(_rAttributeName
);
318 void OPropertyImport::Characters(const OUString
&
319 #if OSL_DEBUG_LEVEL > 0
324 // ignore them (should be whitespaces only)
325 OSL_ENSURE(_rChars
.trim().isEmpty(), "OPropertyImport::Characters: non-whitespace characters!");
328 bool OPropertyImport::handleAttribute(sal_uInt16
/*_nNamespaceKey*/, const OUString
& _rLocalName
, const OUString
& _rValue
)
330 const OAttribute2Property::AttributeAssignment
* pProperty
= m_rContext
.getAttributeMap().getAttributeTranslation(_rLocalName
);
333 // create and store a new PropertyValue
334 PropertyValue aNewValue
;
335 aNewValue
.Name
= pProperty
->sPropertyName
;
337 // convert the value string into the target type
338 aNewValue
.Value
= PropertyConversion::convertString(pProperty
->aPropertyType
, _rValue
, pProperty
->pEnumMap
, pProperty
->bInverseSemantics
);
339 implPushBackPropertyValue( aNewValue
);
342 if (!token::IsXMLToken(_rLocalName
, token::XML_TYPE
)) // xlink:type is valid but ignored for <form:form>
344 #if OSL_DEBUG_LEVEL > 0
345 OString
sMessage( "OPropertyImport::handleAttribute: Can't handle the following:\n" );
346 sMessage
+= OString( " Attribute name: " );
347 sMessage
+= OString( _rLocalName
.getStr(), _rLocalName
.getLength(), osl_getThreadTextEncoding() );
348 sMessage
+= OString( "\n value: " );
349 sMessage
+= OString( _rValue
.getStr(), _rValue
.getLength(), osl_getThreadTextEncoding() );
350 OSL_FAIL( sMessage
.getStr() );
357 //= OPropertyElementsContext
358 OPropertyElementsContext::OPropertyElementsContext(SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
,
359 const OPropertyImportRef
& _rPropertyImporter
)
360 :SvXMLImportContext(_rImport
, _nPrefix
, _rName
)
361 ,m_xPropertyImporter(_rPropertyImporter
)
365 SvXMLImportContext
* OPropertyElementsContext::CreateChildContext(sal_uInt16 _nPrefix
, const OUString
& _rLocalName
,
366 const Reference
< XAttributeList
>&)
368 if( token::IsXMLToken( _rLocalName
, token::XML_PROPERTY
) )
370 return new OSinglePropertyContext(GetImport(), _nPrefix
, _rLocalName
, m_xPropertyImporter
);
372 else if( token::IsXMLToken( _rLocalName
, token::XML_LIST_PROPERTY
) )
374 return new OListPropertyContext( GetImport(), _nPrefix
, _rLocalName
, m_xPropertyImporter
);
378 OSL_FAIL(OStringBuffer("OPropertyElementsContext::CreateChildContext: unknown child element (\"").
379 append(OUStringToOString(_rLocalName
, RTL_TEXTENCODING_ASCII_US
)).
380 append("\")!").getStr());
381 return new SvXMLImportContext(GetImport(), _nPrefix
, _rLocalName
);
385 #if OSL_DEBUG_LEVEL > 0
386 void OPropertyElementsContext::StartElement(const Reference
< XAttributeList
>& _rxAttrList
)
388 OSL_ENSURE(0 == _rxAttrList
->getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!");
389 SvXMLImportContext::StartElement(_rxAttrList
);
392 void OPropertyElementsContext::Characters(const OUString
& _rChars
)
394 OSL_ENSURE(0 == _rChars
.trim(), "OPropertyElementsContext::Characters: non-whitespace characters detected!");
395 SvXMLImportContext::Characters(_rChars
);
400 //= OSinglePropertyContext
401 OSinglePropertyContext::OSinglePropertyContext(SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
,
402 const OPropertyImportRef
& _rPropertyImporter
)
403 :SvXMLImportContext(_rImport
, _nPrefix
, _rName
)
404 ,m_xPropertyImporter(_rPropertyImporter
)
408 SvXMLImportContext
* OSinglePropertyContext::CreateChildContext(sal_uInt16 _nPrefix
, const OUString
& _rLocalName
,
409 const Reference
< XAttributeList
>&)
411 OSL_FAIL(OStringBuffer("OSinglePropertyContext::CreateChildContext: unknown child element (\"").
412 append(OUStringToOString(_rLocalName
, RTL_TEXTENCODING_ASCII_US
)).
413 append("\")!").getStr());
414 return new SvXMLImportContext(GetImport(), _nPrefix
, _rLocalName
);
417 void OSinglePropertyContext::StartElement(const Reference
< XAttributeList
>& _rxAttrList
)
419 ::com::sun::star::beans::PropertyValue aPropValue
; // the property the instance imports currently
420 ::com::sun::star::uno::Type aPropType
; // the type of the property the instance imports currently
422 OUString sType
, sValue
;
423 const SvXMLNamespaceMap
& rMap
= GetImport().GetNamespaceMap();
424 const sal_Int16 nAttrCount
= _rxAttrList
.is() ? _rxAttrList
->getLength() : 0;
425 for( sal_Int16 i
=0; i
< nAttrCount
; i
++ )
427 const OUString
& rAttrName
= _rxAttrList
->getNameByIndex( i
);
431 rMap
.GetKeyByAttrName( rAttrName
,
433 if( XML_NAMESPACE_FORM
== nPrefix
)
435 if( token::IsXMLToken( aLocalName
, token::XML_PROPERTY_NAME
) )
436 aPropValue
.Name
= _rxAttrList
->getValueByIndex( i
);
439 else if( XML_NAMESPACE_OFFICE
== nPrefix
)
441 if( token::IsXMLToken( aLocalName
, token::XML_VALUE_TYPE
) )
442 sType
= _rxAttrList
->getValueByIndex( i
);
443 else if( token::IsXMLToken( aLocalName
,
444 token::XML_VALUE
) ||
445 token::IsXMLToken( aLocalName
,
446 token::XML_BOOLEAN_VALUE
) ||
447 token::IsXMLToken( aLocalName
,
448 token::XML_STRING_VALUE
) )
449 sValue
= _rxAttrList
->getValueByIndex( i
);
453 // the name of the property
454 OSL_ENSURE(!aPropValue
.Name
.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!");
456 // needs to be translated into a ::com::sun::star::uno::Type
457 aPropType
= PropertyConversion::xmlTypeToUnoType( sType
);
458 if( TypeClass_VOID
== aPropType
.getTypeClass() )
460 aPropValue
.Value
= Any();
465 PropertyConversion::convertString(aPropType
,
469 // now that we finally have our property value, add it to our parent object
470 if( !aPropValue
.Name
.isEmpty() )
471 m_xPropertyImporter
->implPushBackGenericPropertyValue(aPropValue
);
474 //= OListPropertyContext
475 OListPropertyContext::OListPropertyContext( SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
,
476 const OPropertyImportRef
& _rPropertyImporter
)
477 :SvXMLImportContext( _rImport
, _nPrefix
, _rName
)
478 ,m_xPropertyImporter( _rPropertyImporter
)
482 void OListPropertyContext::StartElement( const Reference
< XAttributeList
>& _rxAttrList
)
484 sal_Int32 nAttributeCount
= _rxAttrList
->getLength();
486 sal_uInt16 nNamespace
;
487 OUString sAttributeName
;
488 const SvXMLNamespaceMap
& rMap
= GetImport().GetNamespaceMap();
489 for ( sal_Int16 i
= 0; i
< nAttributeCount
; ++i
)
491 nNamespace
= rMap
.GetKeyByAttrName( _rxAttrList
->getNameByIndex( i
), &sAttributeName
);
492 if ( ( XML_NAMESPACE_FORM
== nNamespace
)
493 && ( token::IsXMLToken( sAttributeName
, token::XML_PROPERTY_NAME
) )
496 m_sPropertyName
= _rxAttrList
->getValueByIndex( i
);
498 else if ( ( XML_NAMESPACE_OFFICE
== nNamespace
)
499 && ( token::IsXMLToken( sAttributeName
, token::XML_VALUE_TYPE
) )
502 m_sPropertyType
= _rxAttrList
->getValueByIndex( i
);
506 OSL_FAIL( OStringBuffer( "OListPropertyContext::StartElement: unknown child element (\"").
507 append(OUStringToOString(sAttributeName
, RTL_TEXTENCODING_ASCII_US
)).
508 append("\")!").getStr() );
513 void OListPropertyContext::EndElement()
515 OSL_ENSURE( !m_sPropertyName
.isEmpty() && !m_sPropertyType
.isEmpty(),
516 "OListPropertyContext::EndElement: no property name or type!" );
518 if ( m_sPropertyName
.isEmpty() || m_sPropertyType
.isEmpty() )
521 Sequence
< Any
> aListElements( m_aListValues
.size() );
522 Any
* pListElement
= aListElements
.getArray();
523 com::sun::star::uno::Type aType
= PropertyConversion::xmlTypeToUnoType( m_sPropertyType
);
524 for ( ::std::vector
< OUString
>::const_iterator values
= m_aListValues
.begin();
525 values
!= m_aListValues
.end();
526 ++values
, ++pListElement
529 *pListElement
= PropertyConversion::convertString( aType
, *values
);
532 PropertyValue aSequenceValue
;
533 aSequenceValue
.Name
= m_sPropertyName
;
534 aSequenceValue
.Value
<<= aListElements
;
536 m_xPropertyImporter
->implPushBackGenericPropertyValue( aSequenceValue
);
539 SvXMLImportContext
* OListPropertyContext::CreateChildContext( sal_uInt16 _nPrefix
, const OUString
& _rLocalName
, const Reference
< XAttributeList
>& /*_rxAttrList*/ )
541 if ( token::IsXMLToken( _rLocalName
, token::XML_LIST_VALUE
) )
543 m_aListValues
.resize( m_aListValues
.size() + 1 );
544 return new OListValueContext( GetImport(), _nPrefix
, _rLocalName
, *m_aListValues
.rbegin() );
548 OSL_FAIL( OStringBuffer("OListPropertyContext::CreateChildContext: unknown child element (\"").
549 append(OUStringToOString(_rLocalName
.getStr(), RTL_TEXTENCODING_ASCII_US
)).
550 append("\")!").getStr() );
551 return new SvXMLImportContext( GetImport(), _nPrefix
, _rLocalName
);
555 //= OListValueContext
556 OListValueContext::OListValueContext( SvXMLImport
& _rImport
, sal_uInt16 _nPrefix
, const OUString
& _rName
, OUString
& _rListValueHolder
)
557 :SvXMLImportContext( _rImport
, _nPrefix
, _rName
)
558 ,m_rListValueHolder( _rListValueHolder
)
562 void OListValueContext::StartElement( const Reference
< XAttributeList
>& _rxAttrList
)
564 const sal_Int32 nAttributeCount
= _rxAttrList
->getLength();
566 sal_uInt16 nNamespace
;
567 OUString sAttributeName
;
568 const SvXMLNamespaceMap
& rMap
= GetImport().GetNamespaceMap();
569 for ( sal_Int16 i
= 0; i
< nAttributeCount
; ++i
)
571 nNamespace
= rMap
.GetKeyByAttrName( _rxAttrList
->getNameByIndex( i
), &sAttributeName
);
572 if ( XML_NAMESPACE_OFFICE
== nNamespace
)
574 if ( token::IsXMLToken( sAttributeName
, token::XML_VALUE
)
575 || token::IsXMLToken( sAttributeName
, token::XML_STRING_VALUE
)
576 || token::IsXMLToken( sAttributeName
, token::XML_BOOLEAN_VALUE
)
579 m_rListValueHolder
= _rxAttrList
->getValueByIndex( i
);
584 OSL_FAIL( OStringBuffer( "OListValueContext::StartElement: unknown child element (\"").
585 append(OUStringToOString(sAttributeName
, RTL_TEXTENCODING_ASCII_US
)).
586 append("\")!").getStr() );
590 } // namespace xmloff
592 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */