1 /************************************************************************* 2 * 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * Copyright 2000, 2010 Oracle and/or its affiliates. 6 * 7 * OpenOffice.org - a multi-platform office productivity suite 8 * 9 * This file is part of OpenOffice.org. 10 * 11 * OpenOffice.org is free software: you can redistribute it and/or modify 12 * it under the terms of the GNU Lesser General Public License version 3 13 * only, as published by the Free Software Foundation. 14 * 15 * OpenOffice.org is distributed in the hope that it will be useful, 16 * but WITHOUT ANY WARRANTY; without even the implied warranty of 17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 18 * GNU Lesser General Public License version 3 for more details 19 * (a copy is included in the LICENSE file that accompanied this code). 20 * 21 * You should have received a copy of the GNU Lesser General Public License 22 * version 3 along with OpenOffice.org. If not, see 23 * <http://www.openoffice.org/license.html> 24 * for a copy of the LGPLv3 License. 25 * 26 ************************************************************************/ 27 28 29 // MARKER(update_precomp.py): autogen include statement, do not remove 30 #include "precompiled_framework.hxx" 31 #include <recording/dispatchrecorder.hxx> 32 #include <com/sun/star/frame/DispatchStatement.hpp> 33 #include <threadhelp/writeguard.hxx> 34 #include <threadhelp/readguard.hxx> 35 #include <services.h> 36 #include <vcl/svapp.hxx> 37 38 using namespace ::com::sun::star::uno; 39 40 namespace framework{ 41 42 // used to mark a dispatch as comment (mostly it indicates an error) Changing of this wdefine will impact all using of such comments ... 43 #define REM_AS_COMMENT "rem " 44 45 //***************************************************************************************************************** 46 // XInterface, XTypeProvider, XServiceInfo 47 //***************************************************************************************************************** 48 DEFINE_XINTERFACE_6( 49 DispatchRecorder, 50 OWeakObject, 51 DIRECT_INTERFACE(css::lang::XTypeProvider), 52 DIRECT_INTERFACE(css::lang::XServiceInfo), 53 DIRECT_INTERFACE(css::frame::XDispatchRecorder), 54 DIRECT_INTERFACE(css::container::XIndexReplace), 55 DIRECT_INTERFACE(css::container::XIndexAccess), 56 DIRECT_INTERFACE(css::container::XElementAccess)) 57 58 DEFINE_XTYPEPROVIDER_6( 59 DispatchRecorder, 60 css::lang::XTypeProvider, 61 css::lang::XServiceInfo, 62 css::frame::XDispatchRecorder, 63 css::container::XIndexReplace, 64 css::container::XIndexAccess, 65 css::container::XElementAccess) 66 67 DEFINE_XSERVICEINFO_MULTISERVICE( 68 DispatchRecorder, 69 ::cppu::OWeakObject, 70 SERVICENAME_DISPATCHRECORDER, 71 IMPLEMENTATIONNAME_DISPATCHRECORDER) 72 73 DEFINE_INIT_SERVICE( 74 DispatchRecorder, 75 { 76 } 77 ) 78 79 #include <typelib/typedescription.h> 80 81 //-------------------------------------------------------------------------------------------------- 82 void flatten_struct_members( 83 ::std::vector< Any > * vec, void const * data, 84 typelib_CompoundTypeDescription * pTD ) 85 SAL_THROW( () ) 86 { 87 if (pTD->pBaseTypeDescription) 88 { 89 flatten_struct_members( vec, data, pTD->pBaseTypeDescription ); 90 } 91 for ( sal_Int32 nPos = 0; nPos < pTD->nMembers; ++nPos ) 92 { 93 vec->push_back( 94 Any( (char const *)data + pTD->pMemberOffsets[ nPos ], pTD->ppTypeRefs[ nPos ] ) ); 95 } 96 } 97 //================================================================================================== 98 Sequence< Any > make_seq_out_of_struct( 99 Any const & val ) 100 SAL_THROW( (RuntimeException) ) 101 { 102 Type const & type = val.getValueType(); 103 TypeClass eTypeClass = type.getTypeClass(); 104 if (TypeClass_STRUCT != eTypeClass && TypeClass_EXCEPTION != eTypeClass) 105 { 106 throw RuntimeException( 107 type.getTypeName() + 108 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("is no struct or exception!") ), 109 Reference< XInterface >() ); 110 } 111 typelib_TypeDescription * pTD = 0; 112 TYPELIB_DANGER_GET( &pTD, type.getTypeLibType() ); 113 OSL_ASSERT( pTD ); 114 if (! pTD) 115 { 116 throw RuntimeException( 117 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM("cannot get type descr of type ") ) + 118 type.getTypeName(), 119 Reference< XInterface >() ); 120 } 121 122 ::std::vector< Any > vec; 123 vec.reserve( ((typelib_CompoundTypeDescription *)pTD)->nMembers ); // good guess 124 flatten_struct_members( &vec, val.getValue(), (typelib_CompoundTypeDescription *)pTD ); 125 TYPELIB_DANGER_RELEASE( pTD ); 126 return Sequence< Any >( &vec[ 0 ], vec.size() ); 127 } 128 129 //*********************************************************************** 130 DispatchRecorder::DispatchRecorder( const css::uno::Reference< css::lang::XMultiServiceFactory >& xSMGR ) 131 : ThreadHelpBase ( &Application::GetSolarMutex() ) 132 , ::cppu::OWeakObject( ) 133 , m_xSMGR ( xSMGR ) 134 , m_xConverter( m_xSMGR->createInstance(::rtl::OUString::createFromAscii("com.sun.star.script.Converter")), css::uno::UNO_QUERY ) 135 { 136 } 137 138 //************************************************************************ 139 DispatchRecorder::~DispatchRecorder() 140 { 141 } 142 143 //************************************************************************* 144 // generate header 145 void SAL_CALL DispatchRecorder::startRecording( const css::uno::Reference< css::frame::XFrame >& ) throw( css::uno::RuntimeException ) 146 { 147 /* SAFE{ */ 148 /* } */ 149 } 150 151 //************************************************************************* 152 void SAL_CALL DispatchRecorder::recordDispatch( const css::util::URL& aURL, 153 const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException ) 154 { 155 ::rtl::OUString aTarget; 156 157 com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_False ); 158 m_aStatements.push_back( aStatement ); 159 } 160 161 //************************************************************************* 162 void SAL_CALL DispatchRecorder::recordDispatchAsComment( const css::util::URL& aURL, 163 const css::uno::Sequence< css::beans::PropertyValue >& lArguments ) throw( css::uno::RuntimeException ) 164 { 165 ::rtl::OUString aTarget; 166 167 // last parameter must be set to true -> it's a comment 168 com::sun::star::frame::DispatchStatement aStatement( aURL.Complete, aTarget, lArguments, 0, sal_True ); 169 m_aStatements.push_back( aStatement ); 170 } 171 172 //************************************************************************* 173 void SAL_CALL DispatchRecorder::endRecording() throw( css::uno::RuntimeException ) 174 { 175 /* SAFE{ */ 176 WriteGuard aWriteLock(m_aLock); 177 m_aStatements.clear(); 178 /* } */ 179 } 180 181 //************************************************************************* 182 ::rtl::OUString SAL_CALL DispatchRecorder::getRecordedMacro() throw( css::uno::RuntimeException ) 183 { 184 /* SAFE{ */ 185 WriteGuard aWriteLock(m_aLock); 186 187 if ( m_aStatements.empty() ) 188 return ::rtl::OUString(); 189 190 ::rtl::OUStringBuffer aScriptBuffer; 191 aScriptBuffer.ensureCapacity(10000); 192 m_nRecordingID = 1; 193 194 aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n"); 195 aScriptBuffer.appendAscii("rem define variables\n"); 196 aScriptBuffer.appendAscii("dim document as object\n"); 197 aScriptBuffer.appendAscii("dim dispatcher as object\n"); 198 aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n"); 199 aScriptBuffer.appendAscii("rem get access to the document\n"); 200 aScriptBuffer.appendAscii("document = ThisComponent.CurrentController.Frame\n"); 201 aScriptBuffer.appendAscii("dispatcher = createUnoService(\"com.sun.star.frame.DispatchHelper\")\n\n"); 202 203 std::vector< com::sun::star::frame::DispatchStatement>::iterator p; 204 for ( p = m_aStatements.begin(); p != m_aStatements.end(); p++ ) 205 implts_recordMacro( p->aCommand, p->aArgs, p->bIsComment, aScriptBuffer ); 206 ::rtl::OUString sScript = aScriptBuffer.makeStringAndClear(); 207 return sScript; 208 /* } */ 209 } 210 211 //************************************************************************* 212 void SAL_CALL DispatchRecorder::AppendToBuffer( css::uno::Any aValue, ::rtl::OUStringBuffer& aArgumentBuffer ) 213 { 214 // if value == bool 215 if (aValue.getValueTypeClass() == css::uno::TypeClass_STRUCT ) 216 { 217 // structs are recorded as arrays, convert to "Sequence of any" 218 Sequence< Any > aSeq = make_seq_out_of_struct( aValue ); 219 aArgumentBuffer.appendAscii("Array("); 220 for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ ) 221 { 222 AppendToBuffer( aSeq[nAny], aArgumentBuffer ); 223 if ( nAny+1 < aSeq.getLength() ) 224 // not last argument 225 aArgumentBuffer.appendAscii(","); 226 } 227 228 aArgumentBuffer.appendAscii(")"); 229 } 230 else if (aValue.getValueTypeClass() == css::uno::TypeClass_SEQUENCE ) 231 { 232 // convert to "Sequence of any" 233 css::uno::Sequence < css::uno::Any > aSeq; 234 css::uno::Any aNew; 235 try { aNew = m_xConverter->convertTo( aValue, ::getCppuType((const css::uno::Sequence < css::uno::Any >*)0) ); } 236 catch (css::uno::Exception&) {} 237 238 aNew >>= aSeq; 239 aArgumentBuffer.appendAscii("Array("); 240 for ( sal_Int32 nAny=0; nAny<aSeq.getLength(); nAny++ ) 241 { 242 AppendToBuffer( aSeq[nAny], aArgumentBuffer ); 243 if ( nAny+1 < aSeq.getLength() ) 244 // not last argument 245 aArgumentBuffer.appendAscii(","); 246 } 247 248 aArgumentBuffer.appendAscii(")"); 249 } 250 else if (aValue.getValueTypeClass() == css::uno::TypeClass_STRING ) 251 { 252 // strings need \" 253 ::rtl::OUString sVal; 254 aValue >>= sVal; 255 256 // encode non printable characters or '"' by using the CHR$ function 257 if ( sVal.getLength() ) 258 { 259 const sal_Unicode* pChars = sVal.getStr(); 260 sal_Bool bInString = sal_False; 261 for ( sal_Int32 nChar=0; nChar<sVal.getLength(); nChar ++ ) 262 { 263 if ( pChars[nChar] < 32 || pChars[nChar] == '"' ) 264 { 265 // problematic character detected 266 if ( bInString ) 267 { 268 // close current string 269 aArgumentBuffer.appendAscii("\""); 270 bInString = sal_False; 271 } 272 273 if ( nChar>0 ) 274 // if this is not the first character, parts of the string have already been added 275 aArgumentBuffer.appendAscii("+"); 276 277 // add the character constant 278 aArgumentBuffer.appendAscii("CHR$("); 279 aArgumentBuffer.append( (sal_Int32) pChars[nChar] ); 280 aArgumentBuffer.appendAscii(")"); 281 } 282 else 283 { 284 if ( !bInString ) 285 { 286 if ( nChar>0 ) 287 // if this is not the first character, parts of the string have already been added 288 aArgumentBuffer.appendAscii("+"); 289 290 // start a new string 291 aArgumentBuffer.appendAscii("\""); 292 bInString = sal_True; 293 } 294 295 aArgumentBuffer.append( pChars[nChar] ); 296 } 297 } 298 299 // close string 300 if ( bInString ) 301 aArgumentBuffer.appendAscii("\""); 302 } 303 else 304 aArgumentBuffer.appendAscii("\"\""); 305 } 306 else if (aValue.getValueType() == getCppuCharType()) 307 { 308 // character variables are recorded as strings, back conversion must be handled in client code 309 sal_Unicode nVal = *((sal_Unicode*)aValue.getValue()); 310 aArgumentBuffer.appendAscii("\""); 311 if ( (sal_Unicode(nVal) == '\"') ) 312 // encode \" to \"\" 313 aArgumentBuffer.append((sal_Unicode)nVal); 314 aArgumentBuffer.append((sal_Unicode)nVal); 315 aArgumentBuffer.appendAscii("\""); 316 } 317 else 318 { 319 css::uno::Any aNew; 320 try 321 { 322 aNew = m_xConverter->convertToSimpleType( aValue, css::uno::TypeClass_STRING ); 323 } 324 catch (css::script::CannotConvertException&) {} 325 catch (css::uno::Exception&) {} 326 ::rtl::OUString sVal; 327 aNew >>= sVal; 328 329 if (aValue.getValueTypeClass() == css::uno::TypeClass_ENUM ) 330 { 331 ::rtl::OUString aName = aValue.getValueType().getTypeName(); 332 aArgumentBuffer.append( aName ); 333 aArgumentBuffer.appendAscii("."); 334 } 335 336 aArgumentBuffer.append(sVal); 337 } 338 } 339 340 void SAL_CALL DispatchRecorder::implts_recordMacro( const ::rtl::OUString& aURL, 341 const css::uno::Sequence< css::beans::PropertyValue >& lArguments, 342 sal_Bool bAsComment, ::rtl::OUStringBuffer& aScriptBuffer ) 343 { 344 ::rtl::OUStringBuffer aArgumentBuffer(1000); 345 ::rtl::OUString sArrayName; 346 // this value is used to name the arrays of aArgumentBuffer 347 sArrayName = ::rtl::OUString::createFromAscii("args"); 348 sArrayName += ::rtl::OUString::valueOf((sal_Int32)m_nRecordingID); 349 350 aScriptBuffer.appendAscii("rem ----------------------------------------------------------------------\n"); 351 352 sal_Int32 nLength = lArguments.getLength(); 353 sal_Int32 nValidArgs = 0; 354 for( sal_Int32 i=0; i<nLength; ++i ) 355 { 356 if(!lArguments[i].Value.hasValue()) 357 continue; 358 359 ::rtl::OUStringBuffer sValBuffer(100); 360 try 361 { 362 AppendToBuffer(lArguments[i].Value, sValBuffer); 363 } 364 catch(const css::uno::Exception&) 365 { 366 sValBuffer.setLength(0); 367 } 368 if (!sValBuffer.getLength()) 369 continue; 370 371 { 372 // add arg().Name 373 if(bAsComment) 374 aArgumentBuffer.appendAscii(REM_AS_COMMENT); 375 aArgumentBuffer.append (sArrayName); 376 aArgumentBuffer.appendAscii("("); 377 aArgumentBuffer.append (nValidArgs); 378 aArgumentBuffer.appendAscii(").Name = \""); 379 aArgumentBuffer.append (lArguments[i].Name); 380 aArgumentBuffer.appendAscii("\"\n"); 381 382 // add arg().Value 383 if(bAsComment) 384 aArgumentBuffer.appendAscii(REM_AS_COMMENT); 385 aArgumentBuffer.append (sArrayName); 386 aArgumentBuffer.appendAscii("("); 387 aArgumentBuffer.append (nValidArgs); 388 aArgumentBuffer.appendAscii(").Value = "); 389 aArgumentBuffer.append (sValBuffer.makeStringAndClear()); 390 aArgumentBuffer.appendAscii("\n"); 391 392 ++nValidArgs; 393 } 394 } 395 396 // if aArgumentBuffer exist - pack it into the aScriptBuffer 397 if(nValidArgs>0) 398 { 399 if(bAsComment) 400 aScriptBuffer.appendAscii(REM_AS_COMMENT); 401 aScriptBuffer.appendAscii("dim "); 402 aScriptBuffer.append (sArrayName); 403 aScriptBuffer.appendAscii("("); 404 aScriptBuffer.append ((sal_Int32)(nValidArgs-1)); // 0 based! 405 aScriptBuffer.appendAscii(") as new com.sun.star.beans.PropertyValue\n"); 406 aScriptBuffer.append (aArgumentBuffer.makeStringAndClear()); 407 aScriptBuffer.appendAscii("\n"); 408 } 409 410 // add code for dispatches 411 if(bAsComment) 412 aScriptBuffer.appendAscii(REM_AS_COMMENT); 413 aScriptBuffer.appendAscii("dispatcher.executeDispatch(document, \""); 414 aScriptBuffer.append (aURL); 415 aScriptBuffer.appendAscii("\", \"\", 0, "); 416 if(nValidArgs<1) 417 aScriptBuffer.appendAscii("Array()"); 418 else 419 { 420 aScriptBuffer.append( sArrayName.getStr() ); 421 aScriptBuffer.appendAscii("()"); 422 } 423 aScriptBuffer.appendAscii(")\n\n"); 424 425 /* SAFE { */ 426 m_nRecordingID++; 427 /* } */ 428 } 429 430 com::sun::star::uno::Type SAL_CALL DispatchRecorder::getElementType() throw (::com::sun::star::uno::RuntimeException) 431 { 432 return ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL); 433 } 434 435 sal_Bool SAL_CALL DispatchRecorder::hasElements() throw (::com::sun::star::uno::RuntimeException) 436 { 437 return (! m_aStatements.empty()); 438 } 439 440 sal_Int32 SAL_CALL DispatchRecorder::getCount() throw (::com::sun::star::uno::RuntimeException) 441 { 442 return m_aStatements.size(); 443 } 444 445 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) 446 { 447 if (idx >= (sal_Int32)m_aStatements.size()) { 448 throw com::sun::star::lang::IndexOutOfBoundsException( 449 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( 450 "Dispatch recorder out of bounds") ), 451 Reference< XInterface >() ); 452 453 } 454 455 Any element(&m_aStatements[idx], 456 ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL)); 457 458 return element; 459 } 460 461 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) 462 { 463 if (element.getValueType() != 464 ::getCppuType((const com::sun::star::frame::DispatchStatement *)NULL)) { 465 throw com::sun::star::lang::IllegalArgumentException( 466 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( 467 "Illegal argument in dispatch recorder") ), 468 Reference< XInterface >(), 2 ); 469 } 470 471 if (idx >= (sal_Int32)m_aStatements.size()) { 472 throw com::sun::star::lang::IndexOutOfBoundsException( 473 ::rtl::OUString( RTL_CONSTASCII_USTRINGPARAM( 474 "Dispatch recorder out of bounds") ), 475 Reference< XInterface >() ); 476 477 } 478 479 com::sun::star::frame::DispatchStatement *pStatement; 480 481 pStatement = (com::sun::star::frame::DispatchStatement *)element.getValue(); 482 483 com::sun::star::frame::DispatchStatement aStatement( 484 pStatement->aCommand, 485 pStatement->aTarget, 486 pStatement->aArgs, 487 pStatement->nFlags, 488 pStatement->bIsComment); 489 490 m_aStatements[idx] = aStatement; 491 } 492 493 } // namespace framework 494