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