| /************************************************************** |
| * |
| * 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_framework.hxx" |
| #include <recording/dispatchrecorder.hxx> |
| #include <com/sun/star/frame/DispatchStatement.hpp> |
| #include <threadhelp/writeguard.hxx> |
| #include <threadhelp/readguard.hxx> |
| #include <services.h> |
| #include <vcl/svapp.hxx> |
| |
| using namespace ::com::sun::star::uno; |
| |
| namespace framework{ |
| |
| // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ... |
| #define REM_AS_COMMENT "rem " |
| |
| //***************************************************************************************************************** |
| // XInterface, XTypeProvider, XServiceInfo |
| //***************************************************************************************************************** |
| DEFINE_XINTERFACE_6( |
| DispatchRecorder, |
| OWeakObject, |
| DIRECT_INTERFACE(css::lang::XTypeProvider), |
| DIRECT_INTERFACE(css::lang::XServiceInfo), |
| DIRECT_INTERFACE(css::frame::XDispatchRecorder), |
| DIRECT_INTERFACE(css::container::XIndexReplace), |
| DIRECT_INTERFACE(css::container::XIndexAccess), |
| DIRECT_INTERFACE(css::container::XElementAccess)) |
| |
| DEFINE_XTYPEPROVIDER_6( |
| DispatchRecorder, |
| css::lang::XTypeProvider, |
| css::lang::XServiceInfo, |
| css::frame::XDispatchRecorder, |
| css::container::XIndexReplace, |
| css::container::XIndexAccess, |
| css::container::XElementAccess) |
| |
| DEFINE_XSERVICEINFO_MULTISERVICE( |
| DispatchRecorder, |
| ::cppu::OWeakObject, |
| SERVICENAME_DISPATCHRECORDER, |
| IMPLEMENTATIONNAME_DISPATCHRECORDER) |
| |
| DEFINE_INIT_SERVICE( |
| DispatchRecorder, |
| { |
| } |
| ) |
| |
| #include <typelib/typedescription.h> |
| |
| //-------------------------------------------------------------------------------------------------- |
| void flatten_struct_members( |
| ::std::vector< Any > * vec, void const * data, |
| typelib_CompoundTypeDescription * pTD ) |
| SAL_THROW( () ) |
| { |
| if (pTD->pBaseTypeDescription) |
| { |
| flatten_struct_members( vec, data, pTD->pBaseTypeDescription ); |
| } |
| for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos ) |
| { |
| vec->push_back( |
| Any( (char const *)data + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) ); |
| } |
| } |
| //================================================================================================== |
| Sequence< Any > make_seq_out_of_struct( |
| Any const & val ) |
| SAL_THROW( (RuntimeException) ) |
| { |
| Type const & type = val.getValueType(); |
| TypeClass eTypeClass = type.getTypeClass(); |
| if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass) |
| { |
| throw RuntimeException( |
| type.getTypeName() + |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("is no struct or exception!") ), |
| Reference< XInterface >() ); |
| } |
| typelib_TypeDescription * pTD = 0; |
| TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() ); |
| OSL_ASSERT( pTD ); |
| if (! pTD) |
| { |
| throw RuntimeException( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get type descr of type ") ) + |
| type.getTypeName(), |
| Reference< XInterface >() ); |
| } |
| |
| ::std::vector< Any > vec; |
| vec.reserve( ((typelib_CompoundTypeDescription *)pTD)->nMembers ); // good guess |
| flatten_struct_members( &vec, val.getValue(), (typelib_CompoundTypeDescription *)pTD ); |
| TYPELIB_DANGER_RELEASE( pTD ); |
| return Sequence< Any >( &vec[ 0 ], vec.size() ); |
| } |
| |
| //*********************************************************************** |
| DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) |
| : ThreadHelpBase ( &Application::GetSolarMutex() ) |
| , ::cppu::OWeakObject( ) |
| , m_xSMGR ( xSMGR ) |
| , m_xConverter( m_xSMGR->createInstance(::rtl::OUString::createFromAscii("com.sun.star.script.Converter")), css::uno::UNO_QUERY ) |
| { |
| } |
| |
| //************************************************************************ |
| DispatchRecorder::~DispatchRecorder() |
| { |
| } |
| |
| //************************************************************************* |
| // generate header |
| void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& ) throw( css::uno::RuntimeException ) |
| { |
| /* SAFE{ */ |
| /* } */ |
| } |
| |
| //************************************************************************* |
| void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL, |
| const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException ) |
| { |
| ::rtl::OUString aTarget; |
| |
| com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_False ); |
| m_aStatements.push_back( aStatement ); |
| } |
| |
| //************************************************************************* |
| void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL, |
| const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException ) |
| { |
| ::rtl::OUString aTarget; |
| |
| // last parameter must be set to true -> it's a comment |
| com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_True ); |
| m_aStatements.push_back( aStatement ); |
| } |
| |
| //************************************************************************* |
| void SAL_CALL DispatchRecorder::endRecording() throw( css::uno::RuntimeException ) |
| { |
| /* SAFE{ */ |
| WriteGuard aWriteLock(m_aLock); |
| m_aStatements.clear(); |
| /* } */ |
| } |
| |
| //************************************************************************* |
| ::rtl::OUString SAL_CALL DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException ) |
| { |
| /* SAFE{ */ |
| WriteGuard aWriteLock(m_aLock); |
| |
| if ( m_aStatements.empty() ) |
| return ::rtl::OUString(); |
| |
| ::rtl::OUStringBuffer aScriptBuffer; |
| aScriptBuffer.ensureCapacity(10000); |
| m_nRecordingID = 1; |
| |
| aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n"); |
| aScriptBuffer.appendAscii("rem define variables\n"); |
| aScriptBuffer.appendAscii("dim document as object\n"); |
| aScriptBuffer.appendAscii("dim dispatcher as object\n"); |
| aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n"); |
| aScriptBuffer.appendAscii("rem get access to the document\n"); |
| aScriptBuffer.appendAscii("document = ThisComponent.CurrentController.Frame\n"); |
| aScriptBuffer.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n"); |
| |
| std::vector< com::sun::star::frame::DispatchStatement>::iterator p; |
| for ( p = m_aStatements.begin(); p != m_aStatements.end(); p++ ) |
| implts_recordMacro( p->aCommand, p->aArgs, p->bIsComment, aScriptBuffer ); |
| ::rtl::OUString sScript = aScriptBuffer.makeStringAndClear(); |
| return sScript; |
| /* } */ |
| } |
| |
| //************************************************************************* |
| void SAL_CALL DispatchRecorder::AppendToBuffer( css::uno::Any aValue, ::rtl::OUStringBuffer& aArgumentBuffer ) |
| { |
| // if value == bool |
| if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT ) |
| { |
| // structs are recorded as arrays, convert to "Sequence of any" |
| Sequence< Any > aSeq = make_seq_out_of_struct( aValue ); |
| aArgumentBuffer.appendAscii("Array("); |
| for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ ) |
| { |
| AppendToBuffer( aSeq[nAny], aArgumentBuffer ); |
| if ( nAny+1 < aSeq.getLength() ) |
| // not last argument |
| aArgumentBuffer.appendAscii(","); |
| } |
| |
| aArgumentBuffer.appendAscii(")"); |
| } |
| else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE ) |
| { |
| // convert to "Sequence of any" |
| css::uno::Sequence < css::uno::Any > aSeq; |
| css::uno::Any aNew; |
| try { aNew = m_xConverter->convertTo( aValue, ::getCppuType((const css::uno::Sequence < css::uno::Any >*)0) ); } |
| catch (css::uno::Exception&) {} |
| |
| aNew >>= aSeq; |
| aArgumentBuffer.appendAscii("Array("); |
| for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ ) |
| { |
| AppendToBuffer( aSeq[nAny], aArgumentBuffer ); |
| if ( nAny+1 < aSeq.getLength() ) |
| // not last argument |
| aArgumentBuffer.appendAscii(","); |
| } |
| |
| aArgumentBuffer.appendAscii(")"); |
| } |
| else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING ) |
| { |
| // strings need \" |
| ::rtl::OUString sVal; |
| aValue >>= sVal; |
| |
| // encode non printable characters or '"' by using the CHR$ function |
| if ( sVal.getLength() ) |
| { |
| const sal_Unicode* pChars = sVal.getStr(); |
| sal_Bool bInString = sal_False; |
| for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ ) |
| { |
| if ( pChars[nChar] < 32 || pChars[nChar] == '"' ) |
| { |
| // problematic character detected |
| if ( bInString ) |
| { |
| // close current string |
| aArgumentBuffer.appendAscii("\""); |
| bInString = sal_False; |
| } |
| |
| if ( nChar>0 ) |
| // if this is not the first character, parts of the string have already been added |
| aArgumentBuffer.appendAscii("+"); |
| |
| // add the character constant |
| aArgumentBuffer.appendAscii("CHR$("); |
| aArgumentBuffer.append( (sal_Int32) pChars[nChar] ); |
| aArgumentBuffer.appendAscii(")"); |
| } |
| else |
| { |
| if ( !bInString ) |
| { |
| if ( nChar>0 ) |
| // if this is not the first character, parts of the string have already been added |
| aArgumentBuffer.appendAscii("+"); |
| |
| // start a new string |
| aArgumentBuffer.appendAscii("\""); |
| bInString = sal_True; |
| } |
| |
| aArgumentBuffer.append( pChars[nChar] ); |
| } |
| } |
| |
| // close string |
| if ( bInString ) |
| aArgumentBuffer.appendAscii("\""); |
| } |
| else |
| aArgumentBuffer.appendAscii("\"\""); |
| } |
| else if (aValue.getValueType() == getCppuCharType()) |
| { |
| // character variables are recorded as strings, back conversion must be handled in client code |
| sal_Unicode nVal = *((sal_Unicode*)aValue.getValue()); |
| aArgumentBuffer.appendAscii("\""); |
| if ( (sal_Unicode(nVal) == '\"') ) |
| // encode \" to \"\" |
| aArgumentBuffer.append((sal_Unicode)nVal); |
| aArgumentBuffer.append((sal_Unicode)nVal); |
| aArgumentBuffer.appendAscii("\""); |
| } |
| else |
| { |
| css::uno::Any aNew; |
| try |
| { |
| aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING ); |
| } |
| catch (css::script::CannotConvertException&) {} |
| catch (css::uno::Exception&) {} |
| ::rtl::OUString sVal; |
| aNew >>= sVal; |
| |
| if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM ) |
| { |
| ::rtl::OUString aName = aValue.getValueType().getTypeName(); |
| aArgumentBuffer.append( aName ); |
| aArgumentBuffer.appendAscii("."); |
| } |
| |
| aArgumentBuffer.append(sVal); |
| } |
| } |
| |
| void SAL_CALL DispatchRecorder::implts_recordMacro( const ::rtl::OUString& aURL, |
| const css::uno::Sequence< css::beans::PropertyValue >& lArguments, |
| sal_Bool bAsComment, ::rtl::OUStringBuffer& aScriptBuffer ) |
| { |
| ::rtl::OUStringBuffer aArgumentBuffer(1000); |
| ::rtl::OUString sArrayName; |
| // this value is used to name the arrays of aArgumentBuffer |
| sArrayName = ::rtl::OUString::createFromAscii("args"); |
| sArrayName += ::rtl::OUString::valueOf((sal_Int32)m_nRecordingID); |
| |
| aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n"); |
| |
| sal_Int32 nLength = lArguments.getLength(); |
| sal_Int32 nValidArgs = 0; |
| for( sal_Int32 i=0; i<nLength; ++i ) |
| { |
| if(!lArguments[i].Value.hasValue()) |
| continue; |
| |
| ::rtl::OUStringBuffer sValBuffer(100); |
| try |
| { |
| AppendToBuffer(lArguments[i].Value, sValBuffer); |
| } |
| catch(const css::uno::Exception&) |
| { |
| sValBuffer.setLength(0); |
| } |
| if (!sValBuffer.getLength()) |
| continue; |
| |
| { |
| // add arg().Name |
| if(bAsComment) |
| aArgumentBuffer.appendAscii(REM_AS_COMMENT); |
| aArgumentBuffer.append (sArrayName); |
| aArgumentBuffer.appendAscii("("); |
| aArgumentBuffer.append (nValidArgs); |
| aArgumentBuffer.appendAscii(").Name = \""); |
| aArgumentBuffer.append (lArguments[i].Name); |
| aArgumentBuffer.appendAscii("\"\n"); |
| |
| // add arg().Value |
| if(bAsComment) |
| aArgumentBuffer.appendAscii(REM_AS_COMMENT); |
| aArgumentBuffer.append (sArrayName); |
| aArgumentBuffer.appendAscii("("); |
| aArgumentBuffer.append (nValidArgs); |
| aArgumentBuffer.appendAscii(").Value = "); |
| aArgumentBuffer.append (sValBuffer.makeStringAndClear()); |
| aArgumentBuffer.appendAscii("\n"); |
| |
| ++nValidArgs; |
| } |
| } |
| |
| // if aArgumentBuffer exist - pack it into the aScriptBuffer |
| if(nValidArgs>0) |
| { |
| if(bAsComment) |
| aScriptBuffer.appendAscii(REM_AS_COMMENT); |
| aScriptBuffer.appendAscii("dim "); |
| aScriptBuffer.append (sArrayName); |
| aScriptBuffer.appendAscii("("); |
| aScriptBuffer.append ((sal_Int32)(nValidArgs-1)); // 0 based! |
| aScriptBuffer.appendAscii(") as new com.sun.star.beans.PropertyValue\n"); |
| aScriptBuffer.append (aArgumentBuffer.makeStringAndClear()); |
| aScriptBuffer.appendAscii("\n"); |
| } |
| |
| // add code for dispatches |
| if(bAsComment) |
| aScriptBuffer.appendAscii(REM_AS_COMMENT); |
| aScriptBuffer.appendAscii("dispatcher.executeDispatch(document, \""); |
| aScriptBuffer.append (aURL); |
| aScriptBuffer.appendAscii("\", \"\", 0, "); |
| if(nValidArgs<1) |
| aScriptBuffer.appendAscii("Array()"); |
| else |
| { |
| aScriptBuffer.append( sArrayName.getStr() ); |
| aScriptBuffer.appendAscii("()"); |
| } |
| aScriptBuffer.appendAscii(")\n\n"); |
| |
| /* SAFE { */ |
| m_nRecordingID++; |
| /* } */ |
| } |
| |
| com::sun::star::uno::Type SAL_CALL DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException) |
| { |
| return ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL); |
| } |
| |
| sal_Bool SAL_CALL DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException) |
| { |
| return (! m_aStatements.empty()); |
| } |
| |
| sal_Int32 SAL_CALL DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException) |
| { |
| return m_aStatements.size(); |
| } |
| |
| com::sun::star::uno::Any SAL_CALL DispatchRecorder::getByIndex(sal_Int32 idx) throw (::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException) |
| { |
| if (idx >= (sal_Int32)m_aStatements.size()) { |
| throw com::sun::star::lang::IndexOutOfBoundsException( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Dispatch recorder out of bounds") ), |
| Reference< XInterface >() ); |
| |
| } |
| |
| Any element(&m_aStatements[idx], |
| ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL)); |
| |
| return element; |
| } |
| |
| void SAL_CALL DispatchRecorder::replaceByIndex(sal_Int32 idx, const com::sun::star::uno::Any& element) throw (::com::sun::star::lang::IllegalArgumentException, ::com::sun::star::lang::IndexOutOfBoundsException, ::com::sun::star::lang::WrappedTargetException, ::com::sun::star::uno::RuntimeException) |
| { |
| if (element.getValueType() != |
| ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL)) { |
| throw com::sun::star::lang::IllegalArgumentException( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Illegal argument in dispatch recorder") ), |
| Reference< XInterface >(), 2 ); |
| } |
| |
| if (idx >= (sal_Int32)m_aStatements.size()) { |
| throw com::sun::star::lang::IndexOutOfBoundsException( |
| ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( |
| "Dispatch recorder out of bounds") ), |
| Reference< XInterface >() ); |
| |
| } |
| |
| com::sun::star::frame::DispatchStatement *pStatement; |
| |
| pStatement = (com::sun::star::frame::DispatchStatement *)element.getValue(); |
| |
| com::sun::star::frame::DispatchStatement aStatement( |
| pStatement->aCommand, |
| pStatement->aTarget, |
| pStatement->aArgs, |
| pStatement->nFlags, |
| pStatement->bIsComment); |
| |
| m_aStatements[idx] = aStatement; |
| } |
| |
| } // namespace framework |