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