blob: e4380dea7107d62bc9b8c43e92952ad37724aad8 [file] [log] [blame]
/**************************************************************
*
* 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_filter.hxx"
#include <filter/msfilter/msvbahelper.hxx>
#include <basic/sbx.hxx>
#include <basic/sbstar.hxx>
#include <basic/basmgr.hxx>
#include <basic/sbmod.hxx>
#include <basic/sbmeth.hxx>
#include <com/sun/star/document/XDocumentPropertiesSupplier.hpp>
#include <com/sun/star/document/XDocumentProperties.hpp>
#include <com/sun/star/document/XDocumentInfoSupplier.hpp>
#include <com/sun/star/lang/XUnoTunnel.hpp>
#include <com/sun/star/script/ModuleType.hpp>
#include <tools/urlobj.hxx>
#include <osl/file.hxx>
#include <unotools/pathoptions.hxx>
using namespace ::com::sun::star;
namespace ooo {
namespace vba {
const static rtl::OUString sUrlPart0 = rtl::OUString::createFromAscii( "vnd.sun.star.script:");
const static rtl::OUString sUrlPart1 = rtl::OUString::createFromAscii( "?language=Basic&location=document");
String makeMacroURL( const String& sMacroName )
{
return sUrlPart0.concat( sMacroName ).concat( sUrlPart1 ) ;
}
::rtl::OUString extractMacroName( const ::rtl::OUString& rMacroUrl )
{
if( (rMacroUrl.getLength() > sUrlPart0.getLength() + sUrlPart1.getLength()) &&
rMacroUrl.match( sUrlPart0 ) &&
rMacroUrl.match( sUrlPart1, rMacroUrl.getLength() - sUrlPart1.getLength() ) )
{
return rMacroUrl.copy( sUrlPart0.getLength(), rMacroUrl.getLength() - sUrlPart0.getLength() - sUrlPart1.getLength() );
}
return ::rtl::OUString();
}
::rtl::OUString trimMacroName( const ::rtl::OUString& rMacroName )
{
// the name may contain whitespaces and may be enclosed in apostrophs
::rtl::OUString aMacroName = rMacroName.trim();
sal_Int32 nMacroLen = aMacroName.getLength();
if( (nMacroLen >= 2) && (aMacroName[ 0 ] == '\'') && (aMacroName[ nMacroLen - 1 ] == '\'') )
aMacroName = aMacroName.copy( 1, nMacroLen - 2 ).trim();
return aMacroName;
}
SfxObjectShell* findShellForUrl( const rtl::OUString& sMacroURLOrPath )
{
SfxObjectShell* pFoundShell=NULL;
SfxObjectShell* pShell = SfxObjectShell::GetFirst();
INetURLObject aObj;
aObj.SetURL( sMacroURLOrPath );
bool bIsURL = aObj.GetProtocol() != INET_PROT_NOT_VALID;
rtl::OUString aURL;
if ( bIsURL )
aURL = sMacroURLOrPath;
else
{
osl::FileBase::getFileURLFromSystemPath( sMacroURLOrPath, aURL );
aObj.SetURL( aURL );
}
OSL_TRACE("Trying to find shell for url %s", rtl::OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 ).getStr() );
while ( pShell )
{
uno::Reference< frame::XModel > xModel = pShell->GetModel();
// are we searching for a template? if so we have to cater for the
// fact that in openoffice a document opened from a template is always
// a new document :/
if ( xModel.is() )
{
OSL_TRACE("shell 0x%x has model with url %s and we look for %s", pShell
, rtl::OUStringToOString( xModel->getURL(), RTL_TEXTENCODING_UTF8 ).getStr()
, rtl::OUStringToOString( aURL, RTL_TEXTENCODING_UTF8 ).getStr()
);
::rtl::OUString aName = xModel->getURL() ;
if (0 == aName.getLength())
{
const static rtl::OUString sTitle( RTL_CONSTASCII_USTRINGPARAM("Title" ) );
uno::Reference< frame::XFrame > xFrame( xModel->getCurrentController()->getFrame(), uno::UNO_QUERY_THROW );
uno::Reference< beans::XPropertySet > xProps( xFrame, uno::UNO_QUERY_THROW );
xProps->getPropertyValue(sTitle) >>= aName;
sal_Int32 pos = 0;
aName = aName.getToken(0,'-',pos);
aName = aName.trim();
if( sMacroURLOrPath.lastIndexOf( aName ) >= 0 )
{
pFoundShell = pShell;
break;
}
}
if ( sMacroURLOrPath.endsWithIgnoreAsciiCaseAsciiL( ".dot", 4 ) )
{
uno::Reference< document::XDocumentInfoSupplier > xDocInfoSupp( xModel, uno::UNO_QUERY );
if( xDocInfoSupp.is() )
{
uno::Reference< document::XDocumentPropertiesSupplier > xDocPropSupp( xDocInfoSupp->getDocumentInfo(), uno::UNO_QUERY_THROW );
uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_QUERY_THROW );
rtl::OUString sCurrName = xDocProps->getTemplateName();
if( sMacroURLOrPath.lastIndexOf( sCurrName ) >= 0 )
{
pFoundShell = pShell;
break;
}
}
}
else
{
// sometimes just the name of the document ( without the path
// is used
bool bDocNameNoPathMatch = false;
if ( aURL.getLength() && aURL.indexOf( '/' ) == -1 )
{
sal_Int32 lastSlashIndex = xModel->getURL().lastIndexOf( '/' );
if ( lastSlashIndex > -1 )
{
bDocNameNoPathMatch = xModel->getURL().copy( lastSlashIndex + 1 ).equals( aURL );
if ( !bDocNameNoPathMatch )
{
rtl::OUString aTmpName = rtl::OUString::createFromAscii("'") + xModel->getURL().copy( lastSlashIndex + 1 ) + rtl::OUString::createFromAscii("'");
bDocNameNoPathMatch = aTmpName.equals( aURL );
}
}
}
if ( aURL.equals( xModel->getURL() ) || bDocNameNoPathMatch )
{
pFoundShell = pShell;
break;
}
}
}
pShell = SfxObjectShell::GetNext( *pShell );
}
return pFoundShell;
}
// sMod can be empty ( but we really need the library to search in )
// if sMod is empty and a macro is found then sMod is updated
// if sMod is empty, only standard modules will be searched (no class, document, form modules)
bool hasMacro( SfxObjectShell* pShell, const String& sLibrary, String& sMod, const String& sMacro )
{
bool bFound = false;
if ( sLibrary.Len() && sMacro.Len() )
{
OSL_TRACE("** Searching for %s.%s in library %s"
,rtl::OUStringToOString( sMod, RTL_TEXTENCODING_UTF8 ).getStr()
,rtl::OUStringToOString( sMacro, RTL_TEXTENCODING_UTF8 ).getStr()
,rtl::OUStringToOString( sLibrary, RTL_TEXTENCODING_UTF8 ).getStr() );
BasicManager* pBasicMgr = pShell-> GetBasicManager();
if ( pBasicMgr )
{
StarBASIC* pBasic = pBasicMgr->GetLib( sLibrary );
if ( !pBasic )
{
sal_uInt16 nId = pBasicMgr->GetLibId( sLibrary );
pBasicMgr->LoadLib( nId );
pBasic = pBasicMgr->GetLib( sLibrary );
}
if ( pBasic )
{
if ( sMod.Len() ) // we wish to find the macro is a specific module
{
SbModule* pModule = pBasic->FindModule( sMod );
if ( pModule )
{
SbxArray* pMethods = pModule->GetMethods();
if ( pMethods )
{
SbMethod* pMethod = static_cast< SbMethod* >( pMethods->Find( sMacro, SbxCLASS_METHOD ) );
if ( pMethod )
bFound = true;
}
}
}
else if( SbMethod* pMethod = dynamic_cast< SbMethod* >( pBasic->Find( sMacro, SbxCLASS_METHOD ) ) )
{
if( SbModule* pModule = pMethod->GetModule() )
{
// when searching for a macro without module name, do not search in class/document/form modules
if( pModule->GetModuleType() == script::ModuleType::NORMAL )
{
sMod = pModule->GetName();
bFound = true;
}
}
}
}
}
}
return bFound;
}
::rtl::OUString getDefaultProjectName( SfxObjectShell* pShell )
{
::rtl::OUString aPrjName;
if( BasicManager* pBasicMgr = pShell ? pShell->GetBasicManager() : 0 )
{
aPrjName = pBasicMgr->GetName();
if( aPrjName.getLength() == 0 )
aPrjName = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) );
}
return aPrjName;
}
void parseMacro( const rtl::OUString& sMacro, String& sContainer, String& sModule, String& sProcedure )
{
sal_Int32 nMacroDot = sMacro.lastIndexOf( '.' );
if ( nMacroDot != -1 )
{
sProcedure = sMacro.copy( nMacroDot + 1 );
sal_Int32 nContainerDot = sMacro.lastIndexOf( '.', nMacroDot - 1 );
if ( nContainerDot != -1 )
{
sModule = sMacro.copy( nContainerDot + 1, nMacroDot - nContainerDot - 1 );
sContainer = sMacro.copy( 0, nContainerDot );
}
else
sModule = sMacro.copy( 0, nMacroDot );
}
else
sProcedure = sMacro;
}
::rtl::OUString resolveVBAMacro( SfxObjectShell* pShell, const ::rtl::OUString& rLibName, const ::rtl::OUString& rModuleName, const ::rtl::OUString& rMacroName )
{
if( pShell )
{
::rtl::OUString aLibName = (rLibName.getLength() > 0) ? rLibName : getDefaultProjectName( pShell );
String aModuleName = rModuleName;
if( hasMacro( pShell, aLibName, aModuleName, rMacroName ) )
return ::rtl::OUStringBuffer( aLibName ).append( sal_Unicode( '.' ) ).append( aModuleName ).append( sal_Unicode( '.' ) ).append( rMacroName ).makeStringAndClear();
}
return ::rtl::OUString();
}
MacroResolvedInfo resolveVBAMacro( SfxObjectShell* pShell, const rtl::OUString& MacroName, bool bSearchGlobalTemplates )
{
if( !pShell )
return MacroResolvedInfo();
// the name may be enclosed in apostrophs
::rtl::OUString aMacroName = trimMacroName( MacroName );
// parse the macro name
sal_Int32 nDocSepIndex = aMacroName.indexOf( '!' );
if( nDocSepIndex > 0 )
{
// macro specified by document name
// find document shell for document name and call ourselves
// recursively
// assume for now that the document name is *this* document
String sDocUrlOrPath = aMacroName.copy( 0, nDocSepIndex );
aMacroName = aMacroName.copy( nDocSepIndex + 1 );
OSL_TRACE("doc search, current shell is 0x%x", pShell );
SfxObjectShell* pFoundShell = 0;
if( bSearchGlobalTemplates )
{
SvtPathOptions aPathOpt;
String aAddinPath = aPathOpt.GetAddinPath();
if( rtl::OUString( sDocUrlOrPath ).indexOf( aAddinPath ) == 0 )
pFoundShell = pShell;
}
if( !pFoundShell )
pFoundShell = findShellForUrl( sDocUrlOrPath );
OSL_TRACE("doc search, after find, found shell is 0x%x", pFoundShell );
return resolveVBAMacro( pFoundShell, aMacroName );
}
// macro is contained in 'this' document ( or code imported from a template
// where that template is a global template or perhaps the template this
// document is created from )
MacroResolvedInfo aRes( pShell );
// macro format = Container.Module.Procedure
String sContainer, sModule, sProcedure;
parseMacro( aMacroName, sContainer, sModule, sProcedure );
#if 0
// As long as service VBAProjectNameProvider isn't supported in the model, disable the createInstance call
// (the ServiceNotRegisteredException is wrongly caught in ScModelObj::createInstance)
uno::Reference< container::XNameContainer > xPrjNameCache;
uno::Reference< lang::XMultiServiceFactory> xSF( pShell->GetModel(), uno::UNO_QUERY);
if ( xSF.is() ) try
{
xPrjNameCache.set( xSF->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "ooo.vba.VBAProjectNameProvider" ) ) ), uno::UNO_QUERY );
}
catch( uno::Exception& ) // createInstance may throw
{
}
#endif
std::vector< rtl::OUString > sSearchList;
if ( sContainer.Len() > 0 )
{
// service VBAProjectNameProvider not implemented
#if 0
// get the Project associated with the Container
if ( xPrjNameCache.is() )
{
if ( xPrjNameCache->hasByName( sContainer ) )
{
rtl::OUString sProject;
xPrjNameCache->getByName( sContainer ) >>= sProject;
sContainer = sProject;
}
}
#endif
sSearchList.push_back( sContainer ); // First Lib to search
}
else
{
// Ok, if we have no Container specified then we need to search them in order, this document, template this document created from, global templates,
// get the name of Project/Library for 'this' document
rtl::OUString sThisProject = getDefaultProjectName( pShell );
sSearchList.push_back( sThisProject ); // First Lib to search
// service VBAProjectNameProvider not implemented
#if 0
if ( xPrjNameCache.is() )
{
// is this document created from a template?
uno::Reference< document::XDocumentInfoSupplier > xDocInfoSupp( pShell->GetModel(), uno::UNO_QUERY_THROW );
uno::Reference< document::XDocumentPropertiesSupplier > xDocPropSupp( xDocInfoSupp->getDocumentInfo(), uno::UNO_QUERY_THROW );
uno::Reference< document::XDocumentProperties > xDocProps( xDocPropSupp->getDocumentProperties(), uno::UNO_QUERY_THROW );
rtl::OUString sCreatedFrom = xDocProps->getTemplateURL();
if ( sCreatedFrom.getLength() )
{
INetURLObject aObj;
aObj.SetURL( sCreatedFrom );
bool bIsURL = aObj.GetProtocol() != INET_PROT_NOT_VALID;
rtl::OUString aURL;
if ( bIsURL )
aURL = sCreatedFrom;
else
{
osl::FileBase::getFileURLFromSystemPath( sCreatedFrom, aURL );
aObj.SetURL( aURL );
}
sCreatedFrom = aObj.GetLastName();
}
sal_Int32 nIndex = sCreatedFrom.lastIndexOf( '.' );
if ( nIndex != -1 )
sCreatedFrom = sCreatedFrom.copy( 0, nIndex );
rtl::OUString sPrj;
if ( sCreatedFrom.getLength() && xPrjNameCache->hasByName( sCreatedFrom ) )
{
xPrjNameCache->getByName( sCreatedFrom ) >>= sPrj;
// Make sure we don't double up with this project
if ( !sPrj.equals( sThisProject ) )
sSearchList.push_back( sPrj );
}
// get list of global template Names
uno::Sequence< rtl::OUString > sTemplateNames = xPrjNameCache->getElementNames();
sal_Int32 nLen = sTemplateNames.getLength();
for ( sal_Int32 index = 0; ( bSearchGlobalTemplates && index < nLen ); ++index )
{
if ( !sCreatedFrom.equals( sTemplateNames[ index ] ) )
{
if ( xPrjNameCache->hasByName( sTemplateNames[ index ] ) )
{
xPrjNameCache->getByName( sTemplateNames[ index ] ) >>= sPrj;
// Make sure we don't double up with this project
if ( !sPrj.equals( sThisProject ) )
sSearchList.push_back( sPrj );
}
}
}
}
#endif
}
std::vector< rtl::OUString >::iterator it_end = sSearchList.end();
for ( std::vector< rtl::OUString >::iterator it = sSearchList.begin(); !aRes.mbFound && (it != it_end); ++it )
{
aRes.mbFound = hasMacro( pShell, *it, sModule, sProcedure );
if ( aRes.mbFound )
sContainer = *it;
}
aRes.msResolvedMacro = sProcedure.Insert( '.', 0 ).Insert( sModule, 0).Insert( '.', 0 ).Insert( sContainer, 0 );
return aRes;
}
// Treat the args as possible inouts ( convertion at bottom of method )
sal_Bool executeMacro( SfxObjectShell* pShell, const String& sMacroName, uno::Sequence< uno::Any >& aArgs, uno::Any& aRet, const uno::Any& /*aCaller*/)
{
sal_Bool bRes = sal_False;
if ( !pShell )
return bRes;
rtl::OUString sUrl = makeMacroURL( sMacroName );
uno::Sequence< sal_Int16 > aOutArgsIndex;
uno::Sequence< uno::Any > aOutArgs;
try
{ ErrCode nErr( ERRCODE_BASIC_INTERNAL_ERROR );
if ( pShell )
{
nErr = pShell->CallXScript( sUrl,
aArgs, aRet, aOutArgsIndex, aOutArgs, false );
sal_Int32 nLen = aOutArgs.getLength();
// convert any out params to seem like they were inouts
if ( nLen )
{
for ( sal_Int32 index=0; index < nLen; ++index )
{
sal_Int32 nOutIndex = aOutArgsIndex[ index ];
aArgs[ nOutIndex ] = aOutArgs[ index ];
}
}
}
bRes = ( nErr == ERRCODE_NONE );
}
catch ( uno::Exception& )
{
bRes = sal_False;
}
return bRes;
}
// ============================================================================
uno::Sequence< ::rtl::OUString > VBAMacroResolver_getSupportedServiceNames()
{
uno::Sequence< ::rtl::OUString > aServiceNames( 1 );
aServiceNames[ 0 ] = ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.script.vba.VBAMacroResolver" ) );
return aServiceNames;
}
::rtl::OUString VBAMacroResolver_getImplementationName()
{
return ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "com.sun.star.comp.vba.VBAMacroResolver" ) );
}
uno::Reference< uno::XInterface > SAL_CALL VBAMacroResolver_createInstance( const uno::Reference< uno::XComponentContext >& ) throw (uno::Exception)
{
return static_cast< ::cppu::OWeakObject* >( new VBAMacroResolver );
}
// ============================================================================
VBAMacroResolver::VBAMacroResolver() :
mpObjShell( 0 )
{
}
VBAMacroResolver::~VBAMacroResolver()
{
}
// com.sun.star.lang.XServiceInfo interface -----------------------------------
::rtl::OUString SAL_CALL VBAMacroResolver::getImplementationName() throw (uno::RuntimeException)
{
return VBAMacroResolver_getImplementationName();
}
sal_Bool SAL_CALL VBAMacroResolver::supportsService( const ::rtl::OUString& rService ) throw (uno::RuntimeException)
{
uno::Sequence< ::rtl::OUString > aServices = VBAMacroResolver_getSupportedServiceNames();
const ::rtl::OUString* pArray = aServices.getConstArray();
const ::rtl::OUString* pArrayEnd = pArray + aServices.getLength();
return ::std::find( pArray, pArrayEnd, rService ) != pArrayEnd;
}
uno::Sequence< ::rtl::OUString > SAL_CALL VBAMacroResolver::getSupportedServiceNames() throw (uno::RuntimeException)
{
return VBAMacroResolver_getSupportedServiceNames();
}
// com.sun.star.lang.XInitialization interface --------------------------------
void SAL_CALL VBAMacroResolver::initialize( const uno::Sequence< uno::Any >& rArgs ) throw (uno::Exception, uno::RuntimeException)
{
OSL_ENSURE( rArgs.getLength() < 2, "VBAMacroResolver::initialize - missing arguments" );
if( rArgs.getLength() < 2 )
throw uno::RuntimeException();
// first argument: document model
mxModel.set( rArgs[ 0 ], uno::UNO_QUERY_THROW );
uno::Reference< lang::XUnoTunnel > xUnoTunnel( mxModel, uno::UNO_QUERY_THROW );
mpObjShell = reinterpret_cast< SfxObjectShell* >( xUnoTunnel->getSomething( SfxObjectShell::getUnoTunnelId() ) );
if( !mpObjShell )
throw uno::RuntimeException();
// second argument: VBA project name
if( !(rArgs[ 1 ] >>= maProjectName) || (maProjectName.getLength() == 0) )
throw uno::RuntimeException();
}
// com.sun.star.script.vba.XVBAMacroResolver interface ------------------------
::rtl::OUString SAL_CALL VBAMacroResolver::resolveVBAMacroToScriptURL( const ::rtl::OUString& rVBAMacroName ) throw (lang::IllegalArgumentException, uno::RuntimeException)
{
if( !mpObjShell )
throw uno::RuntimeException();
// the name may be enclosed in apostrophs
::rtl::OUString aMacroName = trimMacroName( rVBAMacroName );
if( aMacroName.getLength() == 0 )
throw lang::IllegalArgumentException();
// external references not supported here (syntax is "url!macroname" or "[url]!macroname" or "[url]macroname")
if( (aMacroName[ 0 ] == '[') || (aMacroName.indexOf( '!' ) >= 0) )
throw lang::IllegalArgumentException();
// check if macro name starts with project name, replace with "Standard"
// TODO: adjust this when custom VBA project name is supported
sal_Int32 nDotPos = aMacroName.indexOf( '.' );
if( (nDotPos == 0) || (nDotPos + 1 == aMacroName.getLength()) )
throw lang::IllegalArgumentException();
if( (nDotPos > 0) && aMacroName.matchIgnoreAsciiCase( maProjectName ) )
aMacroName = aMacroName.copy( nDotPos + 1 );
// try to find the macro
MacroResolvedInfo aInfo = resolveVBAMacro( mpObjShell, aMacroName, false );
if( !aInfo.mbFound )
throw lang::IllegalArgumentException();
// build and return the script URL
return makeMacroURL( aInfo.msResolvedMacro );
}
::rtl::OUString SAL_CALL VBAMacroResolver::resolveScriptURLtoVBAMacro( const ::rtl::OUString& /*rScriptURL*/ ) throw (lang::IllegalArgumentException, uno::RuntimeException)
{
OSL_ENSURE( false, "VBAMacroResolver::resolveScriptURLtoVBAMacro - not implemented" );
throw uno::RuntimeException();
}
// ============================================================================
} // namespace vba
} // namespace ooo