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