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_sc.hxx"
26
27
28
29 #include <comphelper/processfactory.hxx>
30 #include <tools/debug.hxx>
31 #include <i18npool/mslangid.hxx>
32 #include <vcl/svapp.hxx>
33 #include <vos/xception.hxx>
34 #include <sfx2/objsh.hxx>
35 #include <unotools/charclass.hxx>
36
37 #include <com/sun/star/container/XContentEnumerationAccess.hpp>
38 #include <com/sun/star/lang/XServiceName.hpp>
39 #include <com/sun/star/lang/XSingleServiceFactory.hpp>
40 #include <com/sun/star/lang/XSingleComponentFactory.hpp>
41 #include <com/sun/star/reflection/XIdlClass.hpp>
42 #include <com/sun/star/reflection/XIdlClassProvider.hpp>
43 #include <com/sun/star/beans/XIntrospectionAccess.hpp>
44 #include <com/sun/star/beans/XIntrospection.hpp>
45 #include <com/sun/star/beans/MethodConcept.hpp>
46 #include <com/sun/star/beans/XPropertySet.hpp>
47 #include <com/sun/star/table/XCellRange.hpp>
48 #include <com/sun/star/lang/Locale.hpp>
49 #include <com/sun/star/sheet/XCompatibilityNames.hpp>
50 #include <com/sun/star/sheet/NoConvergenceException.hpp>
51
52 #include "addincol.hxx"
53 #include "addinhelpid.hxx"
54 #include "compiler.hxx"
55 #include "scmatrix.hxx"
56 #include "addinlis.hxx"
57 #include "formula/errorcodes.hxx"
58 #include "scfuncs.hrc"
59 #include "optutil.hxx"
60 #include "addincfg.hxx"
61 #include "scmod.hxx"
62 #include "rangeseq.hxx"
63 #include "funcdesc.hxx"
64
65 using namespace com::sun::star;
66
67 //------------------------------------------------------------------------
68
69 #define SC_CALLERPOS_NONE (-1)
70
71 #define SCADDINSUPPLIER_SERVICE "com.sun.star.sheet.AddIn"
72
73 //------------------------------------------------------------------------
74
75
76
77
78 //------------------------------------------------------------------------
79
ScUnoAddInFuncData(const String & rNam,const String & rLoc,const String & rDesc,sal_uInt16 nCat,const rtl::OString & sHelp,const uno::Reference<reflection::XIdlMethod> & rFunc,const uno::Any & rO,long nAC,const ScAddInArgDesc * pAD,long nCP)80 ScUnoAddInFuncData::ScUnoAddInFuncData( const String& rNam, const String& rLoc,
81 const String& rDesc,
82 sal_uInt16 nCat, const rtl::OString& sHelp,
83 const uno::Reference<reflection::XIdlMethod>& rFunc,
84 const uno::Any& rO,
85 long nAC, const ScAddInArgDesc* pAD,
86 long nCP ) :
87 aOriginalName( rNam ),
88 aLocalName( rLoc ),
89 aUpperName( rNam ),
90 aUpperLocal( rLoc ),
91 aDescription( rDesc ),
92 xFunction( rFunc ),
93 aObject( rO ),
94 nArgCount( nAC ),
95 nCallerPos( nCP ),
96 nCategory( nCat ),
97 sHelpId( sHelp ),
98 bCompInitialized( sal_False )
99 {
100 if ( nArgCount )
101 {
102 pArgDescs = new ScAddInArgDesc[nArgCount];
103 for (long i=0; i<nArgCount; i++)
104 pArgDescs[i] = pAD[i];
105 }
106 else
107 pArgDescs = NULL;
108
109 ScGlobal::pCharClass->toUpper(aUpperName);
110 ScGlobal::pCharClass->toUpper(aUpperLocal);
111 }
112
~ScUnoAddInFuncData()113 ScUnoAddInFuncData::~ScUnoAddInFuncData()
114 {
115 delete[] pArgDescs;
116 }
117
GetCompNames() const118 const uno::Sequence<sheet::LocalizedName>& ScUnoAddInFuncData::GetCompNames() const
119 {
120 if ( !bCompInitialized )
121 {
122 // read sequence of compatibility names on demand
123
124 uno::Reference<sheet::XAddIn> xAddIn;
125 if ( aObject >>= xAddIn )
126 {
127 uno::Reference<sheet::XCompatibilityNames> xComp( xAddIn, uno::UNO_QUERY );
128 if ( xComp.is() && xFunction.is() )
129 {
130 rtl::OUString aMethodName = xFunction->getName();
131 aCompNames = xComp->getCompatibilityNames( aMethodName );
132
133 // change all locale entries to default case
134 // (language in lower case, country in upper case)
135 // for easier searching
136
137 long nSeqLen = aCompNames.getLength();
138 if ( nSeqLen )
139 {
140 sheet::LocalizedName* pArray = aCompNames.getArray();
141 for (long i=0; i<nSeqLen; i++)
142 {
143 lang::Locale& rLocale = pArray[i].Locale;
144 rLocale.Language = rLocale.Language.toAsciiLowerCase();
145 rLocale.Country = rLocale.Country.toAsciiUpperCase();
146 }
147 }
148 }
149 }
150
151 bCompInitialized = sal_True; // also if not successful
152 }
153 return aCompNames;
154 }
155
SetCompNames(const uno::Sequence<sheet::LocalizedName> & rNew)156 void ScUnoAddInFuncData::SetCompNames( const uno::Sequence< sheet::LocalizedName>& rNew )
157 {
158 DBG_ASSERT( !bCompInitialized, "SetCompNames after initializing" );
159
160 aCompNames = rNew;
161
162 // change all locale entries to default case
163 // (language in lower case, country in upper case)
164 // for easier searching
165
166 long nSeqLen = aCompNames.getLength();
167 if ( nSeqLen )
168 {
169 sheet::LocalizedName* pArray = aCompNames.getArray();
170 for (long i=0; i<nSeqLen; i++)
171 {
172 lang::Locale& rLocale = pArray[i].Locale;
173 rLocale.Language = rLocale.Language.toAsciiLowerCase();
174 rLocale.Country = rLocale.Country.toAsciiUpperCase();
175 }
176 }
177
178 bCompInitialized = sal_True;
179 }
180
GetExcelName(LanguageType eDestLang,String & rRetExcelName) const181 sal_Bool ScUnoAddInFuncData::GetExcelName( LanguageType eDestLang, String& rRetExcelName ) const
182 {
183 const uno::Sequence<sheet::LocalizedName>& rSequence = GetCompNames();
184 long nSeqLen = rSequence.getLength();
185 if ( nSeqLen )
186 {
187 const sheet::LocalizedName* pArray = rSequence.getConstArray();
188 long i;
189
190 rtl::OUString aLangStr, aCountryStr;
191 MsLangId::convertLanguageToIsoNames( eDestLang, aLangStr, aCountryStr );
192 rtl::OUString aUserLang = aLangStr.toAsciiLowerCase();
193 rtl::OUString aUserCountry = aCountryStr.toAsciiUpperCase();
194
195 // first check for match of both language and country
196
197 for ( i=0; i<nSeqLen; i++)
198 if ( pArray[i].Locale.Language == aUserLang &&
199 pArray[i].Locale.Country == aUserCountry )
200 {
201 rRetExcelName = pArray[i].Name;
202 return sal_True;
203 }
204
205 // second: check only language
206
207 for ( i=0; i<nSeqLen; i++)
208 if ( pArray[i].Locale.Language == aUserLang )
209 {
210 rRetExcelName = pArray[i].Name;
211 return sal_True;
212 }
213
214 // third: #i57772# fall-back to en-US
215
216 if ( eDestLang != LANGUAGE_ENGLISH_US )
217 return GetExcelName( LANGUAGE_ENGLISH_US, rRetExcelName );
218
219 // forth: use first (default) entry
220
221 rRetExcelName = pArray[0].Name;
222 return sal_True;
223 }
224 return sal_False;
225 }
226
SetFunction(const uno::Reference<reflection::XIdlMethod> & rNewFunc,const uno::Any & rNewObj)227 void ScUnoAddInFuncData::SetFunction( const uno::Reference< reflection::XIdlMethod>& rNewFunc, const uno::Any& rNewObj )
228 {
229 xFunction = rNewFunc;
230 aObject = rNewObj;
231 }
232
SetArguments(long nNewCount,const ScAddInArgDesc * pNewDescs)233 void ScUnoAddInFuncData::SetArguments( long nNewCount, const ScAddInArgDesc* pNewDescs )
234 {
235 delete[] pArgDescs;
236
237 nArgCount = nNewCount;
238 if ( nArgCount )
239 {
240 pArgDescs = new ScAddInArgDesc[nArgCount];
241 for (long i=0; i<nArgCount; i++)
242 pArgDescs[i] = pNewDescs[i];
243 }
244 else
245 pArgDescs = NULL;
246 }
247
SetCallerPos(long nNewPos)248 void ScUnoAddInFuncData::SetCallerPos( long nNewPos )
249 {
250 nCallerPos = nNewPos;
251 }
252
253 //------------------------------------------------------------------------
254
ScUnoAddInCollection()255 ScUnoAddInCollection::ScUnoAddInCollection() :
256 nFuncCount( 0 ),
257 ppFuncData( NULL ),
258 pExactHashMap( NULL ),
259 pNameHashMap( NULL ),
260 pLocalHashMap( NULL ),
261 bInitialized( sal_False )
262 {
263 }
264
~ScUnoAddInCollection()265 ScUnoAddInCollection::~ScUnoAddInCollection()
266 {
267 Clear();
268 }
269
Clear()270 void ScUnoAddInCollection::Clear()
271 {
272 DELETEZ( pExactHashMap );
273 DELETEZ( pNameHashMap );
274 DELETEZ( pLocalHashMap );
275 if ( ppFuncData )
276 {
277 for ( long i=0; i<nFuncCount; i++ )
278 delete ppFuncData[i];
279 delete[] ppFuncData;
280 }
281 ppFuncData = NULL;
282 nFuncCount = 0;
283
284 bInitialized = sal_False;
285 }
286
getContext(uno::Reference<lang::XMultiServiceFactory> xMSF)287 uno::Reference<uno::XComponentContext> getContext(uno::Reference<lang::XMultiServiceFactory> xMSF)
288 {
289 uno::Reference<uno::XComponentContext> xCtx;
290 try {
291 uno::Reference<beans::XPropertySet> xPropset(xMSF, uno::UNO_QUERY);
292 xPropset->getPropertyValue(
293 ::rtl::OUString::createFromAscii("DefaultContext")) >>= xCtx;
294 }
295 catch ( uno::Exception & ) {
296 }
297 return xCtx;
298 }
299
Initialize()300 void ScUnoAddInCollection::Initialize()
301 {
302 DBG_ASSERT( !bInitialized, "Initialize twice?" );
303
304 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
305 uno::Reference<container::XContentEnumerationAccess> xEnAc( xManager, uno::UNO_QUERY );
306 if ( xEnAc.is() )
307 {
308 uno::Reference<container::XEnumeration> xEnum =
309 xEnAc->createContentEnumeration(
310 rtl::OUString::createFromAscii(SCADDINSUPPLIER_SERVICE) );
311 if ( xEnum.is() )
312 {
313 // loop through all AddIns
314 while ( xEnum->hasMoreElements() )
315 {
316 uno::Any aAddInAny = xEnum->nextElement();
317 //? if ( aAddInAny.getReflection()->getTypeClass() == uno::TypeClass_INTERFACE )
318 {
319 uno::Reference<uno::XInterface> xIntFac;
320 aAddInAny >>= xIntFac;
321 if ( xIntFac.is() )
322 {
323 // #i59984# try XSingleComponentFactory in addition to (old) XSingleServiceFactory,
324 // passing the context to the component
325
326 uno::Reference<uno::XInterface> xInterface;
327 uno::Reference<uno::XComponentContext> xCtx = getContext(xManager);
328 uno::Reference<lang::XSingleComponentFactory> xCFac( xIntFac, uno::UNO_QUERY );
329 if (xCtx.is() && xCFac.is())
330 {
331 xInterface = xCFac->createInstanceWithContext(xCtx);
332 if (xInterface.is())
333 ReadFromAddIn( xInterface );
334 }
335
336 if (!xInterface.is())
337 {
338 uno::Reference<lang::XSingleServiceFactory> xFac( xIntFac, uno::UNO_QUERY );
339 if ( xFac.is() )
340 {
341 xInterface = xFac->createInstance();
342 if (xInterface.is())
343 ReadFromAddIn( xInterface );
344 }
345 }
346 }
347 }
348 }
349 }
350 }
351
352 // ReadConfiguration is called after looking at the AddIn implementations.
353 // Duplicated are skipped (by using the service information, they don't have to be updated again
354 // when argument information is needed).
355 ReadConfiguration();
356
357 bInitialized = sal_True; // with or without functions
358 }
359 // -----------------------------------------------------------------------------
360
lcl_GetCategory(const String & rName)361 sal_uInt16 lcl_GetCategory( const String& rName )
362 {
363 static const sal_Char* aFuncNames[SC_FUNCGROUP_COUNT] =
364 {
365 // array index = ID - 1 (ID starts at 1)
366 // all upper case
367 "Database", // ID_FUNCTION_GRP_DATABASE
368 "Date&Time", // ID_FUNCTION_GRP_DATETIME
369 "Financial", // ID_FUNCTION_GRP_FINANZ
370 "Information", // ID_FUNCTION_GRP_INFO
371 "Logical", // ID_FUNCTION_GRP_LOGIC
372 "Mathematical", // ID_FUNCTION_GRP_MATH
373 "Matrix", // ID_FUNCTION_GRP_MATRIX
374 "Statistical", // ID_FUNCTION_GRP_STATISTIC
375 "Spreadsheet", // ID_FUNCTION_GRP_TABLE
376 "Text", // ID_FUNCTION_GRP_TEXT
377 "Add-In" // ID_FUNCTION_GRP_ADDINS
378 };
379 for (sal_uInt16 i=0; i<SC_FUNCGROUP_COUNT; i++)
380 if ( rName.EqualsAscii( aFuncNames[i] ) )
381 return i+1; // IDs start at 1
382
383 return ID_FUNCTION_GRP_ADDINS; // if not found, use Add-In group
384 }
385
386
387 #define CFGPATH_ADDINS "Office.CalcAddIns/AddInInfo"
388 #define CFGSTR_ADDINFUNCTIONS "AddInFunctions"
389
390 #define CFG_FUNCPROP_DISPLAYNAME 0
391 #define CFG_FUNCPROP_DESCRIPTION 1
392 #define CFG_FUNCPROP_CATEGORY 2
393 #define CFG_FUNCPROP_COUNT 3
394 #define CFGSTR_DISPLAYNAME "DisplayName"
395 #define CFGSTR_DESCRIPTION "Description"
396 #define CFGSTR_CATEGORY "Category"
397 // CategoryDisplayName is ignored for now
398
399 #define CFGSTR_COMPATIBILITYNAME "CompatibilityName"
400 #define CFGSTR_PARAMETERS "Parameters"
401
402
ReadConfiguration()403 void ScUnoAddInCollection::ReadConfiguration()
404 {
405 // called only from Initialize
406
407 ScAddInCfg& rAddInConfig = SC_MOD()->GetAddInCfg();
408
409 // additional, temporary config item for the compatibility names
410 ScLinkConfigItem aAllLocalesConfig( rtl::OUString::createFromAscii( CFGPATH_ADDINS ), CONFIG_MODE_ALL_LOCALES );
411 // CommitLink is not used (only reading values)
412
413 const rtl::OUString sSlash('/');
414
415 // get the list of add-ins (services)
416 rtl::OUString aEmptyString;
417 uno::Sequence<rtl::OUString> aServiceNames = rAddInConfig.GetNodeNames( aEmptyString );
418
419 sal_Int32 nServiceCount = aServiceNames.getLength();
420 for ( sal_Int32 nService = 0; nService < nServiceCount; nService++ )
421 {
422 rtl::OUString aServiceName = aServiceNames[nService];
423 ScUnoAddInHelpIdGenerator aHelpIdGenerator( aServiceName );
424
425 rtl::OUString aFunctionsPath = aServiceName;
426 aFunctionsPath += sSlash;
427 aFunctionsPath += rtl::OUString::createFromAscii( CFGSTR_ADDINFUNCTIONS );
428
429 uno::Sequence<rtl::OUString> aFunctionNames = rAddInConfig.GetNodeNames( aFunctionsPath );
430 sal_Int32 nNewCount = aFunctionNames.getLength();
431
432 // allocate pointers
433
434 long nOld = nFuncCount;
435 nFuncCount = nNewCount+nOld;
436 if ( nOld )
437 {
438 ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount];
439 for (long i=0; i<nOld; i++)
440 ppNew[i] = ppFuncData[i];
441 delete[] ppFuncData;
442 ppFuncData = ppNew;
443 }
444 else
445 ppFuncData = new ScUnoAddInFuncData*[nFuncCount];
446
447 //! TODO: adjust bucket count?
448 if ( !pExactHashMap )
449 pExactHashMap = new ScAddInHashMap;
450 if ( !pNameHashMap )
451 pNameHashMap = new ScAddInHashMap;
452 if ( !pLocalHashMap )
453 pLocalHashMap = new ScAddInHashMap;
454
455 //! get the function information in a single call for all functions?
456
457 const rtl::OUString* pFuncNameArray = aFunctionNames.getConstArray();
458 for ( sal_Int32 nFuncPos = 0; nFuncPos < nNewCount; nFuncPos++ )
459 {
460 ppFuncData[nFuncPos+nOld] = NULL;
461
462 // stored function name: (service name).(function)
463 String aFuncName( aServiceName );
464 aFuncName += '.';
465 aFuncName += String( pFuncNameArray[nFuncPos] );
466
467 // skip the function if already known (read from old AddIn service)
468
469 if ( pExactHashMap->find( aFuncName ) == pExactHashMap->end() )
470 {
471 rtl::OUString aLocalName;
472 rtl::OUString aDescription;
473 sal_uInt16 nCategory = ID_FUNCTION_GRP_ADDINS;
474
475 // get direct information on the function
476
477 rtl::OUString aFuncPropPath = aFunctionsPath;
478 aFuncPropPath += sSlash;
479 aFuncPropPath += pFuncNameArray[nFuncPos];
480 aFuncPropPath += sSlash;
481
482 uno::Sequence<rtl::OUString> aFuncPropNames(CFG_FUNCPROP_COUNT);
483 rtl::OUString* pNameArray = aFuncPropNames.getArray();
484 pNameArray[CFG_FUNCPROP_DISPLAYNAME] = aFuncPropPath;
485 pNameArray[CFG_FUNCPROP_DISPLAYNAME] += rtl::OUString::createFromAscii( CFGSTR_DISPLAYNAME );
486 pNameArray[CFG_FUNCPROP_DESCRIPTION] = aFuncPropPath;
487 pNameArray[CFG_FUNCPROP_DESCRIPTION] += rtl::OUString::createFromAscii( CFGSTR_DESCRIPTION );
488 pNameArray[CFG_FUNCPROP_CATEGORY] = aFuncPropPath;
489 pNameArray[CFG_FUNCPROP_CATEGORY] += rtl::OUString::createFromAscii( CFGSTR_CATEGORY );
490
491 uno::Sequence<uno::Any> aFuncProperties = rAddInConfig.GetProperties( aFuncPropNames );
492 if ( aFuncProperties.getLength() == CFG_FUNCPROP_COUNT )
493 {
494 aFuncProperties[CFG_FUNCPROP_DISPLAYNAME] >>= aLocalName;
495 aFuncProperties[CFG_FUNCPROP_DESCRIPTION] >>= aDescription;
496
497 rtl::OUString aCategoryName;
498 aFuncProperties[CFG_FUNCPROP_CATEGORY] >>= aCategoryName;
499 nCategory = lcl_GetCategory( aCategoryName );
500 }
501
502 // get compatibility names
503
504 uno::Sequence<sheet::LocalizedName> aCompNames;
505
506 rtl::OUString aCompPath = aFuncPropPath;
507 aCompPath += rtl::OUString::createFromAscii( CFGSTR_COMPATIBILITYNAME );
508 uno::Sequence<rtl::OUString> aCompPropNames( &aCompPath, 1 );
509
510 uno::Sequence<uno::Any> aCompProperties = aAllLocalesConfig.GetProperties( aCompPropNames );
511 if ( aCompProperties.getLength() == 1 )
512 {
513 uno::Sequence<beans::PropertyValue> aLocalEntries;
514 if ( aCompProperties[0] >>= aLocalEntries )
515 {
516 sal_Int32 nLocaleCount = aLocalEntries.getLength();
517 aCompNames.realloc( nLocaleCount );
518 const beans::PropertyValue* pConfigArray = aLocalEntries.getConstArray();
519 sheet::LocalizedName* pCompArray = aCompNames.getArray();
520
521 for ( sal_Int32 nLocale = 0; nLocale < nLocaleCount; nLocale++ )
522 {
523 const sal_Unicode cLocaleSep = '-'; // separator in configuration locale strings
524
525 // PropertyValue name is the locale (convert from string to Locale struct)
526
527 const rtl::OUString& rLocaleStr = pConfigArray[nLocale].Name;
528 lang::Locale& rLocale = pCompArray[nLocale].Locale;
529 sal_Int32 nSepPos = rLocaleStr.indexOf( cLocaleSep );
530 if ( nSepPos >= 0 )
531 {
532 rLocale.Language = rLocaleStr.copy( 0, nSepPos );
533 rLocale.Country = rLocaleStr.copy( nSepPos+1 );
534 }
535 else
536 rLocale.Language = rLocaleStr; // leave country empty (default ctor from sequence)
537
538 // PropertyValue value is the localized value (string in this case)
539
540 pConfigArray[nLocale].Value >>= pCompArray[nLocale].Name;
541 }
542 }
543 }
544
545 // get argument info
546
547 ScAddInArgDesc* pVisibleArgs = NULL;
548 long nVisibleCount = 0;
549 long nCallerPos = SC_CALLERPOS_NONE;
550
551 rtl::OUString aArgumentsPath = aFuncPropPath;
552 aArgumentsPath += rtl::OUString::createFromAscii( CFGSTR_PARAMETERS );
553
554 uno::Sequence<rtl::OUString> aArgumentNames = rAddInConfig.GetNodeNames( aArgumentsPath );
555 sal_Int32 nArgumentCount = aArgumentNames.getLength();
556 if ( nArgumentCount )
557 {
558 // get DisplayName and Description for each argument
559 uno::Sequence<rtl::OUString> aArgPropNames( nArgumentCount * 2 );
560 rtl::OUString* pPropNameArray = aArgPropNames.getArray();
561
562 sal_Int32 nArgument;
563 sal_Int32 nIndex = 0;
564 const rtl::OUString* pArgNameArray = aArgumentNames.getConstArray();
565 for ( nArgument = 0; nArgument < nArgumentCount; nArgument++ )
566 {
567 rtl::OUString aOneArgPath = aArgumentsPath;
568 aOneArgPath += sSlash;
569 aOneArgPath += pArgNameArray[nArgument];
570 aOneArgPath += sSlash;
571
572 pPropNameArray[nIndex] = aOneArgPath;
573 pPropNameArray[nIndex++] += rtl::OUString::createFromAscii( CFGSTR_DISPLAYNAME );
574 pPropNameArray[nIndex] = aOneArgPath;
575 pPropNameArray[nIndex++] += rtl::OUString::createFromAscii( CFGSTR_DESCRIPTION );
576 }
577
578 uno::Sequence<uno::Any> aArgProperties = rAddInConfig.GetProperties( aArgPropNames );
579 if ( aArgProperties.getLength() == aArgPropNames.getLength() )
580 {
581 const uno::Any* pPropArray = aArgProperties.getConstArray();
582 rtl::OUString sDisplayName;
583 rtl::OUString sDescription;
584
585 ScAddInArgDesc aDesc;
586 aDesc.eType = SC_ADDINARG_NONE; // arg type is not in configuration
587 aDesc.bOptional = sal_False;
588
589 nVisibleCount = nArgumentCount;
590 pVisibleArgs = new ScAddInArgDesc[nVisibleCount];
591
592 nIndex = 0;
593 for ( nArgument = 0; nArgument < nArgumentCount; nArgument++ )
594 {
595 pPropArray[nIndex++] >>= sDisplayName;
596 pPropArray[nIndex++] >>= sDescription;
597
598 aDesc.aInternalName = pArgNameArray[nArgument];
599 aDesc.aName = sDisplayName;
600 aDesc.aDescription = sDescription;
601
602 pVisibleArgs[nArgument] = aDesc;
603 }
604 }
605 }
606
607 rtl::OString sHelpId = aHelpIdGenerator.GetHelpId( pFuncNameArray[nFuncPos] );
608
609 uno::Reference<reflection::XIdlMethod> xFunc; // remains empty
610 uno::Any aObject; // also empty
611
612 // create and insert into the array
613
614 ScUnoAddInFuncData* pData = new ScUnoAddInFuncData(
615 aFuncName, aLocalName, aDescription,
616 nCategory, sHelpId,
617 xFunc, aObject,
618 nVisibleCount, pVisibleArgs, nCallerPos );
619
620 pData->SetCompNames( aCompNames );
621
622 ppFuncData[nFuncPos+nOld] = pData;
623
624 pExactHashMap->insert(
625 ScAddInHashMap::value_type(
626 pData->GetOriginalName(),
627 pData ) );
628 pNameHashMap->insert(
629 ScAddInHashMap::value_type(
630 pData->GetUpperName(),
631 pData ) );
632 pLocalHashMap->insert(
633 ScAddInHashMap::value_type(
634 pData->GetUpperLocal(),
635 pData ) );
636
637 delete[] pVisibleArgs;
638 }
639 }
640 }
641 }
642
LoadComponent(const ScUnoAddInFuncData & rFuncData)643 void ScUnoAddInCollection::LoadComponent( const ScUnoAddInFuncData& rFuncData )
644 {
645 String aFullName = rFuncData.GetOriginalName();
646 xub_StrLen nPos = aFullName.SearchBackward( (sal_Unicode) '.' );
647 if ( nPos != STRING_NOTFOUND && nPos > 0 )
648 {
649 String aServiceName = aFullName.Copy( 0, nPos );
650
651 uno::Reference<lang::XMultiServiceFactory> xServiceFactory = comphelper::getProcessServiceFactory();
652 uno::Reference<uno::XInterface> xInterface( xServiceFactory->createInstance( aServiceName ) );
653
654 if (xInterface.is())
655 UpdateFromAddIn( xInterface, aServiceName );
656 }
657 }
658
GetExcelName(const String & rCalcName,LanguageType eDestLang,String & rRetExcelName)659 sal_Bool ScUnoAddInCollection::GetExcelName( const String& rCalcName,
660 LanguageType eDestLang, String& rRetExcelName )
661 {
662 const ScUnoAddInFuncData* pFuncData = GetFuncData( rCalcName );
663 if ( pFuncData )
664 return pFuncData->GetExcelName( eDestLang, rRetExcelName);
665 return sal_False;
666 }
667
GetCalcName(const String & rExcelName,String & rRetCalcName)668 sal_Bool ScUnoAddInCollection::GetCalcName( const String& rExcelName, String& rRetCalcName )
669 {
670 if (!bInitialized)
671 Initialize();
672
673 String aUpperCmp = rExcelName;
674 ScGlobal::pCharClass->toUpper(aUpperCmp);
675
676 for (long i=0; i<nFuncCount; i++)
677 {
678 ScUnoAddInFuncData* pFuncData = ppFuncData[i];
679 if ( pFuncData )
680 {
681 const uno::Sequence<sheet::LocalizedName>& rSequence = pFuncData->GetCompNames();
682 long nSeqLen = rSequence.getLength();
683 if ( nSeqLen )
684 {
685 const sheet::LocalizedName* pArray = rSequence.getConstArray();
686 for ( long nName=0; nName<nSeqLen; nName++)
687 if ( ScGlobal::pCharClass->upper( pArray[nName].Name ) == aUpperCmp )
688 {
689 //! store upper case for comparing?
690
691 // use the first function that has this name for any language
692 rRetCalcName = pFuncData->GetOriginalName();
693 return sal_True;
694 }
695 }
696 }
697 }
698 return sal_False;
699 }
700
IsTypeName(const rtl::OUString & rName,const uno::Type & rType)701 inline sal_Bool IsTypeName( const rtl::OUString& rName, const uno::Type& rType )
702 {
703 return rName == rType.getTypeName();
704 }
705
lcl_ValidReturnType(const uno::Reference<reflection::XIdlClass> & xClass)706 sal_Bool lcl_ValidReturnType( const uno::Reference<reflection::XIdlClass>& xClass )
707 {
708 // this must match with ScUnoAddInCall::SetResult
709
710 if ( !xClass.is() ) return sal_False;
711
712 switch (xClass->getTypeClass())
713 {
714 // case uno::TypeClass_VOID:
715 // ???
716
717 case uno::TypeClass_ANY: // variable type
718 case uno::TypeClass_ENUM: //! ???
719 case uno::TypeClass_BOOLEAN:
720 case uno::TypeClass_CHAR:
721 case uno::TypeClass_BYTE:
722 case uno::TypeClass_SHORT:
723 case uno::TypeClass_UNSIGNED_SHORT:
724 case uno::TypeClass_LONG:
725 case uno::TypeClass_UNSIGNED_LONG:
726 case uno::TypeClass_FLOAT:
727 case uno::TypeClass_DOUBLE:
728 case uno::TypeClass_STRING:
729 return sal_True; // values or string
730
731 case uno::TypeClass_INTERFACE:
732 {
733 // return type XInterface may contain a XVolatileResult
734 //! XIdlClass needs getType() method!
735
736 rtl::OUString sName = xClass->getName();
737 return (
738 IsTypeName( sName, getCppuType((uno::Reference<sheet::XVolatileResult>*)0) ) ||
739 IsTypeName( sName, getCppuType((uno::Reference<uno::XInterface>*)0) ) );
740 }
741
742 default:
743 {
744 // nested sequences for arrays
745 //! XIdlClass needs getType() method!
746
747 rtl::OUString sName = xClass->getName();
748 return (
749 IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<sal_Int32> >*)0) ) ||
750 IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) ) ||
751 IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) ) ||
752 IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)0) ) );
753 }
754 }
755 return sal_False;
756 }
757
lcl_GetArgType(const uno::Reference<reflection::XIdlClass> & xClass)758 ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>& xClass )
759 {
760 if (!xClass.is())
761 return SC_ADDINARG_NONE;
762
763 uno::TypeClass eType = xClass->getTypeClass();
764
765 if ( eType == uno::TypeClass_LONG ) //! other integer types?
766 return SC_ADDINARG_INTEGER;
767
768 if ( eType == uno::TypeClass_DOUBLE )
769 return SC_ADDINARG_DOUBLE;
770
771 if ( eType == uno::TypeClass_STRING )
772 return SC_ADDINARG_STRING;
773
774 //! XIdlClass needs getType() method!
775 rtl::OUString sName = xClass->getName();
776
777 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<sal_Int32> >*)0) ))
778 return SC_ADDINARG_INTEGER_ARRAY;
779
780 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) ))
781 return SC_ADDINARG_DOUBLE_ARRAY;
782
783 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) ))
784 return SC_ADDINARG_STRING_ARRAY;
785
786 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)0) ))
787 return SC_ADDINARG_MIXED_ARRAY;
788
789 if (IsTypeName( sName, getCppuType((uno::Any*)0) ))
790 return SC_ADDINARG_VALUE_OR_ARRAY;
791
792 if (IsTypeName( sName, getCppuType((uno::Reference<table::XCellRange>*)0) ))
793 return SC_ADDINARG_CELLRANGE;
794
795 if (IsTypeName( sName, getCppuType((uno::Reference<beans::XPropertySet>*)0) ))
796 return SC_ADDINARG_CALLER;
797
798 if (IsTypeName( sName, getCppuType((uno::Sequence<uno::Any>*)0) ))
799 return SC_ADDINARG_VARARGS;
800
801 return SC_ADDINARG_NONE;
802 }
803
ReadFromAddIn(const uno::Reference<uno::XInterface> & xInterface)804 void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface )
805 {
806 uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY );
807 uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY );
808 if ( xAddIn.is() && xName.is() )
809 {
810 // AddIns must use the language for which the office is installed
811 LanguageType eOfficeLang = Application::GetSettings().GetUILanguage();
812
813 lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang ));
814 xAddIn->setLocale( aLocale );
815
816 String aServiceName = String( xName->getServiceName() );
817 ScUnoAddInHelpIdGenerator aHelpIdGenerator( xName->getServiceName() );
818
819 //! pass XIntrospection to ReadFromAddIn
820
821 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
822 if ( xManager.is() )
823 {
824 uno::Reference<beans::XIntrospection> xIntro(
825 xManager->createInstance(rtl::OUString::createFromAscii(
826 "com.sun.star.beans.Introspection" )),
827 uno::UNO_QUERY );
828 if ( xIntro.is() )
829 {
830 uno::Any aObject;
831 aObject <<= xAddIn;
832 uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
833 if (xAcc.is())
834 {
835 uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
836 xAcc->getMethods( beans::MethodConcept::ALL );
837 long nNewCount = aMethods.getLength();
838 if ( nNewCount )
839 {
840 long nOld = nFuncCount;
841 nFuncCount = nNewCount+nOld;
842 if ( nOld )
843 {
844 ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount];
845 for (long i=0; i<nOld; i++)
846 ppNew[i] = ppFuncData[i];
847 delete[] ppFuncData;
848 ppFuncData = ppNew;
849 }
850 else
851 ppFuncData = new ScUnoAddInFuncData*[nFuncCount];
852
853 //! TODO: adjust bucket count?
854 if ( !pExactHashMap )
855 pExactHashMap = new ScAddInHashMap;
856 if ( !pNameHashMap )
857 pNameHashMap = new ScAddInHashMap;
858 if ( !pLocalHashMap )
859 pLocalHashMap = new ScAddInHashMap;
860
861 const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
862 for (long nFuncPos=0; nFuncPos<nNewCount; nFuncPos++)
863 {
864 ppFuncData[nFuncPos+nOld] = NULL;
865
866 uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
867 if (xFunc.is())
868 {
869 // leave out internal functions
870 uno::Reference<reflection::XIdlClass> xClass =
871 xFunc->getDeclaringClass();
872 sal_Bool bSkip = sal_True;
873 if ( xClass.is() )
874 {
875 //! XIdlClass needs getType() method!
876 rtl::OUString sName = xClass->getName();
877 bSkip = (
878 IsTypeName( sName,
879 getCppuType((uno::Reference<uno::XInterface>*)0) ) ||
880 IsTypeName( sName,
881 getCppuType((uno::Reference<reflection::XIdlClassProvider>*)0) ) ||
882 IsTypeName( sName,
883 getCppuType((uno::Reference<lang::XServiceName>*)0) ) ||
884 IsTypeName( sName,
885 getCppuType((uno::Reference<lang::XServiceInfo>*)0) ) ||
886 IsTypeName( sName,
887 getCppuType((uno::Reference<sheet::XAddIn>*)0) ) );
888 }
889 if (!bSkip)
890 {
891 uno::Reference<reflection::XIdlClass> xReturn =
892 xFunc->getReturnType();
893 if ( !lcl_ValidReturnType( xReturn ) )
894 bSkip = sal_True;
895 }
896 if (!bSkip)
897 {
898 rtl::OUString aFuncU = xFunc->getName();
899
900 // stored function name: (service name).(function)
901 String aFuncName = aServiceName;
902 aFuncName += '.';
903 aFuncName += String( aFuncU );
904
905 sal_Bool bValid = sal_True;
906 long nVisibleCount = 0;
907 long nCallerPos = SC_CALLERPOS_NONE;
908
909 uno::Sequence<reflection::ParamInfo> aParams =
910 xFunc->getParameterInfos();
911 long nParamCount = aParams.getLength();
912 const reflection::ParamInfo* pParArr = aParams.getConstArray();
913 long nParamPos;
914 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
915 {
916 if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
917 bValid = sal_False;
918 uno::Reference<reflection::XIdlClass> xParClass =
919 pParArr[nParamPos].aType;
920 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
921 if ( eArgType == SC_ADDINARG_NONE )
922 bValid = sal_False;
923 else if ( eArgType == SC_ADDINARG_CALLER )
924 nCallerPos = nParamPos;
925 else
926 ++nVisibleCount;
927 }
928 if (bValid)
929 {
930 sal_uInt16 nCategory = lcl_GetCategory(
931 String(
932 xAddIn->getProgrammaticCategoryName(
933 aFuncU ) ) );
934
935 rtl::OString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU );
936
937 rtl::OUString aLocalU;
938 try
939 {
940 aLocalU = xAddIn->
941 getDisplayFunctionName( aFuncU );
942 }
943 catch(uno::Exception&)
944 {
945 aLocalU = rtl::OUString::createFromAscii( "###" );
946 }
947 String aLocalName = String( aLocalU );
948
949 rtl::OUString aDescU;
950 try
951 {
952 aDescU = xAddIn->
953 getFunctionDescription( aFuncU );
954 }
955 catch(uno::Exception&)
956 {
957 aDescU = rtl::OUString::createFromAscii( "###" );
958 }
959 String aDescription = String( aDescU );
960
961 ScAddInArgDesc* pVisibleArgs = NULL;
962 if ( nVisibleCount > 0 )
963 {
964 ScAddInArgDesc aDesc;
965 pVisibleArgs = new ScAddInArgDesc[nVisibleCount];
966 long nDestPos = 0;
967 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
968 {
969 uno::Reference<reflection::XIdlClass> xParClass =
970 pParArr[nParamPos].aType;
971 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
972 if ( eArgType != SC_ADDINARG_CALLER )
973 {
974 rtl::OUString aArgName;
975 try
976 {
977 aArgName = xAddIn->
978 getDisplayArgumentName( aFuncU, nParamPos );
979 }
980 catch(uno::Exception&)
981 {
982 aArgName = rtl::OUString::createFromAscii( "###" );
983 }
984 rtl::OUString aArgDesc;
985 try
986 {
987 aArgDesc = xAddIn->
988 getArgumentDescription( aFuncU, nParamPos );
989 }
990 catch(uno::Exception&)
991 {
992 aArgName = rtl::OUString::createFromAscii( "###" );
993 }
994
995 sal_Bool bOptional =
996 ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
997 eArgType == SC_ADDINARG_VARARGS );
998
999 aDesc.eType = eArgType;
1000 aDesc.aName = String( aArgName );
1001 aDesc.aDescription = String( aArgDesc );
1002 aDesc.bOptional = bOptional;
1003 //! initialize aInternalName only from config?
1004 aDesc.aInternalName = pParArr[nParamPos].aName;
1005
1006 pVisibleArgs[nDestPos++] = aDesc;
1007 }
1008 }
1009 DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" );
1010 }
1011
1012 ppFuncData[nFuncPos+nOld] = new ScUnoAddInFuncData(
1013 aFuncName, aLocalName, aDescription,
1014 nCategory, sHelpId,
1015 xFunc, aObject,
1016 nVisibleCount, pVisibleArgs, nCallerPos );
1017
1018 const ScUnoAddInFuncData* pData =
1019 ppFuncData[nFuncPos+nOld];
1020 pExactHashMap->insert(
1021 ScAddInHashMap::value_type(
1022 pData->GetOriginalName(),
1023 pData ) );
1024 pNameHashMap->insert(
1025 ScAddInHashMap::value_type(
1026 pData->GetUpperName(),
1027 pData ) );
1028 pLocalHashMap->insert(
1029 ScAddInHashMap::value_type(
1030 pData->GetUpperLocal(),
1031 pData ) );
1032
1033 delete[] pVisibleArgs;
1034 }
1035 }
1036 }
1037 }
1038 }
1039 }
1040 }
1041 }
1042 }
1043 }
1044
lcl_UpdateFunctionList(ScFunctionList & rFunctionList,const ScUnoAddInFuncData & rFuncData)1045 void lcl_UpdateFunctionList( ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData )
1046 {
1047 String aCompare = rFuncData.GetUpperLocal(); // as used in FillFunctionDescFromData
1048
1049 sal_uLong nCount = rFunctionList.GetCount();
1050 for (sal_uLong nPos=0; nPos<nCount; nPos++)
1051 {
1052 const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos );
1053 if ( pDesc && pDesc->pFuncName && *pDesc->pFuncName == aCompare )
1054 {
1055 ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(pDesc) );
1056 break;
1057 }
1058 }
1059 }
1060
lcl_FindArgDesc(const ScUnoAddInFuncData & rFuncData,const String & rArgIntName)1061 const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, const String& rArgIntName )
1062 {
1063 long nArgCount = rFuncData.GetArgumentCount();
1064 const ScAddInArgDesc* pArguments = rFuncData.GetArguments();
1065 for (long nPos=0; nPos<nArgCount; nPos++)
1066 {
1067 if ( pArguments[nPos].aInternalName == rArgIntName )
1068 return &pArguments[nPos];
1069 }
1070 return NULL;
1071 }
1072
UpdateFromAddIn(const uno::Reference<uno::XInterface> & xInterface,const String & rServiceName)1073 void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& xInterface,
1074 const String& rServiceName )
1075 {
1076 uno::Reference<lang::XLocalizable> xLoc( xInterface, uno::UNO_QUERY );
1077 if ( xLoc.is() ) // optional in new add-ins
1078 {
1079 LanguageType eOfficeLang = Application::GetSettings().GetUILanguage();
1080 lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang ));
1081 xLoc->setLocale( aLocale );
1082 }
1083
1084 // if function list was already initialized, it must be updated
1085
1086 ScFunctionList* pFunctionList = NULL;
1087 if ( ScGlobal::HasStarCalcFunctionList() )
1088 pFunctionList = ScGlobal::GetStarCalcFunctionList();
1089
1090 // only get the function information from Introspection
1091
1092 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
1093 if ( xManager.is() )
1094 {
1095 uno::Reference<beans::XIntrospection> xIntro(
1096 xManager->createInstance(rtl::OUString::createFromAscii(
1097 "com.sun.star.beans.Introspection" )),
1098 uno::UNO_QUERY );
1099 if ( xIntro.is() )
1100 {
1101 uno::Any aObject;
1102 aObject <<= xInterface;
1103 uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
1104 if (xAcc.is())
1105 {
1106 uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
1107 xAcc->getMethods( beans::MethodConcept::ALL );
1108 long nMethodCount = aMethods.getLength();
1109 const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
1110 for (long nFuncPos=0; nFuncPos<nMethodCount; nFuncPos++)
1111 {
1112 uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
1113 if (xFunc.is())
1114 {
1115 rtl::OUString aFuncU = xFunc->getName();
1116
1117 // stored function name: (service name).(function)
1118 String aFuncName = rServiceName;
1119 aFuncName += '.';
1120 aFuncName += String( aFuncU );
1121
1122 // internal names are skipped because no FuncData exists
1123 ScUnoAddInFuncData* pOldData = const_cast<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) );
1124 if ( pOldData )
1125 {
1126 // Create new (complete) argument info.
1127 // As in ReadFromAddIn, the reflection information is authoritative.
1128 // Local names and descriptions from pOldData are looked up using the
1129 // internal argument name.
1130
1131 sal_Bool bValid = sal_True;
1132 long nVisibleCount = 0;
1133 long nCallerPos = SC_CALLERPOS_NONE;
1134
1135 uno::Sequence<reflection::ParamInfo> aParams =
1136 xFunc->getParameterInfos();
1137 long nParamCount = aParams.getLength();
1138 const reflection::ParamInfo* pParArr = aParams.getConstArray();
1139 long nParamPos;
1140 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
1141 {
1142 if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
1143 bValid = sal_False;
1144 uno::Reference<reflection::XIdlClass> xParClass =
1145 pParArr[nParamPos].aType;
1146 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
1147 if ( eArgType == SC_ADDINARG_NONE )
1148 bValid = sal_False;
1149 else if ( eArgType == SC_ADDINARG_CALLER )
1150 nCallerPos = nParamPos;
1151 else
1152 ++nVisibleCount;
1153 }
1154 if (bValid)
1155 {
1156 ScAddInArgDesc* pVisibleArgs = NULL;
1157 if ( nVisibleCount > 0 )
1158 {
1159 ScAddInArgDesc aDesc;
1160 pVisibleArgs = new ScAddInArgDesc[nVisibleCount];
1161 long nDestPos = 0;
1162 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
1163 {
1164 uno::Reference<reflection::XIdlClass> xParClass =
1165 pParArr[nParamPos].aType;
1166 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
1167 if ( eArgType != SC_ADDINARG_CALLER )
1168 {
1169 const ScAddInArgDesc* pOldArgDesc =
1170 lcl_FindArgDesc( *pOldData, pParArr[nParamPos].aName );
1171 if ( pOldArgDesc )
1172 {
1173 aDesc.aName = pOldArgDesc->aName;
1174 aDesc.aDescription = pOldArgDesc->aDescription;
1175 }
1176 else
1177 aDesc.aName = aDesc.aDescription = String::CreateFromAscii( "###" );
1178
1179 sal_Bool bOptional =
1180 ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
1181 eArgType == SC_ADDINARG_VARARGS );
1182
1183 aDesc.eType = eArgType;
1184 aDesc.bOptional = bOptional;
1185 //! initialize aInternalName only from config?
1186 aDesc.aInternalName = pParArr[nParamPos].aName;
1187
1188 pVisibleArgs[nDestPos++] = aDesc;
1189 }
1190 }
1191 DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" );
1192 }
1193
1194 pOldData->SetFunction( xFunc, aObject );
1195 pOldData->SetArguments( nVisibleCount, pVisibleArgs );
1196 pOldData->SetCallerPos( nCallerPos );
1197
1198 if ( pFunctionList )
1199 lcl_UpdateFunctionList( *pFunctionList, *pOldData );
1200
1201 delete[] pVisibleArgs;
1202 }
1203 }
1204 }
1205 }
1206 }
1207 }
1208 }
1209 }
1210
FindFunction(const String & rUpperName,sal_Bool bLocalFirst)1211 String ScUnoAddInCollection::FindFunction( const String& rUpperName, sal_Bool bLocalFirst )
1212 {
1213 if (!bInitialized)
1214 Initialize();
1215
1216 if (nFuncCount == 0)
1217 return EMPTY_STRING;
1218
1219 if ( bLocalFirst )
1220 {
1221 // first scan all local names (used for entering formulas)
1222
1223 ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) );
1224 if ( iLook != pLocalHashMap->end() )
1225 return iLook->second->GetOriginalName();
1226
1227 #if 0
1228 // after that, scan international names (really?)
1229
1230 iLook = pNameHashMap->find( rUpperName );
1231 if ( iLook != pNameHashMap->end() )
1232 return iLook->second->GetOriginalName();
1233 #endif
1234 }
1235 else
1236 {
1237 // first scan international names (used when calling a function)
1238 //! before that, check for exact match???
1239
1240 ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) );
1241 if ( iLook != pNameHashMap->end() )
1242 return iLook->second->GetOriginalName();
1243
1244 // after that, scan all local names (to allow replacing old AddIns with Uno)
1245
1246 iLook = pLocalHashMap->find( rUpperName );
1247 if ( iLook != pLocalHashMap->end() )
1248 return iLook->second->GetOriginalName();
1249 }
1250
1251 return EMPTY_STRING;
1252 }
1253
GetFuncData(const String & rName,bool bComplete)1254 const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const String& rName, bool bComplete )
1255 {
1256 if (!bInitialized)
1257 Initialize();
1258
1259 // rName must be the exact internal name
1260
1261 ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
1262 if ( iLook != pExactHashMap->end() )
1263 {
1264 const ScUnoAddInFuncData* pFuncData = iLook->second;
1265
1266 if ( bComplete && !pFuncData->GetFunction().is() ) //! extra flag?
1267 LoadComponent( *pFuncData );
1268
1269 return pFuncData;
1270 }
1271
1272 return NULL;
1273 }
1274
GetFuncData(long nIndex)1275 const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( long nIndex )
1276 {
1277 if (!bInitialized)
1278 Initialize();
1279
1280 if (nIndex < nFuncCount)
1281 return ppFuncData[nIndex];
1282 return NULL;
1283 }
1284
LocalizeString(String & rName)1285 void ScUnoAddInCollection::LocalizeString( String& rName )
1286 {
1287 if (!bInitialized)
1288 Initialize();
1289
1290 // modify rName - input: exact name
1291
1292 ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
1293 if ( iLook != pExactHashMap->end() )
1294 rName = iLook->second->GetUpperLocal(); //! upper?
1295 }
1296
1297
GetFuncCount()1298 long ScUnoAddInCollection::GetFuncCount()
1299 {
1300 if (!bInitialized)
1301 Initialize();
1302
1303 return nFuncCount;
1304 }
1305
FillFunctionDesc(long nFunc,ScFuncDesc & rDesc)1306 sal_Bool ScUnoAddInCollection::FillFunctionDesc( long nFunc, ScFuncDesc& rDesc )
1307 {
1308 if (!bInitialized)
1309 Initialize();
1310
1311 if (nFunc >= nFuncCount || !ppFuncData[nFunc])
1312 return sal_False;
1313
1314 const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc];
1315
1316 return FillFunctionDescFromData( rFuncData, rDesc );
1317 }
1318
1319 // static
FillFunctionDescFromData(const ScUnoAddInFuncData & rFuncData,ScFuncDesc & rDesc)1320 sal_Bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc )
1321 {
1322 rDesc.Clear();
1323
1324 sal_Bool bIncomplete = !rFuncData.GetFunction().is(); //! extra flag?
1325
1326 long nArgCount = rFuncData.GetArgumentCount();
1327 if ( nArgCount > USHRT_MAX )
1328 return sal_False;
1329
1330 if ( bIncomplete )
1331 nArgCount = 0; // if incomplete, fill without argument info (no wrong order)
1332
1333 // nFIndex is set from outside
1334
1335 rDesc.pFuncName = new String( rFuncData.GetUpperLocal() ); //! upper?
1336 rDesc.nCategory = rFuncData.GetCategory();
1337 rDesc.sHelpId = rFuncData.GetHelpId();
1338
1339 String aDesc = rFuncData.GetDescription();
1340 if (!aDesc.Len())
1341 aDesc = rFuncData.GetLocalName(); // use name if no description is available
1342 rDesc.pFuncDesc = new String( aDesc );
1343
1344 // AddInArgumentType_CALLER is already left out in FuncData
1345
1346 rDesc.nArgCount = (sal_uInt16)nArgCount;
1347 if ( nArgCount )
1348 {
1349 sal_Bool bMultiple = sal_False;
1350 const ScAddInArgDesc* pArgs = rFuncData.GetArguments();
1351
1352 rDesc.ppDefArgNames = new String*[nArgCount];
1353 rDesc.ppDefArgDescs = new String*[nArgCount];
1354 rDesc.pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgCount];
1355 for ( long nArg=0; nArg<nArgCount; nArg++ )
1356 {
1357 rDesc.ppDefArgNames[nArg] = new String( pArgs[nArg].aName );
1358 rDesc.ppDefArgDescs[nArg] = new String( pArgs[nArg].aDescription );
1359 rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional;
1360 rDesc.pDefArgFlags[nArg].bSuppress = false;
1361
1362 // no empty names...
1363 if ( rDesc.ppDefArgNames[nArg]->Len() == 0 )
1364 {
1365 String aDefName( RTL_CONSTASCII_USTRINGPARAM("arg") );
1366 aDefName += String::CreateFromInt32( nArg+1 );
1367 *rDesc.ppDefArgNames[nArg] = aDefName;
1368 }
1369
1370 // last argument repeated?
1371 if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) )
1372 bMultiple = sal_True;
1373 }
1374
1375 if ( bMultiple )
1376 rDesc.nArgCount += VAR_ARGS - 1; // VAR_ARGS means just one repeated arg
1377 }
1378
1379 rDesc.bIncomplete = bIncomplete;
1380
1381 return sal_True;
1382 }
1383
1384
1385 //------------------------------------------------------------------------
1386
ScUnoAddInCall(ScUnoAddInCollection & rColl,const String & rName,long nParamCount)1387 ScUnoAddInCall::ScUnoAddInCall( ScUnoAddInCollection& rColl, const String& rName,
1388 long nParamCount ) :
1389 bValidCount( sal_False ),
1390 nErrCode( errNoCode ), // before function was called
1391 bHasString( sal_True ),
1392 fValue( 0.0 ),
1393 xMatrix( NULL )
1394 {
1395 pFuncData = rColl.GetFuncData( rName, true ); // need fully initialized data
1396 DBG_ASSERT( pFuncData, "Function Data missing" );
1397 if ( pFuncData )
1398 {
1399 long nDescCount = pFuncData->GetArgumentCount();
1400 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1401
1402 // is aVarArg sequence needed?
1403 if ( nParamCount >= nDescCount && nDescCount > 0 &&
1404 pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS )
1405 {
1406 long nVarCount = nParamCount - ( nDescCount - 1 ); // size of last argument
1407 aVarArg.realloc( nVarCount );
1408 bValidCount = sal_True;
1409 }
1410 else if ( nParamCount <= nDescCount )
1411 {
1412 // all args behind nParamCount must be optional
1413 bValidCount = sal_True;
1414 for (long i=nParamCount; i<nDescCount; i++)
1415 if ( !pArgs[i].bOptional )
1416 bValidCount = sal_False;
1417 }
1418 // else invalid (too many arguments)
1419
1420 if ( bValidCount )
1421 aArgs.realloc( nDescCount ); // sequence must always match function signature
1422 }
1423 }
1424
~ScUnoAddInCall()1425 ScUnoAddInCall::~ScUnoAddInCall()
1426 {
1427 // pFuncData is deleted with ScUnoAddInCollection
1428 }
1429
ValidParamCount()1430 sal_Bool ScUnoAddInCall::ValidParamCount()
1431 {
1432 return bValidCount;
1433 }
1434
GetArgType(long nPos)1435 ScAddInArgumentType ScUnoAddInCall::GetArgType( long nPos )
1436 {
1437 if ( pFuncData )
1438 {
1439 long nCount = pFuncData->GetArgumentCount();
1440 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1441
1442 // if last arg is sequence, use "any" type
1443 if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1444 return SC_ADDINARG_VALUE_OR_ARRAY;
1445
1446 if ( nPos < nCount )
1447 return pArgs[nPos].eType;
1448 }
1449 return SC_ADDINARG_VALUE_OR_ARRAY; //! error code !!!!
1450 }
1451
NeedsCaller() const1452 sal_Bool ScUnoAddInCall::NeedsCaller() const
1453 {
1454 return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE;
1455 }
1456
SetCaller(const uno::Reference<uno::XInterface> & rInterface)1457 void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface )
1458 {
1459 xCaller = rInterface;
1460 }
1461
SetCallerFromObjectShell(SfxObjectShell * pObjSh)1462 void ScUnoAddInCall::SetCallerFromObjectShell( SfxObjectShell* pObjSh )
1463 {
1464 if (pObjSh)
1465 {
1466 uno::Reference<uno::XInterface> xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY );
1467 SetCaller( xInt );
1468 }
1469 }
1470
SetParam(long nPos,const uno::Any & rValue)1471 void ScUnoAddInCall::SetParam( long nPos, const uno::Any& rValue )
1472 {
1473 if ( pFuncData )
1474 {
1475 long nCount = pFuncData->GetArgumentCount();
1476 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1477 if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1478 {
1479 long nVarPos = nPos-(nCount-1);
1480 if ( nVarPos < aVarArg.getLength() )
1481 aVarArg.getArray()[nVarPos] = rValue;
1482 else
1483 {
1484 DBG_ERROR("wrong argument number");
1485 }
1486 }
1487 else if ( nPos < aArgs.getLength() )
1488 aArgs.getArray()[nPos] = rValue;
1489 else
1490 {
1491 DBG_ERROR("wrong argument number");
1492 }
1493 }
1494 }
1495
ExecuteCall()1496 void ScUnoAddInCall::ExecuteCall()
1497 {
1498 if ( !pFuncData )
1499 return;
1500
1501 long nCount = pFuncData->GetArgumentCount();
1502 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1503 if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1504 {
1505 // insert aVarArg as last argument
1506 //! after inserting caller (to prevent copying twice)?
1507
1508 DBG_ASSERT( aArgs.getLength() == nCount, "wrong argument count" );
1509 aArgs.getArray()[nCount-1] <<= aVarArg;
1510 }
1511
1512 if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE )
1513 {
1514 uno::Any aCallerAny;
1515 aCallerAny <<= xCaller;
1516
1517 long nUserLen = aArgs.getLength();
1518 long nCallPos = pFuncData->GetCallerPos();
1519 if (nCallPos>nUserLen) // should not happen
1520 {
1521 DBG_ERROR("wrong CallPos");
1522 nCallPos = nUserLen;
1523 }
1524
1525 long nDestLen = nUserLen + 1;
1526 uno::Sequence<uno::Any> aRealArgs( nDestLen );
1527 uno::Any* pDest = aRealArgs.getArray();
1528
1529 const uno::Any* pSource = aArgs.getConstArray();
1530 long nSrcPos = 0;
1531
1532 for ( long nDestPos = 0; nDestPos < nDestLen; nDestPos++ )
1533 {
1534 if ( nDestPos == nCallPos )
1535 pDest[nDestPos] = aCallerAny;
1536 else
1537 pDest[nDestPos] = pSource[nSrcPos++];
1538 }
1539
1540 ExecuteCallWithArgs( aRealArgs );
1541 }
1542 else
1543 ExecuteCallWithArgs( aArgs );
1544 }
1545
ExecuteCallWithArgs(uno::Sequence<uno::Any> & rCallArgs)1546 void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence<uno::Any>& rCallArgs)
1547 {
1548 // rCallArgs may not match argument descriptions (because of caller)
1549
1550 uno::Reference<reflection::XIdlMethod> xFunction;
1551 uno::Any aObject;
1552 if ( pFuncData )
1553 {
1554 xFunction = pFuncData->GetFunction();
1555 aObject = pFuncData->GetObject();
1556 }
1557
1558 if ( xFunction.is() )
1559 {
1560 uno::Any aAny;
1561 nErrCode = 0;
1562
1563 try
1564 {
1565 aAny = xFunction->invoke( aObject, rCallArgs );
1566 }
1567 catch(lang::IllegalArgumentException&)
1568 {
1569 nErrCode = errIllegalArgument;
1570 }
1571 #if 0
1572 catch(FloatingPointException&)
1573 {
1574 nErrCode = errIllegalFPOperation;
1575 }
1576 #endif
1577 catch(reflection::InvocationTargetException& rWrapped)
1578 {
1579 if ( rWrapped.TargetException.getValueType().equals(
1580 getCppuType( (lang::IllegalArgumentException*)0 ) ) )
1581 nErrCode = errIllegalArgument;
1582 else if ( rWrapped.TargetException.getValueType().equals(
1583 getCppuType( (sheet::NoConvergenceException*)0 ) ) )
1584 nErrCode = errNoConvergence;
1585 else
1586 nErrCode = errNoValue;
1587 }
1588
1589 catch(uno::Exception&)
1590 {
1591 nErrCode = errNoValue;
1592 }
1593
1594 if (!nErrCode)
1595 SetResult( aAny ); // convert result to Calc types
1596 }
1597 }
1598
SetResult(const uno::Any & rNewRes)1599 void ScUnoAddInCall::SetResult( const uno::Any& rNewRes )
1600 {
1601 nErrCode = 0;
1602 xVarRes = NULL;
1603
1604 // Reflection* pRefl = rNewRes.getReflection();
1605
1606 uno::TypeClass eClass = rNewRes.getValueTypeClass();
1607 uno::Type aType = rNewRes.getValueType();
1608 switch (eClass)
1609 {
1610 case uno::TypeClass_VOID:
1611 nErrCode = NOTAVAILABLE; // #NA
1612 break;
1613
1614 case uno::TypeClass_ENUM:
1615 case uno::TypeClass_BOOLEAN:
1616 case uno::TypeClass_CHAR:
1617 case uno::TypeClass_BYTE:
1618 case uno::TypeClass_SHORT:
1619 case uno::TypeClass_UNSIGNED_SHORT:
1620 case uno::TypeClass_LONG:
1621 case uno::TypeClass_UNSIGNED_LONG:
1622 case uno::TypeClass_FLOAT:
1623 case uno::TypeClass_DOUBLE:
1624 {
1625 uno::TypeClass eMyClass;
1626 ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes);
1627 bHasString = sal_False;
1628 }
1629 break;
1630
1631 case uno::TypeClass_STRING:
1632 {
1633 rtl::OUString aUStr;
1634 rNewRes >>= aUStr;
1635 aString = String( aUStr );
1636 bHasString = sal_True;
1637 }
1638 break;
1639
1640 case uno::TypeClass_INTERFACE:
1641 {
1642 //! directly extract XVolatileResult from any?
1643 uno::Reference<uno::XInterface> xInterface;
1644 rNewRes >>= xInterface;
1645 if ( xInterface.is() )
1646 xVarRes = uno::Reference<sheet::XVolatileResult>( xInterface, uno::UNO_QUERY );
1647
1648 if (!xVarRes.is())
1649 nErrCode = errNoValue; // unknown interface
1650 }
1651 break;
1652
1653 default:
1654 if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int32> > *)0 ) ) )
1655 {
1656 const uno::Sequence< uno::Sequence<sal_Int32> >* pRowSeq = NULL;
1657
1658 //! use pointer from any!
1659 uno::Sequence< uno::Sequence<sal_Int32> > aSequence;
1660 if ( rNewRes >>= aSequence )
1661 pRowSeq = &aSequence;
1662
1663 if ( pRowSeq )
1664 {
1665 long nRowCount = pRowSeq->getLength();
1666 const uno::Sequence<sal_Int32>* pRowArr = pRowSeq->getConstArray();
1667 long nMaxColCount = 0;
1668 long nCol, nRow;
1669 for (nRow=0; nRow<nRowCount; nRow++)
1670 {
1671 long nTmp = pRowArr[nRow].getLength();
1672 if ( nTmp > nMaxColCount )
1673 nMaxColCount = nTmp;
1674 }
1675 if ( nMaxColCount && nRowCount )
1676 {
1677 xMatrix = new ScMatrix(
1678 static_cast<SCSIZE>(nMaxColCount),
1679 static_cast<SCSIZE>(nRowCount) );
1680 ScMatrix* pMatrix = xMatrix;
1681 for (nRow=0; nRow<nRowCount; nRow++)
1682 {
1683 long nColCount = pRowArr[nRow].getLength();
1684 const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
1685 for (nCol=0; nCol<nColCount; nCol++)
1686 pMatrix->PutDouble( pColArr[nCol],
1687 static_cast<SCSIZE>(nCol),
1688 static_cast<SCSIZE>(nRow) );
1689 for (nCol=nColCount; nCol<nMaxColCount; nCol++)
1690 pMatrix->PutDouble( 0.0,
1691 static_cast<SCSIZE>(nCol),
1692 static_cast<SCSIZE>(nRow) );
1693 }
1694 }
1695 }
1696 }
1697 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<double> > *)0 ) ) )
1698 {
1699 const uno::Sequence< uno::Sequence<double> >* pRowSeq = NULL;
1700
1701 //! use pointer from any!
1702 uno::Sequence< uno::Sequence<double> > aSequence;
1703 if ( rNewRes >>= aSequence )
1704 pRowSeq = &aSequence;
1705
1706 if ( pRowSeq )
1707 {
1708 long nRowCount = pRowSeq->getLength();
1709 const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray();
1710 long nMaxColCount = 0;
1711 long nCol, nRow;
1712 for (nRow=0; nRow<nRowCount; nRow++)
1713 {
1714 long nTmp = pRowArr[nRow].getLength();
1715 if ( nTmp > nMaxColCount )
1716 nMaxColCount = nTmp;
1717 }
1718 if ( nMaxColCount && nRowCount )
1719 {
1720 xMatrix = new ScMatrix(
1721 static_cast<SCSIZE>(nMaxColCount),
1722 static_cast<SCSIZE>(nRowCount) );
1723 ScMatrix* pMatrix = xMatrix;
1724 for (nRow=0; nRow<nRowCount; nRow++)
1725 {
1726 long nColCount = pRowArr[nRow].getLength();
1727 const double* pColArr = pRowArr[nRow].getConstArray();
1728 for (nCol=0; nCol<nColCount; nCol++)
1729 pMatrix->PutDouble( pColArr[nCol],
1730 static_cast<SCSIZE>(nCol),
1731 static_cast<SCSIZE>(nRow) );
1732 for (nCol=nColCount; nCol<nMaxColCount; nCol++)
1733 pMatrix->PutDouble( 0.0,
1734 static_cast<SCSIZE>(nCol),
1735 static_cast<SCSIZE>(nRow) );
1736 }
1737 }
1738 }
1739 }
1740 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<rtl::OUString> > *)0 ) ) )
1741 {
1742 const uno::Sequence< uno::Sequence<rtl::OUString> >* pRowSeq = NULL;
1743
1744 //! use pointer from any!
1745 uno::Sequence< uno::Sequence<rtl::OUString> > aSequence;
1746 if ( rNewRes >>= aSequence )
1747 pRowSeq = &aSequence;
1748
1749 if ( pRowSeq )
1750 {
1751 long nRowCount = pRowSeq->getLength();
1752 const uno::Sequence<rtl::OUString>* pRowArr = pRowSeq->getConstArray();
1753 long nMaxColCount = 0;
1754 long nCol, nRow;
1755 for (nRow=0; nRow<nRowCount; nRow++)
1756 {
1757 long nTmp = pRowArr[nRow].getLength();
1758 if ( nTmp > nMaxColCount )
1759 nMaxColCount = nTmp;
1760 }
1761 if ( nMaxColCount && nRowCount )
1762 {
1763 xMatrix = new ScMatrix(
1764 static_cast<SCSIZE>(nMaxColCount),
1765 static_cast<SCSIZE>(nRowCount) );
1766 ScMatrix* pMatrix = xMatrix;
1767 for (nRow=0; nRow<nRowCount; nRow++)
1768 {
1769 long nColCount = pRowArr[nRow].getLength();
1770 const rtl::OUString* pColArr = pRowArr[nRow].getConstArray();
1771 for (nCol=0; nCol<nColCount; nCol++)
1772 pMatrix->PutString( String( pColArr[nCol] ),
1773 static_cast<SCSIZE>(nCol),
1774 static_cast<SCSIZE>(nRow) );
1775 for (nCol=nColCount; nCol<nMaxColCount; nCol++)
1776 pMatrix->PutString( EMPTY_STRING,
1777 static_cast<SCSIZE>(nCol),
1778 static_cast<SCSIZE>(nRow) );
1779 }
1780 }
1781 }
1782 }
1783 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<uno::Any> > *)0 ) ) )
1784 {
1785 xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes );
1786 }
1787
1788 if (!xMatrix) // no array found
1789 nErrCode = errNoValue; //! code for error in return type???
1790 }
1791 }
1792
1793
1794
1795 //------------------------------------------------------------------------
1796
1797
1798
1799