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 }
756
lcl_GetArgType(const uno::Reference<reflection::XIdlClass> & xClass)757 ScAddInArgumentType lcl_GetArgType( const uno::Reference<reflection::XIdlClass>& xClass )
758 {
759 if (!xClass.is())
760 return SC_ADDINARG_NONE;
761
762 uno::TypeClass eType = xClass->getTypeClass();
763
764 if ( eType == uno::TypeClass_LONG ) //! other integer types?
765 return SC_ADDINARG_INTEGER;
766
767 if ( eType == uno::TypeClass_DOUBLE )
768 return SC_ADDINARG_DOUBLE;
769
770 if ( eType == uno::TypeClass_STRING )
771 return SC_ADDINARG_STRING;
772
773 //! XIdlClass needs getType() method!
774 rtl::OUString sName = xClass->getName();
775
776 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<sal_Int32> >*)0) ))
777 return SC_ADDINARG_INTEGER_ARRAY;
778
779 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<double> >*)0) ))
780 return SC_ADDINARG_DOUBLE_ARRAY;
781
782 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<rtl::OUString> >*)0) ))
783 return SC_ADDINARG_STRING_ARRAY;
784
785 if (IsTypeName( sName, getCppuType((uno::Sequence< uno::Sequence<uno::Any> >*)0) ))
786 return SC_ADDINARG_MIXED_ARRAY;
787
788 if (IsTypeName( sName, getCppuType((uno::Any*)0) ))
789 return SC_ADDINARG_VALUE_OR_ARRAY;
790
791 if (IsTypeName( sName, getCppuType((uno::Reference<table::XCellRange>*)0) ))
792 return SC_ADDINARG_CELLRANGE;
793
794 if (IsTypeName( sName, getCppuType((uno::Reference<beans::XPropertySet>*)0) ))
795 return SC_ADDINARG_CALLER;
796
797 if (IsTypeName( sName, getCppuType((uno::Sequence<uno::Any>*)0) ))
798 return SC_ADDINARG_VARARGS;
799
800 return SC_ADDINARG_NONE;
801 }
802
ReadFromAddIn(const uno::Reference<uno::XInterface> & xInterface)803 void ScUnoAddInCollection::ReadFromAddIn( const uno::Reference<uno::XInterface>& xInterface )
804 {
805 uno::Reference<sheet::XAddIn> xAddIn( xInterface, uno::UNO_QUERY );
806 uno::Reference<lang::XServiceName> xName( xInterface, uno::UNO_QUERY );
807 if ( xAddIn.is() && xName.is() )
808 {
809 // AddIns must use the language for which the office is installed
810 LanguageType eOfficeLang = Application::GetSettings().GetUILanguage();
811
812 lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang ));
813 xAddIn->setLocale( aLocale );
814
815 String aServiceName = String( xName->getServiceName() );
816 ScUnoAddInHelpIdGenerator aHelpIdGenerator( xName->getServiceName() );
817
818 //! pass XIntrospection to ReadFromAddIn
819
820 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
821 if ( xManager.is() )
822 {
823 uno::Reference<beans::XIntrospection> xIntro(
824 xManager->createInstance(rtl::OUString::createFromAscii(
825 "com.sun.star.beans.Introspection" )),
826 uno::UNO_QUERY );
827 if ( xIntro.is() )
828 {
829 uno::Any aObject;
830 aObject <<= xAddIn;
831 uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
832 if (xAcc.is())
833 {
834 uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
835 xAcc->getMethods( beans::MethodConcept::ALL );
836 long nNewCount = aMethods.getLength();
837 if ( nNewCount )
838 {
839 long nOld = nFuncCount;
840 nFuncCount = nNewCount+nOld;
841 if ( nOld )
842 {
843 ScUnoAddInFuncData** ppNew = new ScUnoAddInFuncData*[nFuncCount];
844 for (long i=0; i<nOld; i++)
845 ppNew[i] = ppFuncData[i];
846 delete[] ppFuncData;
847 ppFuncData = ppNew;
848 }
849 else
850 ppFuncData = new ScUnoAddInFuncData*[nFuncCount];
851
852 //! TODO: adjust bucket count?
853 if ( !pExactHashMap )
854 pExactHashMap = new ScAddInHashMap;
855 if ( !pNameHashMap )
856 pNameHashMap = new ScAddInHashMap;
857 if ( !pLocalHashMap )
858 pLocalHashMap = new ScAddInHashMap;
859
860 const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
861 for (long nFuncPos=0; nFuncPos<nNewCount; nFuncPos++)
862 {
863 ppFuncData[nFuncPos+nOld] = NULL;
864
865 uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
866 if (xFunc.is())
867 {
868 // leave out internal functions
869 uno::Reference<reflection::XIdlClass> xClass =
870 xFunc->getDeclaringClass();
871 sal_Bool bSkip = sal_True;
872 if ( xClass.is() )
873 {
874 //! XIdlClass needs getType() method!
875 rtl::OUString sName = xClass->getName();
876 bSkip = (
877 IsTypeName( sName,
878 getCppuType((uno::Reference<uno::XInterface>*)0) ) ||
879 IsTypeName( sName,
880 getCppuType((uno::Reference<reflection::XIdlClassProvider>*)0) ) ||
881 IsTypeName( sName,
882 getCppuType((uno::Reference<lang::XServiceName>*)0) ) ||
883 IsTypeName( sName,
884 getCppuType((uno::Reference<lang::XServiceInfo>*)0) ) ||
885 IsTypeName( sName,
886 getCppuType((uno::Reference<sheet::XAddIn>*)0) ) );
887 }
888 if (!bSkip)
889 {
890 uno::Reference<reflection::XIdlClass> xReturn =
891 xFunc->getReturnType();
892 if ( !lcl_ValidReturnType( xReturn ) )
893 bSkip = sal_True;
894 }
895 if (!bSkip)
896 {
897 rtl::OUString aFuncU = xFunc->getName();
898
899 // stored function name: (service name).(function)
900 String aFuncName = aServiceName;
901 aFuncName += '.';
902 aFuncName += String( aFuncU );
903
904 sal_Bool bValid = sal_True;
905 long nVisibleCount = 0;
906 long nCallerPos = SC_CALLERPOS_NONE;
907
908 uno::Sequence<reflection::ParamInfo> aParams =
909 xFunc->getParameterInfos();
910 long nParamCount = aParams.getLength();
911 const reflection::ParamInfo* pParArr = aParams.getConstArray();
912 long nParamPos;
913 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
914 {
915 if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
916 bValid = sal_False;
917 uno::Reference<reflection::XIdlClass> xParClass =
918 pParArr[nParamPos].aType;
919 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
920 if ( eArgType == SC_ADDINARG_NONE )
921 bValid = sal_False;
922 else if ( eArgType == SC_ADDINARG_CALLER )
923 nCallerPos = nParamPos;
924 else
925 ++nVisibleCount;
926 }
927 if (bValid)
928 {
929 sal_uInt16 nCategory = lcl_GetCategory(
930 String(
931 xAddIn->getProgrammaticCategoryName(
932 aFuncU ) ) );
933
934 rtl::OString sHelpId = aHelpIdGenerator.GetHelpId( aFuncU );
935
936 rtl::OUString aLocalU;
937 try
938 {
939 aLocalU = xAddIn->
940 getDisplayFunctionName( aFuncU );
941 }
942 catch(uno::Exception&)
943 {
944 aLocalU = rtl::OUString::createFromAscii( "###" );
945 }
946 String aLocalName = String( aLocalU );
947
948 rtl::OUString aDescU;
949 try
950 {
951 aDescU = xAddIn->
952 getFunctionDescription( aFuncU );
953 }
954 catch(uno::Exception&)
955 {
956 aDescU = rtl::OUString::createFromAscii( "###" );
957 }
958 String aDescription = String( aDescU );
959
960 ScAddInArgDesc* pVisibleArgs = NULL;
961 if ( nVisibleCount > 0 )
962 {
963 ScAddInArgDesc aDesc;
964 pVisibleArgs = new ScAddInArgDesc[nVisibleCount];
965 long nDestPos = 0;
966 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
967 {
968 uno::Reference<reflection::XIdlClass> xParClass =
969 pParArr[nParamPos].aType;
970 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
971 if ( eArgType != SC_ADDINARG_CALLER )
972 {
973 rtl::OUString aArgName;
974 try
975 {
976 aArgName = xAddIn->
977 getDisplayArgumentName( aFuncU, nParamPos );
978 }
979 catch(uno::Exception&)
980 {
981 aArgName = rtl::OUString::createFromAscii( "###" );
982 }
983 rtl::OUString aArgDesc;
984 try
985 {
986 aArgDesc = xAddIn->
987 getArgumentDescription( aFuncU, nParamPos );
988 }
989 catch(uno::Exception&)
990 {
991 aArgName = rtl::OUString::createFromAscii( "###" );
992 }
993
994 sal_Bool bOptional =
995 ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
996 eArgType == SC_ADDINARG_VARARGS );
997
998 aDesc.eType = eArgType;
999 aDesc.aName = String( aArgName );
1000 aDesc.aDescription = String( aArgDesc );
1001 aDesc.bOptional = bOptional;
1002 //! initialize aInternalName only from config?
1003 aDesc.aInternalName = pParArr[nParamPos].aName;
1004
1005 pVisibleArgs[nDestPos++] = aDesc;
1006 }
1007 }
1008 DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" );
1009 }
1010
1011 ppFuncData[nFuncPos+nOld] = new ScUnoAddInFuncData(
1012 aFuncName, aLocalName, aDescription,
1013 nCategory, sHelpId,
1014 xFunc, aObject,
1015 nVisibleCount, pVisibleArgs, nCallerPos );
1016
1017 const ScUnoAddInFuncData* pData =
1018 ppFuncData[nFuncPos+nOld];
1019 pExactHashMap->insert(
1020 ScAddInHashMap::value_type(
1021 pData->GetOriginalName(),
1022 pData ) );
1023 pNameHashMap->insert(
1024 ScAddInHashMap::value_type(
1025 pData->GetUpperName(),
1026 pData ) );
1027 pLocalHashMap->insert(
1028 ScAddInHashMap::value_type(
1029 pData->GetUpperLocal(),
1030 pData ) );
1031
1032 delete[] pVisibleArgs;
1033 }
1034 }
1035 }
1036 }
1037 }
1038 }
1039 }
1040 }
1041 }
1042 }
1043
lcl_UpdateFunctionList(ScFunctionList & rFunctionList,const ScUnoAddInFuncData & rFuncData)1044 void lcl_UpdateFunctionList( ScFunctionList& rFunctionList, const ScUnoAddInFuncData& rFuncData )
1045 {
1046 String aCompare = rFuncData.GetUpperLocal(); // as used in FillFunctionDescFromData
1047
1048 sal_uLong nCount = rFunctionList.GetCount();
1049 for (sal_uLong nPos=0; nPos<nCount; nPos++)
1050 {
1051 const ScFuncDesc* pDesc = rFunctionList.GetFunction( nPos );
1052 if ( pDesc && pDesc->pFuncName && *pDesc->pFuncName == aCompare )
1053 {
1054 ScUnoAddInCollection::FillFunctionDescFromData( rFuncData, *const_cast<ScFuncDesc*>(pDesc) );
1055 break;
1056 }
1057 }
1058 }
1059
lcl_FindArgDesc(const ScUnoAddInFuncData & rFuncData,const String & rArgIntName)1060 const ScAddInArgDesc* lcl_FindArgDesc( const ScUnoAddInFuncData& rFuncData, const String& rArgIntName )
1061 {
1062 long nArgCount = rFuncData.GetArgumentCount();
1063 const ScAddInArgDesc* pArguments = rFuncData.GetArguments();
1064 for (long nPos=0; nPos<nArgCount; nPos++)
1065 {
1066 if ( pArguments[nPos].aInternalName == rArgIntName )
1067 return &pArguments[nPos];
1068 }
1069 return NULL;
1070 }
1071
UpdateFromAddIn(const uno::Reference<uno::XInterface> & xInterface,const String & rServiceName)1072 void ScUnoAddInCollection::UpdateFromAddIn( const uno::Reference<uno::XInterface>& xInterface,
1073 const String& rServiceName )
1074 {
1075 uno::Reference<lang::XLocalizable> xLoc( xInterface, uno::UNO_QUERY );
1076 if ( xLoc.is() ) // optional in new add-ins
1077 {
1078 LanguageType eOfficeLang = Application::GetSettings().GetUILanguage();
1079 lang::Locale aLocale( MsLangId::convertLanguageToLocale( eOfficeLang ));
1080 xLoc->setLocale( aLocale );
1081 }
1082
1083 // if function list was already initialized, it must be updated
1084
1085 ScFunctionList* pFunctionList = NULL;
1086 if ( ScGlobal::HasStarCalcFunctionList() )
1087 pFunctionList = ScGlobal::GetStarCalcFunctionList();
1088
1089 // only get the function information from Introspection
1090
1091 uno::Reference<lang::XMultiServiceFactory> xManager = comphelper::getProcessServiceFactory();
1092 if ( xManager.is() )
1093 {
1094 uno::Reference<beans::XIntrospection> xIntro(
1095 xManager->createInstance(rtl::OUString::createFromAscii(
1096 "com.sun.star.beans.Introspection" )),
1097 uno::UNO_QUERY );
1098 if ( xIntro.is() )
1099 {
1100 uno::Any aObject;
1101 aObject <<= xInterface;
1102 uno::Reference<beans::XIntrospectionAccess> xAcc = xIntro->inspect(aObject);
1103 if (xAcc.is())
1104 {
1105 uno::Sequence< uno::Reference<reflection::XIdlMethod> > aMethods =
1106 xAcc->getMethods( beans::MethodConcept::ALL );
1107 long nMethodCount = aMethods.getLength();
1108 const uno::Reference<reflection::XIdlMethod>* pArray = aMethods.getConstArray();
1109 for (long nFuncPos=0; nFuncPos<nMethodCount; nFuncPos++)
1110 {
1111 uno::Reference<reflection::XIdlMethod> xFunc = pArray[nFuncPos];
1112 if (xFunc.is())
1113 {
1114 rtl::OUString aFuncU = xFunc->getName();
1115
1116 // stored function name: (service name).(function)
1117 String aFuncName = rServiceName;
1118 aFuncName += '.';
1119 aFuncName += String( aFuncU );
1120
1121 // internal names are skipped because no FuncData exists
1122 ScUnoAddInFuncData* pOldData = const_cast<ScUnoAddInFuncData*>( GetFuncData( aFuncName ) );
1123 if ( pOldData )
1124 {
1125 // Create new (complete) argument info.
1126 // As in ReadFromAddIn, the reflection information is authoritative.
1127 // Local names and descriptions from pOldData are looked up using the
1128 // internal argument name.
1129
1130 sal_Bool bValid = sal_True;
1131 long nVisibleCount = 0;
1132 long nCallerPos = SC_CALLERPOS_NONE;
1133
1134 uno::Sequence<reflection::ParamInfo> aParams =
1135 xFunc->getParameterInfos();
1136 long nParamCount = aParams.getLength();
1137 const reflection::ParamInfo* pParArr = aParams.getConstArray();
1138 long nParamPos;
1139 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
1140 {
1141 if ( pParArr[nParamPos].aMode != reflection::ParamMode_IN )
1142 bValid = sal_False;
1143 uno::Reference<reflection::XIdlClass> xParClass =
1144 pParArr[nParamPos].aType;
1145 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
1146 if ( eArgType == SC_ADDINARG_NONE )
1147 bValid = sal_False;
1148 else if ( eArgType == SC_ADDINARG_CALLER )
1149 nCallerPos = nParamPos;
1150 else
1151 ++nVisibleCount;
1152 }
1153 if (bValid)
1154 {
1155 ScAddInArgDesc* pVisibleArgs = NULL;
1156 if ( nVisibleCount > 0 )
1157 {
1158 ScAddInArgDesc aDesc;
1159 pVisibleArgs = new ScAddInArgDesc[nVisibleCount];
1160 long nDestPos = 0;
1161 for (nParamPos=0; nParamPos<nParamCount; nParamPos++)
1162 {
1163 uno::Reference<reflection::XIdlClass> xParClass =
1164 pParArr[nParamPos].aType;
1165 ScAddInArgumentType eArgType = lcl_GetArgType( xParClass );
1166 if ( eArgType != SC_ADDINARG_CALLER )
1167 {
1168 const ScAddInArgDesc* pOldArgDesc =
1169 lcl_FindArgDesc( *pOldData, pParArr[nParamPos].aName );
1170 if ( pOldArgDesc )
1171 {
1172 aDesc.aName = pOldArgDesc->aName;
1173 aDesc.aDescription = pOldArgDesc->aDescription;
1174 }
1175 else
1176 aDesc.aName = aDesc.aDescription = String::CreateFromAscii( "###" );
1177
1178 sal_Bool bOptional =
1179 ( eArgType == SC_ADDINARG_VALUE_OR_ARRAY ||
1180 eArgType == SC_ADDINARG_VARARGS );
1181
1182 aDesc.eType = eArgType;
1183 aDesc.bOptional = bOptional;
1184 //! initialize aInternalName only from config?
1185 aDesc.aInternalName = pParArr[nParamPos].aName;
1186
1187 pVisibleArgs[nDestPos++] = aDesc;
1188 }
1189 }
1190 DBG_ASSERT( nDestPos==nVisibleCount, "wrong count" );
1191 }
1192
1193 pOldData->SetFunction( xFunc, aObject );
1194 pOldData->SetArguments( nVisibleCount, pVisibleArgs );
1195 pOldData->SetCallerPos( nCallerPos );
1196
1197 if ( pFunctionList )
1198 lcl_UpdateFunctionList( *pFunctionList, *pOldData );
1199
1200 delete[] pVisibleArgs;
1201 }
1202 }
1203 }
1204 }
1205 }
1206 }
1207 }
1208 }
1209
FindFunction(const String & rUpperName,sal_Bool bLocalFirst)1210 String ScUnoAddInCollection::FindFunction( const String& rUpperName, sal_Bool bLocalFirst )
1211 {
1212 if (!bInitialized)
1213 Initialize();
1214
1215 if (nFuncCount == 0)
1216 return EMPTY_STRING;
1217
1218 if ( bLocalFirst )
1219 {
1220 // first scan all local names (used for entering formulas)
1221
1222 ScAddInHashMap::const_iterator iLook( pLocalHashMap->find( rUpperName ) );
1223 if ( iLook != pLocalHashMap->end() )
1224 return iLook->second->GetOriginalName();
1225
1226 #if 0
1227 // after that, scan international names (really?)
1228
1229 iLook = pNameHashMap->find( rUpperName );
1230 if ( iLook != pNameHashMap->end() )
1231 return iLook->second->GetOriginalName();
1232 #endif
1233 }
1234 else
1235 {
1236 // first scan international names (used when calling a function)
1237 //! before that, check for exact match???
1238
1239 ScAddInHashMap::const_iterator iLook( pNameHashMap->find( rUpperName ) );
1240 if ( iLook != pNameHashMap->end() )
1241 return iLook->second->GetOriginalName();
1242
1243 // after that, scan all local names (to allow replacing old AddIns with Uno)
1244
1245 iLook = pLocalHashMap->find( rUpperName );
1246 if ( iLook != pLocalHashMap->end() )
1247 return iLook->second->GetOriginalName();
1248 }
1249
1250 return EMPTY_STRING;
1251 }
1252
GetFuncData(const String & rName,bool bComplete)1253 const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( const String& rName, bool bComplete )
1254 {
1255 if (!bInitialized)
1256 Initialize();
1257
1258 // rName must be the exact internal name
1259
1260 ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
1261 if ( iLook != pExactHashMap->end() )
1262 {
1263 const ScUnoAddInFuncData* pFuncData = iLook->second;
1264
1265 if ( bComplete && !pFuncData->GetFunction().is() ) //! extra flag?
1266 LoadComponent( *pFuncData );
1267
1268 return pFuncData;
1269 }
1270
1271 return NULL;
1272 }
1273
GetFuncData(long nIndex)1274 const ScUnoAddInFuncData* ScUnoAddInCollection::GetFuncData( long nIndex )
1275 {
1276 if (!bInitialized)
1277 Initialize();
1278
1279 if (nIndex < nFuncCount)
1280 return ppFuncData[nIndex];
1281 return NULL;
1282 }
1283
LocalizeString(String & rName)1284 void ScUnoAddInCollection::LocalizeString( String& rName )
1285 {
1286 if (!bInitialized)
1287 Initialize();
1288
1289 // modify rName - input: exact name
1290
1291 ScAddInHashMap::const_iterator iLook( pExactHashMap->find( rName ) );
1292 if ( iLook != pExactHashMap->end() )
1293 rName = iLook->second->GetUpperLocal(); //! upper?
1294 }
1295
1296
GetFuncCount()1297 long ScUnoAddInCollection::GetFuncCount()
1298 {
1299 if (!bInitialized)
1300 Initialize();
1301
1302 return nFuncCount;
1303 }
1304
FillFunctionDesc(long nFunc,ScFuncDesc & rDesc)1305 sal_Bool ScUnoAddInCollection::FillFunctionDesc( long nFunc, ScFuncDesc& rDesc )
1306 {
1307 if (!bInitialized)
1308 Initialize();
1309
1310 if (nFunc >= nFuncCount || !ppFuncData[nFunc])
1311 return sal_False;
1312
1313 const ScUnoAddInFuncData& rFuncData = *ppFuncData[nFunc];
1314
1315 return FillFunctionDescFromData( rFuncData, rDesc );
1316 }
1317
1318 // static
FillFunctionDescFromData(const ScUnoAddInFuncData & rFuncData,ScFuncDesc & rDesc)1319 sal_Bool ScUnoAddInCollection::FillFunctionDescFromData( const ScUnoAddInFuncData& rFuncData, ScFuncDesc& rDesc )
1320 {
1321 rDesc.Clear();
1322
1323 sal_Bool bIncomplete = !rFuncData.GetFunction().is(); //! extra flag?
1324
1325 long nArgCount = rFuncData.GetArgumentCount();
1326 if ( nArgCount > USHRT_MAX )
1327 return sal_False;
1328
1329 if ( bIncomplete )
1330 nArgCount = 0; // if incomplete, fill without argument info (no wrong order)
1331
1332 // nFIndex is set from outside
1333
1334 rDesc.pFuncName = new String( rFuncData.GetUpperLocal() ); //! upper?
1335 rDesc.nCategory = rFuncData.GetCategory();
1336 rDesc.sHelpId = rFuncData.GetHelpId();
1337
1338 String aDesc = rFuncData.GetDescription();
1339 if (!aDesc.Len())
1340 aDesc = rFuncData.GetLocalName(); // use name if no description is available
1341 rDesc.pFuncDesc = new String( aDesc );
1342
1343 // AddInArgumentType_CALLER is already left out in FuncData
1344
1345 rDesc.nArgCount = (sal_uInt16)nArgCount;
1346 if ( nArgCount )
1347 {
1348 sal_Bool bMultiple = sal_False;
1349 const ScAddInArgDesc* pArgs = rFuncData.GetArguments();
1350
1351 rDesc.ppDefArgNames = new String*[nArgCount];
1352 rDesc.ppDefArgDescs = new String*[nArgCount];
1353 rDesc.pDefArgFlags = new ScFuncDesc::ParameterFlags[nArgCount];
1354 for ( long nArg=0; nArg<nArgCount; nArg++ )
1355 {
1356 rDesc.ppDefArgNames[nArg] = new String( pArgs[nArg].aName );
1357 rDesc.ppDefArgDescs[nArg] = new String( pArgs[nArg].aDescription );
1358 rDesc.pDefArgFlags[nArg].bOptional = pArgs[nArg].bOptional;
1359 rDesc.pDefArgFlags[nArg].bSuppress = false;
1360
1361 // no empty names...
1362 if ( rDesc.ppDefArgNames[nArg]->Len() == 0 )
1363 {
1364 String aDefName( RTL_CONSTASCII_USTRINGPARAM("arg") );
1365 aDefName += String::CreateFromInt32( nArg+1 );
1366 *rDesc.ppDefArgNames[nArg] = aDefName;
1367 }
1368
1369 // last argument repeated?
1370 if ( nArg+1 == nArgCount && ( pArgs[nArg].eType == SC_ADDINARG_VARARGS ) )
1371 bMultiple = sal_True;
1372 }
1373
1374 if ( bMultiple )
1375 rDesc.nArgCount += VAR_ARGS - 1; // VAR_ARGS means just one repeated arg
1376 }
1377
1378 rDesc.bIncomplete = bIncomplete;
1379
1380 return sal_True;
1381 }
1382
1383
1384 //------------------------------------------------------------------------
1385
ScUnoAddInCall(ScUnoAddInCollection & rColl,const String & rName,long nParamCount)1386 ScUnoAddInCall::ScUnoAddInCall( ScUnoAddInCollection& rColl, const String& rName,
1387 long nParamCount ) :
1388 bValidCount( sal_False ),
1389 nErrCode( errNoCode ), // before function was called
1390 bHasString( sal_True ),
1391 fValue( 0.0 ),
1392 xMatrix( NULL )
1393 {
1394 pFuncData = rColl.GetFuncData( rName, true ); // need fully initialized data
1395 DBG_ASSERT( pFuncData, "Function Data missing" );
1396 if ( pFuncData )
1397 {
1398 long nDescCount = pFuncData->GetArgumentCount();
1399 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1400
1401 // is aVarArg sequence needed?
1402 if ( nParamCount >= nDescCount && nDescCount > 0 &&
1403 pArgs[nDescCount-1].eType == SC_ADDINARG_VARARGS )
1404 {
1405 long nVarCount = nParamCount - ( nDescCount - 1 ); // size of last argument
1406 aVarArg.realloc( nVarCount );
1407 bValidCount = sal_True;
1408 }
1409 else if ( nParamCount <= nDescCount )
1410 {
1411 // all args behind nParamCount must be optional
1412 bValidCount = sal_True;
1413 for (long i=nParamCount; i<nDescCount; i++)
1414 if ( !pArgs[i].bOptional )
1415 bValidCount = sal_False;
1416 }
1417 // else invalid (too many arguments)
1418
1419 if ( bValidCount )
1420 aArgs.realloc( nDescCount ); // sequence must always match function signature
1421 }
1422 }
1423
~ScUnoAddInCall()1424 ScUnoAddInCall::~ScUnoAddInCall()
1425 {
1426 // pFuncData is deleted with ScUnoAddInCollection
1427 }
1428
ValidParamCount()1429 sal_Bool ScUnoAddInCall::ValidParamCount()
1430 {
1431 return bValidCount;
1432 }
1433
GetArgType(long nPos)1434 ScAddInArgumentType ScUnoAddInCall::GetArgType( long nPos )
1435 {
1436 if ( pFuncData )
1437 {
1438 long nCount = pFuncData->GetArgumentCount();
1439 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1440
1441 // if last arg is sequence, use "any" type
1442 if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1443 return SC_ADDINARG_VALUE_OR_ARRAY;
1444
1445 if ( nPos < nCount )
1446 return pArgs[nPos].eType;
1447 }
1448 return SC_ADDINARG_VALUE_OR_ARRAY; //! error code !!!!
1449 }
1450
NeedsCaller() const1451 sal_Bool ScUnoAddInCall::NeedsCaller() const
1452 {
1453 return pFuncData && pFuncData->GetCallerPos() != SC_CALLERPOS_NONE;
1454 }
1455
SetCaller(const uno::Reference<uno::XInterface> & rInterface)1456 void ScUnoAddInCall::SetCaller( const uno::Reference<uno::XInterface>& rInterface )
1457 {
1458 xCaller = rInterface;
1459 }
1460
SetCallerFromObjectShell(SfxObjectShell * pObjSh)1461 void ScUnoAddInCall::SetCallerFromObjectShell( SfxObjectShell* pObjSh )
1462 {
1463 if (pObjSh)
1464 {
1465 uno::Reference<uno::XInterface> xInt( pObjSh->GetBaseModel(), uno::UNO_QUERY );
1466 SetCaller( xInt );
1467 }
1468 }
1469
SetParam(long nPos,const uno::Any & rValue)1470 void ScUnoAddInCall::SetParam( long nPos, const uno::Any& rValue )
1471 {
1472 if ( pFuncData )
1473 {
1474 long nCount = pFuncData->GetArgumentCount();
1475 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1476 if ( nCount > 0 && nPos >= nCount-1 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1477 {
1478 long nVarPos = nPos-(nCount-1);
1479 if ( nVarPos < aVarArg.getLength() )
1480 aVarArg.getArray()[nVarPos] = rValue;
1481 else
1482 {
1483 DBG_ERROR("wrong argument number");
1484 }
1485 }
1486 else if ( nPos < aArgs.getLength() )
1487 aArgs.getArray()[nPos] = rValue;
1488 else
1489 {
1490 DBG_ERROR("wrong argument number");
1491 }
1492 }
1493 }
1494
ExecuteCall()1495 void ScUnoAddInCall::ExecuteCall()
1496 {
1497 if ( !pFuncData )
1498 return;
1499
1500 long nCount = pFuncData->GetArgumentCount();
1501 const ScAddInArgDesc* pArgs = pFuncData->GetArguments();
1502 if ( nCount > 0 && pArgs[nCount-1].eType == SC_ADDINARG_VARARGS )
1503 {
1504 // insert aVarArg as last argument
1505 //! after inserting caller (to prevent copying twice)?
1506
1507 DBG_ASSERT( aArgs.getLength() == nCount, "wrong argument count" );
1508 aArgs.getArray()[nCount-1] <<= aVarArg;
1509 }
1510
1511 if ( pFuncData->GetCallerPos() != SC_CALLERPOS_NONE )
1512 {
1513 uno::Any aCallerAny;
1514 aCallerAny <<= xCaller;
1515
1516 long nUserLen = aArgs.getLength();
1517 long nCallPos = pFuncData->GetCallerPos();
1518 if (nCallPos>nUserLen) // should not happen
1519 {
1520 DBG_ERROR("wrong CallPos");
1521 nCallPos = nUserLen;
1522 }
1523
1524 long nDestLen = nUserLen + 1;
1525 uno::Sequence<uno::Any> aRealArgs( nDestLen );
1526 uno::Any* pDest = aRealArgs.getArray();
1527
1528 const uno::Any* pSource = aArgs.getConstArray();
1529 long nSrcPos = 0;
1530
1531 for ( long nDestPos = 0; nDestPos < nDestLen; nDestPos++ )
1532 {
1533 if ( nDestPos == nCallPos )
1534 pDest[nDestPos] = aCallerAny;
1535 else
1536 pDest[nDestPos] = pSource[nSrcPos++];
1537 }
1538
1539 ExecuteCallWithArgs( aRealArgs );
1540 }
1541 else
1542 ExecuteCallWithArgs( aArgs );
1543 }
1544
ExecuteCallWithArgs(uno::Sequence<uno::Any> & rCallArgs)1545 void ScUnoAddInCall::ExecuteCallWithArgs(uno::Sequence<uno::Any>& rCallArgs)
1546 {
1547 // rCallArgs may not match argument descriptions (because of caller)
1548
1549 uno::Reference<reflection::XIdlMethod> xFunction;
1550 uno::Any aObject;
1551 if ( pFuncData )
1552 {
1553 xFunction = pFuncData->GetFunction();
1554 aObject = pFuncData->GetObject();
1555 }
1556
1557 if ( xFunction.is() )
1558 {
1559 uno::Any aAny;
1560 nErrCode = 0;
1561
1562 try
1563 {
1564 aAny = xFunction->invoke( aObject, rCallArgs );
1565 }
1566 catch(lang::IllegalArgumentException&)
1567 {
1568 nErrCode = errIllegalArgument;
1569 }
1570 #if 0
1571 catch(FloatingPointException&)
1572 {
1573 nErrCode = errIllegalFPOperation;
1574 }
1575 #endif
1576 catch(reflection::InvocationTargetException& rWrapped)
1577 {
1578 if ( rWrapped.TargetException.getValueType().equals(
1579 getCppuType( (lang::IllegalArgumentException*)0 ) ) )
1580 nErrCode = errIllegalArgument;
1581 else if ( rWrapped.TargetException.getValueType().equals(
1582 getCppuType( (sheet::NoConvergenceException*)0 ) ) )
1583 nErrCode = errNoConvergence;
1584 else
1585 nErrCode = errNoValue;
1586 }
1587
1588 catch(uno::Exception&)
1589 {
1590 nErrCode = errNoValue;
1591 }
1592
1593 if (!nErrCode)
1594 SetResult( aAny ); // convert result to Calc types
1595 }
1596 }
1597
SetResult(const uno::Any & rNewRes)1598 void ScUnoAddInCall::SetResult( const uno::Any& rNewRes )
1599 {
1600 nErrCode = 0;
1601 xVarRes = NULL;
1602
1603 // Reflection* pRefl = rNewRes.getReflection();
1604
1605 uno::TypeClass eClass = rNewRes.getValueTypeClass();
1606 uno::Type aType = rNewRes.getValueType();
1607 switch (eClass)
1608 {
1609 case uno::TypeClass_VOID:
1610 nErrCode = NOTAVAILABLE; // #NA
1611 break;
1612
1613 case uno::TypeClass_ENUM:
1614 case uno::TypeClass_BOOLEAN:
1615 case uno::TypeClass_CHAR:
1616 case uno::TypeClass_BYTE:
1617 case uno::TypeClass_SHORT:
1618 case uno::TypeClass_UNSIGNED_SHORT:
1619 case uno::TypeClass_LONG:
1620 case uno::TypeClass_UNSIGNED_LONG:
1621 case uno::TypeClass_FLOAT:
1622 case uno::TypeClass_DOUBLE:
1623 {
1624 uno::TypeClass eMyClass;
1625 ScApiTypeConversion::ConvertAnyToDouble( fValue, eMyClass, rNewRes);
1626 bHasString = sal_False;
1627 }
1628 break;
1629
1630 case uno::TypeClass_STRING:
1631 {
1632 rtl::OUString aUStr;
1633 rNewRes >>= aUStr;
1634 aString = String( aUStr );
1635 bHasString = sal_True;
1636 }
1637 break;
1638
1639 case uno::TypeClass_INTERFACE:
1640 {
1641 //! directly extract XVolatileResult from any?
1642 uno::Reference<uno::XInterface> xInterface;
1643 rNewRes >>= xInterface;
1644 if ( xInterface.is() )
1645 xVarRes = uno::Reference<sheet::XVolatileResult>( xInterface, uno::UNO_QUERY );
1646
1647 if (!xVarRes.is())
1648 nErrCode = errNoValue; // unknown interface
1649 }
1650 break;
1651
1652 default:
1653 if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<sal_Int32> > *)0 ) ) )
1654 {
1655 const uno::Sequence< uno::Sequence<sal_Int32> >* pRowSeq = NULL;
1656
1657 //! use pointer from any!
1658 uno::Sequence< uno::Sequence<sal_Int32> > aSequence;
1659 if ( rNewRes >>= aSequence )
1660 pRowSeq = &aSequence;
1661
1662 if ( pRowSeq )
1663 {
1664 long nRowCount = pRowSeq->getLength();
1665 const uno::Sequence<sal_Int32>* pRowArr = pRowSeq->getConstArray();
1666 long nMaxColCount = 0;
1667 long nCol, nRow;
1668 for (nRow=0; nRow<nRowCount; nRow++)
1669 {
1670 long nTmp = pRowArr[nRow].getLength();
1671 if ( nTmp > nMaxColCount )
1672 nMaxColCount = nTmp;
1673 }
1674 if ( nMaxColCount && nRowCount )
1675 {
1676 xMatrix = new ScMatrix(
1677 static_cast<SCSIZE>(nMaxColCount),
1678 static_cast<SCSIZE>(nRowCount) );
1679 ScMatrix* pMatrix = xMatrix;
1680 for (nRow=0; nRow<nRowCount; nRow++)
1681 {
1682 long nColCount = pRowArr[nRow].getLength();
1683 const sal_Int32* pColArr = pRowArr[nRow].getConstArray();
1684 for (nCol=0; nCol<nColCount; nCol++)
1685 pMatrix->PutDouble( pColArr[nCol],
1686 static_cast<SCSIZE>(nCol),
1687 static_cast<SCSIZE>(nRow) );
1688 for (nCol=nColCount; nCol<nMaxColCount; nCol++)
1689 pMatrix->PutDouble( 0.0,
1690 static_cast<SCSIZE>(nCol),
1691 static_cast<SCSIZE>(nRow) );
1692 }
1693 }
1694 }
1695 }
1696 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<double> > *)0 ) ) )
1697 {
1698 const uno::Sequence< uno::Sequence<double> >* pRowSeq = NULL;
1699
1700 //! use pointer from any!
1701 uno::Sequence< uno::Sequence<double> > aSequence;
1702 if ( rNewRes >>= aSequence )
1703 pRowSeq = &aSequence;
1704
1705 if ( pRowSeq )
1706 {
1707 long nRowCount = pRowSeq->getLength();
1708 const uno::Sequence<double>* pRowArr = pRowSeq->getConstArray();
1709 long nMaxColCount = 0;
1710 long nCol, nRow;
1711 for (nRow=0; nRow<nRowCount; nRow++)
1712 {
1713 long nTmp = pRowArr[nRow].getLength();
1714 if ( nTmp > nMaxColCount )
1715 nMaxColCount = nTmp;
1716 }
1717 if ( nMaxColCount && nRowCount )
1718 {
1719 xMatrix = new ScMatrix(
1720 static_cast<SCSIZE>(nMaxColCount),
1721 static_cast<SCSIZE>(nRowCount) );
1722 ScMatrix* pMatrix = xMatrix;
1723 for (nRow=0; nRow<nRowCount; nRow++)
1724 {
1725 long nColCount = pRowArr[nRow].getLength();
1726 const double* pColArr = pRowArr[nRow].getConstArray();
1727 for (nCol=0; nCol<nColCount; nCol++)
1728 pMatrix->PutDouble( pColArr[nCol],
1729 static_cast<SCSIZE>(nCol),
1730 static_cast<SCSIZE>(nRow) );
1731 for (nCol=nColCount; nCol<nMaxColCount; nCol++)
1732 pMatrix->PutDouble( 0.0,
1733 static_cast<SCSIZE>(nCol),
1734 static_cast<SCSIZE>(nRow) );
1735 }
1736 }
1737 }
1738 }
1739 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<rtl::OUString> > *)0 ) ) )
1740 {
1741 const uno::Sequence< uno::Sequence<rtl::OUString> >* pRowSeq = NULL;
1742
1743 //! use pointer from any!
1744 uno::Sequence< uno::Sequence<rtl::OUString> > aSequence;
1745 if ( rNewRes >>= aSequence )
1746 pRowSeq = &aSequence;
1747
1748 if ( pRowSeq )
1749 {
1750 long nRowCount = pRowSeq->getLength();
1751 const uno::Sequence<rtl::OUString>* pRowArr = pRowSeq->getConstArray();
1752 long nMaxColCount = 0;
1753 long nCol, nRow;
1754 for (nRow=0; nRow<nRowCount; nRow++)
1755 {
1756 long nTmp = pRowArr[nRow].getLength();
1757 if ( nTmp > nMaxColCount )
1758 nMaxColCount = nTmp;
1759 }
1760 if ( nMaxColCount && nRowCount )
1761 {
1762 xMatrix = new ScMatrix(
1763 static_cast<SCSIZE>(nMaxColCount),
1764 static_cast<SCSIZE>(nRowCount) );
1765 ScMatrix* pMatrix = xMatrix;
1766 for (nRow=0; nRow<nRowCount; nRow++)
1767 {
1768 long nColCount = pRowArr[nRow].getLength();
1769 const rtl::OUString* pColArr = pRowArr[nRow].getConstArray();
1770 for (nCol=0; nCol<nColCount; nCol++)
1771 pMatrix->PutString( String( pColArr[nCol] ),
1772 static_cast<SCSIZE>(nCol),
1773 static_cast<SCSIZE>(nRow) );
1774 for (nCol=nColCount; nCol<nMaxColCount; nCol++)
1775 pMatrix->PutString( EMPTY_STRING,
1776 static_cast<SCSIZE>(nCol),
1777 static_cast<SCSIZE>(nRow) );
1778 }
1779 }
1780 }
1781 }
1782 else if ( aType.equals( getCppuType( (uno::Sequence< uno::Sequence<uno::Any> > *)0 ) ) )
1783 {
1784 xMatrix = ScSequenceToMatrix::CreateMixedMatrix( rNewRes );
1785 }
1786
1787 if (!xMatrix) // no array found
1788 nErrCode = errNoValue; //! code for error in return type???
1789 }
1790 }
1791
1792
1793
1794 //------------------------------------------------------------------------
1795
1796
1797
1798