/**************************************************************
 * 
 * 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
 * 
 * Unless required by applicable law or agreed to in writing,
 * software distributed under the License is distributed on an
 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
 * KIND, either express or implied.  See the License for the
 * specific language governing permissions and limitations
 * under the License.
 * 
 *************************************************************/



// MARKER(update_precomp.py): autogen include statement, do not remove
#include "precompiled_xmloff.hxx"

#include <stdio.h>
#include "layerexport.hxx"
#include "strings.hxx"
#include <xmloff/xmlexp.hxx>
#include <xmloff/nmspmap.hxx>
#include "xmloff/xmlnmspe.hxx"
#include <xmloff/xmluconv.hxx>
#include <xmloff/xmlprmap.hxx>
#include <xmloff/prhdlfac.hxx>
#include "elementexport.hxx"
#include <xmloff/families.hxx>
#include <xmloff/contextid.hxx>
#include <xmloff/controlpropertyhdl.hxx>
#include <tools/diagnose_ex.h>
#include "controlpropertymap.hxx"
#include <com/sun/star/container/XIndexAccess.hpp>
#include <com/sun/star/form/XFormsSupplier2.hpp>
#include <com/sun/star/xforms/XFormsSupplier.hpp>
#include <com/sun/star/form/FormComponentType.hpp>
#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/container/XChild.hpp>
#include <com/sun/star/script/XEventAttacherManager.hpp>
#include "eventexport.hxx"
#include <xmloff/XMLEventExport.hxx>
#include "formevents.hxx"
#include <xmloff/xmlnumfe.hxx>
#include "xmloff/xformsexport.hxx"

/** === begin UNO includes === **/
#include <com/sun/star/text/XText.hpp>
/** === end UNO includes === **/

#include <numeric>

//.........................................................................
namespace xmloff
{
//.........................................................................

	using namespace ::com::sun::star::uno;
	using namespace ::com::sun::star::awt;
	using namespace ::com::sun::star::lang;
	using namespace ::com::sun::star::beans;
	using namespace ::com::sun::star::container;
	using namespace ::com::sun::star::drawing;
	using namespace ::com::sun::star::form;
	using namespace ::com::sun::star::script;
	using namespace ::com::sun::star::util;
	using namespace ::com::sun::star::text;

    typedef ::com::sun::star::xforms::XFormsSupplier XXFormsSupplier;

	//=====================================================================
	//= OFormLayerXMLExport_Impl
	//=====================================================================
	//---------------------------------------------------------------------
	const ::rtl::OUString& OFormLayerXMLExport_Impl::getControlNumberStyleNamePrefix()
	{
		static const ::rtl::OUString s_sControlNumberStyleNamePrefix = ::rtl::OUString::createFromAscii("C");
		return s_sControlNumberStyleNamePrefix;
	}

	//---------------------------------------------------------------------
	OFormLayerXMLExport_Impl::OFormLayerXMLExport_Impl(SvXMLExport& _rContext)
		:m_rContext(_rContext)
		,m_pControlNumberStyles(NULL)
	{
		initializePropertyMaps();

		// add our style family to the export context's style pool
		m_xPropertyHandlerFactory = new OControlPropertyHandlerFactory();
		::vos::ORef< XMLPropertySetMapper > xStylePropertiesMapper = new XMLPropertySetMapper( getControlStylePropertyMap(), m_xPropertyHandlerFactory.getBodyPtr() );
		m_xStyleExportMapper = new OFormComponentStyleExportMapper( xStylePropertiesMapper.getBodyPtr() );

		// our style family
		m_rContext.GetAutoStylePool()->AddFamily(
			XML_STYLE_FAMILY_CONTROL_ID, token::GetXMLToken(token::XML_PARAGRAPH),
			m_xStyleExportMapper.getBodyPtr(),
			::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( XML_STYLE_FAMILY_CONTROL_PREFIX) )
		);

		// add our event translation table
		m_rContext.GetEventExport().AddTranslationTable(g_pFormsEventTranslation);

		clear();
	}

	OFormLayerXMLExport_Impl::~OFormLayerXMLExport_Impl()
	{
	}

	//---------------------------------------------------------------------
	sal_Bool OFormLayerXMLExport_Impl::impl_isFormPageContainingForms(const Reference< XDrawPage >& _rxDrawPage, Reference< XIndexAccess >& _rxForms)
	{
		Reference< XFormsSupplier2 > xFormsSupp(_rxDrawPage, UNO_QUERY);
		OSL_ENSURE(xFormsSupp.is(), "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid draw page (no XFormsSupplier)! Doin' nothing!");
		if (!xFormsSupp.is())
			return sal_False;

        if ( !xFormsSupp->hasForms() )
            // nothing to do at all
            return sal_False;

		_rxForms = Reference< XIndexAccess >(xFormsSupp->getForms(), UNO_QUERY);
		Reference< XServiceInfo > xSI(_rxForms, UNO_QUERY);	// order is important!
		OSL_ENSURE(xSI.is(), "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid collection (must not be NULL and must have a ServiceInfo)!");
		if (!xSI.is())
			return sal_False;

		if (!xSI->supportsService(SERVICE_FORMSCOLLECTION))
		{
			OSL_ENSURE(sal_False, "OFormLayerXMLExport_Impl::impl_isFormPageContainingForms: invalid collection (is no com.sun.star.form.Forms)!");
			// nothing to do
			return sal_False;
		}
		return sal_True;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportGridColumn(const Reference< XPropertySet >& _rxColumn,
		const Sequence< ScriptEventDescriptor >& _rEvents)
	{
        // do the exporting
		OColumnExport aExportImpl(*this, _rxColumn, getControlId( _rxColumn ), _rEvents);
		aExportImpl.doExport();
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportControl(const Reference< XPropertySet >& _rxControl,
		const Sequence< ScriptEventDescriptor >& _rEvents)
	{
		// the list of the referring controls
		::rtl::OUString sReferringControls;
        MapPropertySet2String::const_iterator aReferring = m_aCurrentPageReferring->second.find(_rxControl);
		if (aReferring != m_aCurrentPageReferring->second.end())
			sReferringControls = aReferring->second;

		// the control id (should already have been created in examineForms)
		::rtl::OUString sControlId( getControlId( _rxControl ) );

		// do the exporting
		OControlExport aExportImpl(*this, _rxControl, sControlId, sReferringControls, _rEvents);
		aExportImpl.doExport();
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportForm(const Reference< XPropertySet >& _rxProps,
		const Sequence< ScriptEventDescriptor >& _rEvents)
	{
		OSL_ENSURE(_rxProps.is(), "OFormLayerXMLExport_Impl::exportForm: invalid property set!");
		OFormExport aAttributeHandler(*this, _rxProps, _rEvents);
		aAttributeHandler.doExport();
	}

	//---------------------------------------------------------------------
	::vos::ORef< SvXMLExportPropertyMapper > OFormLayerXMLExport_Impl::getStylePropertyMapper()
	{
		return m_xStyleExportMapper;
	}

	//---------------------------------------------------------------------
	SvXMLExport& OFormLayerXMLExport_Impl::getGlobalContext()
	{
		return m_rContext;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportCollectionElements(const Reference< XIndexAccess >& _rxCollection)
	{
		// step through all the elements of the collection
		sal_Int32 nElements = _rxCollection->getCount();

		Reference< XEventAttacherManager > xElementEventManager(_rxCollection, UNO_QUERY);
		Sequence< ScriptEventDescriptor > aElementEvents;

		Reference< XPropertySetInfo > xPropsInfo;
		Reference< XIndexAccess > xCurrentContainer;
		for (sal_Int32 i=0; i<nElements; ++i)
		{
			try
			{
				// extract the current element
		        Reference< XPropertySet > xCurrentProps( _rxCollection->getByIndex(i), UNO_QUERY );
				OSL_ENSURE(xCurrentProps.is(), "OFormLayerXMLExport_Impl::exportCollectionElements: invalid child element, skipping!");
				if (!xCurrentProps.is())
					continue;

				// check if there is a ClassId property on the current element. If so, we assume it to be a control
				xPropsInfo = xCurrentProps->getPropertySetInfo();
				OSL_ENSURE(xPropsInfo.is(), "OFormLayerXMLExport_Impl::exportCollectionElements: no property set info!");
				if (!xPropsInfo.is())
					// without this, a lot of stuff in the export routines may fail
					continue;

				// if the element is part of a ignore list, we are not allowed to export it
				if ( m_aIgnoreList.end() != m_aIgnoreList.find( xCurrentProps ) )
					continue;

				if (xElementEventManager.is())
					aElementEvents = xElementEventManager->getScriptEvents(i);

				if (xPropsInfo->hasPropertyByName(PROPERTY_COLUMNSERVICENAME))
				{
					exportGridColumn(xCurrentProps, aElementEvents);
				}
				else if (xPropsInfo->hasPropertyByName(PROPERTY_CLASSID))
				{
					exportControl(xCurrentProps, aElementEvents);
				}
				else
				{
					exportForm(xCurrentProps, aElementEvents);
				}
			}
			catch(Exception&)
			{
				OSL_ENSURE(sal_False, "OFormLayerXMLExport_Impl::exportCollectionElements: caught an exception ... skipping the current element!");
				continue;
			}
		}
	}

	//---------------------------------------------------------------------
	::rtl::OUString OFormLayerXMLExport_Impl::getObjectStyleName( const Reference< XPropertySet >& _rxObject )
	{
		::rtl::OUString aObjectStyle;

		MapPropertySet2String::const_iterator aObjectStylePos = m_aGridColumnStyles.find( _rxObject );
		if ( m_aGridColumnStyles.end() != aObjectStylePos )
			aObjectStyle = aObjectStylePos->second;
		return aObjectStyle;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::clear()
	{
		m_aControlIds.clear();
		m_aReferringControls.clear();
		m_aCurrentPageIds = m_aControlIds.end();
		m_aCurrentPageReferring = m_aReferringControls.end();

		m_aControlNumberFormats.clear();
		m_aGridColumnStyles.clear();

		m_aIgnoreList.clear();
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportControlNumberStyles()
	{
		if (m_pControlNumberStyles)
			m_pControlNumberStyles->Export(sal_False);
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportAutoControlNumberStyles()
	{
		if ( m_pControlNumberStyles )
			m_pControlNumberStyles->Export( sal_True );
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportAutoStyles()
	{
		m_rContext.GetAutoStylePool()->exportXML(
			XML_STYLE_FAMILY_CONTROL_ID,
			m_rContext.GetDocHandler(),
			m_rContext.GetMM100UnitConverter(),
			m_rContext.GetNamespaceMap()
		);
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportForms(const Reference< XDrawPage >& _rxDrawPage)
	{
		// get the forms collection of the page
		Reference< XIndexAccess > xCollectionIndex;
		if (!impl_isFormPageContainingForms(_rxDrawPage, xCollectionIndex))
			return;

#if OSL_DEBUG_LEVEL > 0
		sal_Bool bPageIsKnown =
#endif
			implMoveIterators(_rxDrawPage, sal_False);
		OSL_ENSURE(bPageIsKnown, "OFormLayerXMLExport_Impl::exportForms: exporting a page which has not been examined!");

        // export forms collection
		exportCollectionElements(xCollectionIndex);
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::exportXForms() const
    {
        // export XForms models
        ::exportXForms( m_rContext );
    }

	//---------------------------------------------------------------------
	bool OFormLayerXMLExport_Impl::pageContainsForms( const Reference< XDrawPage >& _rxDrawPage ) const
    {
		Reference< XFormsSupplier2 > xFormsSupp( _rxDrawPage, UNO_QUERY );
        DBG_ASSERT( xFormsSupp.is(), "OFormLayerXMLExport_Impl::pageContainsForms: no XFormsSupplier2!" );
        return xFormsSupp.is() && xFormsSupp->hasForms();
    }

	//---------------------------------------------------------------------
	bool OFormLayerXMLExport_Impl::documentContainsXForms() const
    {
        Reference< XXFormsSupplier > xXFormSupp( m_rContext.GetModel(), UNO_QUERY );
        Reference< XNameContainer > xForms;
        if ( xXFormSupp.is() )
            xForms = xXFormSupp->getXForms();
        return xForms.is() && xForms->hasElements();
    }

	//---------------------------------------------------------------------
	sal_Bool OFormLayerXMLExport_Impl::implMoveIterators(const Reference< XDrawPage >& _rxDrawPage, sal_Bool _bClear)
	{
		sal_Bool bKnownPage = sal_False;

		// the one for the ids
		m_aCurrentPageIds = m_aControlIds.find(_rxDrawPage);
		if (m_aControlIds.end() == m_aCurrentPageIds)
		{
			m_aControlIds[_rxDrawPage] = MapPropertySet2String();
			m_aCurrentPageIds = m_aControlIds.find(_rxDrawPage);
		}
		else
		{
			bKnownPage = sal_True;
			if (_bClear && !m_aCurrentPageIds->second.empty() )
				m_aCurrentPageIds->second.clear();
		}

		// the one for the ids of the referring controls
		m_aCurrentPageReferring = m_aReferringControls.find(_rxDrawPage);
		if (m_aReferringControls.end() == m_aCurrentPageReferring)
		{
			m_aReferringControls[_rxDrawPage] = MapPropertySet2String();
			m_aCurrentPageReferring = m_aReferringControls.find(_rxDrawPage);
		}
		else
		{
			bKnownPage = sal_True;
			if (_bClear && !m_aCurrentPageReferring->second.empty() )
				m_aCurrentPageReferring->second.clear();
		}
		return bKnownPage;
	}

	//---------------------------------------------------------------------
	sal_Bool OFormLayerXMLExport_Impl::seekPage(const Reference< XDrawPage >& _rxDrawPage)
	{
        sal_Bool bKnownPage = implMoveIterators( _rxDrawPage, sal_False );
        if ( bKnownPage )
            return sal_True;

        // if the page is not yet known, this does not automatically mean that it has
        // not been examined. Instead, examineForms returns silently and successfully
        // if a page is a XFormsPageSupplier2, but does not have a forms collection
        // (This behaviour of examineForms is a performance optimization, to not force
        // the page to create a forms container just to see that it's empty.)

        // So, in such a case, seekPage is considered to be successfull, too, though the
        // page was not yet known
		Reference< XFormsSupplier2 > xFormsSupp( _rxDrawPage, UNO_QUERY );
        if ( xFormsSupp.is() && !xFormsSupp->hasForms() )
            return sal_True;

        // anything else means that the page has not been examined before, or it's no
        // valid form page. Both cases are Bad (TM).
        return sal_False;
	}

	//---------------------------------------------------------------------
	::rtl::OUString OFormLayerXMLExport_Impl::getControlId(const Reference< XPropertySet >& _rxControl)
	{
		OSL_ENSURE(m_aCurrentPageIds != m_aControlIds.end(), "OFormLayerXMLExport_Impl::getControlId: invalid current page!");
		OSL_ENSURE(m_aCurrentPageIds->second.end() != m_aCurrentPageIds->second.find(_rxControl),
			"OFormLayerXMLExport_Impl::getControlId: can not find the control!");
		return m_aCurrentPageIds->second[_rxControl];
	}

	//---------------------------------------------------------------------
	::rtl::OUString OFormLayerXMLExport_Impl::getImmediateNumberStyle( const Reference< XPropertySet >& _rxObject )
	{
		::rtl::OUString sNumberStyle;

		sal_Int32 nOwnFormatKey = implExamineControlNumberFormat( _rxObject );
		if ( -1 != nOwnFormatKey )
			sNumberStyle = getControlNumberStyleExport()->GetStyleName( nOwnFormatKey );

		return sNumberStyle;
	}

	//---------------------------------------------------------------------
	::rtl::OUString OFormLayerXMLExport_Impl::getControlNumberStyle( const Reference< XPropertySet >& _rxControl )
	{
		::rtl::OUString sNumberStyle;

		ConstMapPropertySet2IntIterator aControlFormatPos = m_aControlNumberFormats.find(_rxControl);
		if (m_aControlNumberFormats.end() != aControlFormatPos)
		{
			OSL_ENSURE(m_pControlNumberStyles, "OFormLayerXMLExport_Impl::getControlNumberStyle: have a control which has a format style, but no style exporter!");
			sNumberStyle = getControlNumberStyleExport()->GetStyleName(aControlFormatPos->second);
		}
		// it's allowed to ask for a control which does not have format information.
		// (This is for performance reasons)

		return sNumberStyle;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::examineForms(const Reference< XDrawPage >& _rxDrawPage)
	{
		// get the forms collection of the page
		Reference< XIndexAccess > xCollectionIndex;
		if (!impl_isFormPageContainingForms(_rxDrawPage, xCollectionIndex))
			return;

		// move the iterator which specify the currently handled page
#if OSL_DEBUG_LEVEL > 0
		sal_Bool bPageIsKnown =
#endif
			implMoveIterators(_rxDrawPage, sal_True);
		OSL_ENSURE(!bPageIsKnown, "OFormLayerXMLExport_Impl::examineForms: examining a page twice!");

		::std::stack< Reference< XIndexAccess > >	aContainerHistory;
		::std::stack< sal_Int32 >					aIndexHistory;

		Reference< XIndexAccess > xLoop = xCollectionIndex;
		sal_Int32 nChildPos = 0;
		do
		{
			if (nChildPos < xLoop->getCount())
			{
		        Reference< XPropertySet	> xCurrent( xLoop->getByIndex( nChildPos ), UNO_QUERY );
				OSL_ENSURE(xCurrent.is(), "OFormLayerXMLExport_Impl::examineForms: invalid child object");
				if (!xCurrent.is())
					continue;

				if (!checkExamineControl(xCurrent))
				{
					// step down
					Reference< XIndexAccess > xNextContainer(xCurrent, UNO_QUERY);
					OSL_ENSURE(xNextContainer.is(), "OFormLayerXMLExport_Impl::examineForms: what the heck is this ... no control, no container?");
					aContainerHistory.push(xLoop);
					aIndexHistory.push(nChildPos);

					xLoop = xNextContainer;
					nChildPos = -1;	// will be incremented below
				}
				++nChildPos;
			}
			else
			{
				// step up
				while ((nChildPos >= xLoop->getCount()) && !aContainerHistory.empty() )
				{
					xLoop = aContainerHistory.top();
					aContainerHistory.pop();
					nChildPos = aIndexHistory.top();
					aIndexHistory.pop();

					++nChildPos;
				}
				if (nChildPos >= xLoop->getCount())
					// exited the loop above because we have no history anymore (0 == aContainerHistory.size()),
					// and on the current level there are no more children
					// -> leave
					break;
			}
		}
		while (xLoop.is());
	}

	//---------------------------------------------------------------------
    namespace
    {
        struct AccumulateSize : public ::std::binary_function< size_t, MapPropertySet2Map::value_type, size_t >
        {
            size_t operator()( size_t _size, const MapPropertySet2Map::value_type& _map ) const
            {
                return _size + _map.second.size();
            }
        };

        ::rtl::OUString lcl_findFreeControlId( const MapPropertySet2Map& _rAllPagesControlIds )
        {
		    static const ::rtl::OUString sControlIdBase( RTL_CONSTASCII_USTRINGPARAM( "control" ) );
			::rtl::OUString sControlId = sControlIdBase;

            size_t nKnownControlCount = ::std::accumulate( _rAllPagesControlIds.begin(), _rAllPagesControlIds.end(), (size_t)0, AccumulateSize() );
            sControlId += ::rtl::OUString::valueOf( (sal_Int32)nKnownControlCount + 1 );

        #ifdef DBG_UTIL
			// Check if the id is already used. It shouldn't, as we currently have no mechanism for removing entries
			// from the map, so the approach used above (take the accumulated map size) should be sufficient. But if
            // somebody changes this (e.g. allows removing entries from the map), the assertion below probably will fail.
            for (   MapPropertySet2Map::const_iterator outer = _rAllPagesControlIds.begin();
                    outer != _rAllPagesControlIds.end();
                    ++outer
                )
                for (   MapPropertySet2String::const_iterator inner = outer->second.begin();
                        inner != outer->second.end();
                        ++inner
                    )
                {
				    OSL_ENSURE( inner->second != sControlId,
					    "lcl_findFreeControlId: auto-generated control ID is already used!" );
                }
		#endif
            return sControlId;
        }
    }

	//---------------------------------------------------------------------
	sal_Bool OFormLayerXMLExport_Impl::checkExamineControl(const Reference< XPropertySet >& _rxObject)
	{
		Reference< XPropertySetInfo > xCurrentInfo = _rxObject->getPropertySetInfo();
		OSL_ENSURE(xCurrentInfo.is(), "OFormLayerXMLExport_Impl::checkExamineControl: no property set info");

		sal_Bool bIsControl = xCurrentInfo->hasPropertyByName( PROPERTY_CLASSID );
		if (bIsControl)
		{
			// ----------------------------------
			// generate a new control id

			// find a free id
			::rtl::OUString sCurrentId = lcl_findFreeControlId( m_aControlIds );
			// add it to the map
			m_aCurrentPageIds->second[_rxObject] = sCurrentId;

			// ----------------------------------
			// check if this control has a "LabelControl" property referring another control
			if ( xCurrentInfo->hasPropertyByName( PROPERTY_CONTROLLABEL ) )
			{
				Reference< XPropertySet > xCurrentReference( _rxObject->getPropertyValue( PROPERTY_CONTROLLABEL ), UNO_QUERY );
				if (xCurrentReference.is())
				{
					::rtl::OUString& sReferencedBy = m_aCurrentPageReferring->second[xCurrentReference];
					if (sReferencedBy.getLength())
						// it's not the first _rxObject referring to the xCurrentReference
						// -> separate the id
						sReferencedBy += ::rtl::OUString(RTL_CONSTASCII_USTRINGPARAM(","));
					sReferencedBy += sCurrentId;
				}
			}

			// ----------------------------------
			// check if the control needs a number format style
			if ( xCurrentInfo->hasPropertyByName( PROPERTY_FORMATKEY ) )
			{
				examineControlNumberFormat(_rxObject);
			}

			// ----------------------------------
            // check if it's a control providing text
            Reference< XText > xControlText( _rxObject, UNO_QUERY );
            if ( xControlText.is() )
            {
				m_rContext.GetTextParagraphExport()->collectTextAutoStyles( xControlText );
            }

			// ----------------------------------
			// check if it is a grid control - in this case, we need special handling for the columns
			sal_Int16 nControlType = FormComponentType::CONTROL;
			_rxObject->getPropertyValue( PROPERTY_CLASSID ) >>= nControlType;
			if ( FormComponentType::GRIDCONTROL == nControlType )
			{
				collectGridColumnStylesAndIds( _rxObject );
			}
		}

		return bIsControl;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds( const Reference< XPropertySet >& _rxControl )
	{
		// loop through all columns of the grid
		try
		{
			Reference< XIndexAccess > xContainer( _rxControl, UNO_QUERY );
			OSL_ENSURE( xContainer.is(), "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: grid control not being a container?!" );
            if ( !xContainer.is() )
                return;

			Reference< XPropertySetInfo > xColumnPropertiesMeta;

			sal_Int32 nCount = xContainer->getCount();
			for ( sal_Int32 i=0; i<nCount; ++i )
			{
                Reference< XPropertySet > xColumnProperties( xContainer->getByIndex( i ), UNO_QUERY );
                OSL_ENSURE( xColumnProperties.is(), "OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: invalid grid column encountered!" );
                if ( !xColumnProperties.is() )
                    continue;

			    // ----------------------------------
			    // generate a new control id

			    // find a free id
			    ::rtl::OUString sCurrentId = lcl_findFreeControlId( m_aControlIds );
			    // add it to the map
			    m_aCurrentPageIds->second[ xColumnProperties ] = sCurrentId;

			    // ----------------------------------
                // determine a number style, if needed
				xColumnPropertiesMeta = xColumnProperties->getPropertySetInfo();
				// get the styles of the column
				::std::vector< XMLPropertyState > aPropertyStates = m_xStyleExportMapper->Filter( xColumnProperties );

				// care for the number format, additionally
				::rtl::OUString sColumnNumberStyle;
				if ( xColumnPropertiesMeta.is() && xColumnPropertiesMeta->hasPropertyByName( PROPERTY_FORMATKEY ) )
					sColumnNumberStyle = getImmediateNumberStyle( xColumnProperties );

				if ( sColumnNumberStyle.getLength() )
				{	// the column indeed has a formatting
					sal_Int32 nStyleMapIndex = m_xStyleExportMapper->getPropertySetMapper()->FindEntryIndex( CTF_FORMS_DATA_STYLE );
						// TODO: move this to the ctor
					OSL_ENSURE ( -1 != nStyleMapIndex, "XMLShapeExport::collectShapeAutoStyles: could not obtain the index for our context id!");

					XMLPropertyState aNumberStyleState( nStyleMapIndex, makeAny( sColumnNumberStyle ) );
					aPropertyStates.push_back( aNumberStyleState );
				}

#if OSL_DEBUG_LEVEL > 0
				::std::vector< XMLPropertyState >::const_iterator aHaveALook = aPropertyStates.begin();
				for ( ; aHaveALook != aPropertyStates.end(); ++aHaveALook )
				{
                    (void)aHaveALook;
				}
#endif

			    // ----------------------------------
                // determine the column style

				if ( !aPropertyStates.empty() )
				{	// add to the style pool
					::rtl::OUString sColumnStyleName = m_rContext.GetAutoStylePool()->Add( XML_STYLE_FAMILY_CONTROL_ID, aPropertyStates );

					OSL_ENSURE( m_aGridColumnStyles.end() == m_aGridColumnStyles.find( xColumnProperties ),
						"OFormLayerXMLExport_Impl::collectGridColumnStylesAndIds: already have a style for this column!" );

					m_aGridColumnStyles.insert( MapPropertySet2String::value_type( xColumnProperties, sColumnStyleName ) );
				}
			}
		}
		catch( const Exception&	)
		{
            DBG_UNHANDLED_EXCEPTION();
		}
	}

	//---------------------------------------------------------------------
	sal_Int32 OFormLayerXMLExport_Impl::implExamineControlNumberFormat( const Reference< XPropertySet >& _rxObject )
	{
		// get the format key relative to our own formats supplier
		sal_Int32 nOwnFormatKey = ensureTranslateFormat( _rxObject );

		if ( -1 != nOwnFormatKey )
			// tell the exporter that we used this format
			getControlNumberStyleExport()->SetUsed( nOwnFormatKey );

		return nOwnFormatKey;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::examineControlNumberFormat( const Reference< XPropertySet >& _rxControl )
	{
		sal_Int32 nOwnFormatKey = implExamineControlNumberFormat( _rxControl );

		if ( -1 == nOwnFormatKey )
			// nothing to do, the number format of this control is void
			return;

		// remember the format key for this control (we'll be asked in getControlNumberStyle for this)
		OSL_ENSURE(m_aControlNumberFormats.end() == m_aControlNumberFormats.find(_rxControl),
			"OFormLayerXMLExport_Impl::examineControlNumberFormat: already handled this control!");
		m_aControlNumberFormats[_rxControl] = nOwnFormatKey;
	}

	//---------------------------------------------------------------------
	sal_Int32 OFormLayerXMLExport_Impl::ensureTranslateFormat(const Reference< XPropertySet >& _rxFormattedControl)
	{
		ensureControlNumberStyleExport();
		OSL_ENSURE(m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: no own formats supplier!");
			// (should have been created in ensureControlNumberStyleExport)

		sal_Int32 nOwnFormatKey = -1;

		// the format key (relative to the control's supplier)
		sal_Int32 nControlFormatKey = -1;
		Any aControlFormatKey = _rxFormattedControl->getPropertyValue(PROPERTY_FORMATKEY);
		if (aControlFormatKey >>= nControlFormatKey)
		{
			// the control's number format
			Reference< XNumberFormatsSupplier > xControlFormatsSupplier;
			_rxFormattedControl->getPropertyValue(PROPERTY_FORMATSSUPPLIER) >>= xControlFormatsSupplier;
			Reference< XNumberFormats > xControlFormats;
			if (xControlFormatsSupplier.is())
				xControlFormats = xControlFormatsSupplier->getNumberFormats();
			OSL_ENSURE(xControlFormats.is(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: formatted control without supplier!");

			// obtain the persistent (does not depend on the formats supplier) representation of the control's format
			Locale aFormatLocale;
			::rtl::OUString sFormatDescription;
			if (xControlFormats.is())
			{
				Reference< XPropertySet > xControlFormat = xControlFormats->getByKey(nControlFormatKey);

				xControlFormat->getPropertyValue(PROPERTY_LOCALE)		>>= aFormatLocale;
				xControlFormat->getPropertyValue(PROPERTY_FORMATSTRING)	>>= sFormatDescription;
			}

			// check if our own formats collection already knows the format
			nOwnFormatKey = m_xControlNumberFormats->queryKey(sFormatDescription, aFormatLocale, sal_False);
			if (-1 == nOwnFormatKey)
			{	// no, we don't
				// -> create a new format
				nOwnFormatKey = m_xControlNumberFormats->addNew(sFormatDescription, aFormatLocale);
			}
			OSL_ENSURE(-1 != nOwnFormatKey, "OFormLayerXMLExport_Impl::ensureTranslateFormat: could not translate the controls format key!");
		}
		else
			OSL_ENSURE(!aControlFormatKey.hasValue(), "OFormLayerXMLExport_Impl::ensureTranslateFormat: invalid number format property value!");

		return nOwnFormatKey;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::ensureControlNumberStyleExport()
	{
		if (!m_pControlNumberStyles)
		{
			// create our number formats supplier (if necessary)
			Reference< XNumberFormatsSupplier > xFormatsSupplier;

			OSL_ENSURE(!m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: inconsistence!");
				// the m_xControlNumberFormats and m_pControlNumberStyles should be maintained together

			try
			{
				// create it for en-US (does not really matter, as we will specify a locale for every
				// concrete language to use)
				Sequence< Any > aSupplierArgs(1);
				aSupplierArgs[0] <<= Locale	(	::rtl::OUString::createFromAscii("en"),
												::rtl::OUString::createFromAscii("US"),
												::rtl::OUString()
											);
				// #110680#
				//Reference< XInterface > xFormatsSupplierUntyped =
				//	::comphelper::getProcessServiceFactory()->createInstanceWithArguments(
				//		SERVICE_NUMBERFORMATSSUPPLIER,
				//		aSupplierArgs
				//	);
				Reference< XInterface > xFormatsSupplierUntyped =
					m_rContext.getServiceFactory()->createInstanceWithArguments(
						SERVICE_NUMBERFORMATSSUPPLIER,
						aSupplierArgs
					);
				OSL_ENSURE(xFormatsSupplierUntyped.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: could not instantiate a number formats supplier!");

				xFormatsSupplier = Reference< XNumberFormatsSupplier >(xFormatsSupplierUntyped, UNO_QUERY);
				if (xFormatsSupplier.is())
					m_xControlNumberFormats = xFormatsSupplier->getNumberFormats();
			}
			catch(const Exception&)
			{
			}

			OSL_ENSURE(m_xControlNumberFormats.is(), "OFormLayerXMLExport_Impl::getControlNumberStyleExport: could not obtain my default number formats!");

			// create the exporter
			m_pControlNumberStyles = new SvXMLNumFmtExport(m_rContext, xFormatsSupplier, getControlNumberStyleNamePrefix());
		}
	}

	//---------------------------------------------------------------------
	SvXMLNumFmtExport* OFormLayerXMLExport_Impl::getControlNumberStyleExport()
	{
		ensureControlNumberStyleExport();
		return m_pControlNumberStyles;
	}

	//---------------------------------------------------------------------
	void OFormLayerXMLExport_Impl::excludeFromExport( const Reference< XControlModel > _rxControl )
	{
		Reference< XPropertySet > xProps( _rxControl, UNO_QUERY );
		OSL_ENSURE( xProps.is(), "OFormLayerXMLExport_Impl::excludeFromExport: invalid control model!" );
#if OSL_DEBUG_LEVEL > 0
		::std::pair< PropertySetBag::iterator, bool > aPos =
#endif
		m_aIgnoreList.insert( xProps );
		OSL_ENSURE( aPos.second, "OFormLayerXMLExport_Impl::excludeFromExport: element already exists in the ignore list!" );
	}

//.........................................................................
}	// namespace xmloff
//.........................................................................


