1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2008 by Sun Microsystems, Inc.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * $RCSfile: dispatchrecorder.cxx,v $
12 * This file is part of OpenOffice.org.
14 * OpenOffice.org is free software: you can redistribute it and/or modify
15 * it under the terms of the GNU Lesser General Public License version 3
16 * only, as published by the Free Software Foundation.
18 * OpenOffice.org is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU Lesser General Public License version 3 for more details
22 * (a copy is included in the LICENSE file that accompanied this code).
24 * You should have received a copy of the GNU Lesser General Public License
25 * version 3 along with OpenOffice.org. If not, see
26 * <http://www.openoffice.org/license.html>
27 * for a copy of the LGPLv3 License.
29 ************************************************************************/
32 // MARKER(update_precomp.py): autogen include statement, do not remove
33 #include "precompiled_framework.hxx"
34 #include <recording/dispatchrecorder.hxx>
35 #include <com/sun/star/frame/DispatchStatement.hpp>
36 #include <threadhelp/writeguard.hxx>
37 #include <threadhelp/readguard.hxx>
39 #include <vcl/svapp.hxx>
41 using namespace ::com::sun::star::uno
;
45 // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ...
46 #define REM_AS_COMMENT "rem "
48 //*****************************************************************************************************************
49 // XInterface, XTypeProvider, XServiceInfo
50 //*****************************************************************************************************************
54 DIRECT_INTERFACE(css::lang::XTypeProvider
),
55 DIRECT_INTERFACE(css::lang::XServiceInfo
),
56 DIRECT_INTERFACE(css::frame::XDispatchRecorder
),
57 DIRECT_INTERFACE(css::container::XIndexReplace
),
58 DIRECT_INTERFACE(css::container::XIndexAccess
),
59 DIRECT_INTERFACE(css::container::XElementAccess
))
61 DEFINE_XTYPEPROVIDER_6(
63 css::lang::XTypeProvider
,
64 css::lang::XServiceInfo
,
65 css::frame::XDispatchRecorder
,
66 css::container::XIndexReplace
,
67 css::container::XIndexAccess
,
68 css::container::XElementAccess
)
70 DEFINE_XSERVICEINFO_MULTISERVICE(
73 SERVICENAME_DISPATCHRECORDER
,
74 IMPLEMENTATIONNAME_DISPATCHRECORDER
)
82 #include <typelib/typedescription.h>
84 //--------------------------------------------------------------------------------------------------
85 void flatten_struct_members(
86 ::std::vector
< Any
> * vec
, void const * data
,
87 typelib_CompoundTypeDescription
* pTD
)
90 if (pTD
->pBaseTypeDescription
)
92 flatten_struct_members( vec
, data
, pTD
->pBaseTypeDescription
);
94 for ( sal_Int32 nPos
= 0; nPos
< pTD
->nMembers
; ++nPos
)
97 Any( (char const *)data
+ pTD
->pMemberOffsets
[ nPos
], pTD
->ppTypeRefs
[ nPos
] ) );
100 //==================================================================================================
101 Sequence
< Any
> make_seq_out_of_struct(
103 SAL_THROW( (RuntimeException
) )
105 Type
const & type
= val
.getValueType();
106 TypeClass eTypeClass
= type
.getTypeClass();
107 if (TypeClass_STRUCT
!= eTypeClass
&& TypeClass_EXCEPTION
!= eTypeClass
)
109 throw RuntimeException(
111 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("is no struct or exception!") ),
112 Reference
< XInterface
>() );
114 typelib_TypeDescription
* pTD
= 0;
115 TYPELIB_DANGER_GET( &pTD
, type
.getTypeLibType() );
119 throw RuntimeException(
120 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get type descr of type ") ) +
122 Reference
< XInterface
>() );
125 ::std::vector
< Any
> vec
;
126 vec
.reserve( ((typelib_CompoundTypeDescription
*)pTD
)->nMembers
); // good guess
127 flatten_struct_members( &vec
, val
.getValue(), (typelib_CompoundTypeDescription
*)pTD
);
128 TYPELIB_DANGER_RELEASE( pTD
);
129 return Sequence
< Any
>( &vec
[ 0 ], vec
.size() );
132 //***********************************************************************
133 DispatchRecorder::DispatchRecorder( const css::uno::Reference
< css::lang::XMultiServiceFactory
>& xSMGR
)
134 : ThreadHelpBase ( &Application::GetSolarMutex() )
135 , ::cppu::OWeakObject( )
137 , m_xConverter( m_xSMGR
->createInstance(::rtl::OUString::createFromAscii("com.sun.star.script.Converter")), css::uno::UNO_QUERY
)
141 //************************************************************************
142 DispatchRecorder::~DispatchRecorder()
146 //*************************************************************************
148 void SAL_CALL
DispatchRecorder::startRecording( const css::uno::Reference
< css::frame::XFrame
>& ) throw( css::uno::RuntimeException
)
154 //*************************************************************************
155 void SAL_CALL
DispatchRecorder::recordDispatch( const css::util::URL
& aURL
,
156 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
)
158 ::rtl::OUString aTarget
;
160 com::sun::star::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, sal_False
);
161 m_aStatements
.push_back( aStatement
);
164 //*************************************************************************
165 void SAL_CALL
DispatchRecorder::recordDispatchAsComment( const css::util::URL
& aURL
,
166 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
)
168 ::rtl::OUString aTarget
;
170 // last parameter must be set to true -> it's a comment
171 com::sun::star::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, sal_True
);
172 m_aStatements
.push_back( aStatement
);
175 //*************************************************************************
176 void SAL_CALL
DispatchRecorder::endRecording() throw( css::uno::RuntimeException
)
179 WriteGuard
aWriteLock(m_aLock
);
180 m_aStatements
.clear();
184 //*************************************************************************
185 ::rtl::OUString SAL_CALL
DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException
)
188 WriteGuard
aWriteLock(m_aLock
);
190 if ( m_aStatements
.empty() )
191 return ::rtl::OUString();
193 ::rtl::OUStringBuffer aScriptBuffer
;
194 aScriptBuffer
.ensureCapacity(10000);
197 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
198 aScriptBuffer
.appendAscii("rem define variables\n");
199 aScriptBuffer
.appendAscii("dim document as object\n");
200 aScriptBuffer
.appendAscii("dim dispatcher as object\n");
201 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
202 aScriptBuffer
.appendAscii("rem get access to the document\n");
203 aScriptBuffer
.appendAscii("document = ThisComponent.CurrentController.Frame\n");
204 aScriptBuffer
.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
206 std::vector
< com::sun::star::frame::DispatchStatement
>::iterator p
;
207 for ( p
= m_aStatements
.begin(); p
!= m_aStatements
.end(); p
++ )
208 implts_recordMacro( p
->aCommand
, p
->aArgs
, p
->bIsComment
, aScriptBuffer
);
209 ::rtl::OUString sScript
= aScriptBuffer
.makeStringAndClear();
214 //*************************************************************************
215 void SAL_CALL
DispatchRecorder::AppendToBuffer( css::uno::Any aValue
, ::rtl::OUStringBuffer
& aArgumentBuffer
)
218 if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRUCT
)
220 // structs are recorded as arrays, convert to "Sequence of any"
221 Sequence
< Any
> aSeq
= make_seq_out_of_struct( aValue
);
222 aArgumentBuffer
.appendAscii("Array(");
223 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
225 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
226 if ( nAny
+1 < aSeq
.getLength() )
228 aArgumentBuffer
.appendAscii(",");
231 aArgumentBuffer
.appendAscii(")");
233 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_SEQUENCE
)
235 // convert to "Sequence of any"
236 css::uno::Sequence
< css::uno::Any
> aSeq
;
238 try { aNew
= m_xConverter
->convertTo( aValue
, ::getCppuType((const css::uno::Sequence
< css::uno::Any
>*)0) ); }
239 catch (css::uno::Exception
&) {}
242 aArgumentBuffer
.appendAscii("Array(");
243 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
245 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
246 if ( nAny
+1 < aSeq
.getLength() )
248 aArgumentBuffer
.appendAscii(",");
251 aArgumentBuffer
.appendAscii(")");
253 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRING
)
256 ::rtl::OUString sVal
;
259 // encode non printable characters or '"' by using the CHR$ function
260 if ( sVal
.getLength() )
262 const sal_Unicode
* pChars
= sVal
.getStr();
263 sal_Bool bInString
= sal_False
;
264 for ( sal_Int32 nChar
=0; nChar
<sVal
.getLength(); nChar
++ )
266 if ( pChars
[nChar
] < 32 || pChars
[nChar
] == '"' )
268 // problematic character detected
271 // close current string
272 aArgumentBuffer
.appendAscii("\"");
273 bInString
= sal_False
;
277 // if this is not the first character, parts of the string have already been added
278 aArgumentBuffer
.appendAscii("+");
280 // add the character constant
281 aArgumentBuffer
.appendAscii("CHR$(");
282 aArgumentBuffer
.append( (sal_Int32
) pChars
[nChar
] );
283 aArgumentBuffer
.appendAscii(")");
290 // if this is not the first character, parts of the string have already been added
291 aArgumentBuffer
.appendAscii("+");
293 // start a new string
294 aArgumentBuffer
.appendAscii("\"");
295 bInString
= sal_True
;
298 aArgumentBuffer
.append( pChars
[nChar
] );
304 aArgumentBuffer
.appendAscii("\"");
307 aArgumentBuffer
.appendAscii("\"\"");
309 else if (aValue
.getValueType() == getCppuCharType())
311 // character variables are recorded as strings, back conversion must be handled in client code
312 sal_Unicode nVal
= *((sal_Unicode
*)aValue
.getValue());
313 aArgumentBuffer
.appendAscii("\"");
314 if ( (sal_Unicode(nVal
) == '\"') )
316 aArgumentBuffer
.append((sal_Unicode
)nVal
);
317 aArgumentBuffer
.append((sal_Unicode
)nVal
);
318 aArgumentBuffer
.appendAscii("\"");
325 aNew
= m_xConverter
->convertToSimpleType( aValue
, css::uno::TypeClass_STRING
);
327 catch (css::script::CannotConvertException
&) {}
328 catch (css::uno::Exception
&) {}
329 ::rtl::OUString sVal
;
332 if (aValue
.getValueTypeClass() == css::uno::TypeClass_ENUM
)
334 ::rtl::OUString aName
= aValue
.getValueType().getTypeName();
335 aArgumentBuffer
.append( aName
);
336 aArgumentBuffer
.appendAscii(".");
339 aArgumentBuffer
.append(sVal
);
343 void SAL_CALL
DispatchRecorder::implts_recordMacro( const ::rtl::OUString
& aURL
,
344 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
,
345 sal_Bool bAsComment
, ::rtl::OUStringBuffer
& aScriptBuffer
)
347 ::rtl::OUStringBuffer
aArgumentBuffer(1000);
348 ::rtl::OUString sArrayName
;
349 // this value is used to name the arrays of aArgumentBuffer
350 sArrayName
= ::rtl::OUString::createFromAscii("args");
351 sArrayName
+= ::rtl::OUString::valueOf((sal_Int32
)m_nRecordingID
);
353 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
355 sal_Int32 nLength
= lArguments
.getLength();
356 sal_Int32 nValidArgs
= 0;
357 for( sal_Int32 i
=0; i
<nLength
; ++i
)
359 if(!lArguments
[i
].Value
.hasValue())
362 ::rtl::OUStringBuffer
sValBuffer(100);
365 AppendToBuffer(lArguments
[i
].Value
, sValBuffer
);
367 catch(const css::uno::Exception
&)
369 sValBuffer
.setLength(0);
371 if (!sValBuffer
.getLength())
377 aArgumentBuffer
.appendAscii(REM_AS_COMMENT
);
378 aArgumentBuffer
.append (sArrayName
);
379 aArgumentBuffer
.appendAscii("(");
380 aArgumentBuffer
.append (nValidArgs
);
381 aArgumentBuffer
.appendAscii(").Name = \"");
382 aArgumentBuffer
.append (lArguments
[i
].Name
);
383 aArgumentBuffer
.appendAscii("\"\n");
387 aArgumentBuffer
.appendAscii(REM_AS_COMMENT
);
388 aArgumentBuffer
.append (sArrayName
);
389 aArgumentBuffer
.appendAscii("(");
390 aArgumentBuffer
.append (nValidArgs
);
391 aArgumentBuffer
.appendAscii(").Value = ");
392 aArgumentBuffer
.append (sValBuffer
.makeStringAndClear());
393 aArgumentBuffer
.appendAscii("\n");
399 // if aArgumentBuffer exist - pack it into the aScriptBuffer
403 aScriptBuffer
.appendAscii(REM_AS_COMMENT
);
404 aScriptBuffer
.appendAscii("dim ");
405 aScriptBuffer
.append (sArrayName
);
406 aScriptBuffer
.appendAscii("(");
407 aScriptBuffer
.append ((sal_Int32
)(nValidArgs
-1)); // 0 based!
408 aScriptBuffer
.appendAscii(") as new com.sun.star.beans.PropertyValue\n");
409 aScriptBuffer
.append (aArgumentBuffer
.makeStringAndClear());
410 aScriptBuffer
.appendAscii("\n");
413 // add code for dispatches
415 aScriptBuffer
.appendAscii(REM_AS_COMMENT
);
416 aScriptBuffer
.appendAscii("dispatcher.executeDispatch(document, \"");
417 aScriptBuffer
.append (aURL
);
418 aScriptBuffer
.appendAscii("\", \"\", 0, ");
420 aScriptBuffer
.appendAscii("Array()");
423 aScriptBuffer
.append( sArrayName
.getStr() );
424 aScriptBuffer
.appendAscii("()");
426 aScriptBuffer
.appendAscii(")\n\n");
433 com::sun::star::uno::Type SAL_CALL
DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException
)
435 return ::getCppuType((const com::sun::star::frame::DispatchStatement
*)NULL
);
438 sal_Bool SAL_CALL
DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException
)
440 return (! m_aStatements
.empty());
443 sal_Int32 SAL_CALL
DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException
)
445 return m_aStatements
.size();
448 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
)
450 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
451 throw com::sun::star::lang::IndexOutOfBoundsException(
452 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
453 "Dispatch recorder out of bounds") ),
454 Reference
< XInterface
>() );
458 Any
element(&m_aStatements
[idx
],
459 ::getCppuType((const com::sun::star::frame::DispatchStatement
*)NULL
));
464 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
)
466 if (element
.getValueType() !=
467 ::getCppuType((const com::sun::star::frame::DispatchStatement
*)NULL
)) {
468 throw com::sun::star::lang::IllegalArgumentException(
469 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
470 "Illegal argument in dispatch recorder") ),
471 Reference
< XInterface
>(), 2 );
474 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
475 throw com::sun::star::lang::IndexOutOfBoundsException(
476 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
477 "Dispatch recorder out of bounds") ),
478 Reference
< XInterface
>() );
482 com::sun::star::frame::DispatchStatement
*pStatement
;
484 pStatement
= (com::sun::star::frame::DispatchStatement
*)element
.getValue();
486 com::sun::star::frame::DispatchStatement
aStatement(
487 pStatement
->aCommand
,
491 pStatement
->bIsComment
);
493 m_aStatements
[idx
] = aStatement
;
496 } // namespace framework