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