/**************************************************************
 * 
 * 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_sfx2.hxx"

#include "doctemplates.hxx"
#include <vos/mutex.hxx>
#include <tools/debug.hxx>
#include <tools/diagnose_ex.h>
#include <tools/urlobj.hxx>
#include <rtl/ustring.hxx>
#include <rtl/ustrbuf.hxx>
#ifndef _SV_RESARY_HXX
#include <tools/resary.hxx>
#endif
#include <vcl/svapp.hxx>
#include <vcl/wrkwin.hxx>
#include <comphelper/sequenceashashmap.hxx>
#include <unotools/pathoptions.hxx>
#include <comphelper/processfactory.hxx>
#include <comphelper/componentcontext.hxx>
#include <com/sun/star/beans/PropertyAttribute.hpp>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/beans/XPropertySetInfo.hpp>
#include <com/sun/star/beans/XPropertyContainer.hpp>
#include <com/sun/star/beans/StringPair.hpp>
#include <com/sun/star/container/XContainerQuery.hpp>
#include <com/sun/star/document/XTypeDetection.hpp>
#include <com/sun/star/document/XStandaloneDocumentInfo.hpp>
#include <com/sun/star/sdbc/XResultSet.hpp>
#include <com/sun/star/sdbc/XRow.hpp>
#include <com/sun/star/ucb/NameClash.hpp>
#include <com/sun/star/ucb/NameClashException.hpp>
#include <com/sun/star/ucb/TransferInfo.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>
#include <com/sun/star/ucb/XContentAccess.hpp>
#include <com/sun/star/frame/XModuleManager.hpp>
#include <com/sun/star/uno/Exception.hpp>

#include <svtools/templatefoldercache.hxx>
#include <unotools/configmgr.hxx>
#include <unotools/ucbhelper.hxx>

#include "sfx2/sfxresid.hxx"
#include "sfxurlrelocator.hxx"
#include "doctemplateslocal.hxx"
#include <sfx2/docfac.hxx>
#include <sfx2/docfile.hxx>
#include "doc.hrc"

//-----------------------------------------------------------------------------

//=============================================================================

#define TEMPLATE_SERVICE_NAME				"com.sun.star.frame.DocumentTemplates"
#define TEMPLATE_IMPLEMENTATION_NAME		"com.sun.star.comp.sfx2.DocumentTemplates"

#define SERVICENAME_TYPEDETECTION			"com.sun.star.document.TypeDetection"
#define SERVICENAME_DOCINFO					"com.sun.star.document.StandaloneDocumentInfo"

#define TEMPLATE_ROOT_URL		"vnd.sun.star.hier:/templates"
#define TITLE					"Title"
#define IS_FOLDER				"IsFolder"
#define IS_DOCUMENT				"IsDocument"
#define TARGET_URL				"TargetURL"
#define TEMPLATE_VERSION		"TemplateComponentVersion"
#define TEMPLATE_VERSION_VALUE	"2"
#define TYPE_FOLDER				"application/vnd.sun.star.hier-folder"
#define TYPE_LINK				"application/vnd.sun.star.hier-link"
#define TYPE_FSYS_FOLDER		"application/vnd.sun.staroffice.fsys-folder"
#define TYPE_FSYS_FILE			"application/vnd.sun.staroffice.fsys-file"

#define PROPERTY_DIRLIST		"DirectoryList"
#define PROPERTY_NEEDSUPDATE	"NeedsUpdate"
#define PROPERTY_TYPE			"TypeDescription"

#define TARGET_DIR_URL			"TargetDirURL"
#define COMMAND_DELETE			"delete"
#define COMMAND_TRANSFER		"transfer"

#define STANDARD_FOLDER			"standard"

#define	C_DELIM					';'

//=============================================================================

using namespace ::com::sun::star;
using namespace ::com::sun::star::beans;
using namespace ::com::sun::star::document;
using namespace ::com::sun::star::io;
using namespace ::com::sun::star::lang;
using namespace ::com::sun::star::sdbc;
using namespace ::com::sun::star::ucb;
using namespace ::com::sun::star::uno;
using namespace ::com::sun::star::container;
using namespace ::com::sun::star::util;

using namespace ::rtl;
using namespace ::ucbhelper;
using namespace ::comphelper;

//=============================================================================

class WaitWindow_Impl : public WorkWindow
{
    Rectangle   _aRect;
    sal_uInt16      _nTextStyle;
    String      _aText;

    public:
                     WaitWindow_Impl();
                    ~WaitWindow_Impl();
    virtual void     Paint( const Rectangle& rRect );
};

#define X_OFFSET 15
#define Y_OFFSET 15

//=============================================================================

struct NamePair_Impl
{
    OUString maShortName;
    OUString maLongName;
};

DECLARE_LIST( NameList_Impl, NamePair_Impl* )

class Updater_Impl;
class GroupList_Impl;
class DocTemplates_EntryData_Impl;
class GroupData_Impl;

//=============================================================================
#include <com/sun/star/task/XInteractionHandler.hpp>
#include <com/sun/star/ucb/XProgressHandler.hpp>
#include <com/sun/star/ucb/XCommandEnvironment.hpp>

class TplTaskEnvironment : public ::cppu::WeakImplHelper1< ucb::XCommandEnvironment >
{
	uno::Reference< task::XInteractionHandler >               m_xInteractionHandler;
	uno::Reference< ucb::XProgressHandler >                   m_xProgressHandler;

public:
	TplTaskEnvironment( const uno::Reference< task::XInteractionHandler>& rxInteractionHandler )
                                : m_xInteractionHandler( rxInteractionHandler )
                            {}

	virtual uno::Reference<task::XInteractionHandler> SAL_CALL getInteractionHandler() throw (uno::RuntimeException)
	{ return m_xInteractionHandler; }

	virtual uno::Reference<ucb::XProgressHandler> SAL_CALL    getProgressHandler() throw (uno::RuntimeException)
	{ return m_xProgressHandler; }
};

class SfxDocTplService_Impl
{
    uno::Reference< XMultiServiceFactory >           mxFactory;
    uno::Reference< XCommandEnvironment >            maCmdEnv;
    uno::Reference< XStandaloneDocumentInfo >        mxInfo;
    uno::Reference< XTypeDetection >                 mxType;

	::osl::Mutex				maMutex;
	Sequence< OUString >		maTemplateDirs;
	OUString					maRootURL;
	NameList_Impl				maNames;
	Locale						maLocale;
	Content						maRootContent;
	Updater_Impl*				mpUpdater;
	sal_Bool					mbIsInitialized : 1;
	sal_Bool					mbLocaleSet		: 1;

	SfxURLRelocator_Impl		maRelocator;

	void						init_Impl();
	void						getDefaultLocale();
	void						getDirList();
	void						readFolderList();
    sal_Bool                    needsUpdate();
	OUString					getLongName( const OUString& rShortName );
	sal_Bool					setTitleForURL( const OUString& rURL, const OUString& aTitle );
	sal_Bool					getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, sal_Bool& bDocHasTitle );

	sal_Bool					addEntry( Content& rParentFolder,
                                          const OUString& rTitle,
                                          const OUString& rTargetURL,
                                          const OUString& rType );

    sal_Bool                    createFolder( const OUString& rNewFolderURL,
                                              sal_Bool  bCreateParent,
                                              sal_Bool  bFsysFolder,
                                              Content   &rNewFolder );

	sal_Bool					CreateNewUniqueFolderWithPrefix( const ::rtl::OUString& aPath,
																const ::rtl::OUString& aPrefix,
																::rtl::OUString& aNewFolderName,
																::rtl::OUString& aNewFolderURL,
																Content& aNewFolder );
	::rtl::OUString				CreateNewUniqueFileWithPrefix( const ::rtl::OUString& aPath,
																const ::rtl::OUString& aPrefix,
																const ::rtl::OUString& aExt );

	uno::Sequence< beans::StringPair > ReadUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath );
	sal_Bool					UpdateUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																  const ::rtl::OUString& aGroupName,
																  const ::rtl::OUString& aNewFolderName );
	sal_Bool					ReplaceUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																  const ::rtl::OUString& aFsysGroupName,
																  const ::rtl::OUString& aOldGroupName,
																  const ::rtl::OUString& aNewGroupName );
	sal_Bool					RemoveUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																  const ::rtl::OUString& aGroupName );
	sal_Bool					WriteUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																const uno::Sequence< beans::StringPair >& aUINames );

	::rtl::OUString				CreateNewGroupFsys( const ::rtl::OUString& rGroupName, Content& aGroup );

    sal_Bool                    removeContent( Content& rContent );
    sal_Bool                    removeContent( const OUString& rContentURL );

    sal_Bool                    setProperty( Content& rContent,
                                             const OUString& rPropName,
                                             const Any& rPropValue );
    sal_Bool                    getProperty( Content& rContent,
                                             const OUString& rPropName,
                                             Any& rPropValue );

    void                        createFromContent( GroupList_Impl& rList,
                                                   Content &rContent,
                                                   sal_Bool bHierarchy,
												   sal_Bool bWriteableContent = sal_False );
    void                        addHierGroup( GroupList_Impl& rList,
                                              const OUString& rTitle,
                                              const OUString& rOwnURL );
    void                        addFsysGroup( GroupList_Impl& rList,
                                              const OUString& rTitle,
											  const OUString& rUITitle,
                                              const OUString& rOwnURL,
											  sal_Bool bWriteableGroup = sal_False );
    void                        removeFromHierarchy( DocTemplates_EntryData_Impl *pData );
    void                        addToHierarchy( GroupData_Impl *pGroup,
                                                DocTemplates_EntryData_Impl *pData );

    void                        removeFromHierarchy( GroupData_Impl *pGroup );
    void                        addGroupToHierarchy( GroupData_Impl *pGroup );

    void                        updateData( DocTemplates_EntryData_Impl *pData );

public:
                                 SfxDocTplService_Impl( uno::Reference< XMultiServiceFactory > xFactory );
                                ~SfxDocTplService_Impl();

    sal_Bool                    init() { if ( !mbIsInitialized ) init_Impl(); return mbIsInitialized; }
    Content                     getContent() { return maRootContent; }

    void                        setLocale( const Locale & rLocale );
    Locale                      getLocale();

	sal_Bool 					storeTemplate( const OUString& rGroupName,
                                               const OUString& rTemplateName,
                                               const uno::Reference< XSTORABLE >& rStorable );

    sal_Bool                    addTemplate( const OUString& rGroupName,
                                             const OUString& rTemplateName,
                                             const OUString& rSourceURL );
    sal_Bool                    removeTemplate( const OUString& rGroupName,
                                                const OUString& rTemplateName );
    sal_Bool                    renameTemplate( const OUString& rGroupName,
                                                const OUString& rOldName,
                                                const OUString& rNewName );

    sal_Bool                    addGroup( const OUString& rGroupName );
    sal_Bool                    removeGroup( const OUString& rGroupName );
    sal_Bool                    renameGroup( const OUString& rOldName,
                                             const OUString& rNewName );

    void                        update( sal_Bool bUpdateNow );
    void                        doUpdate();
    void                        finished() { mpUpdater = NULL; }
};

//=============================================================================

class Updater_Impl : public ::vos::OThread
{
private:
    SfxDocTplService_Impl   *mpDocTemplates;

public:
                             Updater_Impl( SfxDocTplService_Impl* pTemplates );
                            ~Updater_Impl();

    virtual void SAL_CALL   run();
    virtual void SAL_CALL   onTerminated();
};

//=============================================================================

class DocTemplates_EntryData_Impl
{
    OUString            maTitle;
    OUString            maType;
    OUString            maTargetURL;
    OUString            maHierarchyURL;

    sal_Bool            mbInHierarchy   : 1;
    sal_Bool            mbInUse         : 1;
    sal_Bool            mbUpdateType    : 1;
    sal_Bool            mbUpdateLink    : 1;

public:
                        DocTemplates_EntryData_Impl( const OUString& rTitle );

    void                setInUse() { mbInUse = sal_True; }
    void                setHierarchy( sal_Bool bInHierarchy ) { mbInHierarchy = bInHierarchy; }
    void                setUpdateLink( sal_Bool bUpdateLink ) { mbUpdateLink = bUpdateLink; }
    void                setUpdateType( sal_Bool bUpdateType ) { mbUpdateType = bUpdateType; }

    sal_Bool            getInUse() const { return mbInUse; }
    sal_Bool            getInHierarchy() const { return mbInHierarchy; }
    sal_Bool            getUpdateLink() const { return mbUpdateLink; }
    sal_Bool            getUpdateType() const { return mbUpdateType; }

    const OUString&     getHierarchyURL() const { return maHierarchyURL; }
    const OUString&     getTargetURL() const { return maTargetURL; }
    const OUString&     getTitle() const { return maTitle; }
    const OUString&     getType() const { return maType; }

    void                setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; }
    void                setTargetURL( const OUString& rURL ) { maTargetURL = rURL; }
    void                setType( const OUString& rType ) { maType = rType; }
};

//=============================================================================

class GroupData_Impl
{
    DECLARE_LIST( EntryList_Impl, DocTemplates_EntryData_Impl* )
    EntryList_Impl      maEntries;
    OUString            maTitle;
    OUString            maHierarchyURL;
    OUString            maTargetURL;
    sal_Bool            mbInUse         : 1;
    sal_Bool            mbInHierarchy   : 1;

public:
                        GroupData_Impl( const OUString& rTitle );
                        ~GroupData_Impl();

    void                setInUse() { mbInUse = sal_True; }
    void                setHierarchy( sal_Bool bInHierarchy ) { mbInHierarchy = bInHierarchy; }
    void                setHierarchyURL( const OUString& rURL ) { maHierarchyURL = rURL; }
    void                setTargetURL( const OUString& rURL ) { maTargetURL = rURL; }

    sal_Bool            getInUse() { return mbInUse; }
    sal_Bool            getInHierarchy() { return mbInHierarchy; }
    const OUString&     getHierarchyURL() const { return maHierarchyURL; }
    const OUString&     getTargetURL() const { return maTargetURL; }
    const OUString&     getTitle() const { return maTitle; }

    DocTemplates_EntryData_Impl*     addEntry( const OUString& rTitle,
                                  const OUString& rTargetURL,
                                  const OUString& rType,
                                  const OUString& rHierURL );
    sal_uIntPtr               count() { return maEntries.Count(); }
    DocTemplates_EntryData_Impl*     getEntry( sal_uIntPtr nPos ) { return maEntries.GetObject( nPos ); }
};

DECLARE_LIST( GroupList_Impl, GroupData_Impl* )

//=============================================================================
//=============================================================================
//=============================================================================

//-----------------------------------------------------------------------------
// private SfxDocTplService_Impl
//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::init_Impl()
{
    uno::Reference< lang::XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory();
    if ( xFactory.is() )
    {
		uno::Reference < task::XInteractionHandler > xInteractionHandler( xFactory->createInstance( DEFINE_CONST_UNICODE("com.sun.star.task.InteractionHandler") ), uno::UNO_QUERY );
		maCmdEnv = new TplTaskEnvironment( xInteractionHandler );
	}

    ::osl::ClearableMutexGuard aGuard( maMutex );
	sal_Bool bIsInitialized = sal_False;
	sal_Bool bNeedsUpdate   = sal_False;

    if ( !mbLocaleSet )
        getDefaultLocale();

    // convert locale to string
    OUString aLang = maLocale.Language;
    aLang += String( '-' );
    aLang += maLocale.Country;

    // set maRootContent to the root of the templates hierarchy. Create the
    // entry if necessary

    maRootURL = OUString( RTL_CONSTASCII_USTRINGPARAM( TEMPLATE_ROOT_URL ) );
    maRootURL += String( '/' );
    maRootURL += aLang;

	::rtl::OUString aTemplVersPropName( RTL_CONSTASCII_USTRINGPARAM( TEMPLATE_VERSION ) );
	::rtl::OUString aTemplVers( RTL_CONSTASCII_USTRINGPARAM( TEMPLATE_VERSION_VALUE ) );
    if ( Content::create( maRootURL, maCmdEnv, maRootContent ) )
	{
		uno::Any aValue;
		::rtl::OUString aPropValue;
		if ( getProperty( maRootContent, aTemplVersPropName, aValue )
		  && ( aValue >>= aPropValue )
		  && aPropValue.equals( aTemplVers ) )
		{
			bIsInitialized = sal_True;
		}
		else
			removeContent( maRootContent );
	}

	if ( !bIsInitialized )
    {
		if ( createFolder( maRootURL, sal_True, sal_False, maRootContent )
		  && setProperty( maRootContent, aTemplVersPropName, uno::makeAny( aTemplVers ) ) )
			bIsInitialized = sal_True;

        bNeedsUpdate = sal_True;
    }

	if ( bIsInitialized )
    {
        OUString aService( RTL_CONSTASCII_USTRINGPARAM( SERVICENAME_DOCINFO ) );
        try {
            mxInfo = uno::Reference< XStandaloneDocumentInfo > (
                mxFactory->createInstance( aService ), UNO_QUERY );
        } catch (uno::RuntimeException &) {
            OSL_ENSURE(false, "SfxDocTplService_Impl::init_Impl: "
                "cannot create DocumentProperties service");
        }

        aService = OUString( RTL_CONSTASCII_USTRINGPARAM( SERVICENAME_TYPEDETECTION ) );
        mxType = uno::Reference< XTypeDetection > ( mxFactory->createInstance( aService ), UNO_QUERY );

        getDirList();
        readFolderList();

		if ( bNeedsUpdate )
		{
            aGuard.clear();
            ::vos::OClearableGuard aSolarGuard( Application::GetSolarMutex() );

            WaitWindow_Impl* pWin = new WaitWindow_Impl();

            aSolarGuard.clear();
            ::osl::ClearableMutexGuard anotherGuard( maMutex );

			update( sal_True );

            anotherGuard.clear();
            ::vos::OGuard aSecondSolarGuard( Application::GetSolarMutex() );

            delete pWin;
        }
		else if ( needsUpdate() )
			// the UI should be shown only on the first update
			update( sal_True );
    }
    else
    {
        DBG_ERRORFILE( "init_Impl(): Could not create root" );
    }

    mbIsInitialized = bIsInitialized;
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::getDefaultLocale()
{
    if ( !mbLocaleSet )
    {
        ::osl::MutexGuard aGuard( maMutex );
        if ( !mbLocaleSet )
        {
            rtl::OUString aLocale;
            utl::ConfigManager::GetDirectConfigProperty( utl::ConfigManager::LOCALE )
                >>= aLocale;

            if ( aLocale.getLength() > 0 )
            {
                sal_Int32 nPos = aLocale.indexOf( sal_Unicode( '-' ) );
                if ( nPos != -1 )
                {
                    maLocale.Language = aLocale.copy( 0, nPos );
                    nPos = aLocale.indexOf( sal_Unicode( '_' ), nPos + 1 );
                    if ( nPos != -1 )
                    {
                        maLocale.Country
                            = aLocale.copy( maLocale.Language.getLength() + 1,
                                            nPos - maLocale.Language.getLength() - 1 );
                        maLocale.Variant
                            = aLocale.copy( nPos + 1 );
                    }
                    else
                    {
                        maLocale.Country
                            = aLocale.copy( maLocale.Language.getLength() + 1 );
                    }
                }

            }

            mbLocaleSet = sal_True;
        }
    }
}

// -----------------------------------------------------------------------
void SfxDocTplService_Impl::readFolderList()
{
    ::vos::OGuard aGuard( Application::GetSolarMutex() );

    ResStringArray  aShortNames( SfxResId( TEMPLATE_SHORT_NAMES_ARY ) );
    ResStringArray  aLongNames( SfxResId( TEMPLATE_LONG_NAMES_ARY ) );

    NamePair_Impl*  pPair;

    sal_uInt16 nCount = (sal_uInt16)( Min( aShortNames.Count(), aLongNames.Count() ) );

    for ( sal_uInt16 i=0; i<nCount; i++ )
    {
        pPair = new NamePair_Impl;
        pPair->maShortName  = aShortNames.GetString( i );
        pPair->maLongName   = aLongNames.GetString( i );

        maNames.Insert( pPair, LIST_APPEND );
    }
}

// -----------------------------------------------------------------------
OUString SfxDocTplService_Impl::getLongName( const OUString& rShortName )
{
    OUString         aRet;
    NamePair_Impl   *pPair = maNames.First();

    while ( pPair )
    {
        if ( pPair->maShortName == rShortName )
        {
            aRet = pPair->maLongName;
            break;
        }
        else
            pPair = maNames.Next();
    }

    if ( !aRet.getLength() )
        aRet = rShortName;

    return aRet;
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::getDirList()
{
    OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( PROPERTY_DIRLIST ) );
    Any      aValue;

    // Get the template dir list
	// TODO/LATER: let use service, register listener
    INetURLObject   aURL;
    String          aDirs = SvtPathOptions().GetTemplatePath();
    sal_uInt16          nCount = aDirs.GetTokenCount( C_DELIM );

    maTemplateDirs = Sequence< OUString >( nCount );

    for ( sal_uInt16 i=0; i<nCount; i++ )
    {
        aURL.SetSmartProtocol( INET_PROT_FILE );
        aURL.SetURL( aDirs.GetToken( i, C_DELIM ) );
        maTemplateDirs[i] = aURL.GetMainURL( INetURLObject::NO_DECODE );
    }

    aValue <<= maTemplateDirs;

    // Store the template dir list
    setProperty( maRootContent, aPropName, aValue );
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::needsUpdate()
{
	OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( PROPERTY_NEEDSUPDATE ) );
	sal_Bool bHasProperty = sal_False;
    sal_Bool bNeedsUpdate = sal_True;
	Any		 aValue;

	// Get the template dir list
	bHasProperty = getProperty( maRootContent, aPropName, aValue );

	if ( bHasProperty )
		aValue >>= bNeedsUpdate;

	// the old template component also checks this state, but it is initialized from this component
	// so if this componend was already updated the old component does not need such an update
	::svt::TemplateFolderCache aTempCache;
	if ( !bNeedsUpdate )
		bNeedsUpdate = aTempCache.needsUpdate();

	if ( bNeedsUpdate )
		aTempCache.storeState();

    return bNeedsUpdate;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::setTitleForURL( const OUString& rURL, const OUString& aTitle )
{
	sal_Bool bResult = sal_False;
    if ( mxInfo.is() )
    {
        try
        {
            mxInfo->loadFromURL( rURL );
            uno::Reference< XPropertySet > xPropSet( mxInfo, UNO_QUERY_THROW );
            OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
			xPropSet->setPropertyValue( aPropName, uno::makeAny( aTitle ) );
			mxInfo->storeIntoURL( rURL );
			bResult = sal_True;
		}
        catch ( Exception& )
		{
		}
    }

	return bResult;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::getTitleFromURL( const OUString& rURL, OUString& aTitle, OUString& aType, sal_Bool& bDocHasTitle )
{
	bDocHasTitle = sal_False;

    if ( mxInfo.is() )
    {
        try
        {
            mxInfo->loadFromURL( rURL );
		}
        catch ( Exception& )
		{
			// the document is not a StarOffice document
			return sal_False;
		}

        try
		{
            uno::Reference< XPropertySet > aPropSet( mxInfo, UNO_QUERY );
            if ( aPropSet.is() )
            {
                OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
                Any aValue = aPropSet->getPropertyValue( aPropName );
                aValue >>= aTitle;

                aPropName = OUString( RTL_CONSTASCII_USTRINGPARAM( "MIMEType" ) );
                aValue = aPropSet->getPropertyValue( aPropName );
                aValue >>= aType;
            }
        }
        catch ( UnknownPropertyException& ) {}
        catch ( Exception& ) {}
    }

    if ( ! aType.getLength() && mxType.is() )
    {
		::rtl::OUString aDocType = mxType->queryTypeByURL( rURL );
		if ( aDocType.getLength() )
			try
			{
				uno::Reference< container::XNameAccess > xTypeDetection( mxType, uno::UNO_QUERY_THROW );
				SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aDocType ) );
				aType = aTypeProps.getUnpackedValueOrDefault(
							::rtl::OUString::createFromAscii( "MediaType" ),
							::rtl::OUString() );
			}
			catch( uno::Exception& )
			{}
    }

    if ( ! aTitle.getLength() )
    {
        INetURLObject aURL( rURL );
        aURL.CutExtension();
        aTitle = aURL.getName( INetURLObject::LAST_SEGMENT, true,
                               INetURLObject::DECODE_WITH_CHARSET );
    }
	else
		bDocHasTitle = sal_True;

	return sal_True;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::addEntry( Content& rParentFolder,
                                          const OUString& rTitle,
                                          const OUString& rTargetURL,
                                          const OUString& rType )
{
    sal_Bool bAddedEntry = sal_False;

    INetURLObject aLinkObj( rParentFolder.getURL() );
    aLinkObj.insertName( rTitle, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    OUString aLinkURL = aLinkObj.GetMainURL( INetURLObject::NO_DECODE );

    Content aLink;

    if ( ! Content::create( aLinkURL, maCmdEnv, aLink ) )
    {
        Sequence< OUString > aNames(3);
        aNames[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
        aNames[1] = OUString( RTL_CONSTASCII_USTRINGPARAM( IS_FOLDER ) );
        aNames[2] = OUString( RTL_CONSTASCII_USTRINGPARAM( TARGET_URL ) );

        Sequence< Any > aValues(3);
        aValues[0] = makeAny( rTitle );
        aValues[1] = makeAny( sal_Bool( sal_False ) );
        aValues[2] = makeAny( rTargetURL );

        OUString aType( RTL_CONSTASCII_USTRINGPARAM( TYPE_LINK ) );
        OUString aAdditionalProp( RTL_CONSTASCII_USTRINGPARAM( PROPERTY_TYPE ) );

        try
        {
            rParentFolder.insertNewContent( aType, aNames, aValues, aLink );
            setProperty( aLink, aAdditionalProp, makeAny( rType ) );
            bAddedEntry = sal_True;
        }
        catch( Exception& )
        {}
    }
    return bAddedEntry;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::createFolder( const OUString& rNewFolderURL,
                                              sal_Bool  bCreateParent,
                                              sal_Bool  bFsysFolder,
                                              Content   &rNewFolder )
{
    Content         aParent;
    sal_Bool        bCreatedFolder = sal_False;
    INetURLObject   aParentURL( rNewFolderURL );
    OUString        aFolderName = aParentURL.getName( INetURLObject::LAST_SEGMENT, true,
                                                      INetURLObject::DECODE_WITH_CHARSET );

    // compute the parent folder url from the new folder url
    // and remove the final slash, because Content::create doesn't
    // like it
    aParentURL.removeSegment();
    if ( aParentURL.getSegmentCount() >= 1 )
        aParentURL.removeFinalSlash();

    // if the parent exists, we can continue with the creation of the
    // new folder, we have to create the parent otherwise ( as long as
    // bCreateParent is set to true )
    if ( Content::create( aParentURL.GetMainURL( INetURLObject::NO_DECODE ), maCmdEnv, aParent ) )
    {
        try
        {
            Sequence< OUString > aNames(2);
            aNames[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
            aNames[1] = OUString( RTL_CONSTASCII_USTRINGPARAM( IS_FOLDER ) );

            Sequence< Any > aValues(2);
            aValues[0] = makeAny( aFolderName );
            aValues[1] = makeAny( sal_Bool( sal_True ) );

            OUString aType;

            if ( bFsysFolder )
                aType = OUString( RTL_CONSTASCII_USTRINGPARAM( TYPE_FSYS_FOLDER ) );
            else
                aType = OUString( RTL_CONSTASCII_USTRINGPARAM( TYPE_FOLDER ) );

            aParent.insertNewContent( aType, aNames, aValues, rNewFolder );
            bCreatedFolder = sal_True;
        }
        catch( RuntimeException& )
        {
            DBG_ERRORFILE( "createFolder(): got runtime exception" );
        }
        catch( Exception& )
        {
            DBG_ERRORFILE( "createFolder(): Could not create new folder" );
        }
    }
    else if ( bCreateParent )
    {
        // if the parent doesn't exists and bCreateParent is set to true,
        // we try to create the parent and if this was successful, we
        // try to create the new folder again ( but this time, we set
        // bCreateParent to false to avoid endless recusions )
        if ( ( aParentURL.getSegmentCount() >= 1 ) &&
               createFolder( aParentURL.GetMainURL( INetURLObject::NO_DECODE ), bCreateParent, bFsysFolder, aParent ) )
        {
            bCreatedFolder = createFolder( rNewFolderURL, sal_False, bFsysFolder, rNewFolder );
        }
    }

    return bCreatedFolder;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::CreateNewUniqueFolderWithPrefix( const ::rtl::OUString& aPath,
																const ::rtl::OUString& aPrefix,
																::rtl::OUString& aNewFolderName,
																::rtl::OUString& aNewFolderURL,
																Content& aNewFolder )
{
	sal_Bool bCreated = sal_False;
	INetURLObject aDirPath( aPath );

    Content aParent;
   	if ( Content::create( aDirPath.GetMainURL( INetURLObject::NO_DECODE ), maCmdEnv, aParent ) )
   	{
		for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ )
		{
			::rtl::OUString aTryName = aPrefix;
			if ( nInd )
				aTryName += ::rtl::OUString::valueOf( nInd );

        	try
        	{
            	Sequence< OUString > aNames(2);
            	aNames[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
            	aNames[1] = OUString( RTL_CONSTASCII_USTRINGPARAM( IS_FOLDER ) );

            	Sequence< Any > aValues(2);
            	aValues[0] = makeAny( aTryName );
            	aValues[1] = makeAny( sal_Bool( sal_True ) );

            	OUString aType( RTL_CONSTASCII_USTRINGPARAM( TYPE_FSYS_FOLDER ) );

            	bCreated = aParent.insertNewContent( aType, aNames, aValues, aNewFolder );
        	}
        	catch( ucb::NameClashException& )
        	{
				// if there is already an element, retry
        	}
        	catch( Exception& )
        	{
				INetURLObject aObjPath( aDirPath );
				aObjPath.insertName( aTryName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
				// if there is already an element, retry
				// if there was another error, do not try any more
				if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::NO_DECODE ) ) )
					break;
        	}

			if ( bCreated )
			{
				aNewFolderName = aTryName;
				aNewFolderURL = aNewFolder.get()->getIdentifier()->getContentIdentifier();
				break;
			}
		}
	}

	return bCreated;
}

// -----------------------------------------------------------------------
::rtl::OUString SfxDocTplService_Impl::CreateNewUniqueFileWithPrefix( const ::rtl::OUString& aPath,
																		const ::rtl::OUString& aPrefix,
																		const ::rtl::OUString& aExt )
{
	::rtl::OUString aNewFileURL;
	INetURLObject aDirPath( aPath );

   	Content aParent;

	uno::Reference< XCommandEnvironment > aQuietEnv;
	if ( Content::create( aDirPath.GetMainURL( INetURLObject::NO_DECODE ), aQuietEnv, aParent ) )
   	{
		for ( sal_Int32 nInd = 0; nInd < 32000; nInd++ )
		{
			Content aNewFile;
			sal_Bool bCreated = sal_False;
			::rtl::OUString aTryName = aPrefix;
			if ( nInd )
				aTryName += ::rtl::OUString::valueOf( nInd );
			if ( aExt.toChar() != '.' )
				aTryName += ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "." ) );
			aTryName += aExt;

        	try
        	{
            	Sequence< OUString > aNames(2);
            	aNames[0] = OUString( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
            	aNames[1] = OUString( RTL_CONSTASCII_USTRINGPARAM( IS_DOCUMENT ) );

            	Sequence< Any > aValues(2);
            	aValues[0] = makeAny( aTryName );
            	aValues[1] = makeAny( sal_Bool( sal_True ) );

            	OUString aType( RTL_CONSTASCII_USTRINGPARAM( TYPE_FSYS_FILE ) );

            	bCreated = aParent.insertNewContent( aType, aNames, aValues, aNewFile );
        	}
        	catch( ucb::NameClashException& )
        	{
				// if there is already an element, retry
        	}
        	catch( Exception& )
        	{
				INetURLObject aObjPath( aPath );
				aObjPath.insertName( aTryName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
				// if there is already an element, retry
				// if there was another error, do not try any more
				if ( !::utl::UCBContentHelper::Exists( aObjPath.GetMainURL( INetURLObject::NO_DECODE ) ) )
					break;
        	}

			if ( bCreated )
			{
				aNewFileURL = aNewFile.get()->getIdentifier()->getContentIdentifier();
				break;
			}
		}
	}

	return aNewFileURL;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::removeContent( Content& rContent )
{
    sal_Bool bRemoved = sal_False;
    try
    {
        OUString aCmd( RTL_CONSTASCII_USTRINGPARAM( COMMAND_DELETE ) );
        Any aArg = makeAny( sal_Bool( sal_True ) );

        rContent.executeCommand( aCmd, aArg );
        bRemoved = sal_True;
    }
    catch ( RuntimeException& ) {}
    catch ( Exception& ) {}

    return bRemoved;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::removeContent( const OUString& rContentURL )
{
    Content aContent;

    if ( Content::create( rContentURL, maCmdEnv, aContent ) )
        return removeContent( aContent );
    else
        return sal_False;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::setProperty( Content& rContent,
                                             const OUString& rPropName,
                                             const Any& rPropValue )
{
    sal_Bool bPropertySet = sal_False;

    // Store the property
    try
    {
        Any aPropValue( rPropValue );
        uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();

        // check, wether or not the property exists, create it, when not
        if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
        {
            uno::Reference< XPropertyContainer > xProperties( rContent.get(), UNO_QUERY );
            if ( xProperties.is() )
            {
                try
                {
                    xProperties->addProperty( rPropName, PropertyAttribute::MAYBEVOID, rPropValue );
                }
                catch( PropertyExistException& ) {}
                catch( IllegalTypeException& ) { DBG_ERRORFILE( "IllegalTypeException" ); }
                catch( IllegalArgumentException& ) { DBG_ERRORFILE( "IllegalArgumentException" ); }
            }
        }

        // To ensure a reloctable office installation, the path to the
        // office installtion directory must never be stored directly.
        if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
        {
            OUString aValue;
            if ( rPropValue >>= aValue )
            {
                maRelocator.makeRelocatableURL( aValue );
                aPropValue = makeAny( aValue );
            }
            else
            {
                Sequence< OUString > aValues;
                if ( rPropValue >>= aValues )
                {
                    for ( sal_Int32 n = 0; n < aValues.getLength(); n++ )
                    {
                        maRelocator.makeRelocatableURL( aValues[ n ] );
                    }
                    aPropValue = makeAny( aValues );
                }
                else
                {
                    OSL_ENSURE( false, "Unsupported property value type" );
                }
            }
        }

        // now set the property

        rContent.setPropertyValue( rPropName, aPropValue );
        bPropertySet = sal_True;
    }
    catch ( RuntimeException& ) {}
    catch ( Exception& ) {}

    return bPropertySet;
}

// -----------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::getProperty( Content& rContent,
                                             const OUString& rPropName,
                                             Any& rPropValue )
{
    sal_Bool bGotProperty = sal_False;

    // Get the property
    try
    {
        uno::Reference< XPropertySetInfo > aPropInfo = rContent.getProperties();

        // check, wether or not the property exists
        if ( !aPropInfo.is() || !aPropInfo->hasPropertyByName( rPropName ) )
        {
            return sal_False;
        }

        // now get the property

        rPropValue = rContent.getPropertyValue( rPropName );

        // To ensure a reloctable office installation, the path to the
        // office installtion directory must never be stored directly.
        if ( SfxURLRelocator_Impl::propertyCanContainOfficeDir( rPropName ) )
        {
            OUString aValue;
            if ( rPropValue >>= aValue )
            {
                maRelocator.makeAbsoluteURL( aValue );
                rPropValue = makeAny( aValue );
            }
            else
            {
                Sequence< OUString > aValues;
                if ( rPropValue >>= aValues )
                {
                    for ( sal_Int32 n = 0; n < aValues.getLength(); n++ )
                    {
                        maRelocator.makeAbsoluteURL( aValues[ n ] );
                    }
                    rPropValue = makeAny( aValues );
                }
                else
                {
                    OSL_ENSURE( false, "Unsupported property value type" );
                }
            }
        }

        bGotProperty = sal_True;
    }
    catch ( RuntimeException& ) {}
    catch ( Exception& ) {}

    return bGotProperty;
}

// -----------------------------------------------------------------------
// static
bool SfxURLRelocator_Impl::propertyCanContainOfficeDir(
                                        const rtl::OUString & rPropName )
{
    // Note: TargetURL is handled by UCB itself (because it is a property
    //       with a predefined semantic). Additional Core properties introduced
    //       be a client app must be handled by the client app itself, because
    //       the UCB does not know the semantics of those properties.
    return ( rPropName.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( TARGET_DIR_URL ) ) ||
             rPropName.equalsAsciiL(
                RTL_CONSTASCII_STRINGPARAM( PROPERTY_DIRLIST ) ) );
}

//-----------------------------------------------------------------------------
// public SfxDocTplService_Impl
//-----------------------------------------------------------------------------

SfxDocTplService_Impl::SfxDocTplService_Impl( uno::Reference< XMultiServiceFactory > xFactory )
: maRelocator( xFactory )
{
    mxFactory       = xFactory;
    mpUpdater       = NULL;
    mbIsInitialized = sal_False;
    mbLocaleSet     = sal_False;
}

//-----------------------------------------------------------------------------
SfxDocTplService_Impl::~SfxDocTplService_Impl()
{
	::osl::MutexGuard aGuard( maMutex );

    if ( mpUpdater )
    {
        mpUpdater->kill();
        delete mpUpdater;
    }
}

//-----------------------------------------------------------------------------
Locale SfxDocTplService_Impl::getLocale()
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( !mbLocaleSet )
        getDefaultLocale();

    return maLocale;
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::setLocale( const Locale &rLocale )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( mbLocaleSet &&
         ( maLocale.Language != rLocale.Language ) &&
         ( maLocale.Country != rLocale.Country ) )
        mbIsInitialized = sal_False;

    maLocale    = rLocale;
    mbLocaleSet = sal_True;
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::update( sal_Bool bUpdateNow )
{
    ::osl::MutexGuard aGuard( maMutex );

    if ( bUpdateNow )
        doUpdate();
    else
    {
        mpUpdater = new Updater_Impl( this );
        mpUpdater->create();
    }
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::doUpdate()
{
    ::osl::MutexGuard aGuard( maMutex );

	OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( PROPERTY_NEEDSUPDATE ) );
	Any		 aValue;

	aValue <<= sal_True;
    setProperty( maRootContent, aPropName, aValue );

    GroupList_Impl	aGroupList;

    // get the entries from the hierarchy
    createFromContent( aGroupList, maRootContent, sal_True );

    // get the entries from the template directories
    sal_Int32   nCountDir = maTemplateDirs.getLength();
    OUString*   pDirs = maTemplateDirs.getArray();
    Content     aDirContent;

	// the last directory in the list must be writable
	sal_Bool bWriteableDirectory = sal_True;

    // the target folder might not exist, for this reason no interaction handler should be used
    uno::Reference< XCommandEnvironment > aQuietEnv;

    while ( nCountDir )
    {
        nCountDir--;
        if ( Content::create( pDirs[ nCountDir ], aQuietEnv, aDirContent ) )
        {
            createFromContent( aGroupList, aDirContent, sal_False, bWriteableDirectory );
        }

		bWriteableDirectory = sal_False;
    }

    // now check the list
    GroupData_Impl *pGroup = aGroupList.First();
    while ( pGroup )
    {
        if ( pGroup->getInUse() )
        {
            if ( pGroup->getInHierarchy() )
            {
				Content aGroup;
    			if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, aGroup ) )
        			setProperty( aGroup,
								 OUString( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) ),
								 makeAny( pGroup->getTargetURL() ) );

                sal_uIntPtr nCount = pGroup->count();
                for ( sal_uIntPtr i=0; i<nCount; i++ )
                {
                    DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i );
                    if ( ! pData->getInUse() )
                    {
                        if ( pData->getInHierarchy() )
                            removeFromHierarchy( pData ); // delete entry in hierarchy
                        else
                            addToHierarchy( pGroup, pData ); // add entry to hierarchy
                    }
                    else if ( pData->getUpdateType() ||
                              pData->getUpdateLink() )
                    {
                        updateData( pData );
                    }
                }
            }
            else
            {
                addGroupToHierarchy( pGroup ); // add group to hierarchy
            }
        }
        else
            removeFromHierarchy( pGroup ); // delete group from hierarchy

        delete pGroup;
        pGroup = aGroupList.Next();
    }

   	aValue <<= sal_False;
    setProperty( maRootContent, aPropName, aValue );
}

//-----------------------------------------------------------------------------
uno::Sequence< beans::StringPair > SfxDocTplService_Impl::ReadUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath )
{
	INetURLObject aLocObj( aUserPath );
    aLocObj.insertName( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "groupuinames.xml" ) ), false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
	Content aLocContent;

	// TODO/LATER: Use hashmap in future
	uno::Sequence< beans::StringPair > aUINames;
	if ( Content::create( aLocObj.GetMainURL( INetURLObject::NO_DECODE ), uno::Reference < ucb::XCommandEnvironment >(), aLocContent ) )
	{
		try
		{
			uno::Reference< io::XInputStream > xLocStream = aLocContent.openStream();
			if ( xLocStream.is() )
				aUINames = DocTemplLocaleHelper::ReadGroupLocalizationSequence( xLocStream, mxFactory );
		}
		catch( uno::Exception& )
		{}
	}

	return aUINames;
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::UpdateUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																  const ::rtl::OUString& aGroupName,
																  const ::rtl::OUString& aNewFolderName )
{
	uno::Sequence< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
	sal_Int32 nLen = aUINames.getLength();

	// it is possible that the name is used already, but it should be checked before
	for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
		if ( aUINames[nInd].First.equals( aNewFolderName ) )
			return sal_False;

	aUINames.realloc( ++nLen );
	aUINames[nLen-1].First = aNewFolderName;
	aUINames[nLen-1].Second = aGroupName;

	return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames );
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::ReplaceUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																  const ::rtl::OUString& aDefaultFsysGroupName,
																  const ::rtl::OUString& aOldGroupName,
																  const ::rtl::OUString& aNewGroupName )
{
	uno::Sequence< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
	sal_Int32 nLen = aUINames.getLength();

	sal_Bool bChanged = sal_False;
	for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
		if ( aUINames[nInd].Second.equals( aOldGroupName ) )
		{
			aUINames[nInd].Second = aNewGroupName;
			bChanged = sal_True;
		}

	if ( !bChanged )
	{
		aUINames.realloc( ++nLen );
		aUINames[nLen-1].First = aDefaultFsysGroupName;
		aUINames[nLen-1].Second = aNewGroupName;
	}
	return WriteUINamesForTemplateDir_Impl( aUserPath, aUINames );
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::RemoveUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																  const ::rtl::OUString& aGroupName )
{
	uno::Sequence< beans::StringPair > aUINames = ReadUINamesForTemplateDir_Impl( aUserPath );
	sal_Int32 nLen = aUINames.getLength();
	uno::Sequence< beans::StringPair > aNewUINames( nLen );
	sal_Int32 nNewLen = 0;

	sal_Bool bChanged = sal_False;
	for ( sal_Int32 nInd = 0; nInd < nLen; nInd++ )
		if ( aUINames[nInd].Second.equals( aGroupName ) )
			bChanged = sal_True;
		else
		{
			nNewLen++;
			aNewUINames[nNewLen-1].First = aUINames[nInd].First;
			aNewUINames[nNewLen-1].Second = aUINames[nInd].Second;
		}

	aNewUINames.realloc( nNewLen );

	return bChanged ? WriteUINamesForTemplateDir_Impl( aUserPath, aNewUINames ) : sal_True;
}


//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::WriteUINamesForTemplateDir_Impl( const ::rtl::OUString& aUserPath,
																const uno::Sequence< beans::StringPair >& aUINames )
{
	sal_Bool bResult = sal_False;
	try {
		uno::Reference< beans::XPropertySet > xTempFile(
				mxFactory->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.io.TempFile" ) ),
				uno::UNO_QUERY_THROW );

		::rtl::OUString aTempURL;
		uno::Any aUrl = xTempFile->getPropertyValue( ::rtl::OUString::createFromAscii( "Uri" ) );
		aUrl >>= aTempURL;

		uno::Reference< io::XStream > xStream( xTempFile, uno::UNO_QUERY_THROW );
		uno::Reference< io::XOutputStream > xOutStream = xStream->getOutputStream();
		if ( !xOutStream.is() )
			throw uno::RuntimeException();

		DocTemplLocaleHelper::WriteGroupLocalizationSequence( xOutStream, aUINames, mxFactory );
		try {
			// the SAX writer might close the stream
			xOutStream->closeOutput();
		} catch( uno::Exception& )
		{}

		Content aTargetContent( aUserPath, maCmdEnv );
		Content aSourceContent( aTempURL, maCmdEnv );
		aTargetContent.transferContent( aSourceContent,
										InsertOperation_COPY,
										::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "groupuinames.xml" ) ),
										ucb::NameClash::OVERWRITE );
		bResult = sal_True;
	}
	catch ( uno::Exception& )
	{
	}

	return bResult;
}

//-----------------------------------------------------------------------------
::rtl::OUString SfxDocTplService_Impl::CreateNewGroupFsys( const ::rtl::OUString& rGroupName, Content& aGroup )
{
	::rtl::OUString aResultURL;

	if ( maTemplateDirs.getLength() )
	{
		::rtl::OUString aTargetPath = maTemplateDirs[ maTemplateDirs.getLength() - 1 ];

		// create a new folder with the given name
		Content aNewFolder;
		::rtl::OUString aNewFolderName;

		// the Fsys name instead of GroupName should be used, the groupuinames must be added also
		if ( !CreateNewUniqueFolderWithPrefix( aTargetPath,
												rGroupName,
												aNewFolderName,
												aResultURL,
												aNewFolder )
		  && !CreateNewUniqueFolderWithPrefix( aTargetPath,
												::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserGroup" ) ),
												aNewFolderName,
												aResultURL,
												aNewFolder ) )

			return ::rtl::OUString();

		if ( !UpdateUINamesForTemplateDir_Impl( aTargetPath, rGroupName, aNewFolderName ) )
		{
			// we could not create the groupuinames for the folder, so we delete the group in the
			// the folder and return
			removeContent( aNewFolder );
			return ::rtl::OUString();
		}

		// Now set the target url for this group and we are done
		OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
		Any aValue = makeAny( aResultURL );

		if ( ! setProperty( aGroup, aPropName, aValue ) )
		{
			removeContent( aNewFolder );
			return ::rtl::OUString();
		}
	}

	return aResultURL;
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::addGroup( const OUString& rGroupName )
{
	::osl::MutexGuard aGuard( maMutex );

	// Check, wether or not there is a group with this name
	Content		 aNewGroup;
	OUString		aNewGroupURL;
	INetURLObject   aNewGroupObj( maRootURL );

	aNewGroupObj.insertName( rGroupName, false,
					  INetURLObject::LAST_SEGMENT, true,
					  INetURLObject::ENCODE_ALL );

	aNewGroupURL = aNewGroupObj.GetMainURL( INetURLObject::NO_DECODE );

	if ( Content::create( aNewGroupURL, maCmdEnv, aNewGroup ) ||
		 ! createFolder( aNewGroupURL, sal_False, sal_False, aNewGroup ) )
	{
		// if there already was a group with this name or the new group
		// could not be created, we return here
		return sal_False;
	}

	// Get the user template path entry ( new group will always
	// be added in the user template path )
	sal_Int32   nIndex;
	OUString	aUserPath;

	nIndex = maTemplateDirs.getLength();
	if ( nIndex )
		nIndex--;
	else
		return sal_False;   // We don't know where to add the group

	aUserPath = maTemplateDirs[ nIndex ];

	// create a new folder with the given name
	Content		 aNewFolder;
	OUString		aNewFolderName;
	OUString		aNewFolderURL;

	// the Fsys name instead of GroupName should be used, the groupuinames must be added also
	if ( !CreateNewUniqueFolderWithPrefix( aUserPath,
											rGroupName,
											aNewFolderName,
											aNewFolderURL,
											aNewFolder )
	  && !CreateNewUniqueFolderWithPrefix( aUserPath,
											::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserGroup" ) ),
											aNewFolderName,
											aNewFolderURL,
											aNewFolder ) )
	{
		// we could not create the folder, so we delete the group in the
		// hierarchy and return
		removeContent( aNewGroup );
		return sal_False;
	}

	if ( !UpdateUINamesForTemplateDir_Impl( aUserPath, rGroupName, aNewFolderName ) )
	{
		// we could not create the groupuinames for the folder, so we delete the group in the
		// hierarchy, the folder and return
		removeContent( aNewGroup );
		removeContent( aNewFolder );
		return sal_False;
	}

	// Now set the target url for this group and we are done
	OUString aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
	Any aValue = makeAny( aNewFolderURL );

	if ( ! setProperty( aNewGroup, aPropName, aValue ) )
	{
		removeContent( aNewGroup );
		removeContent( aNewFolder );
		return sal_False;
	}

	return sal_True;
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::removeGroup( const OUString& rGroupName )
{
	// remove all the elements that have the prefix aTargetURL
	// if the group does not have other elements remove it

	::osl::MutexGuard aGuard( maMutex );

	sal_Bool bResult = sal_False;

	// create the group url
	INetURLObject aGroupObj( maRootURL );
	aGroupObj.insertName( rGroupName, false,
					  INetURLObject::LAST_SEGMENT, true,
					  INetURLObject::ENCODE_ALL );

	// Get the target url
	Content	 aGroup;
	OUString	aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

	if ( Content::create( aGroupURL, maCmdEnv, aGroup ) )
	{
		OUString	aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
		Any		 aValue;

		OUString	aGroupTargetURL;
		if ( getProperty( aGroup, aPropName, aValue ) )
			aValue >>= aGroupTargetURL;

		if ( !aGroupTargetURL.getLength() )
			return sal_False; // nothing is allowed to be removed

		if ( !maTemplateDirs.getLength() )
			return sal_False;
		::rtl::OUString aGeneralTempPath = maTemplateDirs[ maTemplateDirs.getLength() - 1 ];

		// check that the fs location is in writeble folder and this is not a "My templates" folder
		INetURLObject aGroupParentFolder( aGroupTargetURL );
		if ( !aGroupParentFolder.removeSegment()
		  || !::utl::UCBContentHelper::IsSubPath( aGeneralTempPath,
		  											aGroupParentFolder.GetMainURL( INetURLObject::NO_DECODE ) ) )
			return sal_False;

		// now get the content of the Group
		uno::Reference< XResultSet > xResultSet;
		Sequence< OUString > aProps( 1 );

		aProps[0] = OUString::createFromAscii( TARGET_URL );

		try
		{
			sal_Bool bHasNonRemovable = sal_False;
			sal_Bool bHasShared = sal_False;

			ResultSetInclude eInclude = INCLUDE_DOCUMENTS_ONLY;
			xResultSet = aGroup.createCursor( aProps, eInclude );

			if ( xResultSet.is() )
			{
				uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
				uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );

				while ( xResultSet->next() )
				{
					OUString aTemplTargetURL( xRow->getString( 1 ) );
					OUString aHierURL = xContentAccess->queryContentIdentifierString();

					if ( ::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, aTemplTargetURL ) )
					{
						// this is a user template, and it can be removed
						if ( removeContent( aTemplTargetURL ) )
							removeContent( aHierURL );
						else
							bHasNonRemovable = sal_True;
					}
					else
						bHasShared = sal_True;
				}

				if ( !bHasNonRemovable && !bHasShared )
				{
					if ( removeContent( aGroupTargetURL )
					  || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) )
					{
						removeContent( aGroupURL );
						RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName );
						bResult = sal_True; // the operation is successful only if the whole group is removed
					}
				}
				else if ( !bHasNonRemovable )
				{
					if ( removeContent( aGroupTargetURL )
					  || !::utl::UCBContentHelper::Exists( aGroupTargetURL ) )
					{
						RemoveUINamesForTemplateDir_Impl( aGeneralTempPath, rGroupName );
						setProperty( aGroup, aPropName, uno::makeAny( ::rtl::OUString() ) );
					}
				}
			}
		}
		catch ( Exception& ) {}
	}

	return bResult;
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::renameGroup( const OUString& rOldName,
                                             const OUString& rNewName )
{
    ::osl::MutexGuard aGuard( maMutex );

    // create the group url
    Content         aGroup;
    INetURLObject   aGroupObj( maRootURL );
                    aGroupObj.insertName( rNewName, false,
                                          INetURLObject::LAST_SEGMENT, true,
                                          INetURLObject::ENCODE_ALL );
    OUString        aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    // Check, if there is a group with the new name, return false
    // if there is one.
    if ( Content::create( aGroupURL, maCmdEnv, aGroup ) )
        return sal_False;

    aGroupObj.removeSegment();
    aGroupObj.insertName( rOldName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    // When there is no group with the old name, we can't rename it
    if ( ! Content::create( aGroupURL, maCmdEnv, aGroup ) )
        return sal_False;

	OUString aGroupTargetURL;
	// there is no need to check whether target dir url is in target path, since if the target path is changed
	// the target dir url should be already generated new
	OUString	aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
	Any		 aValue;
	if ( getProperty( aGroup, aPropName, aValue ) )
		aValue >>= aGroupTargetURL;

	if ( !aGroupTargetURL.getLength() )
		return sal_False;

	if ( !maTemplateDirs.getLength() )
		return sal_False;
	::rtl::OUString aGeneralTempPath = maTemplateDirs[ maTemplateDirs.getLength() - 1 ];

	// check that the fs location is in writeble folder and this is not a "My templates" folder
	INetURLObject aGroupParentFolder( aGroupTargetURL );
	if ( !aGroupParentFolder.removeSegment()
	  || !::utl::UCBContentHelper::IsSubPath( aGeneralTempPath,
		  										aGroupParentFolder.GetMainURL( INetURLObject::NO_DECODE ) ) )
		return sal_False;

	// check that the group can be renamed ( all the contents must be in target location )
	sal_Bool bCanBeRenamed = sal_False;
   	try
   	{
		uno::Reference< XResultSet > xResultSet;
		Sequence< OUString > aProps( 1 );

		aProps[0] = OUString::createFromAscii( TARGET_URL );
		ResultSetInclude eInclude = INCLUDE_DOCUMENTS_ONLY;
		xResultSet = aGroup.createCursor( aProps, eInclude );

		if ( xResultSet.is() )
		{
	   		uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY_THROW );
	   		uno::Reference< XRow > xRow( xResultSet, UNO_QUERY_THROW );

	   		while ( xResultSet->next() )
	   		{
		   		OUString aTemplTargetURL( xRow->getString( 1 ) );

				if ( !::utl::UCBContentHelper::IsSubPath( aGroupTargetURL, aTemplTargetURL ) )
					throw uno::Exception();
			}

			bCanBeRenamed = sal_True;
		}
	}
	catch ( Exception& ) {}

	if ( bCanBeRenamed )
	{
    	INetURLObject aGroupTargetObj( aGroupTargetURL );
		::rtl::OUString aFsysName = aGroupTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET );

		if ( aGroupTargetObj.removeSegment()
		  && ReplaceUINamesForTemplateDir_Impl( aGroupTargetObj.GetMainURL( INetURLObject::NO_DECODE ),
		  										aFsysName,
												rOldName,
												rNewName ) )
		{
			// rename the group in the hierarchy
			OUString aTitleProp( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
			Any aTitleValue;
			aTitleValue <<= rNewName;

			return setProperty( aGroup, aTitleProp, aTitleValue );
		}
	}

	return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::storeTemplate( const OUString& rGroupName,
                                               const OUString& rTemplateName,
                                               const uno::Reference< XSTORABLE >& rStorable )
{
    ::osl::MutexGuard aGuard( maMutex );

    // Check, wether or not there is a group with this name
    // Return false, if there is no group with the given name
    Content         aGroup, aTemplate, aTargetGroup, aTemplateToRemove;
    OUString        aGroupURL, aTemplateURL, aTemplateToRemoveTargetURL;
    INetURLObject   aGroupObj( maRootURL );
	sal_Bool		bRemoveOldTemplateContent = sal_False;
    ::rtl::OUString sDocServiceName;

    aGroupObj.insertName( rGroupName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( ! Content::create( aGroupURL, maCmdEnv, aGroup ) )
        return sal_False;

	::rtl::OUString	aGroupTargetURL;
	::rtl::OUString	aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
	Any		 aValue;
	if ( getProperty( aGroup, aPropName, aValue ) )
		aValue >>= aGroupTargetURL;


    // Check, if there's a template with the given name in this group
    // the target template should be overwritten if it is imported by user
	// in case the template is installed by office installation of by an add-in
	// it can not be replaced
    aGroupObj.insertName( rTemplateName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aTemplateURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( Content::create( aTemplateURL, maCmdEnv, aTemplateToRemove ) )
	{
		OUString    aTargetTemplPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_URL ) );

		bRemoveOldTemplateContent = sal_True;
		if ( getProperty( aTemplateToRemove, aTargetTemplPropName, aValue ) )
			aValue >>= aTemplateToRemoveTargetURL;

		if ( !aGroupTargetURL.getLength() || !maTemplateDirs.getLength()
		  || (aTemplateToRemoveTargetURL.getLength() && !::utl::UCBContentHelper::IsSubPath( maTemplateDirs[ maTemplateDirs.getLength() - 1 ], aTemplateToRemoveTargetURL )) )
        	return sal_False; // it is not allowed to remove the template
	}

	try
	{
		uno::Reference< lang::XMultiServiceFactory > xFactory = ::comphelper::getProcessServiceFactory();
		if ( !xFactory.is() )
			throw uno::RuntimeException();

		// get document service name
		uno::Reference< frame::XModuleManager > xModuleManager(
			xFactory->createInstance(
					::rtl::OUString::createFromAscii( "com.sun.star.frame.ModuleManager" ) ),
			uno::UNO_QUERY_THROW );
        sDocServiceName = xModuleManager->identify( uno::Reference< uno::XInterface >( rStorable, uno::UNO_QUERY ) );
        if ( !sDocServiceName.getLength() )
			throw uno::RuntimeException();

		// get the actual filter name
		::rtl::OUString aFilterName;

		uno::Reference< lang::XMultiServiceFactory > xConfigProvider(
				xFactory->createInstance(
					::rtl::OUString::createFromAscii( "com.sun.star.configuration.ConfigurationProvider" ) ),
				uno::UNO_QUERY_THROW );

		uno::Sequence< uno::Any > aArgs( 1 );
		beans::PropertyValue aPathProp;
		aPathProp.Name = ::rtl::OUString::createFromAscii( "nodepath" );
		aPathProp.Value <<= ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "/org.openoffice.Setup/Office/Factories/" ) );
		aArgs[0] <<= aPathProp;

		uno::Reference< container::XNameAccess > xSOFConfig(
			xConfigProvider->createInstanceWithArguments(
									::rtl::OUString::createFromAscii( "com.sun.star.configuration.ConfigurationAccess" ),
									aArgs ),
			uno::UNO_QUERY_THROW );

		uno::Reference< container::XNameAccess > xApplConfig;
        xSOFConfig->getByName( sDocServiceName ) >>= xApplConfig;
		if ( !xApplConfig.is() )
			throw uno::RuntimeException();

		xApplConfig->getByName( ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "ooSetupFactoryActualTemplateFilter" ) ) ) >>= aFilterName;
		if ( !aFilterName.getLength() )
			throw uno::RuntimeException();

		// find the related type name
		::rtl::OUString aTypeName;
		uno::Reference< container::XNameAccess > xFilterFactory(
			xFactory->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.document.FilterFactory" ) ),
			uno::UNO_QUERY_THROW );

		uno::Sequence< beans::PropertyValue > aFilterData;
		xFilterFactory->getByName( aFilterName ) >>= aFilterData;
		for ( sal_Int32 nInd = 0; nInd < aFilterData.getLength(); nInd++ )
			if ( aFilterData[nInd].Name.equalsAscii( "Type" ) )
				aFilterData[nInd].Value >>= aTypeName;

		if ( !aTypeName.getLength() )
			throw uno::RuntimeException();

		// find the mediatype and extension
		uno::Reference< container::XNameAccess > xTypeDetection	=
			mxType.is() ?
				uno::Reference< container::XNameAccess >( mxType, uno::UNO_QUERY_THROW ) :
				uno::Reference< container::XNameAccess >(
					xFactory->createInstance( ::rtl::OUString::createFromAscii( "com.sun.star.document.TypeDetection" ) ),
					uno::UNO_QUERY_THROW );

		SequenceAsHashMap aTypeProps( xTypeDetection->getByName( aTypeName ) );
		uno::Sequence< ::rtl::OUString > aAllExt =
			aTypeProps.getUnpackedValueOrDefault( ::rtl::OUString::createFromAscii( "Extensions" ), Sequence< ::rtl::OUString >() );
		if ( !aAllExt.getLength() )
			throw uno::RuntimeException();

		::rtl::OUString aMediaType = aTypeProps.getUnpackedValueOrDefault( ::rtl::OUString::createFromAscii( "MediaType"  ), ::rtl::OUString() );
		::rtl::OUString aExt = aAllExt[0];

		if ( !aMediaType.getLength() || !aExt.getLength() )
			throw uno::RuntimeException();

		// construct destination url
		if ( !aGroupTargetURL.getLength() )
		{
			aGroupTargetURL = CreateNewGroupFsys( rGroupName, aGroup );

			if ( !aGroupTargetURL.getLength() )
				throw uno::RuntimeException();
		}

		::rtl::OUString aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, rTemplateName, aExt );
		if ( !aNewTemplateTargetURL.getLength() )
		{
			aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aGroupTargetURL, ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "UserTemplate" ) ), aExt );

			if ( !aNewTemplateTargetURL.getLength() )
				throw uno::RuntimeException();
		}

		// store template
		uno::Sequence< PropertyValue > aStoreArgs( 2 );
		aStoreArgs[0].Name = ::rtl::OUString::createFromAscii( "FilterName" );
		aStoreArgs[0].Value <<= aFilterName;
		aStoreArgs[1].Name = ::rtl::OUString::createFromAscii( "DocumentTitle" );
		aStoreArgs[1].Value <<= rTemplateName;

		::rtl::OUString aCurrentDocumentURL = rStorable->getLocation();
		if( !::utl::UCBContentHelper::EqualURLs( aNewTemplateTargetURL, rStorable->getLocation() ))
			rStorable->storeToURL( aNewTemplateTargetURL, aStoreArgs );
		else
			rStorable->store();

		// the storing was successful, now the old template with the same name can be removed if it existed
		if ( aTemplateToRemoveTargetURL.getLength() )
        {
			removeContent( aTemplateToRemoveTargetURL );

            /*
             * pb: #i79496#
             * if the old template was the standard template
             * it is necessary to change the standard template with the new file name
             */
            String sStdTmplFile = SfxObjectFactory::GetStandardTemplate( sDocServiceName );
            if ( INetURLObject( sStdTmplFile ) == INetURLObject( aTemplateToRemoveTargetURL ) )
            {
                SfxObjectFactory::SetStandardTemplate( sDocServiceName, aNewTemplateTargetURL );
            }
        }

		if ( bRemoveOldTemplateContent )
			removeContent( aTemplateToRemove );

		// add the template to hierarchy
		return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aMediaType );
	}
	catch( Exception& )
	{
		// the template was not stored
		return sal_False;
	}
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::addTemplate( const OUString& rGroupName,
                                             const OUString& rTemplateName,
                                             const OUString& rSourceURL )
{
    ::osl::MutexGuard aGuard( maMutex );

    // Check, wether or not there is a group with this name
    // Return false, if there is no group with the given name
    Content         aGroup, aTemplate, aTargetGroup;
    OUString        aGroupURL, aTemplateURL;
    INetURLObject   aGroupObj( maRootURL );

    aGroupObj.insertName( rGroupName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( ! Content::create( aGroupURL, maCmdEnv, aGroup ) )
        return sal_False;

    // Check, if there's a template with the given name in this group
    // Return false, if there already is a template
    aGroupObj.insertName( rTemplateName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aTemplateURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( Content::create( aTemplateURL, maCmdEnv, aTemplate ) )
        return sal_False;

    // get the target url of the group
    OUString    aTargetURL;
    OUString    aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
    Any         aValue;

    if ( getProperty( aGroup, aPropName, aValue ) )
        aValue >>= aTargetURL;

    if ( !aTargetURL.getLength() )
	{
		aTargetURL = CreateNewGroupFsys( rGroupName, aGroup );

		if ( !aTargetURL.getLength() )
			return sal_False;
	}

    // Get the content type
    OUString aTitle, aType, aTargetURL2, aFullName;

	// only StarOffice documents are acceptable
	sal_Bool bDocHasTitle = sal_False;
    if( !getTitleFromURL( rSourceURL, aTitle, aType, bDocHasTitle ) )
		return sal_False;

    INetURLObject   aSourceObj( rSourceURL );
	if ( rTemplateName.equals( aTitle ) )
	{
		/////////////////////////////////////////////////////
    	// addTemplate will sometimes be called just to add an entry in the
    	// hierarchy; the target URL and the source URL will be the same in
    	// this scenario
		// TODO/LATER: get rid of this old hack

    	INetURLObject   aTargetObj( aTargetURL );

    	aTargetObj.insertName( rTemplateName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    	aTargetObj.setExtension( aSourceObj.getExtension() );

    	aTargetURL2 = aTargetObj.GetMainURL( INetURLObject::NO_DECODE );

    	if ( aTargetURL2 == rSourceURL )
        	return addEntry( aGroup, rTemplateName, aTargetURL2, aType );
	}

	/////////////////////////////////////////////////////
    // copy the template into the new group (targeturl)

	INetURLObject aTmpURL( aSourceObj );
	aTmpURL.CutExtension();
	::rtl::OUString aPattern = aTmpURL.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET );

	::rtl::OUString aNewTemplateTargetURL = CreateNewUniqueFileWithPrefix( aTargetURL, aPattern, aSourceObj.getExtension() );
	INetURLObject aNewTemplateTargetObj( aNewTemplateTargetURL );
	::rtl::OUString aNewTemplateTargetName = aNewTemplateTargetObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET );
	if ( !aNewTemplateTargetURL.getLength() || !aNewTemplateTargetName.getLength() )
		return sal_False;

	// get access to source file
	Content aSourceContent;
	uno::Reference < ucb::XCommandEnvironment > xEnv;
	INetURLObject   aSourceURL( rSourceURL );
	if( ! Content::create( aSourceURL.GetMainURL( INetURLObject::NO_DECODE ), xEnv, aSourceContent ) )
		return sal_False;

	if( ! Content::create( aTargetURL, xEnv, aTargetGroup ) )
		return sal_False;

	// transfer source file
    try
    {
		if( ! aTargetGroup.transferContent( aSourceContent,
												InsertOperation_COPY,
												aNewTemplateTargetName,
												NameClash::OVERWRITE ) )
			return sal_False;

		// allow to edit the added template
		Content aResultContent;
		if ( Content::create( aNewTemplateTargetURL, xEnv, aResultContent ) )
		{
			::rtl::OUString aPropertyName( RTL_CONSTASCII_USTRINGPARAM( "IsReadOnly" ) );
			uno::Any aProperty;
			sal_Bool bReadOnly = sal_False;
			if ( getProperty( aResultContent, aPropertyName, aProperty ) && ( aProperty >>= bReadOnly ) && bReadOnly )
				setProperty( aResultContent, aPropertyName, uno::makeAny( (sal_Bool)sal_False ) );
		}
    }
    catch ( ContentCreationException& )
    { return sal_False; }
    catch ( Exception& )
    { return sal_False; }


	// either the document has title and it is the same as requested, or we have to set it
	sal_Bool bCorrectTitle = ( bDocHasTitle && aTitle.equals( rTemplateName ) );
	if ( !bCorrectTitle )
	{
		if ( !bDocHasTitle )
		{
			INetURLObject aNewTmpObj( aNewTemplateTargetObj );
			aNewTmpObj.CutExtension();
			bCorrectTitle = ( aNewTmpObj.getName( INetURLObject::LAST_SEGMENT, true, INetURLObject::DECODE_WITH_CHARSET ).equals( rTemplateName ) );
		}

		if ( !bCorrectTitle )
			bCorrectTitle = setTitleForURL( aNewTemplateTargetURL, rTemplateName );
	}

    if ( bCorrectTitle )
	{
    	// create a new entry in the hierarchy
    	return addEntry( aGroup, rTemplateName, aNewTemplateTargetURL, aType );
	}

	// TODO/LATER: The user could be notified here that the renaming has failed
    // create a new entry in the hierarchy
    addEntry( aGroup, aTitle, aNewTemplateTargetURL, aType );
	return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::removeTemplate( const OUString& rGroupName,
                                                const OUString& rTemplateName )
{
    ::osl::MutexGuard aGuard( maMutex );

    // Check, wether or not there is a group with this name
    // Return false, if there is no group with the given name
    Content         aGroup, aTemplate;
    OUString        aGroupURL, aTemplateURL;
    INetURLObject   aGroupObj( maRootURL );

    aGroupObj.insertName( rGroupName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( ! Content::create( aGroupURL, maCmdEnv, aGroup ) )
        return sal_False;

    // Check, if there's a template with the given name in this group
    // Return false, if there is no template
    aGroupObj.insertName( rTemplateName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aTemplateURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( !Content::create( aTemplateURL, maCmdEnv, aTemplate ) )
        return sal_False;

    // get the target URL from the template
    OUString    aTargetURL;
    OUString    aPropName( RTL_CONSTASCII_USTRINGPARAM( TARGET_URL ) );
    Any         aValue;

    if ( getProperty( aTemplate, aPropName, aValue ) )
        aValue >>= aTargetURL;

    // delete the target template
    if ( aTargetURL.getLength() )
    {
		if ( !maTemplateDirs.getLength()
		  || !::utl::UCBContentHelper::IsSubPath( maTemplateDirs[ maTemplateDirs.getLength() - 1 ], aTargetURL ) )
			return sal_False;

        removeContent( aTargetURL );
    }

    // delete the template entry
    return removeContent( aTemplate );
}

//-----------------------------------------------------------------------------
sal_Bool SfxDocTplService_Impl::renameTemplate( const OUString& rGroupName,
                                                const OUString& rOldName,
                                                const OUString& rNewName )
{
    ::osl::MutexGuard aGuard( maMutex );

    // Check, wether or not there is a group with this name
    // Return false, if there is no group with the given name
    Content         aGroup, aTemplate;
    OUString        aGroupURL, aTemplateURL;
    INetURLObject   aGroupObj( maRootURL );

    aGroupObj.insertName( rGroupName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aGroupURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( ! Content::create( aGroupURL, maCmdEnv, aGroup ) )
        return sal_False;

    // Check, if there's a template with the new name in this group
    // Return false, if there is one
    aGroupObj.insertName( rNewName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aTemplateURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( Content::create( aTemplateURL, maCmdEnv, aTemplate ) )
        return sal_False;

    // Check, if there's a template with the old name in this group
    // Return false, if there is no template
    aGroupObj.removeSegment();
    aGroupObj.insertName( rOldName, false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );
    aTemplateURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( !Content::create( aTemplateURL, maCmdEnv, aTemplate ) )
        return sal_False;

	OUString    aTemplateTargetURL;
	OUString    aTargetProp( RTL_CONSTASCII_USTRINGPARAM( TARGET_URL ) );
	Any         aTargetValue;

	if ( getProperty( aTemplate, aTargetProp, aTargetValue ) )
		aTargetValue >>= aTemplateTargetURL;

    if ( !setTitleForURL( aTemplateTargetURL, rNewName ) )
		return sal_False;

    // rename the template entry in the cache
    OUString    aTitleProp( RTL_CONSTASCII_USTRINGPARAM( TITLE ) );
    Any         aTitleValue;
    aTitleValue <<= rNewName;

    return setProperty( aTemplate, aTitleProp, aTitleValue );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------

SFX_IMPL_XSERVICEINFO( SfxDocTplService, TEMPLATE_SERVICE_NAME, TEMPLATE_IMPLEMENTATION_NAME )
SFX_IMPL_SINGLEFACTORY( SfxDocTplService )

//-----------------------------------------------------------------------------
SfxDocTplService::SfxDocTplService( const uno::Reference< XMultiServiceFactory >& xFactory )
{
    pImp = new SfxDocTplService_Impl( xFactory );
}

//-----------------------------------------------------------------------------

SfxDocTplService::~SfxDocTplService()
{
    delete pImp;
}

//-----------------------------------------------------------------------------
//--- XLocalizable ---
//-----------------------------------------------------------------------------

Locale SAL_CALL SfxDocTplService::getLocale()
    throw( RUNTIMEEXCEPTION )
{
    return pImp->getLocale();
}

//-----------------------------------------------------------------------------

void SAL_CALL SfxDocTplService::setLocale( const Locale & rLocale )
    throw( RUNTIMEEXCEPTION )
{
    pImp->setLocale( rLocale );
}

//-----------------------------------------------------------------------------
//--- XDocumentTemplates ---
//-----------------------------------------------------------------------------
uno::Reference< XCONTENT > SAL_CALL SfxDocTplService::getContent()
    throw( RUNTIMEEXCEPTION )
{
    if ( pImp->init() )
        return pImp->getContent().get();
    else
        return NULL;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::storeTemplate( const OUString& GroupName,
                                                   const OUString& TemplateName,
                                                   const uno::Reference< XSTORABLE >& Storable )
    throw( RUNTIMEEXCEPTION )
{
	if ( pImp->init() )
		return pImp->storeTemplate( GroupName, TemplateName, Storable );
	else
    	return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::addTemplate( const OUString& rGroupName,
                                                 const OUString& rTemplateName,
                                                 const OUString& rSourceURL )
    throw( RUNTIMEEXCEPTION )
{
    if ( pImp->init() )
        return pImp->addTemplate( rGroupName, rTemplateName, rSourceURL );
    else
        return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::removeTemplate( const OUString& rGroupName,
                                                    const OUString& rTemplateName )
    throw( RUNTIMEEXCEPTION )
{
    if ( pImp->init() )
        return pImp->removeTemplate( rGroupName, rTemplateName );
    else
        return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::renameTemplate( const OUString& rGroupName,
                                                    const OUString& rOldName,
                                                    const OUString& rNewName )
    throw( RUNTIMEEXCEPTION )
{
    if ( rOldName == rNewName )
        return sal_True;

    if ( pImp->init() )
        return pImp->renameTemplate( rGroupName, rOldName, rNewName );
    else
        return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::addGroup( const OUString& rGroupName )
    throw( RUNTIMEEXCEPTION )
{
    if ( pImp->init() )
        return pImp->addGroup( rGroupName );
    else
        return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::removeGroup( const OUString& rGroupName )
    throw( RUNTIMEEXCEPTION )
{
    if ( pImp->init() )
        return pImp->removeGroup( rGroupName );
    else
        return sal_False;
}

//-----------------------------------------------------------------------------
sal_Bool SAL_CALL SfxDocTplService::renameGroup( const OUString& rOldName,
                                                 const OUString& rNewName )
    throw( RUNTIMEEXCEPTION )
{
    if ( rOldName == rNewName )
        return sal_True;

    if ( pImp->init() )
        return pImp->renameGroup( rOldName, rNewName );
    else
        return sal_False;
}

//-----------------------------------------------------------------------------
void SAL_CALL SfxDocTplService::update()
    throw( RUNTIMEEXCEPTION )
{
    if ( pImp->init() )
        pImp->update( sal_True );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//------------------------------------------------------------------------

Updater_Impl::Updater_Impl( SfxDocTplService_Impl* pTemplates )
{
    mpDocTemplates = pTemplates;
}

//------------------------------------------------------------------------
Updater_Impl::~Updater_Impl()
{
}

//------------------------------------------------------------------------
void SAL_CALL Updater_Impl::run()
{
    mpDocTemplates->doUpdate();
}

//------------------------------------------------------------------------
void SAL_CALL Updater_Impl::onTerminated()
{
    mpDocTemplates->finished();
    delete this;
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
WaitWindow_Impl::WaitWindow_Impl()
    : WorkWindow( NULL, WB_BORDER | WB_3DLOOK )
{
    Rectangle aRect = Rectangle( 0, 0, 300, 30000 );
    _nTextStyle = TEXT_DRAW_CENTER | TEXT_DRAW_VCENTER | TEXT_DRAW_WORDBREAK | TEXT_DRAW_MULTILINE;
    _aText = String( SfxResId( RID_CNT_STR_WAITING ) );
    _aRect = GetTextRect( aRect, _aText, _nTextStyle );
    aRect = _aRect;
    aRect.Right() += 2*X_OFFSET;
    aRect.Bottom() += 2*Y_OFFSET;
    _aRect.SetPos( Point( X_OFFSET, Y_OFFSET ) );
    SetOutputSizePixel( aRect.GetSize() );
    Show();
    Update();
    Flush();
}

//-----------------------------------------------------------------------------
WaitWindow_Impl::~WaitWindow_Impl()
{
    Hide();
}

//-----------------------------------------------------------------------------
void WaitWindow_Impl::Paint( const Rectangle& /*rRect*/ )
{
    DrawText( _aRect, _aText, _nTextStyle );
}

//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::addHierGroup( GroupList_Impl& rList,
                                          const OUString& rTitle,
                                          const OUString& rOwnURL )
{
    // now get the content of the Group
    Content                 aContent;
    uno::Reference< XResultSet > xResultSet;
    Sequence< OUString >    aProps(3);

    aProps[0] = OUString::createFromAscii( TITLE );
    aProps[1] = OUString::createFromAscii( TARGET_URL );
    aProps[2] = OUString::createFromAscii( PROPERTY_TYPE );

    try
    {
        aContent = Content( rOwnURL, maCmdEnv );
        ResultSetInclude eInclude = INCLUDE_DOCUMENTS_ONLY;
        xResultSet = aContent.createCursor( aProps, eInclude );
    }
    catch ( ContentCreationException& )
    {
        DBG_ERRORFILE( "addHierGroup: ContentCreationException" );
    }
    catch ( Exception& ) {}

    if ( xResultSet.is() )
    {
        GroupData_Impl *pGroup = new GroupData_Impl( rTitle );
        pGroup->setHierarchy( sal_True );
        pGroup->setHierarchyURL( rOwnURL );
        rList.Insert( pGroup );

        uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
        uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );

        try
        {
            while ( xResultSet->next() )
            {
                sal_Bool             bUpdateType = sal_False;
                DocTemplates_EntryData_Impl  *pData;

                OUString aTitle( xRow->getString( 1 ) );
                OUString aTargetDir( xRow->getString( 2 ) );
                OUString aType( xRow->getString( 3 ) );
                OUString aHierURL = xContentAccess->queryContentIdentifierString();

                if ( !aType.getLength() )
                {
                    OUString aTmpTitle;

					sal_Bool bDocHasTitle = sal_False;
					if( !getTitleFromURL( aTargetDir, aTmpTitle, aType, bDocHasTitle ) )
					{
            			DBG_ERRORFILE( "addHierGroup(): template of alien format" );
						continue;
					}

                    if ( aType.getLength() )
                        bUpdateType = sal_True;
                }

                pData = pGroup->addEntry( aTitle, aTargetDir, aType, aHierURL );
                pData->setUpdateType( bUpdateType );
            }
        }
        catch ( Exception& ) {}
    }
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::addFsysGroup( GroupList_Impl& rList,
                                          const OUString& rTitle,
										  const OUString& rUITitle,
                                          const OUString& rOwnURL,
										  sal_Bool bWriteableGroup )
{
	::rtl::OUString aTitle;

	if ( !rUITitle.getLength() )
	{
		// reserved FS names that should not be used
    	if ( rTitle.compareToAscii( "wizard" ) == 0 )
        	return;
    	else if ( rTitle.compareToAscii( "internal" ) == 0 )
        	return;

		aTitle = getLongName( rTitle );
	}
	else
		aTitle = rUITitle;

	if ( !aTitle.getLength() )
		return;

    GroupData_Impl *pGroup = rList.First();

    while ( pGroup && pGroup->getTitle() != aTitle )
        pGroup = rList.Next();

    if ( !pGroup )
    {
        pGroup = new GroupData_Impl( aTitle );
        rList.Insert( pGroup );
    }

	if ( bWriteableGroup )
		pGroup->setTargetURL( rOwnURL );

    pGroup->setInUse();

    // now get the content of the Group
    Content                 aContent;
    uno::Reference< XResultSet > xResultSet;
    Sequence< OUString >    aProps(1);
    aProps[0] = OUString::createFromAscii( TITLE );

    try
    {
        // this method is only used during checking of the available template-folders
        // that should happen quietly
        uno::Reference< XCommandEnvironment > aQuietEnv;
        aContent = Content( rOwnURL, aQuietEnv );
        ResultSetInclude eInclude = INCLUDE_DOCUMENTS_ONLY;
        xResultSet = aContent.createCursor( aProps, eInclude );
    }
    catch ( Exception& ) {}

    if ( xResultSet.is() )
    {
        uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
        uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );

        try
        {
            while ( xResultSet->next() )
            {
                OUString aChildTitle( xRow->getString( 1 ) );
                OUString aTargetURL = xContentAccess->queryContentIdentifierString();
                OUString aType;
                OUString aHierURL;

                if ( aChildTitle.compareToAscii( "sfx.tlx" ) == 0
				  || aChildTitle.equalsAsciiL( RTL_CONSTASCII_STRINGPARAM( "groupuinames.xml" ) ) )
                    continue;

				// only StarOffice templates are accepted
				sal_Bool bDocHasTitle = sal_False;
                if( !getTitleFromURL( aTargetURL, aChildTitle, aType, bDocHasTitle ) )
					continue;

                pGroup->addEntry( aChildTitle, aTargetURL, aType, aHierURL );
            }
        }
        catch ( Exception& ) {}
    }
}

// -----------------------------------------------------------------------
void SfxDocTplService_Impl::createFromContent( GroupList_Impl& rList,
                                               Content &rContent,
                                               sal_Bool bHierarchy,
											   sal_Bool bWriteableContent )
{
    OUString aTargetURL = rContent.get()->getIdentifier()->getContentIdentifier();

    // when scanning the file system, we have to add the 'standard' group, too
    if ( ! bHierarchy )
    {
		OUString aUIStdTitle = getLongName( OUString( RTL_CONSTASCII_USTRINGPARAM( STANDARD_FOLDER ) ) );
        addFsysGroup( rList, ::rtl::OUString(), aUIStdTitle, aTargetURL, bWriteableContent );
    }

	// search for predefined UI names
    INetURLObject aLayerObj( aTargetURL );

	// TODO/LATER: Use hashmap in future
	uno::Sequence< beans::StringPair > aUINames;
	if ( !bHierarchy )
		aUINames = ReadUINamesForTemplateDir_Impl( aLayerObj.GetMainURL( INetURLObject::NO_DECODE ) );

    uno::Reference< XResultSet > xResultSet;
    Sequence< OUString > aProps(1);
    aProps[0] = OUString::createFromAscii( TITLE );

    try
    {
        ResultSetInclude eInclude = INCLUDE_FOLDERS_ONLY;
        xResultSet = rContent.createCursor( aProps, eInclude );
    }
    catch ( Exception& ) {}

    if ( xResultSet.is() )
    {
        uno::Reference< XContentAccess > xContentAccess( xResultSet, UNO_QUERY );
        uno::Reference< XRow > xRow( xResultSet, UNO_QUERY );

        try
        {
            while ( xResultSet->next() )
            {
				// TODO/LATER: clarify the encoding of the Title
                OUString aTitle( xRow->getString( 1 ) );
                OUString aTargetSubfolderURL( xContentAccess->queryContentIdentifierString() );

                if ( bHierarchy )
                    addHierGroup( rList, aTitle, aTargetSubfolderURL );
                else
				{
					::rtl::OUString aUITitle;
					for ( sal_Int32 nInd = 0; nInd < aUINames.getLength(); nInd++ )
						if ( aUINames[nInd].First.equals( aTitle ) )
						{
							aUITitle = aUINames[nInd].Second;
							break;
						}

                    addFsysGroup( rList, aTitle, aUITitle, aTargetSubfolderURL, bWriteableContent );
				}
            }
        }
        catch ( Exception& ) {}
    }
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::removeFromHierarchy( DocTemplates_EntryData_Impl *pData )
{
    Content aTemplate;

    if ( Content::create( pData->getHierarchyURL(), maCmdEnv, aTemplate ) )
    {
        removeContent( aTemplate );
    }
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::addToHierarchy( GroupData_Impl *pGroup,
                                            DocTemplates_EntryData_Impl *pData )
{
    Content aGroup, aTemplate;

    if ( ! Content::create( pGroup->getHierarchyURL(), maCmdEnv, aGroup ) )
        return;

    // Check, if there's a template with the given name in this group
    // Return if there is already a template
    INetURLObject aGroupObj( pGroup->getHierarchyURL() );

    aGroupObj.insertName( pData->getTitle(), false,
                      INetURLObject::LAST_SEGMENT, true,
                      INetURLObject::ENCODE_ALL );

    OUString aTemplateURL = aGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( Content::create( aTemplateURL, maCmdEnv, aTemplate ) )
        return;

    addEntry( aGroup, pData->getTitle(),
              pData->getTargetURL(),
              pData->getType() );
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::updateData( DocTemplates_EntryData_Impl *pData )
{
    Content aTemplate;

    if ( ! Content::create( pData->getHierarchyURL(), maCmdEnv, aTemplate ) )
        return;

    OUString aPropName;

    if ( pData->getUpdateType() )
    {
        aPropName = OUString( RTL_CONSTASCII_USTRINGPARAM( PROPERTY_TYPE ) );
        setProperty( aTemplate, aPropName, makeAny( pData->getType() ) );
    }

    if ( pData->getUpdateLink() )
    {
        aPropName = OUString( RTL_CONSTASCII_USTRINGPARAM( TARGET_URL ) );
        setProperty( aTemplate, aPropName, makeAny( pData->getTargetURL() ) );
    }
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::addGroupToHierarchy( GroupData_Impl *pGroup )
{
    OUString aAdditionalProp( RTL_CONSTASCII_USTRINGPARAM( TARGET_DIR_URL ) );
    Content aGroup;

    INetURLObject aNewGroupObj( maRootURL );
    aNewGroupObj.insertName( pGroup->getTitle(), false,
          INetURLObject::LAST_SEGMENT, true,
          INetURLObject::ENCODE_ALL );

    OUString aNewGroupURL = aNewGroupObj.GetMainURL( INetURLObject::NO_DECODE );

    if ( createFolder( aNewGroupURL, sal_False, sal_False, aGroup ) )
    {
        setProperty( aGroup, aAdditionalProp, makeAny( pGroup->getTargetURL() ) );
        pGroup->setHierarchyURL( aNewGroupURL );

        sal_uIntPtr nCount = pGroup->count();
        for ( sal_uIntPtr i=0; i<nCount; i++ )
        {
            DocTemplates_EntryData_Impl *pData = pGroup->getEntry( i );
            addToHierarchy( pGroup, pData ); // add entry to hierarchy
        }
    }
}

//-----------------------------------------------------------------------------
void SfxDocTplService_Impl::removeFromHierarchy( GroupData_Impl *pGroup )
{
    Content aGroup;

    if ( Content::create( pGroup->getHierarchyURL(), maCmdEnv, aGroup ) )
    {
        removeContent( aGroup );
    }
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
GroupData_Impl::GroupData_Impl( const OUString& rTitle )
{
    maTitle = rTitle;
    mbInUse = sal_False;
    mbInHierarchy = sal_False;
}

// -----------------------------------------------------------------------
GroupData_Impl::~GroupData_Impl()
{
    DocTemplates_EntryData_Impl *pData = maEntries.First();
    while ( pData )
    {
        delete pData;
        pData = maEntries.Next();
    }
}

// -----------------------------------------------------------------------
DocTemplates_EntryData_Impl* GroupData_Impl::addEntry( const OUString& rTitle,
                                          const OUString& rTargetURL,
                                          const OUString& rType,
                                          const OUString& rHierURL )
{
    DocTemplates_EntryData_Impl *pData = maEntries.First();

    while ( pData && pData->getTitle() != rTitle )
        pData = maEntries.Next();

    if ( !pData )
    {
        pData = new DocTemplates_EntryData_Impl( rTitle );
        pData->setTargetURL( rTargetURL );
        pData->setType( rType );
        if ( rHierURL.getLength() )
        {
            pData->setHierarchyURL( rHierURL );
            pData->setHierarchy( sal_True );
        }
        maEntries.Insert( pData );
    }
    else
    {
        if ( rHierURL.getLength() )
        {
            pData->setHierarchyURL( rHierURL );
            pData->setHierarchy( sal_True );
        }

		if ( pData->getInHierarchy() )
			pData->setInUse();

        if ( rTargetURL != pData->getTargetURL() )
        {
            pData->setTargetURL( rTargetURL );
            pData->setUpdateLink( sal_True );
        }
    }

    return pData;
}

// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
// -----------------------------------------------------------------------
DocTemplates_EntryData_Impl::DocTemplates_EntryData_Impl( const OUString& rTitle )
{
    maTitle         = rTitle;
    mbInUse         = sal_False;
    mbInHierarchy   = sal_False;
    mbUpdateType    = sal_False;
    mbUpdateLink    = sal_False;
}

// -----------------------------------------------------------------------
SfxURLRelocator_Impl::SfxURLRelocator_Impl( uno::Reference< XMultiServiceFactory > xFactory )
: mxFactory( xFactory )
{
}

// -----------------------------------------------------------------------
SfxURLRelocator_Impl::~SfxURLRelocator_Impl()
{
}

// -----------------------------------------------------------------------
void SfxURLRelocator_Impl::initOfficeInstDirs()
{
    if ( !mxOfficeInstDirs.is() )
    {
        osl::MutexGuard aGuard( maMutex );
        if ( !mxOfficeInstDirs.is() )
        {
            OSL_ENSURE( mxFactory.is(), "No service manager!" );

            uno::Reference< XComponentContext > xCtx;
            uno::Reference< XPropertySet > xPropSet( mxFactory, UNO_QUERY );
            if ( xPropSet.is() )
            {
                xPropSet->getPropertyValue(
                    rtl::OUString(
                        RTL_CONSTASCII_USTRINGPARAM( "DefaultContext" ) ) )
                >>= xCtx;
            }

            OSL_ENSURE( xCtx.is(),
                        "Unable to obtain component context from "
                        "service manager!" );

            if ( xCtx.is() )
            {
                xCtx->getValueByName(
                    rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
                        "/singletons/"
                        "com.sun.star.util.theOfficeInstallationDirectories" ) ) )
                >>= mxOfficeInstDirs;
            }

            OSL_ENSURE( mxOfficeInstDirs.is(),
                        "Unable to obtain office installation directory "
                        "singleton!" );
        }
    }
}

// -----------------------------------------------------------------------
void SfxURLRelocator_Impl::implExpandURL( ::rtl::OUString& io_url )
{
    const INetURLObject aParser( io_url );
    if ( aParser.GetProtocol() != INET_PROT_VND_SUN_STAR_EXPAND )
        return;

    io_url = aParser.GetURLPath( INetURLObject::DECODE_WITH_CHARSET );
    try
    {
        if ( !mxMacroExpander.is() )
        {
            ::comphelper::ComponentContext aContext( mxFactory );
            mxMacroExpander.set( aContext.getSingleton( "com.sun.star.util.theMacroExpander" ), UNO_QUERY_THROW );
        }
        io_url = mxMacroExpander->expandMacros( io_url );
    }
    catch( const Exception& )
    {
    	DBG_UNHANDLED_EXCEPTION();
    }
}

// -----------------------------------------------------------------------
void SfxURLRelocator_Impl::makeRelocatableURL( rtl::OUString & rURL )
{
    if ( rURL.getLength() > 0 )
    {
        initOfficeInstDirs();
        implExpandURL( rURL );
        rURL = mxOfficeInstDirs->makeRelocatableURL( rURL );
    }
}

// -----------------------------------------------------------------------
void SfxURLRelocator_Impl::makeAbsoluteURL( rtl::OUString & rURL )
{
    if ( rURL.getLength() > 0 )
    {
        initOfficeInstDirs();
        implExpandURL( rURL );
        rURL = mxOfficeInstDirs->makeAbsoluteURL( rURL );
    }
}


