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