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