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