build fix: no comphelper/profilezone.hxx in this branch
[LibreOffice.git] / framework / source / recording / dispatchrecorder.cxx
blobaca5335d616403b92c6b94ba9bd2b00396627f59
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/script/Converter.hpp>
23 #include <o3tl/any.hxx>
24 #include <services.h>
25 #include <vcl/svapp.hxx>
26 #include <comphelper/processfactory.hxx>
27 #include <typelib/typedescription.h>
29 using namespace ::com::sun::star::uno;
31 namespace framework{
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(
39 DispatchRecorder,
40 ::cppu::OWeakObject,
41 "com.sun.star.frame.DispatchRecorder",
42 OUString("com.sun.star.comp.framework.DispatchRecorder"))
44 DEFINE_INIT_SERVICE(
45 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 )
61 vec->push_back(
62 Any( static_cast<char const *>(data) + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) );
66 Sequence< Any > make_seq_out_of_struct(
67 Any const & val )
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() );
78 OSL_ASSERT( pTD );
79 if (! pTD)
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 )
93 : m_nRecordingID(0)
94 , m_xConverter(css::script::Converter::create(xContext))
98 DispatchRecorder::~DispatchRecorder()
102 // generate header
103 void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& ) throw( css::uno::RuntimeException, std::exception )
105 /* SAFE{ */
106 /* } */
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 )
112 OUString aTarget;
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 )
121 OUString aTarget;
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 )
130 SolarMutexGuard g;
131 m_aStatements.clear();
134 OUString SAL_CALL DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException, std::exception )
136 SolarMutexGuard g;
138 if ( m_aStatements.empty() )
139 return OUString();
141 OUStringBuffer aScriptBuffer;
142 aScriptBuffer.ensureCapacity(10000);
143 m_nRecordingID = 1;
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();
158 return sScript;
161 void SAL_CALL DispatchRecorder::AppendToBuffer( const css::uno::Any& aValue, OUStringBuffer& aArgumentBuffer )
163 // if value == bool
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() )
173 // not last argument
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;
183 css::uno::Any aNew;
184 try { aNew = m_xConverter->convertTo( aValue, cppu::UnoType<css::uno::Sequence < css::uno::Any >>::get() ); }
185 catch (const css::uno::Exception&) {}
187 aNew >>= aSeq;
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() )
193 // not last argument
194 aArgumentBuffer.append(",");
197 aArgumentBuffer.append(")");
199 else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING )
201 // strings need \"
202 OUString sVal;
203 aValue >>= sVal;
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
215 if ( bInString )
217 // close current string
218 aArgumentBuffer.append("\"");
219 bInString = false;
222 if ( nChar>0 )
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(")");
231 else
233 if ( !bInString )
235 if ( nChar>0 )
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("\"");
241 bInString = true;
244 aArgumentBuffer.append( pChars[nChar] );
248 // close string
249 if ( bInString )
250 aArgumentBuffer.append("\"");
252 else
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 == '\"') )
260 // encode \" to \"\"
261 aArgumentBuffer.append(*nVal);
262 aArgumentBuffer.append(*nVal);
263 aArgumentBuffer.append("\"");
265 else
267 css::uno::Any aNew;
270 aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING );
272 catch (const css::script::CannotConvertException&) {}
273 catch (const css::uno::Exception&) {}
274 OUString sVal;
275 aNew >>= sVal;
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);
293 OUString sArrayName;
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())
304 continue;
306 OUStringBuffer sValBuffer(100);
309 AppendToBuffer(lArguments[i].Value, sValBuffer);
311 catch(const css::uno::Exception&)
313 sValBuffer.setLength(0);
315 if (sValBuffer.isEmpty())
316 continue;
319 // add arg().Name
320 if(bAsComment)
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");
329 // add arg().Value
330 if(bAsComment)
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");
339 ++nValidArgs;
343 // if aArgumentBuffer exist - pack it into the aScriptBuffer
344 if(nValidArgs>0)
346 if(bAsComment)
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
358 if(bAsComment)
359 aScriptBuffer.append(REM_AS_COMMENT);
360 aScriptBuffer.append("dispatcher.executeDispatch(document, \"");
361 aScriptBuffer.append (aURL);
362 aScriptBuffer.append("\", \"\", 0, ");
363 if(nValidArgs<1)
364 aScriptBuffer.append("Array()");
365 else
367 aScriptBuffer.append( sArrayName.getStr() );
368 aScriptBuffer.append("()");
370 aScriptBuffer.append(")\n\n");
372 /* SAFE { */
373 m_nRecordingID++;
374 /* } */
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());
401 return element;
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,
423 pStatement->aTarget,
424 pStatement->aArgs,
425 pStatement->nFlags,
426 pStatement->bIsComment);
428 m_aStatements[idx] = aStatement;
431 } // namespace framework
433 /* vim:set shiftwidth=4 softtabstop=4 expandtab: */