Line data Source code
1 : /* -*- Mode: C++; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
2 : /*
3 : * This file is part of the LibreOffice project.
4 : *
5 : * This Source Code Form is subject to the terms of the Mozilla Public
6 : * License, v. 2.0. If a copy of the MPL was not distributed with this
7 : * file, You can obtain one at http://mozilla.org/MPL/2.0/.
8 : *
9 : * This file incorporates work covered by the following license notice:
10 : *
11 : * Licensed to the Apache Software Foundation (ASF) under one or more
12 : * contributor license agreements. See the NOTICE file distributed
13 : * with this work for additional information regarding copyright
14 : * ownership. The ASF licenses this file to you under the Apache
15 : * License, Version 2.0 (the "License"); you may not use this file
16 : * except in compliance with the License. You may obtain a copy of
17 : * the License at http://www.apache.org/licenses/LICENSE-2.0 .
18 : */
19 :
20 : #include <recording/dispatchrecorder.hxx>
21 : #include <com/sun/star/frame/DispatchStatement.hpp>
22 : #include <com/sun/star/script/Converter.hpp>
23 : #include <services.h>
24 : #include <vcl/svapp.hxx>
25 : #include <comphelper/processfactory.hxx>
26 :
27 : using namespace ::com::sun::star::uno;
28 :
29 : namespace framework{
30 :
31 : // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ...
32 : #define REM_AS_COMMENT "rem "
33 :
34 : // XInterface, XTypeProvider, XServiceInfo
35 :
36 0 : DEFINE_XSERVICEINFO_MULTISERVICE_2(
37 : DispatchRecorder,
38 : ::cppu::OWeakObject,
39 : OUString("com.sun.star.frame.DispatchRecorder"),
40 : OUString("com.sun.star.comp.framework.DispatchRecorder"))
41 :
42 0 : DEFINE_INIT_SERVICE(
43 : DispatchRecorder,
44 : {
45 : }
46 : )
47 :
48 : #include <typelib/typedescription.h>
49 :
50 0 : void flatten_struct_members(
51 : ::std::vector< Any > * vec, void const * data,
52 : typelib_CompoundTypeDescription * pTD )
53 : SAL_THROW(())
54 : {
55 0 : if (pTD->pBaseTypeDescription)
56 : {
57 0 : flatten_struct_members( vec, data, pTD->pBaseTypeDescription );
58 : }
59 0 : for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos )
60 : {
61 : vec->push_back(
62 0 : Any( (char const *)data + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) );
63 : }
64 0 : }
65 :
66 0 : Sequence< Any > make_seq_out_of_struct(
67 : Any const & val )
68 : SAL_THROW( (RuntimeException) )
69 : {
70 0 : Type const & type = val.getValueType();
71 0 : TypeClass eTypeClass = type.getTypeClass();
72 0 : if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass)
73 : {
74 : throw RuntimeException(
75 0 : type.getTypeName() +
76 0 : OUString( "is no struct or exception!" ),
77 0 : Reference< XInterface >() );
78 : }
79 0 : typelib_TypeDescription * pTD = 0;
80 0 : TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() );
81 : OSL_ASSERT( pTD );
82 0 : if (! pTD)
83 : {
84 : throw RuntimeException(
85 0 : OUString( "cannot get type descr of type " ) +
86 0 : type.getTypeName(),
87 0 : Reference< XInterface >() );
88 : }
89 :
90 0 : ::std::vector< Any > vec;
91 0 : vec.reserve( ((typelib_CompoundTypeDescription *)pTD)->nMembers ); // good guess
92 0 : flatten_struct_members( &vec, val.getValue(), (typelib_CompoundTypeDescription *)pTD );
93 0 : TYPELIB_DANGER_RELEASE( pTD );
94 0 : return Sequence< Any >( &vec[ 0 ], vec.size() );
95 : }
96 :
97 0 : DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::uno::XComponentContext >& xContext )
98 : : m_nRecordingID(0)
99 0 : , m_xConverter(css::script::Converter::create(xContext))
100 : {
101 0 : }
102 :
103 0 : DispatchRecorder::~DispatchRecorder()
104 : {
105 0 : }
106 :
107 : // generate header
108 0 : void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& ) throw( css::uno::RuntimeException, std::exception )
109 : {
110 : /* SAFE{ */
111 : /* } */
112 0 : }
113 :
114 0 : void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL,
115 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException, std::exception )
116 : {
117 0 : OUString aTarget;
118 :
119 0 : com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_False );
120 0 : m_aStatements.push_back( aStatement );
121 0 : }
122 :
123 0 : void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL,
124 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException, std::exception )
125 : {
126 0 : OUString aTarget;
127 :
128 : // last parameter must be set to true -> it's a comment
129 0 : com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_True );
130 0 : m_aStatements.push_back( aStatement );
131 0 : }
132 :
133 0 : void SAL_CALL DispatchRecorder::endRecording() throw( css::uno::RuntimeException, std::exception )
134 : {
135 0 : SolarMutexGuard g;
136 0 : m_aStatements.clear();
137 0 : }
138 :
139 0 : OUString SAL_CALL DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException, std::exception )
140 : {
141 0 : SolarMutexGuard g;
142 :
143 0 : if ( m_aStatements.empty() )
144 0 : return OUString();
145 :
146 0 : OUStringBuffer aScriptBuffer;
147 0 : aScriptBuffer.ensureCapacity(10000);
148 0 : m_nRecordingID = 1;
149 :
150 0 : aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n");
151 0 : aScriptBuffer.appendAscii("rem define variables\n");
152 0 : aScriptBuffer.appendAscii("dim document as object\n");
153 0 : aScriptBuffer.appendAscii("dim dispatcher as object\n");
154 0 : aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n");
155 0 : aScriptBuffer.appendAscii("rem get access to the document\n");
156 0 : aScriptBuffer.appendAscii("document = ThisComponent.CurrentController.Frame\n");
157 0 : aScriptBuffer.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n");
158 :
159 0 : std::vector< com::sun::star::frame::DispatchStatement>::iterator p;
160 0 : for ( p = m_aStatements.begin(); p != m_aStatements.end(); ++p )
161 0 : implts_recordMacro( p->aCommand, p->aArgs, p->bIsComment, aScriptBuffer );
162 0 : OUString sScript = aScriptBuffer.makeStringAndClear();
163 0 : return sScript;
164 : }
165 :
166 0 : void SAL_CALL DispatchRecorder::AppendToBuffer( css::uno::Any aValue, OUStringBuffer& aArgumentBuffer )
167 : {
168 : // if value == bool
169 0 : if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT )
170 : {
171 : // structs are recorded as arrays, convert to "Sequence of any"
172 0 : Sequence< Any > aSeq = make_seq_out_of_struct( aValue );
173 0 : aArgumentBuffer.appendAscii("Array(");
174 0 : for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
175 : {
176 0 : AppendToBuffer( aSeq[nAny], aArgumentBuffer );
177 0 : if ( nAny+1 < aSeq.getLength() )
178 : // not last argument
179 0 : aArgumentBuffer.appendAscii(",");
180 : }
181 :
182 0 : aArgumentBuffer.appendAscii(")");
183 : }
184 0 : else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE )
185 : {
186 : // convert to "Sequence of any"
187 0 : css::uno::Sequence < css::uno::Any > aSeq;
188 0 : css::uno::Any aNew;
189 0 : try { aNew = m_xConverter->convertTo( aValue, ::getCppuType((const css::uno::Sequence < css::uno::Any >*)0) ); }
190 0 : catch (const css::uno::Exception&) {}
191 :
192 0 : aNew >>= aSeq;
193 0 : aArgumentBuffer.appendAscii("Array(");
194 0 : for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ )
195 : {
196 0 : AppendToBuffer( aSeq[nAny], aArgumentBuffer );
197 0 : if ( nAny+1 < aSeq.getLength() )
198 : // not last argument
199 0 : aArgumentBuffer.appendAscii(",");
200 : }
201 :
202 0 : aArgumentBuffer.appendAscii(")");
203 : }
204 0 : else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING )
205 : {
206 : // strings need \"
207 0 : OUString sVal;
208 0 : aValue >>= sVal;
209 :
210 : // encode non printable characters or '"' by using the CHR$ function
211 0 : if ( !sVal.isEmpty() )
212 : {
213 0 : const sal_Unicode* pChars = sVal.getStr();
214 0 : bool bInString = false;
215 0 : for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ )
216 : {
217 0 : if ( pChars[nChar] < 32 || pChars[nChar] == '"' )
218 : {
219 : // problematic character detected
220 0 : if ( bInString )
221 : {
222 : // close current string
223 0 : aArgumentBuffer.appendAscii("\"");
224 0 : bInString = false;
225 : }
226 :
227 0 : if ( nChar>0 )
228 : // if this is not the first character, parts of the string have already been added
229 0 : aArgumentBuffer.appendAscii("+");
230 :
231 : // add the character constant
232 0 : aArgumentBuffer.appendAscii("CHR$(");
233 0 : aArgumentBuffer.append( (sal_Int32) pChars[nChar] );
234 0 : aArgumentBuffer.appendAscii(")");
235 : }
236 : else
237 : {
238 0 : if ( !bInString )
239 : {
240 0 : if ( nChar>0 )
241 : // if this is not the first character, parts of the string have already been added
242 0 : aArgumentBuffer.appendAscii("+");
243 :
244 : // start a new string
245 0 : aArgumentBuffer.appendAscii("\"");
246 0 : bInString = true;
247 : }
248 :
249 0 : aArgumentBuffer.append( pChars[nChar] );
250 : }
251 : }
252 :
253 : // close string
254 0 : if ( bInString )
255 0 : aArgumentBuffer.appendAscii("\"");
256 : }
257 : else
258 0 : aArgumentBuffer.appendAscii("\"\"");
259 : }
260 0 : else if (aValue.getValueType() == getCppuCharType())
261 : {
262 : // character variables are recorded as strings, back conversion must be handled in client code
263 0 : sal_Unicode nVal = *((sal_Unicode*)aValue.getValue());
264 0 : aArgumentBuffer.appendAscii("\"");
265 0 : if ( (sal_Unicode(nVal) == '\"') )
266 : // encode \" to \"\"
267 0 : aArgumentBuffer.append((sal_Unicode)nVal);
268 0 : aArgumentBuffer.append((sal_Unicode)nVal);
269 0 : aArgumentBuffer.appendAscii("\"");
270 : }
271 : else
272 : {
273 0 : css::uno::Any aNew;
274 : try
275 : {
276 0 : aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING );
277 : }
278 0 : catch (const css::script::CannotConvertException&) {}
279 0 : catch (const css::uno::Exception&) {}
280 0 : OUString sVal;
281 0 : aNew >>= sVal;
282 :
283 0 : if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM )
284 : {
285 0 : OUString aName = aValue.getValueType().getTypeName();
286 0 : aArgumentBuffer.append( aName );
287 0 : aArgumentBuffer.appendAscii(".");
288 : }
289 :
290 0 : aArgumentBuffer.append(sVal);
291 : }
292 0 : }
293 :
294 0 : void SAL_CALL DispatchRecorder::implts_recordMacro( const OUString& aURL,
295 : const css::uno::Sequence< css::beans::PropertyValue >& lArguments,
296 : bool bAsComment, OUStringBuffer& aScriptBuffer )
297 : {
298 0 : OUStringBuffer aArgumentBuffer(1000);
299 0 : OUString sArrayName;
300 : // this value is used to name the arrays of aArgumentBuffer
301 0 : sArrayName = "args" + OUString::number(m_nRecordingID);
302 :
303 0 : aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n");
304 :
305 0 : sal_Int32 nLength = lArguments.getLength();
306 0 : sal_Int32 nValidArgs = 0;
307 0 : for( sal_Int32 i=0; i<nLength; ++i )
308 : {
309 0 : if(!lArguments[i].Value.hasValue())
310 0 : continue;
311 :
312 0 : OUStringBuffer sValBuffer(100);
313 : try
314 : {
315 0 : AppendToBuffer(lArguments[i].Value, sValBuffer);
316 : }
317 0 : catch(const css::uno::Exception&)
318 : {
319 0 : sValBuffer.setLength(0);
320 : }
321 0 : if (sValBuffer.isEmpty())
322 0 : continue;
323 :
324 : {
325 : // add arg().Name
326 0 : if(bAsComment)
327 0 : aArgumentBuffer.appendAscii(REM_AS_COMMENT);
328 0 : aArgumentBuffer.append (sArrayName);
329 0 : aArgumentBuffer.appendAscii("(");
330 0 : aArgumentBuffer.append (nValidArgs);
331 0 : aArgumentBuffer.appendAscii(").Name = \"");
332 0 : aArgumentBuffer.append (lArguments[i].Name);
333 0 : aArgumentBuffer.appendAscii("\"\n");
334 :
335 : // add arg().Value
336 0 : if(bAsComment)
337 0 : aArgumentBuffer.appendAscii(REM_AS_COMMENT);
338 0 : aArgumentBuffer.append (sArrayName);
339 0 : aArgumentBuffer.appendAscii("(");
340 0 : aArgumentBuffer.append (nValidArgs);
341 0 : aArgumentBuffer.appendAscii(").Value = ");
342 0 : aArgumentBuffer.append (sValBuffer.makeStringAndClear());
343 0 : aArgumentBuffer.appendAscii("\n");
344 :
345 0 : ++nValidArgs;
346 : }
347 0 : }
348 :
349 : // if aArgumentBuffer exist - pack it into the aScriptBuffer
350 0 : if(nValidArgs>0)
351 : {
352 0 : if(bAsComment)
353 0 : aScriptBuffer.appendAscii(REM_AS_COMMENT);
354 0 : aScriptBuffer.appendAscii("dim ");
355 0 : aScriptBuffer.append (sArrayName);
356 0 : aScriptBuffer.appendAscii("(");
357 0 : aScriptBuffer.append ((sal_Int32)(nValidArgs-1)); // 0 based!
358 0 : aScriptBuffer.appendAscii(") as new com.sun.star.beans.PropertyValue\n");
359 0 : aScriptBuffer.append (aArgumentBuffer.makeStringAndClear());
360 0 : aScriptBuffer.appendAscii("\n");
361 : }
362 :
363 : // add code for dispatches
364 0 : if(bAsComment)
365 0 : aScriptBuffer.appendAscii(REM_AS_COMMENT);
366 0 : aScriptBuffer.appendAscii("dispatcher.executeDispatch(document, \"");
367 0 : aScriptBuffer.append (aURL);
368 0 : aScriptBuffer.appendAscii("\", \"\", 0, ");
369 0 : if(nValidArgs<1)
370 0 : aScriptBuffer.appendAscii("Array()");
371 : else
372 : {
373 0 : aScriptBuffer.append( sArrayName.getStr() );
374 0 : aScriptBuffer.appendAscii("()");
375 : }
376 0 : aScriptBuffer.appendAscii(")\n\n");
377 :
378 : /* SAFE { */
379 0 : m_nRecordingID++;
380 : /* } */
381 0 : }
382 :
383 0 : com::sun::star::uno::Type SAL_CALL DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException, std::exception)
384 : {
385 0 : return ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL);
386 : }
387 :
388 0 : sal_Bool SAL_CALL DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException, std::exception)
389 : {
390 0 : return (! m_aStatements.empty());
391 : }
392 :
393 0 : sal_Int32 SAL_CALL DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException, std::exception)
394 : {
395 0 : return m_aStatements.size();
396 : }
397 :
398 0 : 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, std::exception)
399 : {
400 0 : if (idx >= (sal_Int32)m_aStatements.size()) {
401 : throw com::sun::star::lang::IndexOutOfBoundsException(
402 : OUString( "Dispatch recorder out of bounds" ),
403 0 : Reference< XInterface >() );
404 :
405 : }
406 :
407 0 : Any element(&m_aStatements[idx],
408 0 : ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL));
409 :
410 0 : return element;
411 : }
412 :
413 0 : 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, std::exception)
414 : {
415 0 : if (element.getValueType() !=
416 0 : ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL)) {
417 : throw com::sun::star::lang::IllegalArgumentException(
418 : OUString( "Illegal argument in dispatch recorder" ),
419 0 : Reference< XInterface >(), 2 );
420 : }
421 :
422 0 : if (idx >= (sal_Int32)m_aStatements.size()) {
423 : throw com::sun::star::lang::IndexOutOfBoundsException(
424 : OUString( "Dispatch recorder out of bounds" ),
425 0 : Reference< XInterface >() );
426 :
427 : }
428 :
429 : com::sun::star::frame::DispatchStatement *pStatement;
430 :
431 0 : pStatement = (com::sun::star::frame::DispatchStatement *)element.getValue();
432 :
433 : com::sun::star::frame::DispatchStatement aStatement(
434 : pStatement->aCommand,
435 : pStatement->aTarget,
436 : pStatement->aArgs,
437 : pStatement->nFlags,
438 0 : pStatement->bIsComment);
439 :
440 0 : m_aStatements[idx] = aStatement;
441 0 : }
442 :
443 : } // namespace framework
444 :
445 : /* vim:set shiftwidth=4 softtabstop=4 expandtab: */
|