1 /*************************************************************************
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
5 * Copyright 2000, 2010 Oracle and/or its affiliates.
7 * OpenOffice.org - a multi-platform office productivity suite
9 * This file is part of OpenOffice.org.
11 * OpenOffice.org is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License version 3
13 * only, as published by the Free Software Foundation.
15 * OpenOffice.org is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License version 3 for more details
19 * (a copy is included in the LICENSE file that accompanied this code).
21 * You should have received a copy of the GNU Lesser General Public License
22 * version 3 along with OpenOffice.org. If not, see
23 * <http://www.openoffice.org/license.html>
24 * for a copy of the LGPLv3 License.
26 ************************************************************************/
29 // MARKER(update_precomp.py): autogen include statement, do not remove
30 #include "precompiled_framework.hxx"
31 #include <recording/dispatchrecorder.hxx>
32 #include <com/sun/star/frame/DispatchStatement.hpp>
33 #include <threadhelp/writeguard.hxx>
34 #include <threadhelp/readguard.hxx>
36 #include <vcl/svapp.hxx>
38 using namespace ::com::sun::star::uno
;
42 // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ...
43 #define REM_AS_COMMENT "rem "
45 //*****************************************************************************************************************
46 // XInterface, XTypeProvider, XServiceInfo
47 //*****************************************************************************************************************
51 DIRECT_INTERFACE(css::lang::XTypeProvider
),
52 DIRECT_INTERFACE(css::lang::XServiceInfo
),
53 DIRECT_INTERFACE(css::frame::XDispatchRecorder
),
54 DIRECT_INTERFACE(css::container::XIndexReplace
),
55 DIRECT_INTERFACE(css::container::XIndexAccess
),
56 DIRECT_INTERFACE(css::container::XElementAccess
))
58 DEFINE_XTYPEPROVIDER_6(
60 css::lang::XTypeProvider
,
61 css::lang::XServiceInfo
,
62 css::frame::XDispatchRecorder
,
63 css::container::XIndexReplace
,
64 css::container::XIndexAccess
,
65 css::container::XElementAccess
)
67 DEFINE_XSERVICEINFO_MULTISERVICE(
70 SERVICENAME_DISPATCHRECORDER
,
71 IMPLEMENTATIONNAME_DISPATCHRECORDER
)
79 #include <typelib/typedescription.h>
81 //--------------------------------------------------------------------------------------------------
82 void flatten_struct_members(
83 ::std::vector
< Any
> * vec
, void const * data
,
84 typelib_CompoundTypeDescription
* pTD
)
87 if (pTD
->pBaseTypeDescription
)
89 flatten_struct_members( vec
, data
, pTD
->pBaseTypeDescription
);
91 for ( sal_Int32 nPos
= 0; nPos
< pTD
->nMembers
; ++nPos
)
94 Any( (char const *)data
+ pTD
->pMemberOffsets
[ nPos
], pTD
->ppTypeRefs
[ nPos
] ) );
97 //==================================================================================================
98 Sequence
< Any
> make_seq_out_of_struct(
100 SAL_THROW( (RuntimeException
) )
102 Type
const & type
= val
.getValueType();
103 TypeClass eTypeClass
= type
.getTypeClass();
104 if (TypeClass_STRUCT
!= eTypeClass
&& TypeClass_EXCEPTION
!= eTypeClass
)
106 throw RuntimeException(
108 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("is no struct or exception!") ),
109 Reference
< XInterface
>() );
111 typelib_TypeDescription
* pTD
= 0;
112 TYPELIB_DANGER_GET( &pTD
, type
.getTypeLibType() );
116 throw RuntimeException(
117 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get type descr of type ") ) +
119 Reference
< XInterface
>() );
122 ::std::vector
< Any
> vec
;
123 vec
.reserve( ((typelib_CompoundTypeDescription
*)pTD
)->nMembers
); // good guess
124 flatten_struct_members( &vec
, val
.getValue(), (typelib_CompoundTypeDescription
*)pTD
);
125 TYPELIB_DANGER_RELEASE( pTD
);
126 return Sequence
< Any
>( &vec
[ 0 ], vec
.size() );
129 //***********************************************************************
130 DispatchRecorder::DispatchRecorder( const css::uno::Reference
< css::lang::XMultiServiceFactory
>& xSMGR
)
131 : ThreadHelpBase ( &Application::GetSolarMutex() )
132 , ::cppu::OWeakObject( )
134 , m_xConverter( m_xSMGR
->createInstance(::rtl::OUString::createFromAscii("com.sun.star.script.Converter")), css::uno::UNO_QUERY
)
138 //************************************************************************
139 DispatchRecorder::~DispatchRecorder()
143 //*************************************************************************
145 void SAL_CALL
DispatchRecorder::startRecording( const css::uno::Reference
< css::frame::XFrame
>& ) throw( css::uno::RuntimeException
)
151 //*************************************************************************
152 void SAL_CALL
DispatchRecorder::recordDispatch( const css::util::URL
& aURL
,
153 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
)
155 ::rtl::OUString aTarget
;
157 com::sun::star::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, sal_False
);
158 m_aStatements
.push_back( aStatement
);
161 //*************************************************************************
162 void SAL_CALL
DispatchRecorder::recordDispatchAsComment( const css::util::URL
& aURL
,
163 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
)
165 ::rtl::OUString aTarget
;
167 // last parameter must be set to true -> it's a comment
168 com::sun::star::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, sal_True
);
169 m_aStatements
.push_back( aStatement
);
172 //*************************************************************************
173 void SAL_CALL
DispatchRecorder::endRecording() throw( css::uno::RuntimeException
)
176 WriteGuard
aWriteLock(m_aLock
);
177 m_aStatements
.clear();
181 //*************************************************************************
182 ::rtl::OUString SAL_CALL
DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException
)
185 WriteGuard
aWriteLock(m_aLock
);
187 if ( m_aStatements
.empty() )
188 return ::rtl::OUString();
190 ::rtl::OUStringBuffer aScriptBuffer
;
191 aScriptBuffer
.ensureCapacity(10000);
194 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
195 aScriptBuffer
.appendAscii("rem define variables\n");
196 aScriptBuffer
.appendAscii("dim document as object\n");
197 aScriptBuffer
.appendAscii("dim dispatcher as object\n");
198 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
199 aScriptBuffer
.appendAscii("rem get access to the document\n");
200 aScriptBuffer
.appendAscii("document = ThisComponent.CurrentController.Frame\n");
201 aScriptBuffer
.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
203 std::vector
< com::sun::star::frame::DispatchStatement
>::iterator p
;
204 for ( p
= m_aStatements
.begin(); p
!= m_aStatements
.end(); p
++ )
205 implts_recordMacro( p
->aCommand
, p
->aArgs
, p
->bIsComment
, aScriptBuffer
);
206 ::rtl::OUString sScript
= aScriptBuffer
.makeStringAndClear();
211 //*************************************************************************
212 void SAL_CALL
DispatchRecorder::AppendToBuffer( css::uno::Any aValue
, ::rtl::OUStringBuffer
& aArgumentBuffer
)
215 if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRUCT
)
217 // structs are recorded as arrays, convert to "Sequence of any"
218 Sequence
< Any
> aSeq
= make_seq_out_of_struct( aValue
);
219 aArgumentBuffer
.appendAscii("Array(");
220 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
222 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
223 if ( nAny
+1 < aSeq
.getLength() )
225 aArgumentBuffer
.appendAscii(",");
228 aArgumentBuffer
.appendAscii(")");
230 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_SEQUENCE
)
232 // convert to "Sequence of any"
233 css::uno::Sequence
< css::uno::Any
> aSeq
;
235 try { aNew
= m_xConverter
->convertTo( aValue
, ::getCppuType((const css::uno::Sequence
< css::uno::Any
>*)0) ); }
236 catch (css::uno::Exception
&) {}
239 aArgumentBuffer
.appendAscii("Array(");
240 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
242 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
243 if ( nAny
+1 < aSeq
.getLength() )
245 aArgumentBuffer
.appendAscii(",");
248 aArgumentBuffer
.appendAscii(")");
250 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRING
)
253 ::rtl::OUString sVal
;
256 // encode non printable characters or '"' by using the CHR$ function
257 if ( sVal
.getLength() )
259 const sal_Unicode
* pChars
= sVal
.getStr();
260 sal_Bool bInString
= sal_False
;
261 for ( sal_Int32 nChar
=0; nChar
<sVal
.getLength(); nChar
++ )
263 if ( pChars
[nChar
] < 32 || pChars
[nChar
] == '"' )
265 // problematic character detected
268 // close current string
269 aArgumentBuffer
.appendAscii("\"");
270 bInString
= sal_False
;
274 // if this is not the first character, parts of the string have already been added
275 aArgumentBuffer
.appendAscii("+");
277 // add the character constant
278 aArgumentBuffer
.appendAscii("CHR$(");
279 aArgumentBuffer
.append( (sal_Int32
) pChars
[nChar
] );
280 aArgumentBuffer
.appendAscii(")");
287 // if this is not the first character, parts of the string have already been added
288 aArgumentBuffer
.appendAscii("+");
290 // start a new string
291 aArgumentBuffer
.appendAscii("\"");
292 bInString
= sal_True
;
295 aArgumentBuffer
.append( pChars
[nChar
] );
301 aArgumentBuffer
.appendAscii("\"");
304 aArgumentBuffer
.appendAscii("\"\"");
306 else if (aValue
.getValueType() == getCppuCharType())
308 // character variables are recorded as strings, back conversion must be handled in client code
309 sal_Unicode nVal
= *((sal_Unicode
*)aValue
.getValue());
310 aArgumentBuffer
.appendAscii("\"");
311 if ( (sal_Unicode(nVal
) == '\"') )
313 aArgumentBuffer
.append((sal_Unicode
)nVal
);
314 aArgumentBuffer
.append((sal_Unicode
)nVal
);
315 aArgumentBuffer
.appendAscii("\"");
322 aNew
= m_xConverter
->convertToSimpleType( aValue
, css::uno::TypeClass_STRING
);
324 catch (css::script::CannotConvertException
&) {}
325 catch (css::uno::Exception
&) {}
326 ::rtl::OUString sVal
;
329 if (aValue
.getValueTypeClass() == css::uno::TypeClass_ENUM
)
331 ::rtl::OUString aName
= aValue
.getValueType().getTypeName();
332 aArgumentBuffer
.append( aName
);
333 aArgumentBuffer
.appendAscii(".");
336 aArgumentBuffer
.append(sVal
);
340 void SAL_CALL
DispatchRecorder::implts_recordMacro( const ::rtl::OUString
& aURL
,
341 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
,
342 sal_Bool bAsComment
, ::rtl::OUStringBuffer
& aScriptBuffer
)
344 ::rtl::OUStringBuffer
aArgumentBuffer(1000);
345 ::rtl::OUString sArrayName
;
346 // this value is used to name the arrays of aArgumentBuffer
347 sArrayName
= ::rtl::OUString::createFromAscii("args");
348 sArrayName
+= ::rtl::OUString::valueOf((sal_Int32
)m_nRecordingID
);
350 aScriptBuffer
.appendAscii("rem ----------------------------------------------------------------------\n");
352 sal_Int32 nLength
= lArguments
.getLength();
353 sal_Int32 nValidArgs
= 0;
354 for( sal_Int32 i
=0; i
<nLength
; ++i
)
356 if(!lArguments
[i
].Value
.hasValue())
359 ::rtl::OUStringBuffer
sValBuffer(100);
362 AppendToBuffer(lArguments
[i
].Value
, sValBuffer
);
364 catch(const css::uno::Exception
&)
366 sValBuffer
.setLength(0);
368 if (!sValBuffer
.getLength())
374 aArgumentBuffer
.appendAscii(REM_AS_COMMENT
);
375 aArgumentBuffer
.append (sArrayName
);
376 aArgumentBuffer
.appendAscii("(");
377 aArgumentBuffer
.append (nValidArgs
);
378 aArgumentBuffer
.appendAscii(").Name = \"");
379 aArgumentBuffer
.append (lArguments
[i
].Name
);
380 aArgumentBuffer
.appendAscii("\"\n");
384 aArgumentBuffer
.appendAscii(REM_AS_COMMENT
);
385 aArgumentBuffer
.append (sArrayName
);
386 aArgumentBuffer
.appendAscii("(");
387 aArgumentBuffer
.append (nValidArgs
);
388 aArgumentBuffer
.appendAscii(").Value = ");
389 aArgumentBuffer
.append (sValBuffer
.makeStringAndClear());
390 aArgumentBuffer
.appendAscii("\n");
396 // if aArgumentBuffer exist - pack it into the aScriptBuffer
400 aScriptBuffer
.appendAscii(REM_AS_COMMENT
);
401 aScriptBuffer
.appendAscii("dim ");
402 aScriptBuffer
.append (sArrayName
);
403 aScriptBuffer
.appendAscii("(");
404 aScriptBuffer
.append ((sal_Int32
)(nValidArgs
-1)); // 0 based!
405 aScriptBuffer
.appendAscii(") as new com.sun.star.beans.PropertyValue\n");
406 aScriptBuffer
.append (aArgumentBuffer
.makeStringAndClear());
407 aScriptBuffer
.appendAscii("\n");
410 // add code for dispatches
412 aScriptBuffer
.appendAscii(REM_AS_COMMENT
);
413 aScriptBuffer
.appendAscii("dispatcher.executeDispatch(document, \"");
414 aScriptBuffer
.append (aURL
);
415 aScriptBuffer
.appendAscii("\", \"\", 0, ");
417 aScriptBuffer
.appendAscii("Array()");
420 aScriptBuffer
.append( sArrayName
.getStr() );
421 aScriptBuffer
.appendAscii("()");
423 aScriptBuffer
.appendAscii(")\n\n");
430 com::sun::star::uno::Type SAL_CALL
DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException
)
432 return ::getCppuType((const com::sun::star::frame::DispatchStatement
*)NULL
);
435 sal_Bool SAL_CALL
DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException
)
437 return (! m_aStatements
.empty());
440 sal_Int32 SAL_CALL
DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException
)
442 return m_aStatements
.size();
445 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
)
447 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
448 throw com::sun::star::lang::IndexOutOfBoundsException(
449 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
450 "Dispatch recorder out of bounds") ),
451 Reference
< XInterface
>() );
455 Any
element(&m_aStatements
[idx
],
456 ::getCppuType((const com::sun::star::frame::DispatchStatement
*)NULL
));
461 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
)
463 if (element
.getValueType() !=
464 ::getCppuType((const com::sun::star::frame::DispatchStatement
*)NULL
)) {
465 throw com::sun::star::lang::IllegalArgumentException(
466 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
467 "Illegal argument in dispatch recorder") ),
468 Reference
< XInterface
>(), 2 );
471 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
472 throw com::sun::star::lang::IndexOutOfBoundsException(
473 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM(
474 "Dispatch recorder out of bounds") ),
475 Reference
< XInterface
>() );
479 com::sun::star::frame::DispatchStatement
*pStatement
;
481 pStatement
= (com::sun::star::frame::DispatchStatement
*)element
.getValue();
483 com::sun::star::frame::DispatchStatement
aStatement(
484 pStatement
->aCommand
,
488 pStatement
->bIsComment
);
490 m_aStatements
[idx
] = aStatement
;
493 } // namespace framework