/**************************************************************
 * 
 * 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_svx.hxx"
#include <svx/dataaccessdescriptor.hxx>
#include <comphelper/stl_types.hxx>
#include <comphelper/propertysetinfo.hxx>
#include <comphelper/genericpropertyset.hxx>
#include <osl/diagnose.h>
#include <com/sun/star/sdbc/XConnection.hpp>
#include <com/sun/star/ucb/XContent.hpp>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <tools/urlobj.hxx>

//........................................................................
namespace svx
{
//........................................................................

	using namespace ::com::sun::star::uno;
	using namespace ::com::sun::star::sdbc;
	using namespace ::com::sun::star::beans;
	using namespace ::com::sun::star::ucb;
	using namespace ::comphelper;

#define CONST_CHAR( propname ) propname, sizeof(propname) - 1

#ifndef SVX_LIGHT
	//====================================================================
	//= ODADescriptorImpl
	//====================================================================
	class ODADescriptorImpl
	{
	protected:
		sal_Bool					m_bSetOutOfDate			: 1;
		sal_Bool					m_bSequenceOutOfDate	: 1;

	public:
        typedef ::std::map< DataAccessDescriptorProperty, Any >     DescriptorValues;
		DescriptorValues			m_aValues;
		Sequence< PropertyValue >	m_aAsSequence;
		Reference< XPropertySet >	m_xAsSet;

        typedef ::std::map< ::rtl::OUString, PropertyMapEntry* >    MapString2PropertyEntry;

	public:
		ODADescriptorImpl();
		ODADescriptorImpl(const ODADescriptorImpl& _rSource);

		void invalidateExternRepresentations();

		void updateSequence();
		void updateSet();

		/** builds the descriptor from a property value sequence
			@return <TRUE/>
				if and only if the sequence contained valid properties only
		*/
		sal_Bool buildFrom( const Sequence< PropertyValue >& _rValues );

		/** builds the descriptor from a property set
			@return <TRUE/>
				if and only if the set contained valid properties only
		*/
		sal_Bool buildFrom( const Reference< XPropertySet >& _rValues );

	protected:
        static PropertyValue		            buildPropertyValue( const DescriptorValues::const_iterator& _rPos );
		static const MapString2PropertyEntry&   getPropertyMap( );
        static PropertyMapEntry*                getPropertyMapEntry( const DescriptorValues::const_iterator& _rPos );
	};

	//--------------------------------------------------------------------
	ODADescriptorImpl::ODADescriptorImpl()
		:m_bSetOutOfDate(sal_True)
		,m_bSequenceOutOfDate(sal_True)
	{
	}

	//--------------------------------------------------------------------
	ODADescriptorImpl::ODADescriptorImpl(const ODADescriptorImpl& _rSource)
        :m_bSetOutOfDate( _rSource.m_bSetOutOfDate )
        ,m_bSequenceOutOfDate( _rSource.m_bSequenceOutOfDate )
		,m_aValues( _rSource.m_aValues )
	{
		if (!m_bSetOutOfDate)
			m_xAsSet = _rSource.m_xAsSet;
		if (!m_bSequenceOutOfDate)
			m_aAsSequence = _rSource.m_aAsSequence;
	}

	//--------------------------------------------------------------------
	sal_Bool ODADescriptorImpl::buildFrom( const Sequence< PropertyValue >& _rValues )
	{
        const MapString2PropertyEntry& rProperties = getPropertyMap();

		sal_Bool bValidPropsOnly = sal_True;

		// loop through the sequence, and fill our m_aValues
		const PropertyValue* pValues = _rValues.getConstArray();
		const PropertyValue* pValuesEnd = pValues + _rValues.getLength();
		for (;pValues != pValuesEnd; ++pValues)
		{
            MapString2PropertyEntry::const_iterator aPropPos = rProperties.find( pValues->Name );
            if ( aPropPos != rProperties.end() )
			{
				DataAccessDescriptorProperty eProperty = (DataAccessDescriptorProperty)aPropPos->second->mnHandle;
				m_aValues[eProperty] = pValues->Value;
			}
			else
				// unknown property
				bValidPropsOnly = sal_False;
		}

		if (bValidPropsOnly)
		{
			m_aAsSequence = _rValues;
			m_bSequenceOutOfDate = sal_False;
		}
		else
			m_bSequenceOutOfDate = sal_True;

		return bValidPropsOnly;
	}

	//--------------------------------------------------------------------
	sal_Bool ODADescriptorImpl::buildFrom( const Reference< XPropertySet >& _rxValues )
	{
		Reference< XPropertySetInfo > xPropInfo;
		if (_rxValues.is())
			xPropInfo = _rxValues->getPropertySetInfo();
		if (!xPropInfo.is())
		{
			OSL_ENSURE(sal_False, "ODADescriptorImpl::buildFrom: invalid property set!");
			return sal_False;
		}

		// build a PropertyValue sequence with the current values
		Sequence< Property > aProperties = xPropInfo->getProperties();
		const Property* pProperty = aProperties.getConstArray();
		const Property* pPropertyEnd = pProperty + aProperties.getLength();

		Sequence< PropertyValue > aValues(aProperties.getLength());
		PropertyValue* pValues = aValues.getArray();

		for (;pProperty != pPropertyEnd; ++pProperty, ++pValues)
		{
			pValues->Name = pProperty->Name;
			pValues->Value = _rxValues->getPropertyValue(pProperty->Name);
		}

		sal_Bool bValidPropsOnly = buildFrom(aValues);
		if (bValidPropsOnly)
		{
			m_xAsSet = _rxValues;
			m_bSetOutOfDate = sal_False;
		}
		else
			m_bSetOutOfDate = sal_True;

		return bValidPropsOnly;
	}

	//--------------------------------------------------------------------
	void ODADescriptorImpl::invalidateExternRepresentations()
	{
		m_bSetOutOfDate = sal_True;
		m_bSequenceOutOfDate = sal_True;
	}

	//--------------------------------------------------------------------
	const ODADescriptorImpl::MapString2PropertyEntry& ODADescriptorImpl::getPropertyMap( )
	{
		// the properties we know
        static MapString2PropertyEntry s_aProperties;
        if ( s_aProperties.empty() )
        {
		    static PropertyMapEntry s_aDesriptorProperties[] =
		    {
			    { CONST_CHAR("ActiveConnection"),	daConnection,			&::getCppuType( static_cast< Reference< XConnection >* >(NULL) ),	PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("BookmarkSelection"),	daBookmarkSelection,	&::getBooleanCppuType( ),											PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("Column"),				daColumnObject,			&::getCppuType( static_cast< Reference< XPropertySet >* >(NULL) ),	PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("ColumnName"),			daColumnName,			&::getCppuType( static_cast< ::rtl::OUString* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("Command"),			daCommand,				&::getCppuType( static_cast< ::rtl::OUString* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("CommandType"),		daCommandType,			&::getCppuType( static_cast< sal_Int32* >(NULL) ),					PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("Component"),			daComponent,			&::getCppuType( static_cast< Reference< XContent >* >(NULL) ),		PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("ConnectionResource"),	daConnectionResource,	&::getCppuType( static_cast< ::rtl::OUString* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("Cursor"),				daCursor,				&::getCppuType( static_cast< Reference< XResultSet>* >(NULL) ),		PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("DataSourceName"),		daDataSource,			&::getCppuType( static_cast< ::rtl::OUString* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("DatabaseLocation"),	daDatabaseLocation,		&::getCppuType( static_cast< ::rtl::OUString* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("EscapeProcessing"),	daEscapeProcessing,		&::getBooleanCppuType( ),											PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("Filter"),				daFilter,				&::getCppuType( static_cast< ::rtl::OUString* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { CONST_CHAR("Selection"),			daSelection,			&::getCppuType( static_cast< Sequence< Any >* >(NULL) ),			PropertyAttribute::TRANSIENT, 0 },
			    { NULL, 0, 0, NULL, 0, 0 }
		    };

            PropertyMapEntry* pEntry = s_aDesriptorProperties;
            while ( pEntry->mpName )
            {
                s_aProperties[ ::rtl::OUString::createFromAscii( pEntry->mpName ) ] = pEntry;
                ++pEntry;
            }
        }

		return s_aProperties;
	}

	//--------------------------------------------------------------------
	PropertyMapEntry* ODADescriptorImpl::getPropertyMapEntry( const DescriptorValues::const_iterator& _rPos )
	{
        const MapString2PropertyEntry& rProperties = getPropertyMap();

		sal_Int32 nNeededHandle = (sal_Int32)(_rPos->first);

        for ( MapString2PropertyEntry::const_iterator loop = rProperties.begin();
              loop != rProperties.end();
              ++loop
            )
        {
			if ( nNeededHandle == loop->second->mnHandle )
				return loop->second;
		}
        throw RuntimeException();
	}

	//--------------------------------------------------------------------
	PropertyValue ODADescriptorImpl::buildPropertyValue( const DescriptorValues::const_iterator& _rPos )
	{
		// the map entry
        PropertyMapEntry* pProperty = getPropertyMapEntry( _rPos );

		// build the property value
		PropertyValue aReturn;
        aReturn.Name	= ::rtl::OUString( pProperty->mpName, pProperty->mnNameLen, RTL_TEXTENCODING_ASCII_US );
		aReturn.Handle	= pProperty->mnHandle;
		aReturn.Value	= _rPos->second;
		aReturn.State	= PropertyState_DIRECT_VALUE;

		// outta here
		return aReturn;
	}

	//--------------------------------------------------------------------
	void ODADescriptorImpl::updateSequence()
	{
		if (!m_bSequenceOutOfDate)
			return;

		m_aAsSequence.realloc(m_aValues.size());
		PropertyValue* pValue = m_aAsSequence.getArray();

		// loop through all our values
		for	(	DescriptorValues::const_iterator aLoop = m_aValues.begin();
				aLoop != m_aValues.end();
				++aLoop, ++pValue
			)
		{
			*pValue = buildPropertyValue(aLoop);
		}

		// don't need to rebuild next time
		m_bSequenceOutOfDate = sal_False;
	}

	//--------------------------------------------------------------------
	void ODADescriptorImpl::updateSet()
	{
		if (!m_bSetOutOfDate)
			return;

		// will be the current values
		Sequence< PropertyValue > aValuesToSet(m_aValues.size());
		PropertyValue* pValuesToSet = aValuesToSet.getArray();

		// build a new property set info
		PropertySetInfo* pPropSetInfo = new PropertySetInfo;

		// loop through all our values
		for	(	DescriptorValues::const_iterator aLoop = m_aValues.begin();
				aLoop != m_aValues.end();
				++aLoop, ++pValuesToSet
			)
		{
			PropertyMapEntry* pMapEntry = getPropertyMapEntry( aLoop );
			pPropSetInfo->add( pMapEntry, 1 );

			*pValuesToSet = buildPropertyValue(aLoop);
		}

		// create the generic set
		m_xAsSet = GenericPropertySet_CreateInstance( pPropSetInfo );

		// no we have the set, still need to set the current values
		const PropertyValue* pSetValues = aValuesToSet.getConstArray();
		const PropertyValue* pSetValuesEnd = pSetValues + aValuesToSet.getLength();
		for	(; pSetValues != pSetValuesEnd; ++pSetValues)
			m_xAsSet->setPropertyValue(pSetValues->Name, pSetValues->Value);

		// don't need to rebuild next time
		m_bSetOutOfDate = sal_True;
	}
#endif

	//====================================================================
	//= ODataAccessDescriptor
	//====================================================================
	//--------------------------------------------------------------------
	ODataAccessDescriptor::ODataAccessDescriptor()
#ifndef SVX_LIGHT
		:m_pImpl(new ODADescriptorImpl)
#else
		:m_pImpl(NULL)
#endif
	{
	}

	//--------------------------------------------------------------------
	ODataAccessDescriptor::ODataAccessDescriptor( const ODataAccessDescriptor& _rSource )
#ifndef SVX_LIGHT
		:m_pImpl(new ODADescriptorImpl(*_rSource.m_pImpl))
#else
		:m_pImpl(NULL)
#endif
	{
	}

	//--------------------------------------------------------------------
	const ODataAccessDescriptor& ODataAccessDescriptor::operator=(const ODataAccessDescriptor& _rSource)
	{
#ifndef SVX_LIGHT
		delete m_pImpl;
		m_pImpl = new ODADescriptorImpl(*_rSource.m_pImpl);
#else
		OSL_ENSURE(sal_False, "ODataAccessDescriptor::operator=: not available in the SVX_LIGHT version!");
#endif
		return *this;
	}

	//--------------------------------------------------------------------
	ODataAccessDescriptor::ODataAccessDescriptor( const Reference< XPropertySet >& _rValues )
#ifndef SVX_LIGHT
		:m_pImpl(new ODADescriptorImpl)
#else
		:m_pImpl(NULL)
#endif
	{
#ifndef SVX_LIGHT
		m_pImpl->buildFrom(_rValues);
#else
		OSL_ENSURE(sal_False, "ODataAccessDescriptor::ODataAccessDescriptor: not available in the SVX_LIGHT version!");
#endif
	}

	//--------------------------------------------------------------------
	ODataAccessDescriptor::ODataAccessDescriptor( const Any& _rValues )
#ifndef SVX_LIGHT
		:m_pImpl(new ODADescriptorImpl)
#else
		:m_pImpl(NULL)
#endif
	{
#ifndef SVX_LIGHT
		// check if we know the format in the Any
		Sequence< PropertyValue > aValues;
		Reference< XPropertySet > xValues;
		if ( _rValues >>= aValues )
			m_pImpl->buildFrom( aValues );
		else if ( _rValues >>= xValues )
			m_pImpl->buildFrom( xValues );
#else
		OSL_ENSURE(sal_False, "ODataAccessDescriptor::ODataAccessDescriptor: not available in the SVX_LIGHT version!");
#endif
	}

	//--------------------------------------------------------------------
	ODataAccessDescriptor::ODataAccessDescriptor( const Sequence< PropertyValue >& _rValues )
#ifndef SVX_LIGHT
		:m_pImpl(new ODADescriptorImpl)
#else
		:m_pImpl(NULL)
#endif
	{
#ifndef SVX_LIGHT
		m_pImpl->buildFrom(_rValues);
#else
		OSL_ENSURE(sal_False, "ODataAccessDescriptor::ODataAccessDescriptor: not available in the SVX_LIGHT version!");
#endif
	}

	//--------------------------------------------------------------------
	ODataAccessDescriptor::~ODataAccessDescriptor()
	{
		delete m_pImpl;
	}

	//--------------------------------------------------------------------
	void ODataAccessDescriptor::clear()
	{
#ifndef SVX_LIGHT
		m_pImpl->m_aValues.clear();
#endif
	}

	//--------------------------------------------------------------------
	void ODataAccessDescriptor::erase(DataAccessDescriptorProperty _eWhich)
	{
#ifndef SVX_LIGHT
		OSL_ENSURE(has(_eWhich), "ODataAccessDescriptor::erase: invalid call!");
		if (has(_eWhich))
			m_pImpl->m_aValues.erase(_eWhich);
#endif
	}

	//--------------------------------------------------------------------
	sal_Bool ODataAccessDescriptor::has(DataAccessDescriptorProperty _eWhich) const
	{
#ifndef SVX_LIGHT
		return m_pImpl->m_aValues.find(_eWhich) != m_pImpl->m_aValues.end();
#else
		return sal_False;
#endif
	}

	//--------------------------------------------------------------------
	const Any& ODataAccessDescriptor::operator [] ( DataAccessDescriptorProperty _eWhich ) const
	{
#ifndef SVX_LIGHT
		if (!has(_eWhich))
		{
			OSL_ENSURE(sal_False, "ODataAccessDescriptor::operator[]: invalid acessor!");
			static const Any aDummy;
			return aDummy;
		}

		return m_pImpl->m_aValues[_eWhich];
#else
		static const Any aDummy;
		return aDummy;
#endif
	}

	//--------------------------------------------------------------------
	Any& ODataAccessDescriptor::operator[] ( DataAccessDescriptorProperty _eWhich )
	{
#ifndef SVX_LIGHT
		m_pImpl->invalidateExternRepresentations();
		return m_pImpl->m_aValues[_eWhich];
#else
		static const Any aDummy;
		return aDummy;
#endif
	}

	//--------------------------------------------------------------------
	void ODataAccessDescriptor::initializeFrom(const Reference< XPropertySet >& _rxValues, sal_Bool _bClear)
	{
#ifndef SVX_LIGHT
		if (_bClear)
			clear();
		m_pImpl->buildFrom(_rxValues);
#endif
	}

	//--------------------------------------------------------------------
	void ODataAccessDescriptor::initializeFrom(const Sequence< PropertyValue >& _rValues, sal_Bool _bClear)
	{
#ifndef SVX_LIGHT
		if (_bClear)
			clear();
		m_pImpl->buildFrom(_rValues);
#endif
	}

	//--------------------------------------------------------------------
	Sequence< PropertyValue > ODataAccessDescriptor::createPropertyValueSequence()
	{
#ifndef SVX_LIGHT
		m_pImpl->updateSequence();
		return m_pImpl->m_aAsSequence;
#else
		return Sequence< PropertyValue >();
#endif
	}
	//--------------------------------------------------------------------
	Sequence< Any > ODataAccessDescriptor::createAnySequence()
	{
#ifndef SVX_LIGHT
		m_pImpl->updateSequence();
		Sequence< Any > aRet(m_pImpl->m_aAsSequence.getLength());
		const PropertyValue* pBegin = m_pImpl->m_aAsSequence.getConstArray();
		const PropertyValue* pEnd	  = pBegin + m_pImpl->m_aAsSequence.getLength();
		for(sal_Int32 i=0;pBegin != pEnd;++pBegin,++i)
			aRet[i] <<= *pBegin;
		return aRet;
#else
		return Sequence< createAnySequence >();
#endif
	}

	//--------------------------------------------------------------------
	Reference< XPropertySet > ODataAccessDescriptor::createPropertySet()
	{
#ifndef SVX_LIGHT
		m_pImpl->updateSet();
		return m_pImpl->m_xAsSet;
#else
		return Reference< XPropertySet >();
#endif
	}
	//--------------------------------------------------------------------
	::rtl::OUString ODataAccessDescriptor::getDataSource() const
	{
#ifndef SVX_LIGHT
		::rtl::OUString	sDataSourceName;
		if ( has(daDataSource) )
			(*this)[daDataSource] >>= sDataSourceName;
		else if ( has(daDatabaseLocation) )
			(*this)[daDatabaseLocation] >>= sDataSourceName;
		return sDataSourceName;
#else
		return ::rtl::OUString();
#endif
	}
	//--------------------------------------------------------------------
	void ODataAccessDescriptor::setDataSource(const ::rtl::OUString& _sDataSourceNameOrLocation)
	{
#ifndef SVX_LIGHT
		if ( _sDataSourceNameOrLocation.getLength() )
		{
			INetURLObject aURL(_sDataSourceNameOrLocation);
			(*this)[ (( aURL.GetProtocol() == INET_PROT_FILE ) ? daDatabaseLocation : daDataSource)] <<= _sDataSourceNameOrLocation;
		}
		else
			(*this)[ daDataSource ] <<= ::rtl::OUString();
#endif
	}

//........................................................................
}	// namespace svx
//........................................................................


