/* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
/*
 * This file is part of the LibreOffice project.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This file incorporates work covered by the following license notice:
 *
 *   Licensed to the Apache Software Foundation (ASF) under one or more
 *   contributor license agreements. See the NOTICE file distributed
 *   with this work for additional information regarding copyright
 *   ownership. The ASF licenses this file to you under the Apache
 *   License, Version 2.0 (the "License"); you may not use this file
 *   except in compliance with the License. You may obtain a copy of
 *   the License at http://www.apache.org/licenses/LICENSE-2.0 .
 */
 
#include "elementexport.hxx"
 
#include "strings.hxx"
#include <utility>
#include <xmloff/xmlnamespace.hxx>
#include "eventexport.hxx"
#include "formenums.hxx"
#include "formcellbinding.hxx"
#include <xmloff/xformsexport.hxx>
#include "property_meta_data.hxx"
 
#include <com/sun/star/text/XText.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/io/XPersistObject.hpp>
#include <com/sun/star/util/Duration.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/form/FormSubmitEncoding.hpp>
#include <com/sun/star/form/FormSubmitMethod.hpp>
#include <com/sun/star/sdb/CommandType.hpp>
#include <com/sun/star/form/NavigationBarMode.hpp>
#include <com/sun/star/form/TabulatorCycle.hpp>
#include <com/sun/star/form/FormButtonType.hpp>
#include <com/sun/star/awt/ScrollBarOrientation.hpp>
#include <com/sun/star/awt/VisualEffect.hpp>
#include <com/sun/star/form/ListSourceType.hpp>
#include <com/sun/star/awt/ImagePosition.hpp>
 
#include <sax/tools/converter.hxx>
#include <tools/gen.hxx>
#include <xmloff/txtprmap.hxx>
#include <com/sun/star/form/binding/XBindableValue.hpp>
#include <com/sun/star/form/binding/XListEntrySink.hpp>
#include <tools/urlobj.hxx>
#include <xmloff/xmlexp.hxx>
#include <xmloff/namespacemap.hxx>
#include <xmloff/XMLEventExport.hxx>
#include <xmloff/xmluconv.hxx>
#include <xmloff/xmltoken.hxx>
#include <xmloff/maptype.hxx>
#include <tools/time.hxx>
#include <comphelper/diagnose_ex.hxx>
#include <comphelper/extract.hxx>
#include <sal/log.hxx>
 
#include <algorithm>
 
namespace xmloff
{
 
    #if OSL_DEBUG_LEVEL > 0
        #define RESET_BIT( bitfield, bit ) \
            bitfield = bitfield & ~bit
    #else
        #define RESET_BIT( bitfield, bit )
    #endif
 
    using namespace ::xmloff::token;
    using namespace ::com::sun::star;
    using namespace ::com::sun::star::uno;
    using namespace ::com::sun::star::sdb;
    using namespace ::com::sun::star::awt;
    using namespace ::com::sun::star::form;
    using namespace ::com::sun::star::lang;
    using namespace ::com::sun::star::beans;
    using namespace ::com::sun::star::container;
    using namespace ::com::sun::star::script;
    using namespace ::com::sun::star::io;
    using namespace ::com::sun::star::text;
    using namespace ::com::sun::star::form::binding;
 
    //= OElementExport
    OElementExport::OElementExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxProps,
        const Sequence< ScriptEventDescriptor >& _rEvents)
        :OPropertyExport(_rContext, _rxProps)
        ,m_aEvents(_rEvents)
    {
    }
 
    OElementExport::~OElementExport()
    {
    }
 
    void OElementExport::doExport()
    {
        // collect some general information about the element
        examine();
 
        // first add the attributes necessary for the element
        m_rContext.getGlobalContext().ClearAttrList();
 
        // add the attributes
        exportAttributes();
 
        // start the XML element
        implStartElement(getXMLElementName());
 
        // the sub elements (mostly control type dependent)
        exportSubTags();
 
        implEndElement();
    }
 
    void OElementExport::examine()
    {
        // nothing to do here
    }
 
    void OElementExport::exportAttributes()
    {
        // nothing to do here
    }
 
    void OElementExport::exportSubTags()
    {
        // the properties which where not exported 'til now
        exportRemainingProperties();
 
        // the script:events sub tags
        exportEvents();
    }
 
    void OElementExport::implStartElement(const OUString& _pName)
    {
        m_pXMLElement = std::make_unique<SvXMLElementExport>(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, _pName, true, true);
    }
 
    void OElementExport::implEndElement()
    {
        m_pXMLElement.reset();
    }
 
    void OElementExport::exportServiceNameAttribute()
    {
        Reference< XPersistObject > xPersistence(m_xProps, UNO_QUERY);
        if (!xPersistence.is())
        {
            OSL_FAIL("OElementExport::exportServiceNameAttribute: no XPersistObject!");
            return;
        }
 
        OUString sServiceName = xPersistence->getServiceName();
        // we don't want to write the old service name directly: it's a name used for compatibility reasons, but
        // as we start some kind of new file format here (with this xml export), we don't care about
        // compatibility ...
        // So we translate the old persistence service name into new ones, if possible
 
        OUString sToWriteServiceName = sServiceName;
#define CHECK_N_TRANSLATE( persistentComponent, serviceName )   \
        else if (sServiceName == persistentComponent) \
            sToWriteServiceName = serviceName
 
        if (sServiceName == SERVICE_PERSISTENT_COMPONENT_EDIT)
        {
            // special handling for the edit field: we have two controls using this as persistence service name
            sToWriteServiceName = SERVICE_EDIT;
            Reference< XServiceInfo > xSI(m_xProps, UNO_QUERY);
            if (xSI.is() && xSI->supportsService(SERVICE_FORMATTEDFIELD))
                sToWriteServiceName = SERVICE_FORMATTEDFIELD;
        }
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_FORM,           SERVICE_FORM );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_LISTBOX,        SERVICE_LISTBOX );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_COMBOBOX,       SERVICE_COMBOBOX );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_RADIOBUTTON,    SERVICE_RADIOBUTTON );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_GROUPBOX,       SERVICE_GROUPBOX );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_FIXEDTEXT,      SERVICE_FIXEDTEXT );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_COMMANDBUTTON,  SERVICE_COMMANDBUTTON );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_CHECKBOX,       SERVICE_CHECKBOX );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_GRID,           SERVICE_GRID );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_IMAGEBUTTON,    SERVICE_IMAGEBUTTON );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_FILECONTROL,    SERVICE_FILECONTROL );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_TIMEFIELD,      SERVICE_TIMEFIELD );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_DATEFIELD,      SERVICE_DATEFIELD );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_NUMERICFIELD,   SERVICE_NUMERICFIELD );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_CURRENCYFIELD,  SERVICE_CURRENCYFIELD );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_PATTERNFIELD,   SERVICE_PATTERNFIELD );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_HIDDENCONTROL,  SERVICE_HIDDENCONTROL );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_IMAGECONTROL,   SERVICE_IMAGECONTROL );
        CHECK_N_TRANSLATE( SERVICE_PERSISTENT_COMPONENT_FORMATTEDFIELD, SERVICE_FORMATTEDFIELD );
#if OSL_DEBUG_LEVEL > 0
        Reference< XServiceInfo > xSI(m_xProps, UNO_QUERY);
        OSL_ENSURE(xSI.is() && xSI->supportsService(sToWriteServiceName),
            "OElementExport::exportServiceNameAttribute: wrong service name translation!");
 
#endif
        sToWriteServiceName =
            m_rContext.getGlobalContext().GetNamespaceMap().GetQNameByKey(
                XML_NAMESPACE_OOO, sToWriteServiceName );
 
        // now write this
        AddAttribute(
            OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::ServiceName),
            OAttributeMetaData::getCommonControlAttributeName(CCAFlags::ServiceName),
            sToWriteServiceName);
    }
 
    void OElementExport::exportEvents()
    {
        if (!m_aEvents.hasElements())
            // nothing to do
            return;
 
        Reference< XNameReplace > xWrapper = new OEventDescriptorMapper(m_aEvents);
        m_rContext.getGlobalContext().GetEventExport().Export(xWrapper);
    }
 
    //= OControlExport
    OControlExport::OControlExport(IFormsExportContext& _rContext,  const Reference< XPropertySet >& _rxControl,
        OUString _sControlId, OUString _sReferringControls,
        const Sequence< ScriptEventDescriptor >& _rEvents)
        :OElementExport(_rContext, _rxControl, _rEvents)
        ,m_sControlId(std::move(_sControlId))
        ,m_sReferringControls(std::move(_sReferringControls))
        ,m_nClassId(FormComponentType::CONTROL)
        ,m_eType( UNKNOWN )
        ,m_nIncludeCommon(CCAFlags::NONE)
        ,m_nIncludeDatabase(DAFlags::NONE)
        ,m_nIncludeSpecial(SCAFlags::NONE)
        ,m_nIncludeEvents(EAFlags::NONE)
        ,m_nIncludeBindings(BAFlags::NONE)
    {
        OSL_ENSURE(m_xProps.is(), "OControlExport::OControlExport: invalid arguments!");
    }
 
    void OControlExport::exportOuterAttributes()
    {
        // the control id
        if (CCAFlags::Name & m_nIncludeCommon)
        {
            exportStringPropertyAttribute(
                OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Name),
                OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Name),
                PROPERTY_NAME
                );
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::Name;
        #endif
        }
 
        // the service name
        if (m_nIncludeCommon & CCAFlags::ServiceName)
        {
            exportServiceNameAttribute();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ServiceName;
        #endif
        }
    }
 
    void OControlExport::exportInnerAttributes()
    {
        // the control id
        if (CCAFlags::ControlId & m_nIncludeCommon)
        {
            OSL_ENSURE(!m_sControlId.isEmpty(), "OControlExport::exportInnerAttributes: have no control id for the control!");
            m_rContext.getGlobalContext().AddAttributeIdLegacy(
                XML_NAMESPACE_FORM, m_sControlId);
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ControlId;
        #endif
        }
 
        // "new-style" properties ...
        exportGenericHandlerAttributes();
 
        // common control attributes
        exportCommonControlAttributes();
 
        // common database attributes
        exportDatabaseAttributes();
 
        // attributes related to external bindings
        exportBindingAttributes();
 
        // attributes special to the respective control type
        exportSpecialAttributes();
 
        // add the style references to the attributes
        flagStyleProperties();
    }
 
    void OControlExport::exportAttributes()
    {
        exportOuterAttributes();
    }
 
    void OControlExport::exportSubTags()
    {
        // for the upcoming exportRemainingProperties:
        // if a control has the LabelControl property, this is not stored with the control itself, but instead with
        // the control which is referenced by this property. As the base class' exportRemainingProperties doesn't
        // know anything about this, we need to prevent that it tries to export this property
        exportedProperty(PROPERTY_CONTROLLABEL);
 
        // if it's a control supporting XText, then we need to declare all text-related properties
        // as "already exported". This prevents them from being exported as generic "form:property"-tags.
        // *If* we would export them this way, they would be completely superfluous, and sometimes even
        // disastrous, since they may, at import time, override paragraph properties which already have
        // been set before
        Reference< XText > xControlText( m_xProps, UNO_QUERY );
        if ( xControlText.is() )
        {
            const XMLPropertyMapEntry* pCharAttributeProperties = XMLTextPropertySetMapper::getPropertyMapForType( TextPropMap::TEXT );
            while ( !pCharAttributeProperties->IsEnd() )
            {
                exportedProperty( pCharAttributeProperties->getApiName() );
                ++pCharAttributeProperties;
            }
 
            const XMLPropertyMapEntry* pParaAttributeProperties = XMLTextPropertySetMapper::getPropertyMapForType( TextPropMap::SHAPE_PARA );
            while ( !pParaAttributeProperties->IsEnd() )
            {
                exportedProperty( pParaAttributeProperties->getApiName() );
                ++pParaAttributeProperties;
            }
 
            // the RichText property is not exported. The presence of the text:p element
            // will be used - upon reading - as indicator for the value of the RichText property
            exportedProperty( PROPERTY_RICH_TEXT );
 
            // strange thing: paragraphs support both a CharStrikeout and a CharCrossedOut property
            // The former is a short/enum value, the latter a boolean. The former has a real meaning
            // (the strikeout type), the latter hasn't. But, when the CharCrossedOut is exported and
            // later on imported, it overwrites anything which has previously been imported for
            // CharStrikeout.
            // #i27729#
            exportedProperty( u"CharCrossedOut"_ustr );
        }
 
        if ( m_eType == LISTBOX )
        {
            // will be exported in exportListSourceAsElements:
            if ( controlHasUserSuppliedListEntries() )
                exportedProperty( PROPERTY_DEFAULT_SELECT_SEQ );
 
            // will not be exported in a generic way. Either exportListSourceAsElements cares
            // for them, or we don't need them
            exportedProperty( PROPERTY_STRING_ITEM_LIST );
            exportedProperty( PROPERTY_VALUE_SEQ );
            exportedProperty( PROPERTY_SELECT_SEQ );
            exportedProperty( PROPERTY_LISTSOURCE );
        }
        if ( m_eType == COMBOBOX )
            exportedProperty( PROPERTY_STRING_ITEM_LIST );
 
        // let the base class export the remaining properties and the events
        OElementExport::exportSubTags();
 
        // special sub tags for some controls
        switch (m_eType)
        {
            case LISTBOX:
                // don't export the list entries if the are not provided by the user, but obtained implicitly
                // from other sources
                // #i26944#
                if ( controlHasUserSuppliedListEntries() )
                    exportListSourceAsElements();
                break;
            case GRID:
            {   // a grid control requires us to store all columns as sub elements
                Reference< XIndexAccess > xColumnContainer(m_xProps, UNO_QUERY);
                OSL_ENSURE(xColumnContainer.is(), "OControlExport::exportSubTags: a grid control which is no IndexAccess?!!");
                if (xColumnContainer.is())
                    m_rContext.exportCollectionElements(xColumnContainer);
            }
            break;
            case COMBOBOX:
            {   // a combox box description has sub elements: the items
                DBG_CHECK_PROPERTY( PROPERTY_STRING_ITEM_LIST, Sequence< OUString > );
 
                // don't export the list entries if the are not provided by the user, but obtained implicitly
                // from other sources
                // #i26944#
                if ( controlHasUserSuppliedListEntries() )
                {
                    // get the item list
                    Sequence< OUString > aListItems;
                    m_xProps->getPropertyValue(PROPERTY_STRING_ITEM_LIST) >>= aListItems;
                    // loop through it and write the sub elements
                    for (const auto& rListItem : aListItems)
                    {
                        m_rContext.getGlobalContext().ClearAttrList();
                        AddAttribute(
                            OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Label),
                            OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Label),
                            rListItem);
                        SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, u"item"_ustr, true, true);
                    }
                }
            }
            break;
 
            case TEXT_AREA:
            {
                // if we act as rich text control, we need to export some text:p elements
                if ( xControlText.is() )
                {
                    bool bActingAsRichText = false;
                    if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_RICH_TEXT ) )
                    {
                        OSL_VERIFY(m_xProps->getPropertyValue( PROPERTY_RICH_TEXT ) >>= bActingAsRichText );
                    }
 
                    if ( bActingAsRichText )
                        m_rContext.getGlobalContext().GetTextParagraphExport()->exportText( xControlText );
                }
            }
            break;
            default:
                // nothing do to
                break;
        }
    }
 
    void OControlExport::exportGenericHandlerAttributes()
    {
        const Sequence< Property > aProperties = m_xPropertyInfo->getProperties();
        for ( auto const & prop : aProperties )
        {
            try
            {
                // see if this property can already be handled with an IPropertyHandler (which, on the long
                // term, should be the case for most, if not all, properties)
                const PropertyDescription* propDescription = metadata::getPropertyDescription( prop.Name );
                if ( propDescription == nullptr )
                    continue;
 
                // let the factory provide the concrete handler. Note that caching, if desired, is the task
                // of the factory
                PPropertyHandler handler = (*propDescription->factory)( propDescription->propertyId );
                if ( !handler )
                {
                    SAL_WARN( "xmloff.forms", "OControlExport::exportGenericHandlerAttributes: invalid property handler provided by the factory!" );
                    continue;
                }
 
                // that's a property which has a direct mapping to an attribute
                if ( !shouldExportProperty( prop.Name ) )
                    // TODO: in the future, we surely need a more sophisticated approach to this, involving the property
                    // handler, or the property description
                {
                    exportedProperty( prop.Name );
                    continue;
                }
 
                const Any propValue = m_xProps->getPropertyValue( prop.Name );
                OUString attributeValue = handler->getAttributeValue( propValue );
 
                AddAttribute(
                    propDescription->attribute.namespacePrefix,
                    token::GetXMLToken( propDescription->attribute.attributeToken ),
                    attributeValue
                );
 
                exportedProperty( prop.Name );
            }
            catch( const Exception& )
            {
                DBG_UNHANDLED_EXCEPTION("xmloff.forms");
            }
        }
    }
 
    void OControlExport::exportCommonControlAttributes()
    {
        size_t i=0;
 
        // I decided to handle all the properties here with some static arrays describing the property-attribute
        // relations. This leads to somewhat ugly code :), but the only alternative I can think of right now
        // would require maps and O(log n) searches, which seems somewhat expensive as this code is used
        // very frequently.
 
        // the extra indents for the respective blocks are to ensure that there is no copy'n'paste error, using
        // map identifiers from the wrong block
 
        // some string properties
        {
            // the attribute ids of all properties which are expected to be of type string
            static const CCAFlags nStringPropertyAttributeIds[] =
            {
                CCAFlags::Label, CCAFlags::Title
            };
            // the names of all properties which are expected to be of type string
            static constexpr OUString aStringPropertyNames[] =
            {
                PROPERTY_LABEL, PROPERTY_TITLE
            };
            OSL_ENSURE( std::size(aStringPropertyNames) ==
                        std::size(nStringPropertyAttributeIds),
                        "OControlExport::exportCommonControlAttributes: somebody tampered with the maps (1)!");
 
            for (i=0; i<std::size(nStringPropertyAttributeIds); ++i)
                if (nStringPropertyAttributeIds[i] & m_nIncludeCommon)
                {
                    exportStringPropertyAttribute(
                        OAttributeMetaData::getCommonControlAttributeNamespace(nStringPropertyAttributeIds[i]),
                        OAttributeMetaData::getCommonControlAttributeName(nStringPropertyAttributeIds[i]),
                        aStringPropertyNames[i]
                        );
                #if OSL_DEBUG_LEVEL > 0
                    //  reset the bit for later checking
                    m_nIncludeCommon = m_nIncludeCommon & ~nStringPropertyAttributeIds[i];
                #endif
                }
        }
 
        // some boolean properties
        {
            static const CCAFlags nBooleanPropertyAttributeIds[] =
            {   // attribute flags
                CCAFlags::CurrentSelected, CCAFlags::Disabled, CCAFlags::Dropdown, CCAFlags::Printable, CCAFlags::ReadOnly, CCAFlags::Selected, CCAFlags::TabStop, CCAFlags::EnableVisible
            };
            static constexpr OUString pBooleanPropertyNames[] =
            {   // property names
                PROPERTY_STATE, PROPERTY_ENABLED,
                PROPERTY_DROPDOWN, PROPERTY_PRINTABLE,
                PROPERTY_READONLY, PROPERTY_DEFAULT_STATE,
                PROPERTY_TABSTOP, PROPERTY_ENABLEVISIBLE
            };
            static const BoolAttrFlags nBooleanPropertyAttrFlags[] =
            {   // attribute defaults
                BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultFalse | BoolAttrFlags::InverseSemantics, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultVoid, BoolAttrFlags::DefaultFalse
            };
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nIdCount = std::size(nBooleanPropertyAttributeIds);
            static const sal_Int32 nNameCount = std::size(pBooleanPropertyNames);
            static const sal_Int32 nFlagsCount = std::size(nBooleanPropertyAttrFlags);
            OSL_ENSURE((nIdCount == nNameCount) && (nNameCount == nFlagsCount),
                "OControlExport::exportCommonControlAttributes: somebody tampered with the maps (2)!");
        #endif
            for (i=0; i<std::size(nBooleanPropertyAttributeIds); ++i)
                if (nBooleanPropertyAttributeIds[i] & m_nIncludeCommon)
                {
                    exportBooleanPropertyAttribute(
                        OAttributeMetaData::getCommonControlAttributeNamespace(nBooleanPropertyAttributeIds[i]),
                        OAttributeMetaData::getCommonControlAttributeName(nBooleanPropertyAttributeIds[i]),
                        pBooleanPropertyNames[i],
                        nBooleanPropertyAttrFlags[i]);
        #if OSL_DEBUG_LEVEL > 0
                    //  reset the bit for later checking
                    m_nIncludeCommon = m_nIncludeCommon & ~nBooleanPropertyAttributeIds[i];
        #endif
                }
        }
 
        // some integer properties
        {
            // now the common handling
            static const CCAFlags nIntegerPropertyAttributeIds[] =
            {   // attribute flags
                CCAFlags::Size, CCAFlags::TabIndex
            };
            static constexpr OUString pIntegerPropertyNames[] =
            {   // property names
                PROPERTY_LINECOUNT, PROPERTY_TABINDEX
            };
            static const sal_Int16 nIntegerPropertyAttrDefaults[] =
            {   // attribute defaults
                5, 0
            };
 
            if ( m_nIncludeCommon & CCAFlags::MaxLength )
                exportedProperty(PROPERTY_MAXTEXTLENGTH);
 
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nIdCount = std::size(nIntegerPropertyAttributeIds);
            static const sal_Int32 nNameCount = std::size(pIntegerPropertyNames);
            static const sal_Int32 nDefaultCount = std::size(nIntegerPropertyAttrDefaults);
            OSL_ENSURE((nIdCount == nNameCount) && (nNameCount == nDefaultCount),
                "OControlExport::exportCommonControlAttributes: somebody tampered with the maps (3)!");
        #endif
            for (i=0; i<std::size(nIntegerPropertyAttributeIds); ++i)
                if (nIntegerPropertyAttributeIds[i] & m_nIncludeCommon)
                {
                    exportInt16PropertyAttribute(
                        OAttributeMetaData::getCommonControlAttributeNamespace(nIntegerPropertyAttributeIds[i]),
                        OAttributeMetaData::getCommonControlAttributeName(nIntegerPropertyAttributeIds[i]),
                        pIntegerPropertyNames[i],
                        nIntegerPropertyAttrDefaults[i]);
        #if OSL_DEBUG_LEVEL > 0
                    //  reset the bit for later checking
                    m_nIncludeCommon = m_nIncludeCommon & ~nIntegerPropertyAttributeIds[i];
        #endif
                }
 
        }
 
        // some enum properties
        {
            if (m_nIncludeCommon & CCAFlags::ButtonType)
            {
                exportEnumPropertyAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::ButtonType),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::ButtonType),
                    PROPERTY_BUTTONTYPE,
                    aFormButtonTypeMap,
                    FormButtonType_PUSH);
        #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ButtonType;
        #endif
            }
            if ( m_nIncludeCommon & CCAFlags::Orientation )
            {
                exportEnumPropertyAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace( CCAFlags::Orientation ),
                    OAttributeMetaData::getCommonControlAttributeName( CCAFlags::Orientation ),
                    PROPERTY_ORIENTATION,
                    aOrientationMap,
                    ScrollBarOrientation::HORIZONTAL
                );
        #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::Orientation;
        #endif
            }
 
            if ( m_nIncludeCommon & CCAFlags::VisualEffect )
            {
                exportEnumPropertyAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace( CCAFlags::VisualEffect ),
                    OAttributeMetaData::getCommonControlAttributeName( CCAFlags::VisualEffect ),
                    PROPERTY_VISUAL_EFFECT,
                    aVisualEffectMap,
                    VisualEffect::LOOK3D
                );
            #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::VisualEffect;
            #endif
            }
        }
 
        // some properties which require a special handling
 
        // the target frame
        if (m_nIncludeCommon & CCAFlags::TargetFrame)
        {
            exportTargetFrameAttribute();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::TargetFrame;
        #endif
        }
 
        // max text length
        if ( m_nIncludeCommon & CCAFlags::MaxLength )
        {
            // normally, the respective property would be "MaxTextLen"
            // However, if the model has a property "PersistenceMaxTextLength", then we prefer this
 
            // determine the name of the property to export
            OUString sTextLenPropertyName( PROPERTY_MAXTEXTLENGTH );
            if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_PERSISTENCE_MAXTEXTLENGTH ) )
                sTextLenPropertyName = PROPERTY_PERSISTENCE_MAXTEXTLENGTH;
 
            // export it
            exportInt16PropertyAttribute(
                OAttributeMetaData::getCommonControlAttributeNamespace( CCAFlags::MaxLength ),
                OAttributeMetaData::getCommonControlAttributeName( CCAFlags::MaxLength ),
                sTextLenPropertyName,
                0
            );
 
            // in either way, both properties count as "exported"
            exportedProperty( PROPERTY_MAXTEXTLENGTH );
            exportedProperty( PROPERTY_PERSISTENCE_MAXTEXTLENGTH );
 
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::MaxLength;
        #endif
        }
 
        if (m_nIncludeCommon & CCAFlags::TargetLocation)
        {
            exportTargetLocationAttribute(false);
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::TargetLocation;
        #endif
        }
 
        // OJ #99721#
        if (m_nIncludeCommon & CCAFlags::ImageData)
        {
            exportImageDataAttribute();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::ImageData;
        #endif
        }
 
        // the for attribute
        // the target frame
        if (m_nIncludeCommon & CCAFlags::For)
        {
            if (!m_sReferringControls.isEmpty())
            {   // there is at least one control referring to the one we're handling currently
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::For),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::For),
                    m_sReferringControls);
            }
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags::For;
        #endif
        }
 
        if ((CCAFlags::CurrentValue | CCAFlags::Value) & m_nIncludeCommon)
        {
            OUString pCurrentValuePropertyName;
            OUString pValuePropertyName;
 
            // get the property names
            getValuePropertyNames(m_eType, m_nClassId, pCurrentValuePropertyName, pValuePropertyName);
 
            // add the attributes if necessary and possible
            if (!pCurrentValuePropertyName.isEmpty() && (CCAFlags::CurrentValue & m_nIncludeCommon))
            {
                static const OUString pCurrentValueAttributeName = OAttributeMetaData::getCommonControlAttributeName(CCAFlags::CurrentValue);
                // don't export the current-value if this value originates from a data binding
                // #i26944#
                if ( controlHasActiveDataBinding() )
                    exportedProperty( pCurrentValuePropertyName );
                else
                {
                    static const sal_uInt16 nCurrentValueAttributeNamespaceKey = OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::CurrentValue);
                    exportGenericPropertyAttribute(
                        nCurrentValueAttributeNamespaceKey,
                        pCurrentValueAttributeName,
                        pCurrentValuePropertyName
                    );
                }
            }
 
            if (!pValuePropertyName.isEmpty() && (CCAFlags::Value & m_nIncludeCommon))
            {
                static const OUString pValueAttributeName = OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Value);
                static const sal_uInt16 nValueAttributeNamespaceKey = OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Value);
                exportGenericPropertyAttribute(
                    nValueAttributeNamespaceKey,
                    pValueAttributeName,
                    pValuePropertyName);
            }
 
            OSL_ENSURE((pValuePropertyName.isEmpty()) == (CCAFlags::NONE == (CCAFlags::Value & m_nIncludeCommon)),
                "OControlExport::exportCommonControlAttributes: no property found for the value attribute!");
            OSL_ENSURE((pCurrentValuePropertyName.isEmpty()) == (CCAFlags::NONE == (CCAFlags::CurrentValue & m_nIncludeCommon)),
                "OControlExport::exportCommonControlAttributes: no property found for the current-value attribute!");
 
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeCommon = m_nIncludeCommon & ~CCAFlags(CCAFlags::CurrentValue | CCAFlags::Value);
        #endif
        }
 
        OSL_ENSURE(CCAFlags::NONE == m_nIncludeCommon,
            "OControlExport::exportCommonControlAttributes: forgot some flags!");
            // in the dbg_util version, we should have removed every bit we handled from the mask, so it should
            // be 0 now ...
    }
 
    void OControlExport::exportDatabaseAttributes()
    {
#if OSL_DEBUG_LEVEL > 0
        DAFlags nIncludeDatabase = m_nIncludeDatabase;
#endif
        // the only string property: DataField
        if (DAFlags::DataField & m_nIncludeDatabase)
        {
            exportStringPropertyAttribute(
                OAttributeMetaData::getDatabaseAttributeNamespace(),
                OAttributeMetaData::getDatabaseAttributeName(DAFlags::DataField),
                PROPERTY_DATAFIELD);
            RESET_BIT( nIncludeDatabase, DAFlags::DataField );
        }
 
        // InputRequired
        if ( DAFlags::InputRequired & m_nIncludeDatabase )
        {
            exportBooleanPropertyAttribute(
                OAttributeMetaData::getDatabaseAttributeNamespace(),
                OAttributeMetaData::getDatabaseAttributeName( DAFlags::InputRequired ),
                PROPERTY_INPUT_REQUIRED,
                BoolAttrFlags::DefaultFalse | BoolAttrFlags::DefaultVoid
            );
            RESET_BIT( nIncludeDatabase, DAFlags::InputRequired );
        }
 
        // the only int16 property: BoundColumn
        if (DAFlags::BoundColumn & m_nIncludeDatabase)
        {
            exportInt16PropertyAttribute(
                OAttributeMetaData::getDatabaseAttributeNamespace(),
                OAttributeMetaData::getDatabaseAttributeName(DAFlags::BoundColumn),
                PROPERTY_BOUNDCOLUMN,
                0,
                true);
            RESET_BIT( nIncludeDatabase, DAFlags::BoundColumn );
        }
 
        // ConvertEmptyToNull
        if (DAFlags::ConvertEmpty & m_nIncludeDatabase)
        {
            exportBooleanPropertyAttribute(
                OAttributeMetaData::getDatabaseAttributeNamespace(),
                OAttributeMetaData::getDatabaseAttributeName(DAFlags::ConvertEmpty),
                PROPERTY_EMPTY_IS_NULL,
                BoolAttrFlags::DefaultFalse
                );
            RESET_BIT( nIncludeDatabase, DAFlags::ConvertEmpty );
        }
 
        // the only enum property: ListSourceType
        if (DAFlags::ListSource_TYPE & m_nIncludeDatabase)
        {
            exportEnumPropertyAttribute(
                OAttributeMetaData::getDatabaseAttributeNamespace(),
                OAttributeMetaData::getDatabaseAttributeName(DAFlags::ListSource_TYPE),
                PROPERTY_LISTSOURCETYPE,
                aListSourceTypeMap,
                ListSourceType_VALUELIST
                );
            RESET_BIT( nIncludeDatabase, DAFlags::ListSource_TYPE );
        }
 
        if (m_nIncludeDatabase & DAFlags::ListSource)
        {
            exportListSourceAsAttribute();
            RESET_BIT( nIncludeDatabase, DAFlags::ListSource );
        }
 
#if OSL_DEBUG_LEVEL > 0
        OSL_ENSURE(DAFlags::NONE == nIncludeDatabase,
            "OControlExport::exportDatabaseAttributes: forgot some flags!");
            // in the dbg_util version, we should have removed every bit we handled from the mask, so it should
            // be 0 now ...
#endif
    }
 
    void OControlExport::exportBindingAttributes()
    {
#if OSL_DEBUG_LEVEL > 0
        BAFlags nIncludeBinding = m_nIncludeBindings;
#endif
 
        if ( m_nIncludeBindings & BAFlags::LinkedCell )
        {
            exportCellBindingAttributes( bool(m_nIncludeBindings & BAFlags::ListLinkingType) );
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            nIncludeBinding = nIncludeBinding & ~BAFlags( BAFlags::LinkedCell | BAFlags::ListLinkingType );
        #endif
        }
 
        if ( m_nIncludeBindings & BAFlags::ListCellRange )
        {
            exportCellListSourceRange();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            nIncludeBinding = nIncludeBinding & ~BAFlags::ListCellRange;
        #endif
        }
 
        if ( m_nIncludeBindings & BAFlags::XFormsBind )
        {
            exportXFormsBindAttributes();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            nIncludeBinding = nIncludeBinding & ~BAFlags::XFormsBind;
        #endif
        }
 
        if ( m_nIncludeBindings & BAFlags::XFormsListBind )
        {
            exportXFormsListAttributes();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            nIncludeBinding = nIncludeBinding & ~BAFlags::XFormsListBind;
        #endif
        }
 
        if ( m_nIncludeBindings & BAFlags::XFormsSubmission )
        {
            exportXFormsSubmissionAttributes();
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            nIncludeBinding = nIncludeBinding & ~BAFlags::XFormsSubmission;
        #endif
        }
 
        #if OSL_DEBUG_LEVEL > 0
        OSL_ENSURE( BAFlags::NONE == nIncludeBinding,
            "OControlExport::exportBindingAttributes: forgot some flags!");
            // in the debug version, we should have removed every bit we handled from the mask, so it should
            // be 0 now ...
        #endif
    }
 
    void OControlExport::exportSpecialAttributes()
    {
        sal_Int32 i=0;
 
        // the boolean properties
        {
            static const SCAFlags nBooleanPropertyAttributeIds[] =
            {   // attribute flags
                SCAFlags::Validation, SCAFlags::MultiLine, SCAFlags::AutoCompletion, SCAFlags::Multiple, SCAFlags::DefaultButton, SCAFlags::IsTristate,
                SCAFlags::Toggle, SCAFlags::FocusOnClick
            };
            static constexpr OUString pBooleanPropertyNames[] =
            {   // property names
                PROPERTY_STRICTFORMAT, PROPERTY_MULTILINE,
                PROPERTY_AUTOCOMPLETE,
                PROPERTY_MULTISELECTION,
                PROPERTY_DEFAULTBUTTON, PROPERTY_TRISTATE,
                PROPERTY_TOGGLE, PROPERTY_FOCUS_ON_CLICK
            };
            static const sal_Int32 nIdCount = std::size(nBooleanPropertyAttributeIds);
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nNameCount = std::size(pBooleanPropertyNames);
            OSL_ENSURE((nIdCount == nNameCount),
                "OControlExport::exportSpecialAttributes: somebody tampered with the maps (1)!");
        #endif
            const SCAFlags* pAttributeId = nBooleanPropertyAttributeIds;
            for ( i = 0; i < nIdCount; ++i, ++pAttributeId )
            {
                if ( *pAttributeId & m_nIncludeSpecial)
                {
                    exportBooleanPropertyAttribute(
                        OAttributeMetaData::getSpecialAttributeNamespace( *pAttributeId ),
                        OAttributeMetaData::getSpecialAttributeName( *pAttributeId ),
                        pBooleanPropertyNames[i],
                        ( *pAttributeId == SCAFlags::FocusOnClick ) ? BoolAttrFlags::DefaultTrue : BoolAttrFlags::DefaultFalse
                    );
            #if OSL_DEBUG_LEVEL > 0
                    //  reset the bit for later checking
                    m_nIncludeSpecial = m_nIncludeSpecial & ~*pAttributeId;
            #endif
                }
            }
        }
 
        // the integer properties
        {
            static const SCAFlags nIntegerPropertyAttributeIds[] =
            {   // attribute flags
                SCAFlags::PageStepSize
            };
            static constexpr OUString pIntegerPropertyNames[] =
            {   // property names
                PROPERTY_BLOCK_INCREMENT
            };
            static const sal_Int32 nIntegerPropertyAttrDefaults[] =
            {   // attribute defaults (XML defaults, not runtime defaults!)
                10
            };
 
            static const sal_Int32 nIdCount = std::size( nIntegerPropertyAttributeIds );
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nNameCount = std::size( pIntegerPropertyNames );
            OSL_ENSURE( ( nIdCount == nNameCount ),
                "OControlExport::exportSpecialAttributes: somebody tampered with the maps (2)!" );
            static const sal_Int32 nDefaultCount = std::size( nIntegerPropertyAttrDefaults );
            OSL_ENSURE( ( nIdCount == nDefaultCount ),
                "OControlExport::exportSpecialAttributes: somebody tampered with the maps (3)!" );
        #endif
            for ( i = 0; i < nIdCount; ++i )
                if ( nIntegerPropertyAttributeIds[i] & m_nIncludeSpecial )
                {
                    exportInt32PropertyAttribute(
                        OAttributeMetaData::getSpecialAttributeNamespace( nIntegerPropertyAttributeIds[i] ),
                        OAttributeMetaData::getSpecialAttributeName( nIntegerPropertyAttributeIds[i] ),
                        pIntegerPropertyNames[i],
                        nIntegerPropertyAttrDefaults[i]
                    );
            #if OSL_DEBUG_LEVEL > 0
                    //  reset the bit for later checking
                    m_nIncludeSpecial = m_nIncludeSpecial & ~nIntegerPropertyAttributeIds[i];
            #endif
                }
 
            if ( SCAFlags::StepSize & m_nIncludeSpecial )
            {
                OUString sPropertyName;
                if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_LINE_INCREMENT ) )
                    sPropertyName = PROPERTY_LINE_INCREMENT;
                else if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_SPIN_INCREMENT ) )
                    sPropertyName = PROPERTY_SPIN_INCREMENT;
                else
                    OSL_FAIL( "OControlExport::exportSpecialAttributes: not property which can be mapped to step-size attribute!" );
 
                if ( !sPropertyName.isEmpty() )
                    exportInt32PropertyAttribute(
                        OAttributeMetaData::getSpecialAttributeNamespace( SCAFlags::StepSize ),
                        OAttributeMetaData::getSpecialAttributeName( SCAFlags::StepSize ),
                        sPropertyName,
                        1
                    );
 
            #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::StepSize;
            #endif
            }
 
        }
 
        // the enum properties
        {
            if (SCAFlags::State & m_nIncludeSpecial)
            {
                exportEnumPropertyAttribute(
                    OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::State),
                    OAttributeMetaData::getSpecialAttributeName(SCAFlags::State),
                    PROPERTY_DEFAULT_STATE,
                    aCheckStateMap,
                    TRISTATE_FALSE);
            #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::State;
            #endif
            }
 
            if (SCAFlags::CurrentState & m_nIncludeSpecial)
            {
                exportEnumPropertyAttribute(
                    OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::CurrentState),
                    OAttributeMetaData::getSpecialAttributeName(SCAFlags::CurrentState),
                    PROPERTY_STATE,
                    aCheckStateMap,
                    TRISTATE_FALSE);
            #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::CurrentState;
            #endif
            }
        }
 
        // some properties which require a special handling
        // the repeat delay
        {
            if ( m_nIncludeSpecial & SCAFlags::RepeatDelay )
            {
                DBG_CHECK_PROPERTY( PROPERTY_REPEAT_DELAY, sal_Int32 );
 
                sal_Int32 nRepeatDelay = 0;
                m_xProps->getPropertyValue( PROPERTY_REPEAT_DELAY ) >>= nRepeatDelay;
                tools::Time aTime(tools::Time::EMPTY);
                aTime.MakeTimeFromMS( nRepeatDelay );
                util::Duration aDuration;
                aDuration.Hours   = aTime.GetHour();
                aDuration.Minutes = aTime.GetMin();
                aDuration.Seconds = aTime.GetSec();
                aDuration.NanoSeconds = (nRepeatDelay % 1000) * 1000000;
 
                OUStringBuffer buf;
                ::sax::Converter::convertDuration(buf, aDuration);
                AddAttribute(OAttributeMetaData::getSpecialAttributeNamespace( SCAFlags::RepeatDelay )
                            ,OAttributeMetaData::getSpecialAttributeName( SCAFlags::RepeatDelay )
                            ,buf.makeStringAndClear());
 
                exportedProperty( PROPERTY_REPEAT_DELAY );
 
            #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::RepeatDelay;
            #endif
            }
        }
 
        // the EchoChar property needs special handling, cause it's a Int16, but must be stored as one-character-string
        {
            if (SCAFlags::EchoChar & m_nIncludeSpecial)
            {
                DBG_CHECK_PROPERTY( PROPERTY_ECHO_CHAR, sal_Int16 );
                sal_Int16 nValue(0);
                m_xProps->getPropertyValue(PROPERTY_ECHO_CHAR) >>= nValue;
                if (nValue)
                {
                    OUString sCharacter(reinterpret_cast<const sal_Unicode*>(&nValue), 1);
                    AddAttribute(
                        OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::EchoChar),
                        OAttributeMetaData::getSpecialAttributeName(SCAFlags::EchoChar),
                        sCharacter);
                }
                exportedProperty(PROPERTY_ECHO_CHAR);
            #if OSL_DEBUG_LEVEL > 0
                //  reset the bit for later checking
                m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags::EchoChar;
            #endif
            }
        }
 
        // the string properties
        {
            static const SCAFlags nStringPropertyAttributeIds[] =
            {   // attribute flags
                SCAFlags::GroupName
            };
            static constexpr OUString pStringPropertyNames[] =
            {   // property names
                PROPERTY_GROUP_NAME
            };
 
            static const sal_Int32 nIdCount = std::size( nStringPropertyAttributeIds );
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nNameCount = std::size( pStringPropertyNames );
            OSL_ENSURE( ( nIdCount == nNameCount ),
                "OControlExport::exportSpecialAttributes: somebody tampered with the maps (2)!" );
        #endif
            for ( i = 0; i < nIdCount; ++i )
                if ( nStringPropertyAttributeIds[i] & m_nIncludeSpecial )
                {
                    exportStringPropertyAttribute(
                        OAttributeMetaData::getSpecialAttributeNamespace( nStringPropertyAttributeIds[i] ),
                        OAttributeMetaData::getSpecialAttributeName( nStringPropertyAttributeIds[i] ),
                        pStringPropertyNames[i]
                    );
            #if OSL_DEBUG_LEVEL > 0
                    //  reset the bit for later checking
                    m_nIncludeSpecial = m_nIncludeSpecial & ~nStringPropertyAttributeIds[i];
            #endif
                }
        }
 
        if ((SCAFlags::MinValue | SCAFlags::MaxValue) & m_nIncludeSpecial)
        {
            // need to export the min value and the max value as attributes
            // It depends on the real type (FormComponentType) of the control, which properties hold these
            // values
            OUString pMinValuePropertyName;
            OUString pMaxValuePropertyName;
            getValueLimitPropertyNames(m_nClassId, pMinValuePropertyName, pMaxValuePropertyName);
 
            OSL_ENSURE((pMinValuePropertyName.isEmpty()) == (SCAFlags::NONE == (SCAFlags::MinValue & m_nIncludeSpecial)),
                "OControlExport::exportCommonControlAttributes: no property found for the min value attribute!");
            OSL_ENSURE((pMaxValuePropertyName.isEmpty()) == (SCAFlags::NONE == (SCAFlags::MaxValue & m_nIncludeSpecial)),
                "OControlExport::exportCommonControlAttributes: no property found for the max value attribute!");
 
            // add the two attributes
            static const OUString pMinValueAttributeName = OAttributeMetaData::getSpecialAttributeName(SCAFlags::MinValue);
            static const OUString pMaxValueAttributeName = OAttributeMetaData::getSpecialAttributeName(SCAFlags::MaxValue);
            static const sal_uInt16 nMinValueNamespaceKey = OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::MinValue);
            static const sal_uInt16 nMaxValueNamespaceKey = OAttributeMetaData::getSpecialAttributeNamespace(SCAFlags::MaxValue);
 
            if (!pMinValuePropertyName.isEmpty() && (SCAFlags::MinValue & m_nIncludeSpecial))
                exportGenericPropertyAttribute(
                    nMinValueNamespaceKey,
                    pMinValueAttributeName,
                    pMinValuePropertyName);
 
            if (!pMaxValuePropertyName.isEmpty() && (SCAFlags::MaxValue & m_nIncludeSpecial))
                exportGenericPropertyAttribute(
                    nMaxValueNamespaceKey,
                    pMaxValueAttributeName,
                    pMaxValuePropertyName);
        #if OSL_DEBUG_LEVEL > 0
            //  reset the bit for later checking
            m_nIncludeSpecial = m_nIncludeSpecial & ~SCAFlags(SCAFlags::MinValue | SCAFlags::MaxValue);
        #endif
        }
 
        if ( SCAFlags::ImagePosition & m_nIncludeSpecial )
        {
            exportImagePositionAttributes();
            RESET_BIT( m_nIncludeSpecial, SCAFlags::ImagePosition );
        }
 
        OSL_ENSURE(SCAFlags::NONE == m_nIncludeSpecial,
            "OControlExport::exportSpecialAttributes: forgot some flags!");
            // in the dbg_util version, we should have removed every bit we handled from the mask, so it should
            // be 0 now ...
    }
 
    OUString OControlExport::getScalarListSourceValue() const
    {
        OUString sListSource;
        Any aListSource = m_xProps->getPropertyValue( PROPERTY_LISTSOURCE );
        if ( !( aListSource >>= sListSource ) )
        {
            Sequence< OUString > aListSourceSequence;
            aListSource >>= aListSourceSequence;
            if ( aListSourceSequence.hasElements() )
                sListSource = aListSourceSequence[ 0 ];
        }
        return sListSource;
    }
 
    void OControlExport::exportListSourceAsAttribute()
    {
        // DAFlags::ListSource needs some special handling
        DBG_CHECK_PROPERTY_NO_TYPE( PROPERTY_LISTSOURCE );
 
        OUString sListSource = getScalarListSourceValue();
        if ( !sListSource.isEmpty() )
        {   // the ListSource property needs to be exported as attribute, and it is not empty
            AddAttribute(
                OAttributeMetaData::getDatabaseAttributeNamespace(),
                OAttributeMetaData::getDatabaseAttributeName(DAFlags::ListSource),
                sListSource);
        }
 
        exportedProperty( PROPERTY_LISTSOURCE );
    }
 
    void OControlExport::getSequenceInt16PropertyAsSet(const OUString& _rPropertyName, Int16Set& _rOut)
    {
        Sequence< sal_Int16 > aValueSequence;
        DBG_CHECK_PROPERTY(_rPropertyName, Sequence< sal_Int16 >);
        m_xProps->getPropertyValue(_rPropertyName) >>= aValueSequence;
 
        for (const auto& rValue : aValueSequence)
            _rOut.insert(rValue);
    }
 
    void OControlExport::exportListSourceAsElements()
    {
        // the string lists
        Sequence< OUString > aItems, aValues;
        DBG_CHECK_PROPERTY( PROPERTY_STRING_ITEM_LIST, Sequence< OUString > );
        m_xProps->getPropertyValue(PROPERTY_STRING_ITEM_LIST) >>= aItems;
 
        DBG_CHECK_PROPERTY( PROPERTY_LISTSOURCE, Sequence< OUString > );
        if ( DAFlags::NONE == ( m_nIncludeDatabase & DAFlags::ListSource ) )
            m_xProps->getPropertyValue(PROPERTY_LISTSOURCE) >>= aValues;
        // if we exported the list source as attribute, we do not repeat it as sub elements
 
        // the selection lists
        Int16Set aSelection, aDefaultSelection;
        getSequenceInt16PropertyAsSet(PROPERTY_SELECT_SEQ, aSelection);
        getSequenceInt16PropertyAsSet(PROPERTY_DEFAULT_SELECT_SEQ, aDefaultSelection);
 
        // the string for "true"
        OUString sTrue;
        OUStringBuffer sBuffer;
        ::sax::Converter::convertBool(sBuffer, true);
        sTrue = sBuffer.makeStringAndClear();
 
        // loop through both lists ('til the maximum of both lengths)
        const OUString* pItems = aItems.getConstArray();
        const OUString* pValues = aValues.getConstArray();
 
        sal_Int32 nItems = aItems.getLength();
        sal_Int32 nValues = aValues.getLength();
 
        sal_Int16 nMaxLen = static_cast<sal_Int16>(std::max(nItems, nValues));
 
        for (sal_Int16 i=0; i<nMaxLen; ++i )
        {
            m_rContext.getGlobalContext().ClearAttrList();
            if (i < nItems)
            {
                // there is an item at this position
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Label),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Label),
                    *pItems);
                ++pItems;
            }
            if (i < nValues)
            {
                // there is a value at this position
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Value),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Value),
                    *pValues);
                ++pValues;
            }
 
            Int16Set::const_iterator aSelectedPos = aSelection.find(i);
            if (aSelection.end() != aSelectedPos)
            {   // the item at this position is selected
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::CurrentSelected),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::CurrentSelected),
                    sTrue
                    );
                aSelection.erase(aSelectedPos);
            }
 
            Int16Set::const_iterator aDefaultSelectedPos = aDefaultSelection.find(i);
            if (aDefaultSelection.end() != aDefaultSelectedPos)
            {   // the item at this position is selected as default
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Selected),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Selected),
                    sTrue
                    );
                aDefaultSelection.erase(aDefaultSelectedPos);
            }
            SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, u"option"_ustr, true, true);
        }
 
        // There may be more "selected" or "default-selected" items than there are in the lists in real,
        // so we need to store some additional "form:option" items which have no name and no label, but
        // one or both of the selected flags.
        // 21.05.2001 - 85388 - frank.schoenheit@germany.sun.com
 
        if (aSelection.empty() && aDefaultSelection.empty())
            return;
 
        sal_Int16 nLastSelected = -1;
        if ( !aSelection.empty() )
            nLastSelected = *(--aSelection.end());
 
        sal_Int16 nLastDefaultSelected = -1;
        if ( !aDefaultSelection.empty() )
            nLastDefaultSelected = *(--aDefaultSelection.end());
 
        // the maximum element in both sets
        sal_Int16 nLastReferredEntry = std::max(nLastSelected, nLastDefaultSelected);
        OSL_ENSURE(nLastReferredEntry >= nMaxLen, "OControlExport::exportListSourceAsElements: inconsistence!");
            // if the maximum (selected or default selected) entry number is less than the maximum item count
            // in both lists, the entry number should have been removed from the set
 
        for (sal_Int16 i=nMaxLen; i<=nLastReferredEntry; ++i)
        {
            if (aSelection.end() != aSelection.find(i))
            {   // the (not existent) item at this position is selected
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::CurrentSelected),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::CurrentSelected),
                    sTrue
                    );
            }
 
            if (aDefaultSelection.end() != aDefaultSelection.find(i))
            {   // the (not existent) item at this position is selected as default
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Selected),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Selected),
                    sTrue
                    );
            }
            SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, u"option"_ustr, true, true);
        }
    }
 
    void OControlExport::implStartElement(const OUString& _pName)
    {
        // before we let the base class start it's outer element, we add a wrapper element
        OUString pOuterElementName = getOuterXMLElementName();
        if (!pOuterElementName.isEmpty())
            m_pOuterElement = std::make_unique<SvXMLElementExport>(
                                        m_rContext.getGlobalContext(),
                                        XML_NAMESPACE_FORM,
                                        pOuterElementName, true,
                                        true);
 
        // add the attributes for the inner element
        exportInnerAttributes();
 
        // and start the inner element
        OElementExport::implStartElement(_pName);
    }
 
    void OControlExport::implEndElement()
    {
        // end the inner element
        OElementExport::implEndElement();
 
        // end the outer element if it exists
        m_pOuterElement.reset();
    }
 
    OUString OControlExport::getOuterXMLElementName() const
    {
        return u""_ustr;
    }
 
    OUString OControlExport::getXMLElementName() const
    {
        return getElementName(m_eType);
    }
 
    void OControlExport::examine()
    {
        OSL_ENSURE( ( m_nIncludeCommon == CCAFlags::NONE ) && ( m_nIncludeSpecial == SCAFlags::NONE ) && ( m_nIncludeDatabase == DAFlags::NONE )
                 && ( m_nIncludeEvents == EAFlags::NONE ) && ( m_nIncludeBindings == BAFlags::NONE),
                 "OControlExport::examine: called me twice? Not initialized?" );
 
        // get the class id to decide which kind of element we need in the XML stream
        m_nClassId = FormComponentType::CONTROL;
        DBG_CHECK_PROPERTY( PROPERTY_CLASSID, sal_Int16 );
        m_xProps->getPropertyValue(PROPERTY_CLASSID) >>= m_nClassId;
        bool knownType = false;
        switch (m_nClassId)
        {
            case FormComponentType::DATEFIELD:
                m_eType = DATE;
                knownType = true;
                [[fallthrough]];
            case FormComponentType::TIMEFIELD:
                if ( !knownType )
                {
                    m_eType = TIME;
                    knownType = true;
                }
                m_nIncludeSpecial |= SCAFlags::Validation;
                [[fallthrough]];
            case FormComponentType::NUMERICFIELD:
            case FormComponentType::CURRENCYFIELD:
            case FormComponentType::PATTERNFIELD:
                if ( !knownType )
                {
                    m_eType = FORMATTED_TEXT;
                    knownType = true;
                }
                [[fallthrough]];
            case FormComponentType::TEXTFIELD:
            {   // it's some kind of edit. To know which type we need further investigation
 
                if ( !knownType )
                {
                    // check if it's a formatted field
                    if (m_xPropertyInfo->hasPropertyByName(PROPERTY_FORMATKEY))
                    {
                        m_eType = FORMATTED_TEXT;
                    }
                    else
                    {
                        // all other controls are represented by an ordinary edit control, but which XML control type
                        // it is depends on the current values of some properties
 
                        // if the EchoChar string is not empty, it is a password field
                        sal_Int16 nEchoChar = 0;
                        if (m_xPropertyInfo->hasPropertyByName(PROPERTY_ECHOCHAR))
                            // grid columns do not have this property...
                            m_xProps->getPropertyValue(PROPERTY_ECHOCHAR) >>= nEchoChar;
                        if (nEchoChar)
                        {
                            m_eType = PASSWORD;
                            m_nIncludeSpecial |= SCAFlags::EchoChar;
                        }
                        else
                        {
                            // if the MultiLine property is sal_True, it is a TextArea
                            bool bMultiLine = false;
                            if (m_xPropertyInfo->hasPropertyByName(PROPERTY_MULTILINE))
                                // grid columns do not have this property...
                                bMultiLine = ::cppu::any2bool(m_xProps->getPropertyValue(PROPERTY_MULTILINE));
 
                            if ( bMultiLine )
                                m_eType = TEXT_AREA;
                            else
                                // the only case left is represented by a Text element
                                m_eType = TEXT;
                        }
                    }
                }
 
                // attributes which are common to all the types:
                // common attributes
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled |
                    CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title;
 
                if  (   ( m_nClassId != FormComponentType::DATEFIELD )
                    &&  ( m_nClassId != FormComponentType::TIMEFIELD )
                    )
                    // date and time field values are handled differently nowadays
                    m_nIncludeCommon |= CCAFlags::Value;
 
                // database attributes
                m_nIncludeDatabase = DAFlags::DataField | DAFlags::InputRequired;
 
                // event attributes
                m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnSelect;
 
                // only text and pattern fields have a ConvertEmptyToNull property
                if  (   ( m_nClassId == FormComponentType::TEXTFIELD )
                    ||  ( m_nClassId == FormComponentType::PATTERNFIELD )
                    )
                    m_nIncludeDatabase |= DAFlags::ConvertEmpty;
 
                // all controls but the file control fields have a readonly property
                if ( m_nClassId != FormComponentType::FILECONTROL )
                    m_nIncludeCommon |= CCAFlags::ReadOnly;
 
                // a text field has a max text len
                if ( m_nClassId == FormComponentType::TEXTFIELD )
                    m_nIncludeCommon |= CCAFlags::MaxLength;
 
                // max and min values and validation:
                if (FORMATTED_TEXT == m_eType)
                {   // in general all controls represented as formatted-text have these props
                    if  ( FormComponentType::PATTERNFIELD != m_nClassId )   // except the PatternField
                        m_nIncludeSpecial |= SCAFlags::MaxValue | SCAFlags::MinValue;
 
                    if (FormComponentType::TEXTFIELD != m_nClassId)
                        // and the FormattedField does not have a validation flag
                        m_nIncludeSpecial |= SCAFlags::Validation;
                }
 
                // if it's not a password field or rich text control, the CurrentValue needs to be stored, too
                if  (   ( PASSWORD != m_eType )
                    &&  ( DATE != m_eType )
                    &&  ( TIME != m_eType )
                    )
                {
                    m_nIncludeCommon |= CCAFlags::CurrentValue;
                }
            }
            break;
 
            case FormComponentType::FILECONTROL:
                m_eType = FILE;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::CurrentValue | CCAFlags::Disabled |
                    CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title |
                    CCAFlags::Value;
                m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnSelect;
                break;
 
            case FormComponentType::FIXEDTEXT:
                m_eType = FIXED_TEXT;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Label |
                    CCAFlags::Printable | CCAFlags::Title | CCAFlags::For;
                m_nIncludeSpecial = SCAFlags::MultiLine;
                m_nIncludeEvents = EAFlags::ControlEvents;
                break;
 
            case FormComponentType::COMBOBOX:
                m_eType = COMBOBOX;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::CurrentValue |
                    CCAFlags::Disabled | CCAFlags::Dropdown | CCAFlags::MaxLength | CCAFlags::Printable | CCAFlags::ReadOnly | CCAFlags::Size |
                    CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title | CCAFlags::Value;
                m_nIncludeSpecial = SCAFlags::AutoCompletion;
                m_nIncludeDatabase = DAFlags::ConvertEmpty | DAFlags::DataField | DAFlags::InputRequired | DAFlags::ListSource | DAFlags::ListSource_TYPE;
                m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnSelect;
                break;
 
            case FormComponentType::LISTBOX:
                m_eType = LISTBOX;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Dropdown |
                    CCAFlags::Printable | CCAFlags::Size | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title;
                m_nIncludeSpecial = SCAFlags::Multiple;
                m_nIncludeDatabase = DAFlags::BoundColumn | DAFlags::DataField | DAFlags::InputRequired | DAFlags::ListSource_TYPE;
                m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange | EAFlags::OnClick | EAFlags::OnDoubleClick;
                // check if we need to export the ListSource as attribute
                {
                    // for a list box, if the ListSourceType is VALUE_LIST, no ListSource is stored, but instead
                    // a sequence of pairs which is build from the StringItemList and the ValueList
                    ListSourceType eListSourceType = ListSourceType_VALUELIST;
                    bool bSuccess =
                        m_xProps->getPropertyValue(PROPERTY_LISTSOURCETYPE) >>= eListSourceType;
                    OSL_ENSURE(bSuccess, "OControlExport::examineControl: could not retrieve the ListSourceType!");
                    if (ListSourceType_VALUELIST != eListSourceType)
                    {
                        m_nIncludeDatabase |= DAFlags::ListSource;
                    }
                }
 
                break;
 
            case FormComponentType::COMMANDBUTTON:
                m_eType = BUTTON;
                m_nIncludeCommon |= CCAFlags::TabStop | CCAFlags::Label;
                m_nIncludeSpecial = SCAFlags::DefaultButton | SCAFlags::Toggle | SCAFlags::FocusOnClick | SCAFlags::ImagePosition | SCAFlags::RepeatDelay;
                [[fallthrough]];
            case FormComponentType::IMAGEBUTTON:
                if (BUTTON != m_eType)
                {
                    // not coming from the previous case
                    m_eType = IMAGE;
                }
                m_nIncludeCommon |=
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::ButtonType | CCAFlags::Disabled |
                    CCAFlags::ImageData | CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TargetFrame |
                    CCAFlags::TargetLocation | CCAFlags::Title;
                m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnClick  | EAFlags::OnDoubleClick;
                break;
 
            case FormComponentType::CHECKBOX:
                m_eType = CHECKBOX;
                m_nIncludeSpecial = SCAFlags::CurrentState | SCAFlags::IsTristate | SCAFlags::State;
                [[fallthrough]];
            case FormComponentType::RADIOBUTTON:
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Label | CCAFlags::Printable |
                    CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title | CCAFlags::Value | CCAFlags::VisualEffect;
                if (CHECKBOX != m_eType)
                {   // not coming from the previous case
                    m_eType = RADIO;
                    m_nIncludeCommon |= CCAFlags::CurrentSelected | CCAFlags::Selected;
                }
                if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_IMAGE_POSITION ) )
                    m_nIncludeSpecial |= SCAFlags::ImagePosition;
                if ( m_xPropertyInfo->hasPropertyByName( PROPERTY_GROUP_NAME ) )
                    m_nIncludeSpecial |= SCAFlags::GroupName;
                m_nIncludeDatabase = DAFlags::DataField | DAFlags::InputRequired;
                m_nIncludeEvents = EAFlags::ControlEvents | EAFlags::OnChange;
                break;
 
            case FormComponentType::GROUPBOX:
                m_eType = FRAME;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Label |
                    CCAFlags::Printable | CCAFlags::Title | CCAFlags::For;
                m_nIncludeEvents = EAFlags::ControlEvents;
                break;
 
            case FormComponentType::IMAGECONTROL:
                m_eType = IMAGE_FRAME;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::ImageData |
                    CCAFlags::Printable | CCAFlags::ReadOnly | CCAFlags::Title;
                m_nIncludeDatabase = DAFlags::DataField | DAFlags::InputRequired;
                m_nIncludeEvents = EAFlags::ControlEvents;
                break;
 
            case FormComponentType::HIDDENCONTROL:
                m_eType = HIDDEN;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Value;
                break;
 
            case FormComponentType::GRIDCONTROL:
                m_eType = GRID;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Printable |
                    CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Title;
                m_nIncludeEvents = EAFlags::ControlEvents;
                break;
 
            case FormComponentType::SCROLLBAR:
            case FormComponentType::SPINBUTTON:
                m_eType = VALUERANGE;
                m_nIncludeCommon =
                    CCAFlags::Name | CCAFlags::ServiceName | CCAFlags::Disabled | CCAFlags::Printable |
                    CCAFlags::Title | CCAFlags::CurrentValue | CCAFlags::Value | CCAFlags::Orientation;
                m_nIncludeSpecial = SCAFlags::MaxValue | SCAFlags::StepSize | SCAFlags::MinValue | SCAFlags::RepeatDelay;
 
                if ( m_nClassId == FormComponentType::SCROLLBAR )
                    m_nIncludeSpecial |= SCAFlags::PageStepSize ;
 
                m_nIncludeEvents = EAFlags::ControlEvents;
                break;
 
            default:
                OSL_FAIL("OControlExport::examineControl: unknown control type (class id)!");
                [[fallthrough]];
 
            case FormComponentType::NAVIGATIONBAR:
                // TODO: should we have an own file format for this?
                // NO break
 
            case FormComponentType::CONTROL:
                m_eType = GENERIC_CONTROL;
                // unknown control type
                m_nIncludeCommon = CCAFlags::Name | CCAFlags::ServiceName;
                    // at least a name should be there, 'cause without a name the control could never have been
                    // inserted into its parent container
                    // In addition, the service name is absolutely necessary to create the control upon reading.
                m_nIncludeEvents = EAFlags::ControlEvents;
                    // we always should be able to export events - this is not control type dependent
                break;
        }
 
        // in general, all control types need to export the control id
        m_nIncludeCommon |= CCAFlags::ControlId;
 
        // is it a control bound to a calc cell?
        if ( FormCellBindingHelper::livesInSpreadsheetDocument( m_xProps ) )
        {
            FormCellBindingHelper aHelper( m_xProps, nullptr );
            {
                if ( FormCellBindingHelper::isCellBinding( aHelper.getCurrentBinding( ) ) )
                {
                    m_nIncludeBindings |= BAFlags::LinkedCell;
                    if ( m_nClassId == FormComponentType::LISTBOX )
                        m_nIncludeBindings |= BAFlags::ListLinkingType;
                }
            }
 
            // is it a list-like control which uses a calc cell range as list source?
            {
                if ( FormCellBindingHelper::isCellRangeListSource( aHelper.getCurrentListSource( ) ) )
                    m_nIncludeBindings |= BAFlags::ListCellRange;
            }
        }
 
        // is control bound to XForms?
        if( !getXFormsBindName( m_xProps ).isEmpty() )
        {
            m_nIncludeBindings |= BAFlags::XFormsBind;
        }
 
        // is (list-)control bound to XForms list?
        if( !getXFormsListBindName( m_xProps ).isEmpty() )
        {
            m_nIncludeBindings |= BAFlags::XFormsListBind;
        }
 
        // does the control have an XForms submission?
        if( !getXFormsSubmissionName( m_xProps ).isEmpty() )
        {
            m_nIncludeBindings |= BAFlags::XFormsSubmission;
        }
    }
 
    void OControlExport::exportCellBindingAttributes( bool _bIncludeListLinkageType )
    {
        try
        {
            FormCellBindingHelper aHelper( m_xProps, nullptr );
            Reference< XValueBinding > xBinding( aHelper.getCurrentBinding() );
            OSL_ENSURE( xBinding.is(), "OControlExport::exportCellBindingAttributes: invalid bindable or invalid binding!" );
            if ( xBinding.is() )
            {
                AddAttribute(
                    OAttributeMetaData::getBindingAttributeNamespace(),
                    OAttributeMetaData::getBindingAttributeName( BAFlags::LinkedCell ),
                    aHelper.getStringAddressFromCellBinding( xBinding )
                );
 
                if ( _bIncludeListLinkageType )
                {
                    sal_Int16 nLinkageType = FormCellBindingHelper::isCellIntegerBinding( xBinding ) ? 1 : 0;
 
                    OUStringBuffer sBuffer;
                    SvXMLUnitConverter::convertEnum(
                        sBuffer,
                        nLinkageType,
                        aListLinkageMap
                    );
 
                    AddAttribute(
                        OAttributeMetaData::getBindingAttributeNamespace(),
                        OAttributeMetaData::getBindingAttributeName( BAFlags::ListLinkingType ),
                        sBuffer.makeStringAndClear()
                    );
                }
 
            }
        }
        catch( const Exception& )
        {
            TOOLS_WARN_EXCEPTION( "xmloff.forms", "OControlExport::exportCellBindingAttributes" );
        }
    }
 
    void OControlExport::exportXFormsBindAttributes()
    {
        OUString sBindName = getXFormsBindName( m_xProps );
        AddAttribute( XML_NAMESPACE_XFORMS, XML_BIND, sBindName );
    }
    void OControlExport::exportXFormsListAttributes()
    {
        OUString sBindName = getXFormsListBindName( m_xProps );
        AddAttribute( XML_NAMESPACE_FORM, XML_XFORMS_LIST_SOURCE, sBindName );
    }
    void OControlExport::exportXFormsSubmissionAttributes()
    {
        OUString sSubmission = getXFormsSubmissionName( m_xProps );
        AddAttribute( XML_NAMESPACE_FORM, XML_XFORMS_SUBMISSION, sSubmission );
    }
    void OControlExport::exportCellListSourceRange( )
    {
        try
        {
            Reference< XListEntrySink > xSink( m_xProps, UNO_QUERY );
            Reference< XListEntrySource > xSource;
            if ( xSink.is() )
                xSource = xSink->getListEntrySource();
            OSL_ENSURE( xSource.is(), "OControlExport::exportCellListSourceRange: list source or sink!" );
            if ( xSource.is() )
            {
                FormCellBindingHelper aHelper( m_xProps, nullptr );
 
                AddAttribute(
                    OAttributeMetaData::getBindingAttributeNamespace(),
                    OAttributeMetaData::getBindingAttributeName( BAFlags::ListCellRange ),
                    aHelper.getStringAddressFromCellListSource( xSource )
                );
            }
        }
        catch( const Exception& )
        {
            TOOLS_WARN_EXCEPTION( "xmloff.forms", "OControlExport::exportCellListSourceRange" );
        }
    }
 
    void OControlExport::exportImagePositionAttributes()
    {
        try
        {
            sal_Int16 nImagePosition = ImagePosition::Centered;
            OSL_VERIFY( m_xProps->getPropertyValue( PROPERTY_IMAGE_POSITION ) >>= nImagePosition );
            OSL_ENSURE( ( nImagePosition >= ImagePosition::LeftTop ) && ( nImagePosition <= ImagePosition::Centered ),
                "OControlExport::exportImagePositionAttributes: don't know this image position!" );
 
            if ( ( nImagePosition < ImagePosition::LeftTop ) || ( nImagePosition > ImagePosition::Centered ) )
                // this is important to prevent potential buffer overflows below, so don't optimize
                nImagePosition = ImagePosition::Centered;
 
            if ( nImagePosition == ImagePosition::Centered )
            {
                AddAttribute( XML_NAMESPACE_FORM, GetXMLToken( XML_IMAGE_POSITION ), GetXMLToken( XML_CENTER ) );
            }
            else
            {
                const XMLTokenEnum eXmlImagePositions[] =
                {
                    XML_START, XML_END, XML_TOP, XML_BOTTOM
                };
                const XMLTokenEnum eXmlImageAligns[] =
                {
                    XML_START, XML_CENTER, XML_END
                };
 
                XMLTokenEnum eXmlImagePosition = eXmlImagePositions[ nImagePosition / 3 ];
                XMLTokenEnum eXmlImageAlign    = eXmlImageAligns   [ nImagePosition % 3 ];
 
                AddAttribute( XML_NAMESPACE_FORM, GetXMLToken( XML_IMAGE_POSITION ), GetXMLToken( eXmlImagePosition ) );
                AddAttribute( XML_NAMESPACE_FORM, GetXMLToken( XML_IMAGE_ALIGN    ), GetXMLToken( eXmlImageAlign    ) );
            }
 
            exportedProperty( PROPERTY_IMAGE_POSITION );
            // some of the controls which have an ImagePosition also have an ImageAlign for compatibility
            // reasons. Since the ImageAlign values simply represent a sub set of the ImagePosition values,
            // we don't need to export ImageAlign anymore
            exportedProperty( PROPERTY_IMAGE_ALIGN );
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("xmloff.forms");
        }
    }
 
    bool OControlExport::controlHasActiveDataBinding() const
    {
        try
        {
            // currently exchanging the data with a database column?
            OUString sBoundFieldPropertyName( u"BoundField"_ustr );
            if ( m_xPropertyInfo.is() && m_xPropertyInfo->hasPropertyByName( sBoundFieldPropertyName ) )
            {
                Reference< XPropertySet > xBoundField;
                m_xProps->getPropertyValue( sBoundFieldPropertyName ) >>= xBoundField;
                if ( xBoundField.is() )
                    return true;
            }
 
            // currently exchanging data with an external binding?
            Reference< XBindableValue > xBindable( m_xProps, UNO_QUERY );
            if ( xBindable.is() && xBindable->getValueBinding().is() )
                return true;
        }
        catch( const Exception& )
        {
            TOOLS_WARN_EXCEPTION( "xmloff.forms", "OColumnExport::controlHasActiveDataBinding" );
        }
 
        return false;
    }
 
    bool OControlExport::controlHasUserSuppliedListEntries() const
    {
        try
        {
            // an external list source?
            Reference< XListEntrySink > xEntrySink( m_xProps, UNO_QUERY );
            if ( xEntrySink.is() && xEntrySink->getListEntrySource().is() )
                return false;
 
            if ( m_xPropertyInfo.is() && m_xPropertyInfo->hasPropertyByName( PROPERTY_LISTSOURCETYPE ) )
            {
                ListSourceType eListSourceType = ListSourceType_VALUELIST;
                OSL_VERIFY( m_xProps->getPropertyValue( PROPERTY_LISTSOURCETYPE ) >>= eListSourceType );
                if ( eListSourceType == ListSourceType_VALUELIST )
                    // for value lists, the list entries as entered by the user are used
                    return true;
 
                // for every other type, the list entries are filled with some data obtained
                // from a database - if and only if the ListSource property is not empty
                return getScalarListSourceValue().isEmpty();
            }
        }
        catch( const Exception& )
        {
            DBG_UNHANDLED_EXCEPTION("xmloff.forms");
        }
 
        OSL_FAIL( "OControlExport::controlHasUserSuppliedListEntries: unreachable code!" );
            // this method should be called for list and combo boxes only
        return true;
    }
 
    //= OColumnExport
    OColumnExport::OColumnExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxControl, const OUString& _rControlId,
        const Sequence< ScriptEventDescriptor >& _rEvents)
        :OControlExport(_rContext, _rxControl, _rControlId, OUString(), _rEvents)
    {
    }
 
    OColumnExport::~OColumnExport()
    {
    }
 
    void OColumnExport::exportServiceNameAttribute()
    {
        // the attribute "service name" (which has a slightly different meaning for columns
        DBG_CHECK_PROPERTY( PROPERTY_COLUMNSERVICENAME, OUString );
        OUString sColumnServiceName;
        m_xProps->getPropertyValue(PROPERTY_COLUMNSERVICENAME) >>= sColumnServiceName;
        // the service name is a full qualified one (i.e. com.sun.star.form.TextField), but the
        // real service name for the column (for use with the XGridColumnFactory) is only the last
        // token of this complete name.
        sal_Int32 nLastSep = sColumnServiceName.lastIndexOf('.');
        OSL_ENSURE(-1 != nLastSep, "OColumnExport::startExportElement: invalid service name!");
        sColumnServiceName = sColumnServiceName.copy(nLastSep + 1);
        sColumnServiceName =
            m_rContext.getGlobalContext().GetNamespaceMap().GetQNameByKey(
                XML_NAMESPACE_OOO, sColumnServiceName );
        // add the attribute
        AddAttribute( OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::ServiceName)
                    , OAttributeMetaData::getCommonControlAttributeName(CCAFlags::ServiceName)
                    , sColumnServiceName);
        // flag the property as "handled"
        exportedProperty(PROPERTY_COLUMNSERVICENAME);
 
    }
 
    OUString OColumnExport::getOuterXMLElementName() const
    {
        return u"column"_ustr;
    }
 
    void OColumnExport::exportAttributes()
    {
        OControlExport::exportAttributes();
 
        // the attribute "label"
        exportStringPropertyAttribute(
            OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::Label),
            OAttributeMetaData::getCommonControlAttributeName(CCAFlags::Label),
            PROPERTY_LABEL);
 
        // the style attribute
        OUString sStyleName = m_rContext.getObjectStyleName( m_xProps );
        if ( !sStyleName.isEmpty() )
        {
            AddAttribute(
                OAttributeMetaData::getSpecialAttributeNamespace( SCAFlags::ColumnStyleName ),
                OAttributeMetaData::getSpecialAttributeName( SCAFlags::ColumnStyleName ),
                sStyleName
            );
        }
    }
 
    void OColumnExport::examine()
    {
        OControlExport::examine();
 
        // grid columns miss some properties of the controls they're representing
        m_nIncludeCommon &= ~CCAFlags(CCAFlags::For | CCAFlags::Printable | CCAFlags::TabIndex | CCAFlags::TabStop | CCAFlags::Label);
        m_nIncludeSpecial &= ~SCAFlags(SCAFlags::EchoChar | SCAFlags::AutoCompletion | SCAFlags::Multiple | SCAFlags::MultiLine);
 
        if (FormComponentType::DATEFIELD != m_nClassId)
            // except date fields, no column has the DropDown property
            m_nIncludeCommon &= ~CCAFlags::Dropdown;
    }
 
    //= OFormExport
    OFormExport::OFormExport(IFormsExportContext& _rContext, const Reference< XPropertySet >& _rxForm,
        const Sequence< ScriptEventDescriptor >& _rEvents)
        :OElementExport(_rContext, _rxForm, _rEvents)
        ,m_bCreateConnectionResourceElement(false)
    {
        OSL_ENSURE(m_xProps.is(), "OFormExport::OFormExport: invalid arguments!");
    }
 
    OUString OFormExport::getXMLElementName() const
    {
        return u"form"_ustr;
    }
 
    void OFormExport::exportSubTags()
    {
        if ( m_bCreateConnectionResourceElement && m_xProps.is() )
        {
            m_rContext.getGlobalContext().ClearAttrList();
            OUString sPropValue;
            m_xProps->getPropertyValue( PROPERTY_DATASOURCENAME ) >>= sPropValue; // if set it is a file url
            if ( sPropValue.isEmpty() )
                m_xProps->getPropertyValue( PROPERTY_URL ) >>= sPropValue;
            if ( !sPropValue.isEmpty() )
                AddAttribute(
                    OAttributeMetaData::getCommonControlAttributeNamespace(CCAFlags::TargetLocation),
                    OAttributeMetaData::getCommonControlAttributeName(CCAFlags::TargetLocation),
                    m_rContext.getGlobalContext().GetRelativeReference(sPropValue));
            if ( m_rContext.getGlobalContext().GetAttrList().getLength() )
            {
                SvXMLElementExport aFormElement(m_rContext.getGlobalContext(), XML_NAMESPACE_FORM, xmloff::token::XML_CONNECTION_RESOURCE, true, true);
            }
        }
 
        // let the base class export the remaining properties and the events
        OElementExport::exportSubTags();
        // loop through all children
        Reference< XIndexAccess > xCollection(m_xProps, UNO_QUERY);
        OSL_ENSURE(xCollection.is(), "OFormLayerXMLExport::implExportForm: a form which is not an index access? Suspicious!");
 
        if (xCollection.is())
            m_rContext.exportCollectionElements(xCollection);
    }
 
    void OFormExport::exportAttributes()
    {
        sal_Int32 i=0;
 
        // the string properties
        {
            static const FormAttributes eStringPropertyIds[] =
            {
                faName, /*faAction,*/ faCommand, faFilter, faOrder
            };
            static constexpr OUString aStringPropertyNames[] =
            {
                PROPERTY_NAME, /*PROPERTY_TARGETURL,*/ PROPERTY_COMMAND, PROPERTY_FILTER, PROPERTY_ORDER
            };
            static const sal_Int32 nIdCount = std::size(eStringPropertyIds);
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nNameCount = std::size(aStringPropertyNames);
            OSL_ENSURE((nIdCount == nNameCount),
                "OFormExport::exportAttributes: somebody tampered with the maps (1)!");
        #endif
            for (i=0; i<nIdCount; ++i)
                exportStringPropertyAttribute(
                    OAttributeMetaData::getFormAttributeNamespace(eStringPropertyIds[i]),
                    OAttributeMetaData::getFormAttributeName(eStringPropertyIds[i]),
                    aStringPropertyNames[i]);
 
            // #i112082# xlink:type is added as part of exportTargetLocationAttribute
 
            // now export the data source name or databaselocation or connection resource
            OUString sPropValue;
            m_xProps->getPropertyValue( PROPERTY_DATASOURCENAME ) >>= sPropValue;
            m_bCreateConnectionResourceElement = sPropValue.isEmpty();
            if ( !m_bCreateConnectionResourceElement )
            {
                INetURLObject aURL(sPropValue);
                m_bCreateConnectionResourceElement = ( aURL.GetProtocol() == INetProtocol::File );
                if ( !m_bCreateConnectionResourceElement )
                    exportStringPropertyAttribute(
                        OAttributeMetaData::getFormAttributeNamespace(faDatasource),
                        OAttributeMetaData::getFormAttributeName(faDatasource),
                        PROPERTY_DATASOURCENAME);
            }
            else
                exportedProperty(PROPERTY_URL);
            if ( m_bCreateConnectionResourceElement )
                exportedProperty(PROPERTY_DATASOURCENAME);
        }
 
        // the boolean properties
        {
            static const FormAttributes eBooleanPropertyIds[] =
            {
                faAllowDeletes, faAllowInserts, faAllowUpdates, faApplyFilter, faEscapeProcessing, faIgnoreResult
            };
            static constexpr OUString pBooleanPropertyNames[] =
            {
                PROPERTY_ALLOWDELETES,
                PROPERTY_ALLOWINSERTS,
                PROPERTY_ALLOWUPDATES,
                PROPERTY_APPLYFILTER,
                PROPERTY_ESCAPEPROCESSING,
                PROPERTY_IGNORERESULT
            };
            static const BoolAttrFlags nBooleanPropertyAttrFlags[] =
            {
                BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultFalse, BoolAttrFlags::DefaultTrue, BoolAttrFlags::DefaultFalse
            };
            static const sal_Int32 nIdCount = std::size(eBooleanPropertyIds);
        #if OSL_DEBUG_LEVEL > 0
            static const sal_Int32 nNameCount = std::size(pBooleanPropertyNames);
            static const sal_Int32 nFlagsCount = std::size(nBooleanPropertyAttrFlags);
            OSL_ENSURE((nIdCount == nNameCount) && (nNameCount == nFlagsCount),
                "OFormExport::exportAttributes: somebody tampered with the maps (2)!");
        #endif
            for (i=0; i<nIdCount; ++i)
                exportBooleanPropertyAttribute(
                    OAttributeMetaData::getFormAttributeNamespace(eBooleanPropertyIds[i]),
                    OAttributeMetaData::getFormAttributeName(eBooleanPropertyIds[i]),
                    pBooleanPropertyNames[i],
                    nBooleanPropertyAttrFlags[i]
                );
        }
 
        // the enum properties
        {
            exportEnumPropertyAttribute(
                OAttributeMetaData::getFormAttributeNamespace(faEnctype),
                OAttributeMetaData::getFormAttributeName(faEnctype),
                PROPERTY_SUBMIT_ENCODING,
                aSubmitEncodingMap,
                FormSubmitEncoding_URL,
                false
            );
            exportEnumPropertyAttribute(
                OAttributeMetaData::getFormAttributeNamespace(faMethod),
                OAttributeMetaData::getFormAttributeName(faMethod),
                PROPERTY_SUBMIT_METHOD,
                aSubmitMethodMap,
                FormSubmitMethod_GET,
                false
            );
            exportEnumPropertyAttribute(
                OAttributeMetaData::getFormAttributeNamespace(faCommandType),
                OAttributeMetaData::getFormAttributeName(faCommandType),
                PROPERTY_COMMAND_TYPE,
                aCommandTypeMap,
                CommandType::COMMAND,
                false
            );
            exportEnumPropertyAttribute(
                OAttributeMetaData::getFormAttributeNamespace(faNavigationMode),
                OAttributeMetaData::getFormAttributeName(faNavigationMode),
                PROPERTY_NAVIGATION,
                aNavigationTypeMap,
                NavigationBarMode_CURRENT,
                false
            );
            exportEnumPropertyAttribute(
                OAttributeMetaData::getFormAttributeNamespace(faTabbingCycle),
                OAttributeMetaData::getFormAttributeName(faTabbingCycle),
                PROPERTY_CYCLE,
                aTabulatorCycleMap,
                TabulatorCycle_RECORDS,
                true
            );
        }
 
        // the service name
        exportServiceNameAttribute();
        // the target frame
        exportTargetFrameAttribute();
        // the target URL
        exportTargetLocationAttribute(true);    // #i110911# add type attribute (for form, but not for control)
 
        // master fields
        exportStringSequenceAttribute(
            OAttributeMetaData::getFormAttributeNamespace(faMasterFields),
            OAttributeMetaData::getFormAttributeName(faMasterFields),
            PROPERTY_MASTERFIELDS);
        // detail fields
        exportStringSequenceAttribute(
            OAttributeMetaData::getFormAttributeNamespace(faDetailFields),
            OAttributeMetaData::getFormAttributeName(faDetailFields),
            PROPERTY_DETAILFIELDS);
    }
}   // namespace xmloff
 
/* vim:set shiftwidth=4 softtabstop=4 expandtab: */

V547 Expression is always true.

V547 Expression is always false.

V547 Expression is always true.

V547 Expression is always true.

V547 Expression 'bActingAsRichText' is always false.

V547 Expression 'nValue' is always false.

V547 Expression 'nEchoChar' is always false.

V560 A part of conditional expression is always false.

V560 A part of conditional expression is always true: (nIdCount == nNameCount).

V560 A part of conditional expression is always true: (nNameCount == nFlagsCount).

V560 A part of conditional expression is always false.

V560 A part of conditional expression is always true: (nIdCount == nNameCount).

V560 A part of conditional expression is always true: (nNameCount == nDefaultCount).

V560 A part of conditional expression is always false.

V560 A part of conditional expression is always true.

V560 A part of conditional expression is always false.

V560 A part of conditional expression is always false.

V560 A part of conditional expression is always true: (nIdCount == nNameCount).

V560 A part of conditional expression is always true: (nNameCount == nFlagsCount).