Gtk-WARNING gtktreestore.c:1047: Invalid column number 1 added to iter
[LibreOffice.git] / xmloff / source / forms / propertyimport.cxx
blobd89ecc71e687db832e70ab2eec56944936af1452
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 <o3tl/temporary.hxx>
32 #include <osl/diagnose.h>
33 #include <sal/log.hxx>
34 #include <comphelper/extract.hxx>
35 #include <xmloff/xmlnamespace.hxx>
36 #include <tools/date.hxx>
37 #include <tools/time.hxx>
38 #include <com/sun/star/util/Date.hpp>
39 #include <com/sun/star/util/Time.hpp>
40 #include <com/sun/star/util/DateTime.hpp>
41 #include <unotools/datetime.hxx>
42 #include <rtl/strbuf.hxx>
44 using namespace ::xmloff::token;
46 namespace xmloff
49 using namespace ::com::sun::star::uno;
50 using namespace ::com::sun::star::beans;
51 using namespace ::com::sun::star::xml;
52 using ::com::sun::star::xml::sax::XFastAttributeList;
54 // NO using namespace ...util !!!
55 // need a tools Date/Time/DateTime below, which would conflict with the uno types then
57 #define TYPE_DATE 1
58 #define TYPE_TIME 2
59 #define TYPE_DATETIME 3
61 //= PropertyConversion
62 namespace
64 css::util::Time lcl_getTime(double _nValue)
66 css::util::Time aTime;
67 sal_uInt64 nIntValue = static_cast<sal_uInt64>(_nValue * ::tools::Time::nanoSecPerDay);
68 aTime.NanoSeconds = nIntValue % ::tools::Time::nanoSecPerSec;
69 nIntValue /= ::tools::Time::nanoSecPerSec;
70 aTime.Seconds = nIntValue % ::tools::Time::secondPerMinute;
71 nIntValue /= ::tools::Time::secondPerMinute;
72 aTime.Minutes = nIntValue % ::tools::Time::minutePerHour;
73 nIntValue /= ::tools::Time::minutePerHour;
74 OSL_ENSURE(nIntValue < 24, "lcl_getTime: more than a day?");
75 aTime.Hours = nIntValue;
77 return aTime;
80 css::util::Date lcl_getDate( double _nValue )
82 Date aToolsDate(static_cast<sal_uInt32>(_nValue));
83 css::util::Date aDate;
84 ::utl::typeConvert(aToolsDate, aDate);
85 return aDate;
89 namespace
92 Any convertAsEnum(bool bEnumAsInt, const css::uno::Type& _rExpectedType,
93 std::u16string_view _rReadCharacters, const SvXMLEnumMapEntry<sal_uInt16>* _pEnumMap)
95 Any aReturn;
97 sal_uInt16 nEnumValue(0);
98 bool bSuccess = SvXMLUnitConverter::convertEnum(nEnumValue, _rReadCharacters, _pEnumMap);
99 OSL_ENSURE(bSuccess, "PropertyConversion::convertString: could not convert to an enum value!");
101 if (bEnumAsInt)
103 if (TypeClass_SHORT == _rExpectedType.getTypeClass())
104 aReturn <<= static_cast<sal_Int16>(nEnumValue);
105 else
106 aReturn <<= static_cast<sal_Int32>(nEnumValue);
108 else
109 aReturn = ::cppu::int2enum(static_cast<sal_Int32>(nEnumValue), _rExpectedType);
111 return aReturn;
116 Any PropertyConversion::convertString( const css::uno::Type& _rExpectedType,
117 const OUString& _rReadCharacters, const SvXMLEnumMapEntry<sal_uInt16>* _pEnumMap, const bool _bInvertBoolean )
119 Any aReturn;
120 switch (_rExpectedType.getTypeClass())
122 case TypeClass_BOOLEAN: // sal_Bool
124 bool bValue;
125 bool bSuccess =
126 ::sax::Converter::convertBool(bValue, _rReadCharacters);
127 OSL_ENSURE(bSuccess,
128 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
129 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
130 "\" into a boolean!").getStr());
131 aReturn <<= (_bInvertBoolean ? !bValue : bValue);
133 break;
134 case TypeClass_SHORT: // sal_Int16
136 if (!_pEnumMap)
137 { // it's a real int16 property
138 sal_Int32 nValue(0);
139 bool bSuccess =
140 ::sax::Converter::convertNumber(nValue, _rReadCharacters, SAL_MIN_INT16, SAL_MAX_INT16);
141 OSL_ENSURE(bSuccess,
142 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
143 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
144 "\" into a sal_Int16!").getStr());
145 aReturn <<= static_cast<sal_Int16>(nValue);
146 break;
148 aReturn = convertAsEnum(true, _rExpectedType, _rReadCharacters, _pEnumMap);
150 break;
151 case TypeClass_LONG: // sal_Int32
153 if (!_pEnumMap)
154 { // it's a real int32 property
155 sal_Int32 nValue(0);
156 bool bSuccess =
157 ::sax::Converter::convertNumber(nValue, _rReadCharacters);
158 OSL_ENSURE(bSuccess,
159 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
160 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
161 "\" into a sal_Int32!").getStr());
162 aReturn <<= nValue;
163 break;
165 aReturn = convertAsEnum(true, _rExpectedType, _rReadCharacters, _pEnumMap);
167 break;
168 case TypeClass_ENUM:
170 aReturn = convertAsEnum(false, _rExpectedType, _rReadCharacters, _pEnumMap);
172 break;
173 case TypeClass_HYPER:
175 OSL_FAIL("PropertyConversion::convertString: 64-bit integers not implemented yet!");
177 break;
178 case TypeClass_DOUBLE:
180 double nValue;
181 bool bSuccess =
182 ::sax::Converter::convertDouble(nValue, _rReadCharacters);
183 OSL_ENSURE(bSuccess,
184 OStringBuffer(OString::Concat("PropertyConversion::convertString: could not convert \"") +
185 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
186 "\" into a double!").getStr());
187 aReturn <<= nValue;
189 break;
190 case TypeClass_STRING:
191 aReturn <<= _rReadCharacters;
192 break;
193 case TypeClass_STRUCT:
195 sal_Int32 nType = 0;
196 if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Date >::get() ) )
197 nType = TYPE_DATE;
198 else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::Time >::get() ) )
199 nType = TYPE_TIME;
200 else if ( _rExpectedType.equals( ::cppu::UnoType< css::util::DateTime >::get() ) )
201 nType = TYPE_DATETIME;
203 if ( nType )
205 // first extract the double
206 double nValue = 0;
207 bool bSuccess =
208 ::sax::Converter::convertDouble(nValue, _rReadCharacters);
209 OSL_ENSURE(bSuccess,
210 OStringBuffer("PropertyConversion::convertString: could not convert \"" +
211 OUStringToOString(_rReadCharacters, RTL_TEXTENCODING_ASCII_US) +
212 "\" into a double!").getStr());
214 // then convert it into the target type
215 switch (nType)
217 case TYPE_DATE:
219 OSL_ENSURE(std::modf(nValue, &o3tl::temporary(double())) == 0,
220 "PropertyConversion::convertString: a Date value with a fractional part?");
221 aReturn <<= lcl_getDate(nValue);
223 break;
224 case TYPE_TIME:
226 OSL_ENSURE((static_cast<sal_uInt32>(nValue)) == 0,
227 "PropertyConversion::convertString: a tools::Time value with more than a fractional part?");
228 aReturn <<= lcl_getTime(nValue);
230 break;
231 case TYPE_DATETIME:
233 css::util::Time aTime = lcl_getTime(nValue);
234 css::util::Date aDate = lcl_getDate(nValue);
236 css::util::DateTime aDateTime;
237 aDateTime.NanoSeconds = aTime.NanoSeconds;
238 aDateTime.Seconds = aTime.Seconds;
239 aDateTime.Minutes = aTime.Minutes;
240 aDateTime.Hours = aTime.Hours;
241 aDateTime.Day = aDate.Day;
242 aDateTime.Month = aDate.Month;
243 aDateTime.Year = aDate.Year;
244 aReturn <<= aDateTime;
246 break;
249 else
250 OSL_FAIL("PropertyConversion::convertString: unsupported property type!");
252 break;
253 default:
254 OSL_FAIL("PropertyConversion::convertString: invalid type class!");
257 return aReturn;
260 Type PropertyConversion::xmlTypeToUnoType( const OUString& _rType )
262 Type aUnoType( cppu::UnoType<void>::get() );
264 static std::map< OUString, css::uno::Type > s_aTypeNameMap
266 { token::GetXMLToken( token::XML_BOOLEAN ) , cppu::UnoType<bool>::get()},
267 // Not a copy paste error, quotation from:
268 // http://nabble.documentfoundation.org/Question-unoType-for-getXmlToken-dbaccess-reportdesign-module-tp4109071p4109116.html
269 // all numeric types (including the UNO double)
270 // consistently map to XML_FLOAT, so taking the extra precision from the
271 // C++ type "float" to "double" makes absolute sense
272 { token::GetXMLToken( token::XML_FLOAT ) , ::cppu::UnoType<double>::get()},
273 { token::GetXMLToken( token::XML_STRING ) , ::cppu::UnoType<OUString>::get()},
274 { token::GetXMLToken( token::XML_VOID ) , cppu::UnoType<void>::get() },
277 const std::map< OUString, css::uno::Type >::iterator aTypePos = s_aTypeNameMap.find( _rType );
278 OSL_ENSURE( s_aTypeNameMap.end() != aTypePos, "PropertyConversion::xmlTypeToUnoType: invalid property name!" );
279 if ( s_aTypeNameMap.end() != aTypePos )
280 aUnoType = aTypePos->second;
282 return aUnoType;
285 //= OPropertyImport
286 OPropertyImport::OPropertyImport(OFormLayerXMLImport_Impl& _rImport)
287 :SvXMLImportContext(_rImport.getGlobalContext())
288 ,m_rContext(_rImport)
289 ,m_bTrackAttributes(false)
293 css::uno::Reference< css::xml::sax::XFastContextHandler > OPropertyImport::createFastChildContext(
294 sal_Int32 nElement,
295 const css::uno::Reference< css::xml::sax::XFastAttributeList >& /*xAttrList*/ )
297 if( (nElement & TOKEN_MASK) == token::XML_PROPERTIES )
299 return new OPropertyElementsContext( m_rContext.getGlobalContext(), this);
301 else
302 SAL_WARN("xmloff", "unknown element " << SvXMLImport::getPrefixAndNameFromToken(nElement));
303 return nullptr;
306 void OPropertyImport::startFastElement(sal_Int32 /*nElement*/, const Reference< XFastAttributeList >& xAttrList)
309 // assume the 'worst' case: all attributes describe properties. This should save our property array
310 // some reallocs
311 m_aValues.reserve(sax_fastparser::castToFastAttributeList(xAttrList).size());
313 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
315 handleAttribute(aIter.getToken(), aIter.toString());
317 if (m_bTrackAttributes)
318 m_aEncounteredAttributes.insert(aIter.getToken() & TOKEN_MASK);
321 // TODO: create PropertyValues for all the attributes which were not present, because they were implied
322 // this is necessary as soon as we have properties where the XML default is different from the property
323 // default
326 bool OPropertyImport::encounteredAttribute(sal_Int32 nAttributeToken) const
328 OSL_ENSURE(m_bTrackAttributes, "OPropertyImport::encounteredAttribute: attribute tracking not enabled!");
329 return m_aEncounteredAttributes.end() != m_aEncounteredAttributes.find(nAttributeToken & TOKEN_MASK);
332 void OPropertyImport::characters(const OUString& _rChars )
334 // ignore them (should be whitespace only)
335 OSL_ENSURE(o3tl::trim(_rChars).empty(), "OPropertyImport::Characters: non-whitespace characters!");
338 bool OPropertyImport::handleAttribute(sal_Int32 nAttributeToken, const OUString& _rValue)
340 const OAttribute2Property::AttributeAssignment* pProperty = m_rContext.getAttributeMap().getAttributeTranslation(nAttributeToken & TOKEN_MASK);
341 if (pProperty)
343 // create and store a new PropertyValue
344 PropertyValue aNewValue;
345 aNewValue.Name = pProperty->sPropertyName;
347 // convert the value string into the target type
348 if ((nAttributeToken & TOKEN_MASK) == token::XML_HREF)
350 aNewValue.Value <<= m_rContext.getGlobalContext().GetAbsoluteReference(_rValue);
352 else
354 aNewValue.Value = PropertyConversion::convertString(
355 pProperty->aPropertyType, _rValue, pProperty->pEnumMap,
356 pProperty->bInverseSemantics);
358 implPushBackPropertyValue( aNewValue );
359 return true;
361 if ((nAttributeToken & TOKEN_MASK) != token::XML_TYPE) // xlink:type is valid but ignored for <form:form>
363 SAL_WARN( "xmloff", "OPropertyImport::handleAttribute: Can't handle "
364 << SvXMLImport::getPrefixAndNameFromToken(nAttributeToken) << "=" << _rValue );
365 return false;
367 return true;
370 //= OPropertyElementsContext
371 OPropertyElementsContext::OPropertyElementsContext(SvXMLImport& _rImport,
372 OPropertyImportRef _xPropertyImporter)
373 :SvXMLImportContext(_rImport)
374 ,m_xPropertyImporter(std::move(_xPropertyImporter))
378 css::uno::Reference< css::xml::sax::XFastContextHandler > OPropertyElementsContext::createFastChildContext(
379 sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
381 if( (nElement & TOKEN_MASK) == XML_PROPERTY )
383 return new OSinglePropertyContext(GetImport(), m_xPropertyImporter);
385 else if( (nElement & TOKEN_MASK) == XML_LIST_PROPERTY )
387 return new OListPropertyContext( GetImport(), m_xPropertyImporter );
389 return nullptr;
392 #if OSL_DEBUG_LEVEL > 0
393 void OPropertyElementsContext::startFastElement(
394 sal_Int32 /*nElement*/,
395 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
397 OSL_ENSURE(0 == xAttrList->getFastAttributes().getLength(), "OPropertyElementsContext::StartElement: the form:properties element should not have attributes!");
400 void OPropertyElementsContext::characters(const OUString& _rChars)
402 OSL_ENSURE(o3tl::trim(_rChars).empty(), "OPropertyElementsContext::Characters: non-whitespace characters detected!");
404 #endif
406 //= OSinglePropertyContext
407 OSinglePropertyContext::OSinglePropertyContext(SvXMLImport& _rImport,
408 OPropertyImportRef _xPropertyImporter)
409 :SvXMLImportContext(_rImport)
410 ,m_xPropertyImporter(std::move(_xPropertyImporter))
414 void OSinglePropertyContext::startFastElement(
415 sal_Int32 /*nElement*/,
416 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
418 css::beans::PropertyValue aPropValue; // the property the instance imports currently
419 css::uno::Type aPropType; // the type of the property the instance imports currently
421 OUString sType, sValue;
422 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
424 switch (aIter.getToken())
426 case XML_ELEMENT(FORM, XML_PROPERTY_NAME):
427 aPropValue.Name = aIter.toString();
428 break;
429 case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
430 sType = aIter.toString();
431 break;
432 case XML_ELEMENT(OFFICE, XML_VALUE):
433 case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
434 case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
435 sValue = aIter.toString();
436 break;
437 default:
438 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
442 // the name of the property
443 OSL_ENSURE(!aPropValue.Name.isEmpty(), "OSinglePropertyContext::StartElement: invalid property name!");
445 // needs to be translated into a css::uno::Type
446 aPropType = PropertyConversion::xmlTypeToUnoType( sType );
447 if( TypeClass_VOID == aPropType.getTypeClass() )
449 aPropValue.Value = Any();
451 else
453 aPropValue.Value =
454 PropertyConversion::convertString(aPropType,
455 sValue);
458 // now that we finally have our property value, add it to our parent object
459 if( !aPropValue.Name.isEmpty() )
460 m_xPropertyImporter->implPushBackGenericPropertyValue(aPropValue);
463 //= OListPropertyContext
464 OListPropertyContext::OListPropertyContext( SvXMLImport& _rImport,
465 OPropertyImportRef _rPropertyImporter )
466 :SvXMLImportContext( _rImport )
467 ,m_xPropertyImporter(std::move( _rPropertyImporter ))
471 void OListPropertyContext::startFastElement(
472 sal_Int32 /*nElement*/,
473 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
475 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
477 switch (aIter.getToken())
479 case XML_ELEMENT(FORM, XML_PROPERTY_NAME):
480 m_sPropertyName = aIter.toString();
481 break;
482 case XML_ELEMENT(OFFICE, XML_VALUE_TYPE):
483 m_sPropertyType = aIter.toString();
484 break;
485 default:
486 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
491 void OListPropertyContext::endFastElement(sal_Int32 )
493 OSL_ENSURE( !m_sPropertyName.isEmpty() && !m_sPropertyType.isEmpty(),
494 "OListPropertyContext::EndElement: no property name or type!" );
496 if ( m_sPropertyName.isEmpty() || m_sPropertyType.isEmpty() )
497 return;
499 Sequence< Any > aListElements( m_aListValues.size() );
500 Any* pListElement = aListElements.getArray();
501 css::uno::Type aType = PropertyConversion::xmlTypeToUnoType( m_sPropertyType );
502 for ( const auto& rListValue : m_aListValues )
504 *pListElement = PropertyConversion::convertString( aType, rListValue );
505 ++pListElement;
508 PropertyValue aSequenceValue;
509 aSequenceValue.Name = m_sPropertyName;
510 aSequenceValue.Value <<= aListElements;
512 m_xPropertyImporter->implPushBackGenericPropertyValue( aSequenceValue );
515 css::uno::Reference< css::xml::sax::XFastContextHandler > OListPropertyContext::createFastChildContext(
516 sal_Int32 nElement, const css::uno::Reference< css::xml::sax::XFastAttributeList >& )
518 if ( (nElement & TOKEN_MASK) == XML_LIST_VALUE )
520 m_aListValues.emplace_back();
521 return new OListValueContext( GetImport(), *m_aListValues.rbegin() );
523 return nullptr;
526 //= OListValueContext
527 OListValueContext::OListValueContext( SvXMLImport& _rImport, OUString& _rListValueHolder )
528 :SvXMLImportContext( _rImport )
529 ,m_rListValueHolder( _rListValueHolder )
533 void OListValueContext::startFastElement(
534 sal_Int32 /*nElement*/,
535 const css::uno::Reference< css::xml::sax::XFastAttributeList >& xAttrList )
537 for( auto& aIter : sax_fastparser::castToFastAttributeList(xAttrList) )
539 switch(aIter.getToken())
541 case XML_ELEMENT(OFFICE, XML_VALUE):
542 case XML_ELEMENT(OFFICE, XML_STRING_VALUE):
543 case XML_ELEMENT(OFFICE, XML_BOOLEAN_VALUE):
544 m_rListValueHolder = aIter.toString();
545 break;
546 default:
547 XMLOFF_WARN_UNKNOWN("xmloff", aIter);
552 } // namespace xmloff
554 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */