1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
3 * This file is part of the LibreOffice project.
5 * This Source Code Form is subject to the terms of the Mozilla Public
6 * License, v. 2.0. If a copy of the MPL was not distributed with this
7 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
9 * This file incorporates work covered by the following license notice:
11 * Licensed to the Apache Software Foundation (ASF) under one or more
12 * contributor license agreements. See the NOTICE file distributed
13 * with this work for additional information regarding copyright
14 * ownership. The ASF licenses this file to you under the Apache
15 * License, Version 2.0 (the "License"); you may not use this file
16 * except in compliance with the License. You may obtain a copy of
17 * the License at http://www.apache.org/licenses/LICENSE-2.0 .
20 #include <recording/dispatchrecorder.hxx>
21 #include <com/sun/star/frame/DispatchStatement.hpp>
22 #include <com/sun/star/script/Converter.hpp>
23 #include <o3tl/any.hxx>
25 #include <vcl/svapp.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <typelib/typedescription.h>
29 using namespace ::com::sun::star::uno
;
33 // used to mark a dispatch as comment (mostly it indicates an error) Changing of this define will impact all using of such comments...
34 #define REM_AS_COMMENT "rem "
36 // XInterface, XTypeProvider, XServiceInfo
38 DEFINE_XSERVICEINFO_MULTISERVICE_2(
41 "com.sun.star.frame.DispatchRecorder",
42 OUString("com.sun.star.comp.framework.DispatchRecorder"))
51 void flatten_struct_members(
52 ::std::vector
< Any
> * vec
, void const * data
,
53 typelib_CompoundTypeDescription
* pTD
)
55 if (pTD
->pBaseTypeDescription
)
57 flatten_struct_members( vec
, data
, pTD
->pBaseTypeDescription
);
59 for ( sal_Int32 nPos
= 0; nPos
< pTD
->nMembers
; ++nPos
)
62 Any( static_cast<char const *>(data
) + pTD
->pMemberOffsets
[ nPos
], pTD
->ppTypeRefs
[ nPos
] ) );
66 Sequence
< Any
> make_seq_out_of_struct(
69 Type
const & type
= val
.getValueType();
70 TypeClass eTypeClass
= type
.getTypeClass();
71 if (TypeClass_STRUCT
!= eTypeClass
&& TypeClass_EXCEPTION
!= eTypeClass
)
73 throw RuntimeException(
74 type
.getTypeName() + "is no struct or exception!" );
76 typelib_TypeDescription
* pTD
= nullptr;
77 TYPELIB_DANGER_GET( &pTD
, type
.getTypeLibType() );
81 throw RuntimeException(
82 "cannot get type descr of type " + type
.getTypeName() );
85 ::std::vector
< Any
> vec
;
86 vec
.reserve( reinterpret_cast<typelib_CompoundTypeDescription
*>(pTD
)->nMembers
); // good guess
87 flatten_struct_members( &vec
, val
.getValue(), reinterpret_cast<typelib_CompoundTypeDescription
*>(pTD
) );
88 TYPELIB_DANGER_RELEASE( pTD
);
89 return Sequence
< Any
>( &vec
[ 0 ], vec
.size() );
92 DispatchRecorder::DispatchRecorder( const css::uno::Reference
< css::uno::XComponentContext
>& xContext
)
94 , m_xConverter(css::script::Converter::create(xContext
))
98 DispatchRecorder::~DispatchRecorder()
103 void SAL_CALL
DispatchRecorder::startRecording( const css::uno::Reference
< css::frame::XFrame
>& ) throw( css::uno::RuntimeException
, std::exception
)
109 void SAL_CALL
DispatchRecorder::recordDispatch( const css::util::URL
& aURL
,
110 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
, std::exception
)
114 css::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, false );
115 m_aStatements
.push_back( aStatement
);
118 void SAL_CALL
DispatchRecorder::recordDispatchAsComment( const css::util::URL
& aURL
,
119 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
) throw( css::uno::RuntimeException
, std::exception
)
123 // last parameter must be set to true -> it's a comment
124 css::frame::DispatchStatement
aStatement( aURL
.Complete
, aTarget
, lArguments
, 0, true );
125 m_aStatements
.push_back( aStatement
);
128 void SAL_CALL
DispatchRecorder::endRecording() throw( css::uno::RuntimeException
, std::exception
)
131 m_aStatements
.clear();
134 OUString SAL_CALL
DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException
, std::exception
)
138 if ( m_aStatements
.empty() )
141 OUStringBuffer aScriptBuffer
;
142 aScriptBuffer
.ensureCapacity(10000);
145 aScriptBuffer
.append("rem ----------------------------------------------------------------------\n");
146 aScriptBuffer
.append("rem define variables\n");
147 aScriptBuffer
.append("dim document as object\n");
148 aScriptBuffer
.append("dim dispatcher as object\n");
149 aScriptBuffer
.append("rem ----------------------------------------------------------------------\n");
150 aScriptBuffer
.append("rem get access to the document\n");
151 aScriptBuffer
.append("document = ThisComponent.CurrentController.Frame\n");
152 aScriptBuffer
.append("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
154 std::vector
< css::frame::DispatchStatement
>::iterator p
;
155 for ( p
= m_aStatements
.begin(); p
!= m_aStatements
.end(); ++p
)
156 implts_recordMacro( p
->aCommand
, p
->aArgs
, p
->bIsComment
, aScriptBuffer
);
157 OUString sScript
= aScriptBuffer
.makeStringAndClear();
161 void SAL_CALL
DispatchRecorder::AppendToBuffer( const css::uno::Any
& aValue
, OUStringBuffer
& aArgumentBuffer
)
164 if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRUCT
)
166 // structs are recorded as arrays, convert to "Sequence of any"
167 Sequence
< Any
> aSeq
= make_seq_out_of_struct( aValue
);
168 aArgumentBuffer
.append("Array(");
169 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
171 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
172 if ( nAny
+1 < aSeq
.getLength() )
174 aArgumentBuffer
.append(",");
177 aArgumentBuffer
.append(")");
179 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_SEQUENCE
)
181 // convert to "Sequence of any"
182 css::uno::Sequence
< css::uno::Any
> aSeq
;
184 try { aNew
= m_xConverter
->convertTo( aValue
, cppu::UnoType
<css::uno::Sequence
< css::uno::Any
>>::get() ); }
185 catch (const css::uno::Exception
&) {}
188 aArgumentBuffer
.append("Array(");
189 for ( sal_Int32 nAny
=0; nAny
<aSeq
.getLength(); nAny
++ )
191 AppendToBuffer( aSeq
[nAny
], aArgumentBuffer
);
192 if ( nAny
+1 < aSeq
.getLength() )
194 aArgumentBuffer
.append(",");
197 aArgumentBuffer
.append(")");
199 else if (aValue
.getValueTypeClass() == css::uno::TypeClass_STRING
)
205 // encode non printable characters or '"' by using the CHR$ function
206 if ( !sVal
.isEmpty() )
208 const sal_Unicode
* pChars
= sVal
.getStr();
209 bool bInString
= false;
210 for ( sal_Int32 nChar
=0; nChar
<sVal
.getLength(); nChar
++ )
212 if ( pChars
[nChar
] < 32 || pChars
[nChar
] == '"' )
214 // problematic character detected
217 // close current string
218 aArgumentBuffer
.append("\"");
223 // if this is not the first character, parts of the string have already been added
224 aArgumentBuffer
.append("+");
226 // add the character constant
227 aArgumentBuffer
.append("CHR$(");
228 aArgumentBuffer
.append( (sal_Int32
) pChars
[nChar
] );
229 aArgumentBuffer
.append(")");
236 // if this is not the first character, parts of the string have already been added
237 aArgumentBuffer
.append("+");
239 // start a new string
240 aArgumentBuffer
.append("\"");
244 aArgumentBuffer
.append( pChars
[nChar
] );
250 aArgumentBuffer
.append("\"");
253 aArgumentBuffer
.append("\"\"");
255 else if (auto nVal
= o3tl::tryAccess
<sal_Unicode
>(aValue
))
257 // character variables are recorded as strings, back conversion must be handled in client code
258 aArgumentBuffer
.append("\"");
259 if ( (*nVal
== '\"') )
261 aArgumentBuffer
.append(*nVal
);
262 aArgumentBuffer
.append(*nVal
);
263 aArgumentBuffer
.append("\"");
270 aNew
= m_xConverter
->convertToSimpleType( aValue
, css::uno::TypeClass_STRING
);
272 catch (const css::script::CannotConvertException
&) {}
273 catch (const css::uno::Exception
&) {}
277 if (aValue
.getValueTypeClass() == css::uno::TypeClass_ENUM
)
279 OUString aName
= aValue
.getValueType().getTypeName();
280 aArgumentBuffer
.append( aName
);
281 aArgumentBuffer
.append(".");
284 aArgumentBuffer
.append(sVal
);
288 void SAL_CALL
DispatchRecorder::implts_recordMacro( const OUString
& aURL
,
289 const css::uno::Sequence
< css::beans::PropertyValue
>& lArguments
,
290 bool bAsComment
, OUStringBuffer
& aScriptBuffer
)
292 OUStringBuffer
aArgumentBuffer(1000);
294 // this value is used to name the arrays of aArgumentBuffer
295 sArrayName
= "args" + OUString::number(m_nRecordingID
);
297 aScriptBuffer
.append("rem ----------------------------------------------------------------------\n");
299 sal_Int32 nLength
= lArguments
.getLength();
300 sal_Int32 nValidArgs
= 0;
301 for( sal_Int32 i
=0; i
<nLength
; ++i
)
303 if(!lArguments
[i
].Value
.hasValue())
306 OUStringBuffer
sValBuffer(100);
309 AppendToBuffer(lArguments
[i
].Value
, sValBuffer
);
311 catch(const css::uno::Exception
&)
313 sValBuffer
.setLength(0);
315 if (sValBuffer
.isEmpty())
321 aArgumentBuffer
.append(REM_AS_COMMENT
);
322 aArgumentBuffer
.append (sArrayName
);
323 aArgumentBuffer
.append("(");
324 aArgumentBuffer
.append (nValidArgs
);
325 aArgumentBuffer
.append(").Name = \"");
326 aArgumentBuffer
.append (lArguments
[i
].Name
);
327 aArgumentBuffer
.append("\"\n");
331 aArgumentBuffer
.append(REM_AS_COMMENT
);
332 aArgumentBuffer
.append (sArrayName
);
333 aArgumentBuffer
.append("(");
334 aArgumentBuffer
.append (nValidArgs
);
335 aArgumentBuffer
.append(").Value = ");
336 aArgumentBuffer
.append (sValBuffer
.makeStringAndClear());
337 aArgumentBuffer
.append("\n");
343 // if aArgumentBuffer exist - pack it into the aScriptBuffer
347 aScriptBuffer
.append(REM_AS_COMMENT
);
348 aScriptBuffer
.append("dim ");
349 aScriptBuffer
.append (sArrayName
);
350 aScriptBuffer
.append("(");
351 aScriptBuffer
.append ((sal_Int32
)(nValidArgs
-1)); // 0 based!
352 aScriptBuffer
.append(") as new com.sun.star.beans.PropertyValue\n");
353 aScriptBuffer
.append (aArgumentBuffer
.makeStringAndClear());
354 aScriptBuffer
.append("\n");
357 // add code for dispatches
359 aScriptBuffer
.append(REM_AS_COMMENT
);
360 aScriptBuffer
.append("dispatcher.executeDispatch(document, \"");
361 aScriptBuffer
.append (aURL
);
362 aScriptBuffer
.append("\", \"\", 0, ");
364 aScriptBuffer
.append("Array()");
367 aScriptBuffer
.append( sArrayName
.getStr() );
368 aScriptBuffer
.append("()");
370 aScriptBuffer
.append(")\n\n");
377 css::uno::Type SAL_CALL
DispatchRecorder::getElementType() throw (css::uno::RuntimeException
, std::exception
)
379 return cppu::UnoType
<css::frame::DispatchStatement
>::get();
382 sal_Bool SAL_CALL
DispatchRecorder::hasElements() throw (css::uno::RuntimeException
, std::exception
)
384 return (! m_aStatements
.empty());
387 sal_Int32 SAL_CALL
DispatchRecorder::getCount() throw (css::uno::RuntimeException
, std::exception
)
389 return m_aStatements
.size();
392 css::uno::Any SAL_CALL
DispatchRecorder::getByIndex(sal_Int32 idx
) throw (css::lang::IndexOutOfBoundsException
, css::lang::WrappedTargetException
, css::uno::RuntimeException
, std::exception
)
394 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
395 throw css::lang::IndexOutOfBoundsException( "Dispatch recorder out of bounds" );
398 Any
element(&m_aStatements
[idx
],
399 cppu::UnoType
<css::frame::DispatchStatement
>::get());
404 void SAL_CALL
DispatchRecorder::replaceByIndex(sal_Int32 idx
, const css::uno::Any
& element
) throw (css::lang::IllegalArgumentException
, css::lang::IndexOutOfBoundsException
, css::lang::WrappedTargetException
, css::uno::RuntimeException
, std::exception
)
406 if (element
.getValueType() !=
407 cppu::UnoType
<css::frame::DispatchStatement
>::get()) {
408 throw css::lang::IllegalArgumentException(
409 "Illegal argument in dispatch recorder",
410 Reference
< XInterface
>(), 2 );
413 if (idx
>= (sal_Int32
)m_aStatements
.size()) {
414 throw css::lang::IndexOutOfBoundsException(
415 "Dispatch recorder out of bounds" );
419 auto pStatement
= o3tl::doAccess
<css::frame::DispatchStatement
>(element
);
421 css::frame::DispatchStatement
aStatement(
422 pStatement
->aCommand
,
426 pStatement
->bIsComment
);
428 m_aStatements
[idx
] = aStatement
;
431 } // namespace framework
433 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */