blob: 5d717b96b1bbcf9e9df1cab55b5fa847d6a8de6f [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"
/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil -*- */
#include <tools/debug.hxx>
#include <sfx2/objsh.hxx>
#include <sfx2/app.hxx>
#include <basic/basmgr.hxx>
#include <basic/sbmod.hxx>
#include <svx/svxerr.hxx>
#include <filter/msfilter/svxmsbas.hxx>
#include <msvbasic.hxx>
#include <filter/msfilter/msocximex.hxx>
#include <sot/storinfo.hxx>
#include <comphelper/processfactory.hxx>
#include <com/sun/star/beans/XPropertySet.hpp>
#include <com/sun/star/awt/Size.hpp>
#include <com/sun/star/awt/XControlModel.hpp>
using namespace com::sun::star::beans;
using namespace com::sun::star::io;
using namespace com::sun::star::awt;
#include <comphelper/storagehelper.hxx>
#include <com/sun/star/container/XNameContainer.hpp>
#include <com/sun/star/script/XLibraryContainer.hpp>
#include <com/sun/star/script/ModuleInfo.hpp>
#include <com/sun/star/script/ModuleType.hpp>
#include <com/sun/star/script/vba/XVBACompatibility.hpp>
#include <com/sun/star/script/vba/XVBAModuleInfo.hpp>
using namespace com::sun::star::container;
using namespace com::sun::star::script;
using namespace com::sun::star::uno;
using namespace com::sun::star::lang;
using namespace com::sun::star;
using rtl::OUString;
static ::rtl::OUString sVBAOption( RTL_CONSTASCII_USTRINGPARAM( "Option VBASupport 1\n" ) );
int SvxImportMSVBasic::Import( const String& rStorageName,
const String &rSubStorageName,
sal_Bool bAsComment, sal_Bool bStripped )
{
std::vector< String > codeNames;
return Import( rStorageName, rSubStorageName, codeNames, bAsComment, bStripped );
}
int SvxImportMSVBasic::Import( const String& rStorageName,
const String &rSubStorageName,
const std::vector< String >& codeNames,
sal_Bool bAsComment, sal_Bool bStripped )
{
int nRet = 0;
if( bImport && ImportCode_Impl( rStorageName, rSubStorageName, codeNames,
bAsComment, bStripped ))
nRet |= 1;
if (bImport)
ImportForms_Impl(rStorageName, rSubStorageName);
if( bCopy && CopyStorage_Impl( rStorageName, rSubStorageName ))
nRet |= 2;
return nRet;
}
bool SvxImportMSVBasic::ImportForms_Impl(const String& rStorageName,
const String& rSubStorageName)
{
SvStorageRef xVBAStg(xRoot->OpenSotStorage(rStorageName,
STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL));
if (!xVBAStg.Is() || xVBAStg->GetError())
return false;
std::vector<String> aUserForms;
SvStorageInfoList aContents;
xVBAStg->FillInfoList(&aContents);
for (sal_uInt16 nI = 0; nI < aContents.Count(); ++nI)
{
SvStorageInfo& rInfo = aContents.GetObject(nI);
if (!rInfo.IsStream() && rInfo.GetName() != rSubStorageName)
aUserForms.push_back(rInfo.GetName());
}
if (aUserForms.empty())
return false;
bool bRet = true;
try
{
Reference<XMultiServiceFactory> xSF(comphelper::getProcessServiceFactory());
Reference<XComponentContext> xContext;
Reference<XPropertySet> xProps(xSF, UNO_QUERY);
xProps->getPropertyValue(OUString(RTL_CONSTASCII_USTRINGPARAM("DefaultContext")) ) >>= xContext;
Reference<XLibraryContainer> xLibContainer = rDocSh.GetDialogContainer();
DBG_ASSERT( xLibContainer.is(), "No BasicContainer!" );
String aLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) );
Reference<XNameContainer> xLib;
if (xLibContainer.is())
{
if( !xLibContainer->hasByName(aLibName))
xLibContainer->createLibrary(aLibName);
Any aLibAny = xLibContainer->getByName( aLibName );
aLibAny >>= xLib;
}
if(xLib.is())
{
typedef std::vector<String>::iterator myIter;
myIter aEnd = aUserForms.end();
for (myIter aIter = aUserForms.begin(); aIter != aEnd; ++aIter)
{
SvStorageRef xForm (xVBAStg->OpenSotStorage(*aIter,
STREAM_READWRITE | STREAM_NOCREATE | STREAM_SHARE_DENYALL));
if (!xForm.Is() || xForm->GetError())
continue;
SvStorageStreamRef xFrame = xForm->OpenSotStream(
String( RTL_CONSTASCII_USTRINGPARAM( "\3VBFrame" ) ),
STREAM_STD_READ | STREAM_NOCREATE);
if (!xFrame.Is() || xFrame->GetError())
continue;
SvStorageStreamRef xTypes = xForm->OpenSotStream(
String( 'f' ), STREAM_STD_READ | STREAM_NOCREATE);
if (!xTypes.Is() || xTypes->GetError())
continue;
//<UserForm Name=""><VBFrame></VBFrame>"
String sData;
String sLine;
while(xFrame->ReadByteStringLine(sLine, RTL_TEXTENCODING_MS_1252))
{
sData += sLine;
sData += '\n';
}
sData.ConvertLineEnd();
Reference<container::XNameContainer> xDialog(
xSF->createInstance(
OUString(RTL_CONSTASCII_USTRINGPARAM(
"com.sun.star.awt.UnoControlDialogModel"))), uno::UNO_QUERY);
OCX_UserForm aForm(xVBAStg, *aIter, *aIter, xDialog, xSF );
aForm.pDocSh = &rDocSh;
sal_Bool bOk = aForm.Read(xTypes);
DBG_ASSERT(bOk, "Had unexpected content, not risking this module");
if (bOk)
aForm.Import(xLib);
}
}
}
catch(...)
{
DBG_ERRORFILE( "SvxImportMSVBasic::ImportForms_Impl - any exception caught" );
//bRet = false;
}
return bRet;
}
sal_Bool SvxImportMSVBasic::CopyStorage_Impl( const String& rStorageName,
const String& rSubStorageName)
{
sal_Bool bValidStg = sal_False;
{
SvStorageRef xVBAStg( xRoot->OpenSotStorage( rStorageName,
STREAM_READWRITE | STREAM_NOCREATE |
STREAM_SHARE_DENYALL ));
if( xVBAStg.Is() && !xVBAStg->GetError() )
{
SvStorageRef xVBASubStg( xVBAStg->OpenSotStorage( rSubStorageName,
STREAM_READWRITE | STREAM_NOCREATE |
STREAM_SHARE_DENYALL ));
if( xVBASubStg.Is() && !xVBASubStg->GetError() )
{
// then we will copy these storages into the (temporary) storage of the document
bValidStg = sal_True;
}
}
}
if( bValidStg )
{
String aDstStgName( GetMSBasicStorageName() );
SotStorageRef xDst = SotStorage::OpenOLEStorage( rDocSh.GetStorage(), aDstStgName, STREAM_READWRITE | STREAM_TRUNC );
SotStorageRef xSrc = xRoot->OpenSotStorage( rStorageName, STREAM_STD_READ );
// TODO/LATER: should we commit the storage?
xSrc->CopyTo( xDst );
xDst->Commit();
ErrCode nError = xDst->GetError();
if ( nError == ERRCODE_NONE )
nError = xSrc->GetError();
if ( nError != ERRCODE_NONE )
xRoot->SetError( nError );
else
bValidStg = sal_True;
}
return bValidStg;
}
sal_Bool SvxImportMSVBasic::ImportCode_Impl( const String& rStorageName,
const String &rSubStorageName,
const std::vector< String >& codeNames,
sal_Bool bAsComment, sal_Bool bStripped )
{
sal_Bool bRet = sal_False;
VBA_Impl aVBA( *xRoot, bAsComment );
if( aVBA.Open(rStorageName,rSubStorageName) )
{
Reference<XLibraryContainer> xLibContainer = rDocSh.GetBasicContainer();
DBG_ASSERT( xLibContainer.is(), "No BasicContainer!" );
/* Set library container to VBA compatibility mode. This will create
the VBA Globals object and store it in the Basic manager of the
document. */
if( !bAsComment ) try
{
Reference< vba::XVBACompatibility >( xLibContainer, UNO_QUERY_THROW )->setVBACompatibilityMode( sal_True );
}
catch( Exception& )
{
}
sal_uInt16 nStreamCount = aVBA.GetNoStreams();
Reference<XNameContainer> xLib;
String aLibName( RTL_CONSTASCII_USTRINGPARAM( "Standard" ) );
if( xLibContainer.is() && nStreamCount )
{
if( !xLibContainer->hasByName( aLibName ) )
xLibContainer->createLibrary( aLibName );
Any aLibAny = xLibContainer->getByName( aLibName );
aLibAny >>= xLib;
}
if( xLib.is() )
{
Reference< script::vba::XVBAModuleInfo > xVBAModuleInfo( xLib, UNO_QUERY );
Reference< container::XNameAccess > xVBACodeNamedObjectAccess;
if ( !bAsComment )
{
Reference< XMultiServiceFactory> xSF(rDocSh.GetModel(), UNO_QUERY);
if ( xSF.is() )
{
try
{
xVBACodeNamedObjectAccess.set( xSF->createInstance( rtl::OUString(RTL_CONSTASCII_USTRINGPARAM( "ooo.vba.VBAObjectModuleObjectProvider"))), UNO_QUERY );
}
catch( Exception& ) { }
}
}
typedef std::hash_map< rtl::OUString, uno::Any, ::rtl::OUStringHash,
::std::equal_to< ::rtl::OUString > > NameModuleDataHash;
typedef std::hash_map< rtl::OUString, script::ModuleInfo, ::rtl::OUStringHash,
::std::equal_to< ::rtl::OUString > > NameModuleInfoHash;
NameModuleDataHash moduleData;
NameModuleInfoHash moduleInfos;
for( sal_uInt16 i=0; i<nStreamCount;i++)
{
StringArray aDecompressed = aVBA.Decompress(i);
#if 0
/* DR 2005-08-11 #124850# Do not filter special characters from module name.
Just put the original module name and let the Basic interpreter deal with
it. Needed for roundtrip...
*/
ByteString sByteBasic(aVBA.GetStreamName(i),
RTL_TEXTENCODING_ASCII_US,
(RTL_UNICODETOTEXT_FLAGS_UNDEFINED_UNDERLINE|
RTL_UNICODETOTEXT_FLAGS_INVALID_UNDERLINE |
RTL_UNICODETOTEXT_FLAGS_PRIVATE_MAPTO0 |
RTL_UNICODETOTEXT_FLAGS_NOCOMPOSITE)
);
const String sBasicModule(sByteBasic,
RTL_TEXTENCODING_ASCII_US);
#else
const String &sBasicModule = aVBA.GetStreamName( i);
#endif
/* #117718# expose information regarding type of Module
* Class, Form or plain 'ould VBA module with a REM statment
* at the top of the module. Mapping of Module Name
* to type is performed in VBA_Impl::Open() method,
* ( msvbasic.cxx ) by examining the PROJECT stream.
*/
// using name from aVBA.GetStreamName
// because the encoding of the same returned
// is the same as the encoding for the names
// that are keys in the map used by GetModuleType method
const String &sOrigVBAModName = aVBA.GetStreamName( i );
ModType mType = aVBA.GetModuleType( sOrigVBAModName );
rtl::OUString sClassRem( RTL_CONSTASCII_USTRINGPARAM( "Rem Attribute VBA_ModuleType=" ) );
rtl::OUString modeTypeComment;
switch( mType )
{
case ModuleType::CLASS:
modeTypeComment = sClassRem +
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAClassModule\n" ) );
break;
case ModuleType::FORM:
modeTypeComment = sClassRem +
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAFormModule\n" ) );
break;
case ModuleType::DOCUMENT:
modeTypeComment = sClassRem +
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBADocumentModule\n" ) );
break;
case ModuleType::NORMAL:
modeTypeComment = sClassRem +
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAModule\n" ) );
break;
case ModuleType::UNKNOWN:
modeTypeComment = sClassRem +
::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( "VBAUnknown\n" ) );
break;
default:
DBG_ERRORFILE( "SvxImportMSVBasic::ImportCode_Impl - unknown module type" );
break;
}
static ::rtl::OUString sClassOption( RTL_CONSTASCII_USTRINGPARAM( "Option ClassModule\n" ) );
if ( !bAsComment )
{
modeTypeComment += sVBAOption;
if ( mType == ModuleType::CLASS )
modeTypeComment += sClassOption;
}
String sModule(sBasicModule); //#i52606# no need to split Macros in 64KB blocks any more!
String sTemp;
if (bAsComment)
{
sTemp+=String(RTL_CONSTASCII_USTRINGPARAM( "Sub " ));
String sMunge(sModule);
//Streams can have spaces in them, but modulenames
//cannot !
sMunge.SearchAndReplaceAll(' ','_');
sTemp += sMunge;
sTemp.AppendAscii("\n");
};
::rtl::OUString aSource(sTemp);
for(sal_uLong j=0;j<aDecompressed.GetSize();j++)
{
if (bStripped)
{
String *pStr = aDecompressed.Get(j);
bool bMac = true;
xub_StrLen nBegin = pStr->Search('\x0D');
if ((STRING_NOTFOUND != nBegin) && (pStr->Len() > 1) && (pStr->GetChar(nBegin+1) == '\x0A'))
bMac = false;
const char cLineEnd = bMac ? '\x0D' : '\x0A';
const String sAttribute(String::CreateFromAscii(
bAsComment ? "Rem Attribute" : "Attribute"));
nBegin = 0;
while (STRING_NOTFOUND != (nBegin = pStr->Search(sAttribute, nBegin)))
{
if ((nBegin) && pStr->GetChar(nBegin-1) != cLineEnd)
{
// npower #i63766# Need to skip instances of Attribute
// that are NOT Attribute statements
nBegin = nBegin + sAttribute.Len();
continue;
}
xub_StrLen nEnd = pStr->Search(cLineEnd ,nBegin);
// DR #i26521# catch STRING_NOTFOUND, will loop endless otherwise
if( nEnd == STRING_NOTFOUND )
pStr->Erase();
else
pStr->Erase(nBegin, (nEnd-nBegin)+1);
}
}
if( aDecompressed.Get(j)->Len() )
{
aSource+=::rtl::OUString( *aDecompressed.Get(j) );
}
}
if (bAsComment)
{
aSource += rtl::OUString::createFromAscii("\nEnd Sub");
}
::rtl::OUString aModName( sModule );
aSource = modeTypeComment + aSource;
Any aSourceAny;
OSL_TRACE("erm %d", mType );
aSourceAny <<= aSource;
if ( !bAsComment )
{
OSL_TRACE("vba processing %d", mType );
script::ModuleInfo sModuleInfo;
sModuleInfo.ModuleType = mType;
moduleInfos[ aModName ] = sModuleInfo;
}
moduleData[ aModName ] = aSourceAny;
}
// Hack for missing codenames ( only know to happen in excel but... )
// only makes sense to do this if we are importing non-commented basic
if ( !bAsComment )
{
for ( std::vector< String >::const_iterator it = codeNames.begin(); it != codeNames.end(); ++it )
{
script::ModuleInfo sModuleInfo;
sModuleInfo.ModuleType = ModuleType::DOCUMENT;
moduleInfos[ *it ] = sModuleInfo;
moduleData[ *it ] = uno::makeAny( sVBAOption );
}
}
NameModuleDataHash::iterator it_end = moduleData.end();
for ( NameModuleDataHash::iterator it = moduleData.begin(); it != it_end; ++it )
{
NameModuleInfoHash::iterator it_info = moduleInfos.find( it->first );
if ( it_info != moduleInfos.end() )
{
ModuleInfo& sModuleInfo = it_info->second;
if ( sModuleInfo.ModuleType == ModuleType::FORM )
// hack, the module ( imo document basic should...
// know the XModel... ) but it doesn't
sModuleInfo.ModuleObject.set( rDocSh.GetModel(), UNO_QUERY );
// document modules, we should be able to access
// the api objects at this time
else if ( sModuleInfo.ModuleType == ModuleType::DOCUMENT )
{
if ( xVBACodeNamedObjectAccess.is() )
{
try
{
sModuleInfo.ModuleObject.set( xVBACodeNamedObjectAccess->getByName( it->first ), uno::UNO_QUERY );
OSL_TRACE("** Straight up creation of Module");
}
catch(uno::Exception& e)
{
OSL_TRACE("Failed to get documument object for %s", rtl::OUStringToOString( it->first, RTL_TEXTENCODING_UTF8 ).getStr() );
}
}
}
xVBAModuleInfo->insertModuleInfo( it->first, sModuleInfo );
}
if( xLib->hasByName( it->first ) )
xLib->replaceByName( it->first, it->second );
else
xLib->insertByName( it->first, it->second );
}
bRet = true;
}
}
return bRet;
}
/* vi:set tabstop=4 shiftwidth=4 expandtab: */