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