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