Version 7.5.1.1, tag libreoffice-7.5.1.1
[LibreOffice.git] / xmloff / source / forms / propertyimport.cxx
blob81772ac41644c0f5e1a3d76a6c77878feb869f68
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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>
22 #include <cmath>
24 #include "propertyimport.hxx"
26 #include <sax/tools/converter.hxx>
28 #include <utility>
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;
47 namespace xmloff
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
59 #define TYPE_DATE 1
60 #define TYPE_TIME 2
61 #define TYPE_DATETIME 3
63 //= PropertyConversion
64 namespace
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;
79 return aTime;
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);
87 return aDate;
91 Any PropertyConversion::convertString( const css::uno::Type& _rExpectedType,
92 const OUString& _rReadCharacters, const SvXMLEnumMapEntry<sal_uInt16>* _pEnumMap, const bool _bInvertBoolean )
94 Any aReturn;
95 bool bEnumAsInt = false;
96 switch (_rExpectedType.getTypeClass())
98 case TypeClass_BOOLEAN: // sal_Bool
100 bool bValue;
101 bool bSuccess =
102 ::sax::Converter::convertBool(bValue, _rReadCharacters);
103 OSL_ENSURE(bSuccess,
104 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
105 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
106 "\" into a boolean!").getStr());
107 aReturn <<= (_bInvertBoolean ? !bValue : bValue);
109 break;
110 case TypeClass_SHORT: // sal_Int16
111 case TypeClass_LONG: // sal_Int32
112 if (!_pEnumMap)
113 { // it's a real int32/16 property
114 sal_Int32 nValue(0);
115 bool bSuccess =
116 ::sax::Converter::convertNumber(nValue, _rReadCharacters);
117 OSL_ENSURE(bSuccess,
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);
123 else
124 aReturn <<= nValue;
125 break;
127 bEnumAsInt = true;
128 [[fallthrough]];
129 case TypeClass_ENUM:
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!");
135 if (bEnumAsInt)
136 if (TypeClass_SHORT == _rExpectedType.getTypeClass())
137 aReturn <<= static_cast<sal_Int16>(nEnumValue);
138 else
139 aReturn <<= static_cast<sal_Int32>(nEnumValue);
140 else
141 aReturn = ::cppu::int2enum(static_cast<sal_Int32>(nEnumValue), _rExpectedType);
143 break;
144 case TypeClass_HYPER:
146 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
148 break;
149 case TypeClass_DOUBLE:
151 double nValue;
152 bool bSuccess =
153 ::sax::Converter::convertDouble(nValue, _rReadCharacters);
154 OSL_ENSURE(bSuccess,
155 OStringBuffer(OString::Concat("PropertyConversion::convertString: could not convert \"") +
156 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
157 "\" into a double!").getStr());
158 aReturn <<= nValue;
160 break;
161 case TypeClass_STRING:
162 aReturn <<= _rReadCharacters;
163 break;
164 case TypeClass_STRUCT:
166 sal_Int32 nType = 0;
167 if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Date >::get() ) )
168 nType = TYPE_DATE;
169 else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Time >::get() ) )
170 nType = TYPE_TIME;
171 else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::DateTime >::get() ) )
172 nType = TYPE_DATETIME;
174 if ( nType )
176 // first extract the double
177 double nValue = 0;
178 bool bSuccess =
179 ::sax::Converter::convertDouble(nValue, _rReadCharacters);
180 OSL_ENSURE(bSuccess,
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
186 switch (nType)
188 case TYPE_DATE:
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);
194 break;
195 case TYPE_TIME:
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);
201 break;
202 case TYPE_DATETIME:
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;
217 break;
220 else
221 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
223 break;
224 default:
225 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
228 return aReturn;
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;
253 return aUnoType;
256 //= OPropertyImport
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(
265 sal_Int32 nElement,
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);
272 else
273 SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
274 return nullptr;
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
281 // some reallocs
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
294 // default
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);
312 if (pProperty)
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);
323 else
325 aNewValue.Value = PropertyConversion::convertString(
326 pProperty->aPropertyType, _rValue, pProperty->pEnumMap,
327 pProperty->bInverseSemantics);
329 implPushBackPropertyValue( aNewValue );
330 return true;
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 );
336 return false;
338 return true;
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 );
360 return nullptr;
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!");
375 #endif
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();
399 break;
400 case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
401 sType = aIter.toString();
402 break;
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();
407 break;
408 default:
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();
422 else
424 aPropValue.Value =
425 PropertyConversion::convertString(aPropType,
426 sValue);
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();
452 break;
453 case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
454 m_sPropertyType = aIter.toString();
455 break;
456 default:
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() )
468 return;
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 );
476 ++pListElement;
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() );
494 return nullptr;
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();
516 break;
517 default:
518 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
523 } // namespace xmloff
525 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */