Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
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/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
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 .
18 : */
19 :
20 :
21 : #include <recording/dispatchrecorder.hxx>
22 : #include <com/sun/star/frame/DispatchStatement.hpp>
23 : #include <com/sun/star/script/Converter.hpp>
24 : #include <threadhelp/writeguard.hxx>
25 : #include <threadhelp/readguard.hxx>
26 : #include <services.h>
27 : #include <vcl/svapp.hxx>
28 : #include <comphelper/processfactory.hxx>
29 :
30 : using namespace ::com::sun::star::uno;
31 :
32 : namespace framework{
33 :
34 : // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ...
35 : #define REM_AS_COMMENT "rem "
36 :
37 : //*****************************************************************************************************************
38 : // XInterface, XTypeProvider, XServiceInfo
39 : //*****************************************************************************************************************
40 98 : DEFINE_XINTERFACE_6(
41 : DispatchRecorder,
42 : OWeakObject,
43 : DIRECT_INTERFACE(css::lang::XTypeProvider),
44 : DIRECT_INTERFACE(css::lang::XServiceInfo),
45 : DIRECT_INTERFACE(css::frame::XDispatchRecorder),
46 : DIRECT_INTERFACE(css::container::XIndexReplace),
47 : DIRECT_INTERFACE(css::container::XIndexAccess),
48 : DIRECT_INTERFACE(css::container::XElementAccess))
49 :
50 0 : DEFINE_XTYPEPROVIDER_6(
51 : DispatchRecorder,
52 : css::lang::XTypeProvider,
53 : css::lang::XServiceInfo,
54 : css::frame::XDispatchRecorder,
55 : css::container::XIndexReplace,
56 : css::container::XIndexAccess,
57 : css::container::XElementAccess)
58 :
59 45 : DEFINE_XSERVICEINFO_MULTISERVICE_2(
60 : DispatchRecorder,
61 : ::cppu::OWeakObject,
62 : OUString("com.sun.star.frame.DispatchRecorder"),
63 : OUString("com.sun.star.comp.framework.DispatchRecorder"))
64 :
65 2 : DEFINE_INIT_SERVICE(
66 : DispatchRecorder,
67 : {
68 : }
69 : )
70 :
71 : #include <typelib/typedescription.h>
72 :
73 : //--------------------------------------------------------------------------------------------------
74 0 : void flatten_struct_members(
75 : ::std::vector< Any > * vec, void const * data,
76 : typelib_CompoundTypeDescription * pTD )
77 : SAL_THROW(())
78 : {
79 0 : if (pTD->pBaseTypeDescription)
80 : {
81 0 : flatten_struct_members( vec, data, pTD->pBaseTypeDescription );
82 : }
83 0 : for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos )
84 : {
85 : vec->push_back(
86 0 : Any( (char const *)data + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) );
87 : }
88 0 : }
89 : //==================================================================================================
90 0 : Sequence< Any > make_seq_out_of_struct(
91 : Any const & val )
92 : SAL_THROW( (RuntimeException) )
93 : {
94 0 : Type const & type = val.getValueType();
95 0 : TypeClass eTypeClass = type.getTypeClass();
96 0 : if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass)
97 : {
98 : throw RuntimeException(
99 0 : type.getTypeName() +
100 0 : OUString( "is no struct or exception!" ),
101 0 : Reference< XInterface >() );
102 : }
103 0 : typelib_TypeDescription * pTD = 0;
104 0 : TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() );
105 : OSL_ASSERT( pTD );
106 0 : if (! pTD)
107 : {
108 : throw RuntimeException(
109 0 : OUString( "cannot get type descr of type " ) +
110 0 : type.getTypeName(),
111 0 : Reference< XInterface >() );
112 : }
113 :
114 0 : ::std::vector< Any > vec;
115 0 : vec.reserve( ((typelib_CompoundTypeDescription *)pTD)->nMembers ); // good guess
116 0 : flatten_struct_members( &vec, val.getValue(), (typelib_CompoundTypeDescription *)pTD );
117 0 : TYPELIB_DANGER_RELEASE( pTD );
118 0 : return Sequence< Any >( &vec[ 0 ], vec.size() );
119 : }
120 :
121 : //***********************************************************************
122 2 : DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xContext )
123 2 : : ThreadHelpBase ( &Application::GetSolarMutex() )
124 : , ::cppu::OWeakObject( )
125 2 : , m_xConverter( css::script::Converter::create(xContext) )
126 : {
127 2 : }
128 :
129 : //************************************************************************
130 4 : DispatchRecorder::~DispatchRecorder()
131 : {
132 4 : }
133 :
134 : //*************************************************************************
135 : // generate header
136 8 : void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& ) throw( css::uno::RuntimeException )
137 : {
138 : /* SAFE{ */
139 : /* } */
140 8 : }
141 :
142 : //*************************************************************************
143 3 : void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL,
144 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException )
145 : {
146 3 : OUString aTarget;
147 :
148 6 : com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_False );
149 6 : m_aStatements.push_back( aStatement );
150 3 : }
151 :
152 : //*************************************************************************
153 2 : void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL,
154 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException )
155 : {
156 2 : OUString aTarget;
157 :
158 : // last parameter must be set to true -> it's a comment
159 4 : com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_True );
160 4 : m_aStatements.push_back( aStatement );
161 2 : }
162 :
163 : //*************************************************************************
164 8 : void SAL_CALL DispatchRecorder::endRecording() throw( css::uno::RuntimeException )
165 : {
166 : /* SAFE{ */
167 8 : WriteGuard aWriteLock(m_aLock);
168 8 : m_aStatements.clear();
169 : /* } */
170 8 : }
171 :
172 : //*************************************************************************
173 8 : OUString SAL_CALL DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException )
174 : {
175 : /* SAFE{ */
176 8 : WriteGuard aWriteLock(m_aLock);
177 :
178 8 : if ( m_aStatements.empty() )
179 4 : return OUString();
180 :
181 8 : OUStringBuffer aScriptBuffer;
182 4 : aScriptBuffer.ensureCapacity(10000);
183 4 : m_nRecordingID = 1;
184 :
185 4 : aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n");
186 4 : aScriptBuffer.appendAscii("rem define variables\n");
187 4 : aScriptBuffer.appendAscii("dim document as object\n");
188 4 : aScriptBuffer.appendAscii("dim dispatcher as object\n");
189 4 : aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n");
190 4 : aScriptBuffer.appendAscii("rem get access to the document\n");
191 4 : aScriptBuffer.appendAscii("document = ThisComponent.CurrentController.Frame\n");
192 4 : aScriptBuffer.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
193 :
194 4 : std::vector< com::sun::star::frame::DispatchStatement>::iterator p;
195 8 : for ( p = m_aStatements.begin(); p != m_aStatements.end(); ++p )
196 4 : implts_recordMacro( p->aCommand, p->aArgs, p->bIsComment, aScriptBuffer );
197 8 : OUString sScript = aScriptBuffer.makeStringAndClear();
198 12 : return sScript;
199 : /* } */
200 : }
201 :
202 : //*************************************************************************
203 3 : void SAL_CALL DispatchRecorder::AppendToBuffer( css::uno::Any aValue, OUStringBuffer& aArgumentBuffer )
204 : {
205 : // if value == bool
206 3 : if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT )
207 : {
208 : // structs are recorded as arrays, convert to "Sequence of any"
209 0 : Sequence< Any > aSeq = make_seq_out_of_struct( aValue );
210 0 : aArgumentBuffer.appendAscii("Array(");
211 0 : for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
212 : {
213 0 : AppendToBuffer( aSeq[nAny], aArgumentBuffer );
214 0 : if ( nAny+1 < aSeq.getLength() )
215 : // not last argument
216 0 : aArgumentBuffer.appendAscii(",");
217 : }
218 :
219 0 : aArgumentBuffer.appendAscii(")");
220 : }
221 3 : else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE )
222 : {
223 : // convert to "Sequence of any"
224 0 : css::uno::Sequence < css::uno::Any > aSeq;
225 0 : css::uno::Any aNew;
226 0 : try { aNew = m_xConverter->convertTo( aValue, ::getCppuType((const css::uno::Sequence < css::uno::Any >*)0) ); }
227 0 : catch (const css::uno::Exception&) {}
228 :
229 0 : aNew >>= aSeq;
230 0 : aArgumentBuffer.appendAscii("Array(");
231 0 : for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
232 : {
233 0 : AppendToBuffer( aSeq[nAny], aArgumentBuffer );
234 0 : if ( nAny+1 < aSeq.getLength() )
235 : // not last argument
236 0 : aArgumentBuffer.appendAscii(",");
237 : }
238 :
239 0 : aArgumentBuffer.appendAscii(")");
240 : }
241 3 : else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING )
242 : {
243 : // strings need \"
244 3 : OUString sVal;
245 3 : aValue >>= sVal;
246 :
247 : // encode non printable characters or '"' by using the CHR$ function
248 3 : if ( !sVal.isEmpty() )
249 : {
250 3 : const sal_Unicode* pChars = sVal.getStr();
251 3 : sal_Bool bInString = sal_False;
252 105 : for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ )
253 : {
254 102 : if ( pChars[nChar] < 32 || pChars[nChar] == '"' )
255 : {
256 : // problematic character detected
257 0 : if ( bInString )
258 : {
259 : // close current string
260 0 : aArgumentBuffer.appendAscii("\"");
261 0 : bInString = sal_False;
262 : }
263 :
264 0 : if ( nChar>0 )
265 : // if this is not the first character, parts of the string have already been added
266 0 : aArgumentBuffer.appendAscii("+");
267 :
268 : // add the character constant
269 0 : aArgumentBuffer.appendAscii("CHR$(");
270 0 : aArgumentBuffer.append( (sal_Int32) pChars[nChar] );
271 0 : aArgumentBuffer.appendAscii(")");
272 : }
273 : else
274 : {
275 102 : if ( !bInString )
276 : {
277 3 : if ( nChar>0 )
278 : // if this is not the first character, parts of the string have already been added
279 0 : aArgumentBuffer.appendAscii("+");
280 :
281 : // start a new string
282 3 : aArgumentBuffer.appendAscii("\"");
283 3 : bInString = sal_True;
284 : }
285 :
286 102 : aArgumentBuffer.append( pChars[nChar] );
287 : }
288 : }
289 :
290 : // close string
291 3 : if ( bInString )
292 3 : aArgumentBuffer.appendAscii("\"");
293 : }
294 : else
295 0 : aArgumentBuffer.appendAscii("\"\"");
296 : }
297 0 : else if (aValue.getValueType() == getCppuCharType())
298 : {
299 : // character variables are recorded as strings, back conversion must be handled in client code
300 0 : sal_Unicode nVal = *((sal_Unicode*)aValue.getValue());
301 0 : aArgumentBuffer.appendAscii("\"");
302 0 : if ( (sal_Unicode(nVal) == '\"') )
303 : // encode \" to \"\"
304 0 : aArgumentBuffer.append((sal_Unicode)nVal);
305 0 : aArgumentBuffer.append((sal_Unicode)nVal);
306 0 : aArgumentBuffer.appendAscii("\"");
307 : }
308 : else
309 : {
310 0 : css::uno::Any aNew;
311 : try
312 : {
313 0 : aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING );
314 : }
315 0 : catch (const css::script::CannotConvertException&) {}
316 0 : catch (const css::uno::Exception&) {}
317 0 : OUString sVal;
318 0 : aNew >>= sVal;
319 :
320 0 : if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM )
321 : {
322 0 : OUString aName = aValue.getValueType().getTypeName();
323 0 : aArgumentBuffer.append( aName );
324 0 : aArgumentBuffer.appendAscii(".");
325 : }
326 :
327 0 : aArgumentBuffer.append(sVal);
328 : }
329 3 : }
330 :
331 4 : void SAL_CALL DispatchRecorder::implts_recordMacro( const OUString& aURL,
332 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
333 : sal_Bool bAsComment, OUStringBuffer& aScriptBuffer )
334 : {
335 4 : OUStringBuffer aArgumentBuffer(1000);
336 8 : OUString sArrayName;
337 : // this value is used to name the arrays of aArgumentBuffer
338 4 : sArrayName = OUString("args");
339 4 : sArrayName += OUString::valueOf((sal_Int32)m_nRecordingID);
340 :
341 4 : aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n");
342 :
343 4 : sal_Int32 nLength = lArguments.getLength();
344 4 : sal_Int32 nValidArgs = 0;
345 7 : for( sal_Int32 i=0; i<nLength; ++i )
346 : {
347 3 : if(!lArguments[i].Value.hasValue())
348 0 : continue;
349 :
350 3 : OUStringBuffer sValBuffer(100);
351 : try
352 : {
353 3 : AppendToBuffer(lArguments[i].Value, sValBuffer);
354 : }
355 0 : catch(const css::uno::Exception&)
356 : {
357 0 : sValBuffer.setLength(0);
358 : }
359 3 : if (sValBuffer.isEmpty())
360 0 : continue;
361 :
362 : {
363 : // add arg().Name
364 3 : if(bAsComment)
365 1 : aArgumentBuffer.appendAscii(REM_AS_COMMENT);
366 3 : aArgumentBuffer.append (sArrayName);
367 3 : aArgumentBuffer.appendAscii("(");
368 3 : aArgumentBuffer.append (nValidArgs);
369 3 : aArgumentBuffer.appendAscii(").Name = \"");
370 3 : aArgumentBuffer.append (lArguments[i].Name);
371 3 : aArgumentBuffer.appendAscii("\"\n");
372 :
373 : // add arg().Value
374 3 : if(bAsComment)
375 1 : aArgumentBuffer.appendAscii(REM_AS_COMMENT);
376 3 : aArgumentBuffer.append (sArrayName);
377 3 : aArgumentBuffer.appendAscii("(");
378 3 : aArgumentBuffer.append (nValidArgs);
379 3 : aArgumentBuffer.appendAscii(").Value = ");
380 3 : aArgumentBuffer.append (sValBuffer.makeStringAndClear());
381 3 : aArgumentBuffer.appendAscii("\n");
382 :
383 3 : ++nValidArgs;
384 : }
385 3 : }
386 :
387 : // if aArgumentBuffer exist - pack it into the aScriptBuffer
388 4 : if(nValidArgs>0)
389 : {
390 3 : if(bAsComment)
391 1 : aScriptBuffer.appendAscii(REM_AS_COMMENT);
392 3 : aScriptBuffer.appendAscii("dim ");
393 3 : aScriptBuffer.append (sArrayName);
394 3 : aScriptBuffer.appendAscii("(");
395 3 : aScriptBuffer.append ((sal_Int32)(nValidArgs-1)); // 0 based!
396 3 : aScriptBuffer.appendAscii(") as new com.sun.star.beans.PropertyValue\n");
397 3 : aScriptBuffer.append (aArgumentBuffer.makeStringAndClear());
398 3 : aScriptBuffer.appendAscii("\n");
399 : }
400 :
401 : // add code for dispatches
402 4 : if(bAsComment)
403 2 : aScriptBuffer.appendAscii(REM_AS_COMMENT);
404 4 : aScriptBuffer.appendAscii("dispatcher.executeDispatch(document, \"");
405 4 : aScriptBuffer.append (aURL);
406 4 : aScriptBuffer.appendAscii("\", \"\", 0, ");
407 4 : if(nValidArgs<1)
408 1 : aScriptBuffer.appendAscii("Array()");
409 : else
410 : {
411 3 : aScriptBuffer.append( sArrayName.getStr() );
412 3 : aScriptBuffer.appendAscii("()");
413 : }
414 4 : aScriptBuffer.appendAscii(")\n\n");
415 :
416 : /* SAFE { */
417 8 : m_nRecordingID++;
418 : /* } */
419 4 : }
420 :
421 1 : com::sun::star::uno::Type SAL_CALL DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException)
422 : {
423 1 : return ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL);
424 : }
425 :
426 1 : sal_Bool SAL_CALL DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException)
427 : {
428 1 : return (! m_aStatements.empty());
429 : }
430 :
431 2 : sal_Int32 SAL_CALL DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException)
432 : {
433 2 : return m_aStatements.size();
434 : }
435 :
436 6 : 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)
437 : {
438 6 : if (idx >= (sal_Int32)m_aStatements.size()) {
439 : throw com::sun::star::lang::IndexOutOfBoundsException(
440 : OUString( "Dispatch recorder out of bounds" ),
441 1 : Reference< XInterface >() );
442 :
443 : }
444 :
445 5 : Any element(&m_aStatements[idx],
446 10 : ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL));
447 :
448 5 : return element;
449 : }
450 :
451 4 : 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)
452 : {
453 4 : if (element.getValueType() !=
454 4 : ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL)) {
455 : throw com::sun::star::lang::IllegalArgumentException(
456 : OUString( "Illegal argument in dispatch recorder" ),
457 1 : Reference< XInterface >(), 2 );
458 : }
459 :
460 3 : if (idx >= (sal_Int32)m_aStatements.size()) {
461 : throw com::sun::star::lang::IndexOutOfBoundsException(
462 : OUString( "Dispatch recorder out of bounds" ),
463 1 : Reference< XInterface >() );
464 :
465 : }
466 :
467 : com::sun::star::frame::DispatchStatement *pStatement;
468 :
469 2 : pStatement = (com::sun::star::frame::DispatchStatement *)element.getValue();
470 :
471 : com::sun::star::frame::DispatchStatement aStatement(
472 : pStatement->aCommand,
473 : pStatement->aTarget,
474 : pStatement->aArgs,
475 : pStatement->nFlags,
476 2 : pStatement->bIsComment);
477 :
478 2 : m_aStatements[idx] = aStatement;
479 2 : }
480 :
481 90 : } // namespace framework
482 :
483 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|