Version 7.6.3.2-android, tag libreoffice-7.6.3.2-android
[LibreOffice.git] / framework / source / recording / dispatchrecorder.cxx
blob9c4a1bc90a810f611a64a67b5ca68e662db88550
1 /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 /*
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/lang/IllegalArgumentException.hpp>
23 #include <com/sun/star/lang/IndexOutOfBoundsException.hpp>
24 #include <com/sun/star/script/CannotConvertException.hpp>
25 #include <com/sun/star/script/Converter.hpp>
26 #include <o3tl/any.hxx>
27 #include <osl/diagnose.h>
28 #include <vcl/svapp.hxx>
29 #include <typelib/typedescription.h>
30 #include <cppuhelper/supportsservice.hxx>
32 using namespace ::com::sun::star::uno;
34 namespace framework{
36 // used to mark a dispatch as comment (mostly it indicates an error) Changing of this define will impact all using of such comments...
37 constexpr OUStringLiteral REM_AS_COMMENT = u"rem ";
39 // XInterface, XTypeProvider, XServiceInfo
41 OUString SAL_CALL DispatchRecorder::getImplementationName()
43 return "com.sun.star.comp.framework.DispatchRecorder";
46 sal_Bool SAL_CALL DispatchRecorder::supportsService( const OUString& sServiceName )
48 return cppu::supportsService(this, sServiceName);
51 css::uno::Sequence< OUString > SAL_CALL DispatchRecorder::getSupportedServiceNames()
53 return { "com.sun.star.frame.DispatchRecorder" };
58 static void flatten_struct_members(
59 ::std::vector< Any > * vec, void const * data,
60 typelib_CompoundTypeDescription * pTD )
62 if (pTD->pBaseTypeDescription)
64 flatten_struct_members( vec, data, pTD->pBaseTypeDescription );
66 for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos )
68 vec->push_back(
69 Any( static_cast<char const *>(data) + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) );
73 static Sequence< Any > make_seq_out_of_struct(
74 Any const & val )
76 Type const & type = val.getValueType();
77 TypeClass eTypeClass = type.getTypeClass();
78 if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass)
80 throw RuntimeException(
81 type.getTypeName() + "is no struct or exception!" );
83 typelib_TypeDescription * pTD = nullptr;
84 TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() );
85 OSL_ASSERT( pTD );
86 if (! pTD)
88 throw RuntimeException(
89 "cannot get type descr of type " + type.getTypeName() );
92 ::std::vector< Any > vec;
93 vec.reserve( reinterpret_cast<typelib_CompoundTypeDescription *>(pTD)->nMembers ); // good guess
94 flatten_struct_members( &vec, val.getValue(), reinterpret_cast<typelib_CompoundTypeDescription *>(pTD) );
95 TYPELIB_DANGER_RELEASE( pTD );
96 return Sequence< Any >( vec.data(), vec.size() );
99 DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xContext )
100 : m_nRecordingID(0)
101 , m_xConverter(css::script::Converter::create(xContext))
105 DispatchRecorder::~DispatchRecorder()
109 // generate header
110 void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& )
112 /* SAFE{ */
113 /* } */
116 void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL,
117 const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
119 css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, false );
120 m_aStatements.push_back( aStatement );
123 void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL,
124 const css::uno::Sequence< css::beans::PropertyValue >& lArguments )
126 // last parameter must be set to true -> it's a comment
127 css::frame::DispatchStatement aStatement( aURL.Complete, OUString(), lArguments, 0, true );
128 m_aStatements.push_back( aStatement );
131 void SAL_CALL DispatchRecorder::endRecording()
133 SolarMutexGuard g;
134 m_aStatements.clear();
137 OUString SAL_CALL DispatchRecorder::getRecordedMacro()
139 SolarMutexGuard g;
141 if ( m_aStatements.empty() )
142 return OUString();
144 OUStringBuffer aScriptBuffer;
145 aScriptBuffer.ensureCapacity(10000);
146 m_nRecordingID = 1;
148 aScriptBuffer.append(
149 "rem ----------------------------------------------------------------------\n"
150 "rem define variables\n"
151 "dim document as object\n"
152 "dim dispatcher as object\n"
153 "rem ----------------------------------------------------------------------\n"
154 "rem get access to the document\n"
155 "document = ThisComponent.CurrentController.Frame\n"
156 "dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
158 for (auto const& statement : m_aStatements)
159 implts_recordMacro( statement.aCommand, statement.aArgs, statement.bIsComment, aScriptBuffer );
160 OUString sScript = aScriptBuffer.makeStringAndClear();
161 return sScript;
164 void DispatchRecorder::AppendToBuffer( const css::uno::Any& aValue, OUStringBuffer& aArgumentBuffer )
166 // if value == bool
167 if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT )
169 // structs are recorded as arrays, convert to "Sequence of any"
170 Sequence< Any > aSeq = make_seq_out_of_struct( aValue );
171 aArgumentBuffer.append("Array(");
172 for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
174 AppendToBuffer( aSeq[nAny], aArgumentBuffer );
175 if ( nAny+1 < aSeq.getLength() )
176 // not last argument
177 aArgumentBuffer.append(",");
180 aArgumentBuffer.append(")");
182 else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE )
184 // convert to "Sequence of any"
185 css::uno::Sequence < css::uno::Any > aSeq;
186 css::uno::Any aNew;
187 try { aNew = m_xConverter->convertTo( aValue, cppu::UnoType<css::uno::Sequence < css::uno::Any >>::get() ); }
188 catch (const css::uno::Exception&) {}
190 aNew >>= aSeq;
191 aArgumentBuffer.append("Array(");
192 for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
194 AppendToBuffer( aSeq[nAny], aArgumentBuffer );
195 if ( nAny+1 < aSeq.getLength() )
196 // not last argument
197 aArgumentBuffer.append(",");
200 aArgumentBuffer.append(")");
202 else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING )
204 // strings need \"
205 OUString sVal;
206 aValue >>= sVal;
208 // encode non printable characters or '"' by using the CHR$ function
209 if ( !sVal.isEmpty() )
211 const sal_Unicode* pChars = sVal.getStr();
212 bool bInString = false;
213 for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ )
215 if ( pChars[nChar] < 32 || pChars[nChar] == '"' )
217 // problematic character detected
218 if ( bInString )
220 // close current string
221 aArgumentBuffer.append("\"");
222 bInString = false;
225 if ( nChar>0 )
226 // if this is not the first character, parts of the string have already been added
227 aArgumentBuffer.append("+");
229 // add the character constant
230 aArgumentBuffer.append("CHR$(");
231 aArgumentBuffer.append( static_cast<sal_Int32>(pChars[nChar]) );
232 aArgumentBuffer.append(")");
234 else
236 if ( !bInString )
238 if ( nChar>0 )
239 // if this is not the first character, parts of the string have already been added
240 aArgumentBuffer.append("+");
242 // start a new string
243 aArgumentBuffer.append("\"");
244 bInString = true;
247 aArgumentBuffer.append( pChars[nChar] );
251 // close string
252 if ( bInString )
253 aArgumentBuffer.append("\"");
255 else
256 aArgumentBuffer.append("\"\"");
258 else if (auto nVal = o3tl::tryAccess<sal_Unicode>(aValue))
260 // character variables are recorded as strings, back conversion must be handled in client code
261 aArgumentBuffer.append("\"");
262 if ( *nVal == '\"' )
263 // encode \" to \"\"
264 aArgumentBuffer.append(*nVal);
265 aArgumentBuffer.append(*nVal);
266 aArgumentBuffer.append("\"");
268 else
270 css::uno::Any aNew;
273 aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING );
275 catch (const css::script::CannotConvertException&) {}
276 catch (const css::uno::Exception&) {}
277 OUString sVal;
278 aNew >>= sVal;
280 if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM )
282 OUString aName = aValue.getValueType().getTypeName();
283 aArgumentBuffer.append( aName );
284 aArgumentBuffer.append(".");
287 aArgumentBuffer.append(sVal);
291 void DispatchRecorder::implts_recordMacro( std::u16string_view aURL,
292 const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
293 bool bAsComment, OUStringBuffer& aScriptBuffer )
295 OUStringBuffer aArgumentBuffer(1000);
296 // this value is used to name the arrays of aArgumentBuffer
297 OUString sArrayName = "args" + OUString::number(m_nRecordingID);
299 aScriptBuffer.append("rem ----------------------------------------------------------------------\n");
301 sal_Int32 nLength = lArguments.getLength();
302 sal_Int32 nValidArgs = 0;
303 for( sal_Int32 i=0; i<nLength; ++i )
305 if(!lArguments[i].Value.hasValue())
306 continue;
308 OUStringBuffer sValBuffer(100);
311 AppendToBuffer(lArguments[i].Value, sValBuffer);
313 catch(const css::uno::Exception&)
315 sValBuffer.setLength(0);
317 if (sValBuffer.isEmpty())
318 continue;
321 // add arg().Name
322 if(bAsComment)
323 aArgumentBuffer.append(REM_AS_COMMENT);
324 aArgumentBuffer.append(sArrayName
325 + "(" + OUString::number(nValidArgs)
326 + ").Name = \"" + lArguments[i].Name
327 + "\"\n");
329 // add arg().Value
330 if(bAsComment)
331 aArgumentBuffer.append(REM_AS_COMMENT);
332 aArgumentBuffer.append(sArrayName
333 + "(" + OUString::number(nValidArgs)
334 + ").Value = " + sValBuffer + "\n");
336 ++nValidArgs;
340 // if aArgumentBuffer exist - pack it into the aScriptBuffer
341 if(nValidArgs>0)
343 if(bAsComment)
344 aScriptBuffer.append(REM_AS_COMMENT);
345 aScriptBuffer.append("dim ");
346 aScriptBuffer.append (sArrayName);
347 aScriptBuffer.append("(");
348 aScriptBuffer.append (static_cast<sal_Int32>(nValidArgs-1)); // 0 based!
349 aScriptBuffer.append(") as new com.sun.star.beans.PropertyValue\n");
350 aScriptBuffer.append (aArgumentBuffer);
351 aScriptBuffer.append("\n");
354 // add code for dispatches
355 if(bAsComment)
356 aScriptBuffer.append(REM_AS_COMMENT);
357 aScriptBuffer.append("dispatcher.executeDispatch(document, \"");
358 aScriptBuffer.append(aURL);
359 aScriptBuffer.append("\", \"\", 0, ");
360 if(nValidArgs<1)
361 aScriptBuffer.append("Array()");
362 else
364 aScriptBuffer.append( sArrayName );
365 aScriptBuffer.append("()");
367 aScriptBuffer.append(")\n\n");
369 /* SAFE { */
370 m_nRecordingID++;
371 /* } */
374 css::uno::Type SAL_CALL DispatchRecorder::getElementType()
376 return cppu::UnoType<css::frame::DispatchStatement>::get();
379 sal_Bool SAL_CALL DispatchRecorder::hasElements()
381 return (! m_aStatements.empty());
384 sal_Int32 SAL_CALL DispatchRecorder::getCount()
386 return m_aStatements.size();
389 css::uno::Any SAL_CALL DispatchRecorder::getByIndex(sal_Int32 idx)
391 if (idx >= static_cast<sal_Int32>(m_aStatements.size()))
392 throw css::lang::IndexOutOfBoundsException( "Dispatch recorder out of bounds" );
394 Any element(&m_aStatements[idx],
395 cppu::UnoType<css::frame::DispatchStatement>::get());
397 return element;
400 void SAL_CALL DispatchRecorder::replaceByIndex(sal_Int32 idx, const css::uno::Any& element)
402 if (element.getValueType() !=
403 cppu::UnoType<css::frame::DispatchStatement>::get()) {
404 throw css::lang::IllegalArgumentException(
405 "Illegal argument in dispatch recorder",
406 Reference< XInterface >(), 2 );
409 if (idx >= static_cast<sal_Int32>(m_aStatements.size()))
410 throw css::lang::IndexOutOfBoundsException(
411 "Dispatch recorder out of bounds" );
413 auto pStatement = o3tl::doAccess<css::frame::DispatchStatement>(element);
415 css::frame::DispatchStatement aStatement(
416 pStatement->aCommand,
417 pStatement->aTarget,
418 pStatement->aArgs,
419 pStatement->nFlags,
420 pStatement->bIsComment);
422 m_aStatements[idx] = aStatement;
425 } // namespace framework
428 extern "C" SAL_DLLPUBLIC_EXPORT css::uno::XInterface*
429 framework_DispatchRecorder_get_implementation(
430 css::uno::XComponentContext* context, css::uno::Sequence<css::uno::Any> const& )
432 return cppu::acquire(new framework::DispatchRecorder(context));
435 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */