1 /**************************************************************
2 *
3 * Licensed to the Apache Software Foundation (ASF) under one
4 * or more contributor license agreements. See the NOTICE file
5 * distributed with this work for additional information
6 * regarding copyright ownership. The ASF licenses this file
7 * to you under the Apache License, Version 2.0 (the
8 * "License"); you may not use this file except in compliance
9 * with the License. You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing,
14 * software distributed under the License is distributed on an
15 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
16 * KIND, either express or implied. See the License for the
17 * specific language governing permissions and limitations
18 * under the License.
19 *
20 *************************************************************/
21
22
23
24 //------------------------------------------------------------------
25 //
26 // date functions add in
27 //
28 //------------------------------------------------------------------
29
30 #include "datefunc.hxx"
31 #ifndef _SCA_DATEFUNC_HRC
32 #include "datefunc.hrc"
33 #endif
34 #include <cppuhelper/factory.hxx>
35 #include <osl/diagnose.h>
36 #include <rtl/ustrbuf.hxx>
37 #include <tools/resmgr.hxx>
38 #include <tools/rcid.h>
39 #include <com/sun/star/util/Date.hpp>
40
41 using namespace ::com::sun::star;
42 using namespace ::rtl;
43
44 //------------------------------------------------------------------
45
46 #define ADDIN_SERVICE "com.sun.star.sheet.AddIn"
47 #define MY_SERVICE "com.sun.star.sheet.addin.DateFunctions"
48 #define MY_IMPLNAME "com.sun.star.sheet.addin.DateFunctionsImpl"
49
50 //------------------------------------------------------------------
51
52 #define STR_FROM_ANSI( s ) OUString( s, strlen( s ), RTL_TEXTENCODING_MS_1252 )
53
54 //------------------------------------------------------------------
55
56 const sal_uInt32 ScaList::nStartSize = 16;
57 const sal_uInt32 ScaList::nIncrSize = 16;
58
ScaList()59 ScaList::ScaList() :
60 pData( new void*[ nStartSize ] ),
61 nSize( nStartSize ),
62 nCount( 0 ),
63 nCurr( 0 )
64 {
65 }
66
~ScaList()67 ScaList::~ScaList()
68 {
69 delete[] pData;
70 }
71
_Grow()72 void ScaList::_Grow()
73 {
74 nSize += nIncrSize;
75
76 void** pNewData = new void*[ nSize ];
77 memcpy( pNewData, pData, nCount * sizeof( void* ) );
78
79 delete[] pData;
80 pData = pNewData;
81 }
82
Insert(void * pNew,sal_uInt32 nIndex)83 void ScaList::Insert( void* pNew, sal_uInt32 nIndex )
84 {
85 if( nIndex >= nCount )
86 Append( pNew );
87 else
88 {
89 Grow();
90
91 void** pIns = pData + nIndex;
92 memmove( pIns + 1, pIns, (nCount - nIndex) * sizeof( void* ) );
93
94 *pIns = pNew;
95 nCount++;
96 }
97 }
98
99
100 //------------------------------------------------------------------
101
~ScaStringList()102 ScaStringList::~ScaStringList()
103 {
104 for( OUString* pStr = First(); pStr; pStr = Next() )
105 delete pStr;
106 }
107
108 //------------------------------------------------------------------
109
ScaResId(sal_uInt16 nId,ResMgr & rResMgr)110 ScaResId::ScaResId( sal_uInt16 nId, ResMgr& rResMgr ) :
111 ResId( nId, rResMgr )
112 {
113 }
114
115
116 //------------------------------------------------------------------
117
118 #define UNIQUE sal_False // function name does not exist in Calc
119 #define DOUBLE sal_True // function name exists in Calc
120
121 #define STDPAR sal_False // all parameters are described
122 #define INTPAR sal_True // first parameter is internal
123
124 #define FUNCDATA( FuncName, ParamCount, Category, Double, IntPar ) \
125 { "get" #FuncName, DATE_FUNCNAME_##FuncName, DATE_FUNCDESC_##FuncName, DATE_DEFFUNCNAME_##FuncName, ParamCount, Category, Double, IntPar }
126
127 const ScaFuncDataBase pFuncDataArr[] =
128 {
129 FUNCDATA( DiffWeeks, 3, ScaCat_DateTime, UNIQUE, INTPAR ),
130 FUNCDATA( DiffMonths, 3, ScaCat_DateTime, UNIQUE, INTPAR ),
131 FUNCDATA( DiffYears, 3, ScaCat_DateTime, UNIQUE, INTPAR ),
132 FUNCDATA( IsLeapYear, 1, ScaCat_DateTime, UNIQUE, INTPAR ),
133 FUNCDATA( DaysInMonth, 1, ScaCat_DateTime, UNIQUE, INTPAR ),
134 FUNCDATA( DaysInYear, 1, ScaCat_DateTime, UNIQUE, INTPAR ),
135 FUNCDATA( WeeksInYear, 1, ScaCat_DateTime, UNIQUE, INTPAR ),
136 FUNCDATA( Rot13, 1, ScaCat_Text, UNIQUE, STDPAR )
137 };
138
139 #undef FUNCDATA
140
141
142 //------------------------------------------------------------------
143
ScaFuncData(const ScaFuncDataBase & rBaseData,ResMgr & rResMgr)144 ScaFuncData::ScaFuncData( const ScaFuncDataBase& rBaseData, ResMgr& rResMgr ) :
145 aIntName( OUString::createFromAscii( rBaseData.pIntName ) ),
146 nUINameID( rBaseData.nUINameID ),
147 nDescrID( rBaseData.nDescrID ),
148 nCompListID( rBaseData.nCompListID ),
149 nParamCount( rBaseData.nParamCount ),
150 eCat( rBaseData.eCat ),
151 bDouble( rBaseData.bDouble ),
152 bWithOpt( rBaseData.bWithOpt )
153 {
154 ScaResStringArrLoader aArrLoader( RID_DATE_DEFFUNCTION_NAMES, nCompListID, rResMgr );
155 const ResStringArray& rArr = aArrLoader.GetStringArray();
156
157 for( sal_uInt16 nIndex = 0; nIndex < rArr.Count(); nIndex++ )
158 aCompList.Append( rArr.GetString( nIndex ) );
159 }
160
~ScaFuncData()161 ScaFuncData::~ScaFuncData()
162 {
163 }
164
GetStrIndex(sal_uInt16 nParam) const165 sal_uInt16 ScaFuncData::GetStrIndex( sal_uInt16 nParam ) const
166 {
167 if( !bWithOpt )
168 nParam++;
169 return (nParam > nParamCount) ? (nParamCount * 2) : (nParam * 2);
170 }
171
172
173 //------------------------------------------------------------------
174
ScaFuncDataList(ResMgr & rResMgr)175 ScaFuncDataList::ScaFuncDataList( ResMgr& rResMgr ) :
176 nLast( 0xFFFFFFFF )
177 {
178 const sal_uInt32 nCnt = sizeof( pFuncDataArr ) / sizeof( ScaFuncDataBase );
179
180 for( sal_uInt16 nIndex = 0; nIndex < nCnt; nIndex++ )
181 Append( new ScaFuncData( pFuncDataArr[ nIndex ], rResMgr ) );
182 }
183
~ScaFuncDataList()184 ScaFuncDataList::~ScaFuncDataList()
185 {
186 for( ScaFuncData* pFData = First(); pFData; pFData = Next() )
187 delete pFData;
188 }
189
Get(const OUString & rProgrammaticName) const190 const ScaFuncData* ScaFuncDataList::Get( const OUString& rProgrammaticName ) const
191 {
192 if( aLastName == rProgrammaticName )
193 return Get( nLast );
194
195 for( sal_uInt32 nIndex = 0; nIndex < Count(); nIndex++ )
196 {
197 const ScaFuncData* pCurr = Get( nIndex );
198 if( pCurr->Is( rProgrammaticName ) )
199 {
200 const_cast< ScaFuncDataList* >( this )->aLastName = rProgrammaticName;
201 const_cast< ScaFuncDataList* >( this )->nLast = nIndex;
202 return pCurr;
203 }
204 }
205 return NULL;
206 }
207
208
209 //------------------------------------------------------------------
210
ScaFuncRes(ResId & rResId,ResMgr & rResMgr,sal_uInt16 nIndex,OUString & rRet)211 ScaFuncRes::ScaFuncRes( ResId& rResId, ResMgr& rResMgr, sal_uInt16 nIndex, OUString& rRet ) :
212 Resource( rResId )
213 {
214 rRet = String( ScaResId( nIndex, rResMgr ) );
215 FreeResource();
216 }
217
218
219 //------------------------------------------------------------------
220 //
221 // entry points for service registration / instantiation
222 //
223 //------------------------------------------------------------------
224
ScaDateAddIn_CreateInstance(const uno::Reference<lang::XMultiServiceFactory> &)225 uno::Reference< uno::XInterface > SAL_CALL ScaDateAddIn_CreateInstance(
226 const uno::Reference< lang::XMultiServiceFactory >& )
227 {
228 static uno::Reference< uno::XInterface > xInst = (cppu::OWeakObject*) new ScaDateAddIn();
229 return xInst;
230 }
231
232
233 //------------------------------------------------------------------------
234
235 extern "C" {
236
component_getImplementationEnvironment(const sal_Char ** ppEnvTypeName,uno_Environment **)237 void SAL_CALL component_getImplementationEnvironment(
238 const sal_Char ** ppEnvTypeName, uno_Environment ** /*ppEnv*/ )
239 {
240 *ppEnvTypeName = CPPU_CURRENT_LANGUAGE_BINDING_NAME;
241 }
242
component_getFactory(const sal_Char * pImplName,void * pServiceManager,void *)243 void * SAL_CALL component_getFactory(
244 const sal_Char * pImplName, void * pServiceManager, void * /*pRegistryKey*/ )
245 {
246 void* pRet = 0;
247
248 if ( pServiceManager &&
249 OUString::createFromAscii( pImplName ) == ScaDateAddIn::getImplementationName_Static() )
250 {
251 uno::Reference< lang::XSingleServiceFactory > xFactory( cppu::createOneInstanceFactory(
252 reinterpret_cast< lang::XMultiServiceFactory* >( pServiceManager ),
253 ScaDateAddIn::getImplementationName_Static(),
254 ScaDateAddIn_CreateInstance,
255 ScaDateAddIn::getSupportedServiceNames_Static() ) );
256
257 if (xFactory.is())
258 {
259 xFactory->acquire();
260 pRet = xFactory.get();
261 }
262 }
263
264 return pRet;
265 }
266
267 } // extern C
268
269 //------------------------------------------------------------------------
270 //
271 // "normal" service implementation
272 //
273 //------------------------------------------------------------------------
274
ScaDateAddIn()275 ScaDateAddIn::ScaDateAddIn() :
276 pDefLocales( NULL ),
277 pResMgr( NULL ),
278 pFuncDataList( NULL )
279 {
280 }
281
~ScaDateAddIn()282 ScaDateAddIn::~ScaDateAddIn()
283 {
284 if( pFuncDataList )
285 delete pFuncDataList;
286 if( pDefLocales )
287 delete[] pDefLocales;
288
289 // pResMgr already deleted (_all_ resource managers are deleted _before_ this dtor is called)
290 }
291
292 static const sal_Char* pLang[] = { "de", "en" };
293 static const sal_Char* pCoun[] = { "DE", "US" };
294 static const sal_uInt32 nNumOfLoc = sizeof( pLang ) / sizeof( sal_Char* );
295
InitDefLocales()296 void ScaDateAddIn::InitDefLocales()
297 {
298 pDefLocales = new lang::Locale[ nNumOfLoc ];
299
300 for( sal_uInt32 nIndex = 0; nIndex < nNumOfLoc; nIndex++ )
301 {
302 pDefLocales[ nIndex ].Language = OUString::createFromAscii( pLang[ nIndex ] );
303 pDefLocales[ nIndex ].Country = OUString::createFromAscii( pCoun[ nIndex ] );
304 }
305 }
306
GetLocale(sal_uInt32 nIndex)307 const lang::Locale& ScaDateAddIn::GetLocale( sal_uInt32 nIndex )
308 {
309 if( !pDefLocales )
310 InitDefLocales();
311
312 return (nIndex < sizeof( pLang )) ? pDefLocales[ nIndex ] : aFuncLoc;
313 }
314
GetResMgr()315 ResMgr& ScaDateAddIn::GetResMgr() throw( uno::RuntimeException )
316 {
317 if( !pResMgr )
318 {
319 InitData(); // try to get resource manager
320 if( !pResMgr )
321 throw uno::RuntimeException();
322 }
323 return *pResMgr;
324 }
325
InitData()326 void ScaDateAddIn::InitData()
327 {
328 if( pResMgr )
329 delete pResMgr;
330
331 OString aModName( "date" );
332 pResMgr = ResMgr::CreateResMgr( aModName.getStr(), aFuncLoc );
333
334 if( pFuncDataList )
335 delete pFuncDataList;
336
337 pFuncDataList = pResMgr ? new ScaFuncDataList( *pResMgr ) : NULL;
338
339 if( pDefLocales )
340 {
341 delete pDefLocales;
342 pDefLocales = NULL;
343 }
344 }
345
GetDisplFuncStr(sal_uInt16 nResId)346 OUString ScaDateAddIn::GetDisplFuncStr( sal_uInt16 nResId ) throw( uno::RuntimeException )
347 {
348 return ScaResStringLoader( RID_DATE_FUNCTION_NAMES, nResId, GetResMgr() ).GetString();
349 }
350
GetFuncDescrStr(sal_uInt16 nResId,sal_uInt16 nStrIndex)351 OUString ScaDateAddIn::GetFuncDescrStr( sal_uInt16 nResId, sal_uInt16 nStrIndex ) throw( uno::RuntimeException )
352 {
353 OUString aRet;
354
355 ScaResPublisher aResPubl( ScaResId( RID_DATE_FUNCTION_DESCRIPTIONS, GetResMgr() ) );
356 ScaResId aResId( nResId, GetResMgr() );
357 aResId.SetRT( RSC_RESOURCE );
358
359 if( aResPubl.IsAvailableRes( aResId ) )
360 ScaFuncRes aSubRes( aResId, GetResMgr(), nStrIndex, aRet );
361
362 aResPubl.FreeResource();
363 return aRet;
364 }
365
366
367 //------------------------------------------------------------------------
368
getImplementationName_Static()369 OUString ScaDateAddIn::getImplementationName_Static()
370 {
371 return OUString::createFromAscii( MY_IMPLNAME );
372 }
373
getSupportedServiceNames_Static()374 uno::Sequence< OUString > ScaDateAddIn::getSupportedServiceNames_Static()
375 {
376 uno::Sequence< OUString > aRet( 2 );
377 OUString* pArray = aRet.getArray();
378 pArray[0] = OUString::createFromAscii( ADDIN_SERVICE );
379 pArray[1] = OUString::createFromAscii( MY_SERVICE );
380 return aRet;
381 }
382
383 // XServiceName
384
getServiceName()385 OUString SAL_CALL ScaDateAddIn::getServiceName() throw( uno::RuntimeException )
386 {
387 // name of specific AddIn service
388 return OUString::createFromAscii( MY_SERVICE );
389 }
390
391 // XServiceInfo
392
getImplementationName()393 OUString SAL_CALL ScaDateAddIn::getImplementationName() throw( uno::RuntimeException )
394 {
395 return getImplementationName_Static();
396 }
397
supportsService(const OUString & aServiceName)398 sal_Bool SAL_CALL ScaDateAddIn::supportsService( const OUString& aServiceName ) throw( uno::RuntimeException )
399 {
400 return aServiceName.equalsAscii( ADDIN_SERVICE ) ||
401 aServiceName.equalsAscii( MY_SERVICE );
402 }
403
getSupportedServiceNames()404 uno::Sequence< OUString > SAL_CALL ScaDateAddIn::getSupportedServiceNames() throw( uno::RuntimeException )
405 {
406 return getSupportedServiceNames_Static();
407 }
408
409 // XLocalizable
410
setLocale(const lang::Locale & eLocale)411 void SAL_CALL ScaDateAddIn::setLocale( const lang::Locale& eLocale ) throw( uno::RuntimeException )
412 {
413 aFuncLoc = eLocale;
414 InitData(); // change of locale invalidates resources!
415 }
416
getLocale()417 lang::Locale SAL_CALL ScaDateAddIn::getLocale() throw( uno::RuntimeException )
418 {
419 return aFuncLoc;
420 }
421
422 //------------------------------------------------------------------
423 //
424 // function descriptions start here
425 //
426 //------------------------------------------------------------------
427
428 // XAddIn
429
getProgrammaticFuntionName(const OUString &)430 OUString SAL_CALL ScaDateAddIn::getProgrammaticFuntionName( const OUString& ) throw( uno::RuntimeException )
431 {
432 // not used by calc
433 // (but should be implemented for other uses of the AddIn service)
434 return OUString();
435 }
436
getDisplayFunctionName(const OUString & aProgrammaticName)437 OUString SAL_CALL ScaDateAddIn::getDisplayFunctionName( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
438 {
439 OUString aRet;
440
441 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName );
442 if( pFData )
443 {
444 aRet = GetDisplFuncStr( pFData->GetUINameID() );
445 if( pFData->IsDouble() )
446 aRet += STR_FROM_ANSI( "_ADD" );
447 }
448 else
449 {
450 aRet = STR_FROM_ANSI( "UNKNOWNFUNC_" );
451 aRet += aProgrammaticName;
452 }
453
454 return aRet;
455 }
456
getFunctionDescription(const OUString & aProgrammaticName)457 OUString SAL_CALL ScaDateAddIn::getFunctionDescription( const OUString& aProgrammaticName ) throw( uno::RuntimeException )
458 {
459 OUString aRet;
460
461 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName );
462 if( pFData )
463 aRet = GetFuncDescrStr( pFData->GetDescrID(), 1 );
464
465 return aRet;
466 }
467
getDisplayArgumentName(const OUString & aProgrammaticName,sal_Int32 nArgument)468 OUString SAL_CALL ScaDateAddIn::getDisplayArgumentName(
469 const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
470 {
471 OUString aRet;
472
473 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName );
474 if( pFData && (nArgument <= 0xFFFF) )
475 {
476 sal_uInt16 nStr = pFData->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) );
477 if( nStr )
478 aRet = GetFuncDescrStr( pFData->GetDescrID(), nStr );
479 else
480 aRet = STR_FROM_ANSI( "internal" );
481 }
482
483 return aRet;
484 }
485
getArgumentDescription(const OUString & aProgrammaticName,sal_Int32 nArgument)486 OUString SAL_CALL ScaDateAddIn::getArgumentDescription(
487 const OUString& aProgrammaticName, sal_Int32 nArgument ) throw( uno::RuntimeException )
488 {
489 OUString aRet;
490
491 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName );
492 if( pFData && (nArgument <= 0xFFFF) )
493 {
494 sal_uInt16 nStr = pFData->GetStrIndex( static_cast< sal_uInt16 >( nArgument ) );
495 if( nStr )
496 aRet = GetFuncDescrStr( pFData->GetDescrID(), nStr + 1 );
497 else
498 aRet = STR_FROM_ANSI( "for internal use only" );
499 }
500
501 return aRet;
502 }
503
getProgrammaticCategoryName(const OUString & aProgrammaticName)504 OUString SAL_CALL ScaDateAddIn::getProgrammaticCategoryName(
505 const OUString& aProgrammaticName ) throw( uno::RuntimeException )
506 {
507 OUString aRet;
508
509 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName );
510 if( pFData )
511 {
512 switch( pFData->GetCategory() )
513 {
514 case ScaCat_DateTime: aRet = STR_FROM_ANSI( "Date&Time" ); break;
515 case ScaCat_Text: aRet = STR_FROM_ANSI( "Text" ); break;
516 case ScaCat_Finance: aRet = STR_FROM_ANSI( "Financial" ); break;
517 case ScaCat_Inf: aRet = STR_FROM_ANSI( "Information" ); break;
518 case ScaCat_Math: aRet = STR_FROM_ANSI( "Mathematical" ); break;
519 case ScaCat_Tech: aRet = STR_FROM_ANSI( "Technical" ); break;
520 default: // to prevent compiler warnings
521 break;
522 }
523 }
524
525 if( !aRet.getLength() )
526 aRet = STR_FROM_ANSI( "Add-In" );
527 return aRet;
528 }
529
getDisplayCategoryName(const OUString & aProgrammaticName)530 OUString SAL_CALL ScaDateAddIn::getDisplayCategoryName(
531 const OUString& aProgrammaticName ) throw( uno::RuntimeException )
532 {
533 return getProgrammaticCategoryName( aProgrammaticName );
534 }
535
536
537 // XCompatibilityNames
538
getCompatibilityNames(const OUString & aProgrammaticName)539 uno::Sequence< sheet::LocalizedName > SAL_CALL ScaDateAddIn::getCompatibilityNames(
540 const OUString& aProgrammaticName ) throw( uno::RuntimeException )
541 {
542 const ScaFuncData* pFData = pFuncDataList->Get( aProgrammaticName );
543 if( !pFData )
544 return uno::Sequence< sheet::LocalizedName >( 0 );
545
546 const ScaStringList& rStrList = pFData->GetCompNameList();
547 sal_uInt32 nCount = rStrList.Count();
548
549 uno::Sequence< sheet::LocalizedName > aRet( nCount );
550 sheet::LocalizedName* pArray = aRet.getArray();
551
552 for( sal_uInt32 nIndex = 0; nIndex < nCount; nIndex++ )
553 pArray[ nIndex ] = sheet::LocalizedName( GetLocale( nIndex ), *rStrList.Get( nIndex ) );
554
555 return aRet;
556 }
557
558
559 //------------------------------------------------------------------
560 //
561 // function implementation starts here
562 //
563 //------------------------------------------------------------------
564
565 // auxiliary functions
566
IsLeapYear(sal_uInt16 nYear)567 sal_Bool IsLeapYear( sal_uInt16 nYear )
568 {
569 return ((((nYear % 4) == 0) && ((nYear % 100) != 0)) || ((nYear % 400) == 0));
570 }
571
DaysInMonth(sal_uInt16 nMonth,sal_uInt16 nYear)572 sal_uInt16 DaysInMonth( sal_uInt16 nMonth, sal_uInt16 nYear )
573 {
574 static sal_uInt16 aDaysInMonth[12] = { 31, 28, 31, 30, 31, 30,
575 31, 31, 30, 31, 30, 31 };
576
577 if ( nMonth != 2 )
578 return aDaysInMonth[nMonth-1];
579 else
580 {
581 if ( IsLeapYear(nYear) )
582 return aDaysInMonth[nMonth-1] + 1;
583 else
584 return aDaysInMonth[nMonth-1];
585 }
586 }
587
588 /**
589 * Convert a date to a count of days starting from 01/01/0001
590 *
591 * The internal representation of a Date used in this Addin
592 * is the number of days between 01/01/0001 and the date
593 * this function converts a Day , Month, Year representation
594 * to this internal Date value.
595 */
596
DateToDays(sal_uInt16 nDay,sal_uInt16 nMonth,sal_uInt16 nYear)597 sal_Int32 DateToDays( sal_uInt16 nDay, sal_uInt16 nMonth, sal_uInt16 nYear )
598 {
599 sal_Int32 nDays = ((sal_Int32)nYear-1) * 365;
600 nDays += ((nYear-1) / 4) - ((nYear-1) / 100) + ((nYear-1) / 400);
601
602 for( sal_uInt16 i = 1; i < nMonth; i++ )
603 nDays += DaysInMonth(i,nYear);
604 nDays += nDay;
605
606 return nDays;
607 }
608
609 /**
610 * Convert a count of days starting from 01/01/0001 to a date
611 *
612 * The internal representation of a Date used in this Addin
613 * is the number of days between 01/01/0001 and the date
614 * this function converts this internal Date value
615 * to a Day , Month, Year representation of a Date.
616 */
617
DaysToDate(sal_Int32 nDays,sal_uInt16 & rDay,sal_uInt16 & rMonth,sal_uInt16 & rYear)618 void DaysToDate( sal_Int32 nDays,
619 sal_uInt16& rDay, sal_uInt16& rMonth, sal_uInt16& rYear )
620 throw( lang::IllegalArgumentException )
621 {
622 if( nDays < 0 )
623 throw lang::IllegalArgumentException();
624
625 sal_Int32 nTempDays;
626 sal_Int32 i = 0;
627 sal_Bool bCalc;
628
629 do
630 {
631 nTempDays = nDays;
632 rYear = (sal_uInt16)((nTempDays / 365) - i);
633 nTempDays -= ((sal_Int32) rYear -1) * 365;
634 nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
635 bCalc = sal_False;
636 if ( nTempDays < 1 )
637 {
638 i++;
639 bCalc = sal_True;
640 }
641 else
642 {
643 if ( nTempDays > 365 )
644 {
645 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
646 {
647 i--;
648 bCalc = sal_True;
649 }
650 }
651 }
652 }
653 while ( bCalc );
654
655 rMonth = 1;
656 while ( (sal_Int32)nTempDays > DaysInMonth( rMonth, rYear ) )
657 {
658 nTempDays -= DaysInMonth( rMonth, rYear );
659 rMonth++;
660 }
661 rDay = (sal_uInt16)nTempDays;
662 }
663
664 /**
665 * Get the null date used by the spreadsheet document
666 *
667 * The internal representation of a Date used in this Addin
668 * is the number of days between 01/01/0001 and the date
669 * this function returns this internal Date value for the document null date
670 *
671 */
672
GetNullDate(const uno::Reference<beans::XPropertySet> & xOptions)673 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOptions )
674 throw( uno::RuntimeException )
675 {
676 if (xOptions.is())
677 {
678 try
679 {
680 uno::Any aAny = xOptions->getPropertyValue(
681 OUString::createFromAscii( "NullDate" ) );
682 util::Date aDate;
683 if ( aAny >>= aDate )
684 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
685 }
686 catch (uno::Exception&)
687 {
688 }
689 }
690
691 // no null date available -> no calculations possible
692 throw uno::RuntimeException();
693 }
694
695 // XDateFunctions
696
697 /**
698 * Get week difference between 2 dates
699 *
700 * new Weeks(date1,date2,mode) function for StarCalc
701 *
702 * Two modes of operation are provided.
703 * The first is just a simple division by 7 calculation.
704 *
705 * The second calculates the diffence by week of year.
706 *
707 * The International Standard IS-8601 has decreed that Monday
708 * shall be the first day of the week.
709 *
710 * A week that lies partly in one year and partly in annother
711 * is assigned a number in the the year in which most of its days lie.
712 *
713 * That means that week 1 of any year is the week that contains the 4. January
714 *
715 * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001
716 *
717 * A WeekDay can be then calculated by substracting 1 and calculating the rest of
718 * a division by 7, which gives a 0 - 6 value for Monday - Sunday
719 *
720 * Using the 4. January rule explained above the formula
721 *
722 * nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
723 *
724 * calculates a number between 0-53 for each day which is in the same year as nJan4
725 * where 0 means that this week belonged to the year before.
726 *
727 * If a day in the same or annother year is used in this formula this calculates
728 * an calendar week offset from a given 4. January
729 *
730 * nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
731 *
732 * The 4.January of first Date Argument can thus be used to calculate
733 * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1
734 *
735 * which can be optimized to
736 *
737 * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 )
738 *
739 * Note: All calculations are operating on the long integer data type
740 * % is the modulo operator in C which calculates the rest of an Integer division
741 *
742 *
743 * mode 0 is the interval between the dates in month, that is days / 7
744 *
745 * mode 1 is the difference by week of year
746 *
747 */
748
getDiffWeeks(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)749 sal_Int32 SAL_CALL ScaDateAddIn::getDiffWeeks(
750 const uno::Reference< beans::XPropertySet >& xOptions,
751 sal_Int32 nStartDate, sal_Int32 nEndDate,
752 sal_Int32 nMode ) throw( uno::RuntimeException, lang::IllegalArgumentException )
753 {
754 sal_Int32 nNullDate = GetNullDate( xOptions );
755
756 sal_Int32 nDays1 = nStartDate + nNullDate;
757 sal_Int32 nDays2 = nEndDate + nNullDate;
758
759 sal_Int32 nRet;
760
761 if ( nMode == 1 )
762 {
763 sal_uInt16 nDay,nMonth,nYear;
764 DaysToDate( nDays1, nDay, nMonth, nYear );
765 sal_Int32 nJan4 = DateToDays( 4, 1, nYear );
766
767 nRet = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 );
768 }
769 else
770 {
771 nRet = (nDays2 - nDays1) / 7;
772 }
773 return nRet;
774 }
775
776 /**
777 * Get month difference between 2 dates
778 * =Month(start, end, mode) Function for StarCalc
779 *
780 * two modes are provided
781 *
782 * mode 0 is the interval between the dates in month
783 *
784 * mode 1 is the difference in calendar month
785 */
786
getDiffMonths(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)787 sal_Int32 SAL_CALL ScaDateAddIn::getDiffMonths(
788 const uno::Reference< beans::XPropertySet >& xOptions,
789 sal_Int32 nStartDate, sal_Int32 nEndDate,
790 sal_Int32 nMode ) throw( uno::RuntimeException, lang::IllegalArgumentException )
791 {
792 sal_Int32 nNullDate = GetNullDate( xOptions );
793
794 sal_Int32 nDays1 = nStartDate + nNullDate;
795 sal_Int32 nDays2 = nEndDate + nNullDate;
796
797 sal_uInt16 nDay1,nMonth1,nYear1;
798 sal_uInt16 nDay2,nMonth2,nYear2;
799 DaysToDate(nDays1,nDay1,nMonth1,nYear1);
800 DaysToDate(nDays2,nDay2,nMonth2,nYear2);
801
802 sal_Int32 nRet = nMonth2 - nMonth1 + (nYear2 - nYear1) * 12;
803 if ( nMode == 1 || nDays1 == nDays2 ) return nRet;
804
805 if ( nDays1 < nDays2 )
806 {
807 if ( nDay1 > nDay2 )
808 {
809 nRet -= 1;
810 }
811 }
812 else
813 {
814 if ( nDay1 < nDay2 )
815 {
816 nRet += 1;
817 }
818 }
819
820 return nRet;
821 }
822
823 /**
824 * Get Year difference between 2 dates
825 *
826 * two modes are provided
827 *
828 * mode 0 is the interval between the dates in years
829 *
830 * mode 1 is the difference in calendar years
831 */
832
getDiffYears(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)833 sal_Int32 SAL_CALL ScaDateAddIn::getDiffYears(
834 const uno::Reference< beans::XPropertySet >& xOptions,
835 sal_Int32 nStartDate, sal_Int32 nEndDate,
836 sal_Int32 nMode ) throw( uno::RuntimeException, lang::IllegalArgumentException )
837 {
838 if ( nMode != 1 )
839 return getDiffMonths( xOptions, nStartDate, nEndDate, nMode ) / 12;
840
841 sal_Int32 nNullDate = GetNullDate( xOptions );
842
843 sal_Int32 nDays1 = nStartDate + nNullDate;
844 sal_Int32 nDays2 = nEndDate + nNullDate;
845
846 sal_uInt16 nDay1,nMonth1,nYear1;
847 sal_uInt16 nDay2,nMonth2,nYear2;
848 DaysToDate(nDays1,nDay1,nMonth1,nYear1);
849 DaysToDate(nDays2,nDay2,nMonth2,nYear2);
850
851 return nYear2 - nYear1;
852 }
853
854 /**
855 * Check if a Date is in a leap year in the Gregorian calendar
856 */
857
getIsLeapYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)858 sal_Int32 SAL_CALL ScaDateAddIn::getIsLeapYear(
859 const uno::Reference< beans::XPropertySet >& xOptions,
860 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException )
861 {
862 sal_Int32 nNullDate = GetNullDate( xOptions );
863 sal_Int32 nDays = nDate + nNullDate;
864
865 sal_uInt16 nDay, nMonth, nYear;
866 DaysToDate(nDays,nDay,nMonth,nYear);
867
868 return (sal_Int32)IsLeapYear(nYear);
869 }
870
871 /**
872 * Get the Number of Days in the month for a date
873 */
874
getDaysInMonth(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)875 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInMonth(
876 const uno::Reference<beans::XPropertySet>& xOptions,
877 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException )
878 {
879 sal_Int32 nNullDate = GetNullDate( xOptions );
880 sal_Int32 nDays = nDate + nNullDate;
881
882 sal_uInt16 nDay, nMonth, nYear;
883 DaysToDate(nDays,nDay,nMonth,nYear);
884
885 return DaysInMonth( nMonth, nYear );
886 }
887
888 /**
889 * Get number of days in the year of a date specified
890 */
891
getDaysInYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)892 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInYear(
893 const uno::Reference< beans::XPropertySet >& xOptions,
894 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException )
895 {
896 sal_Int32 nNullDate = GetNullDate( xOptions );
897 sal_Int32 nDays = nDate + nNullDate;
898
899 sal_uInt16 nDay, nMonth, nYear;
900 DaysToDate(nDays,nDay,nMonth,nYear);
901
902 return ( IsLeapYear(nYear) ? 366 : 365 );
903 }
904
905 /**
906 * Get number of weeks in the year for a date
907 *
908 * Most years have 52 weeks, but years that start on a Thursday
909 * and leep years that start on a Wednesday have 53 weeks
910 *
911 * The International Standard IS-8601 has decreed that Monday
912 * shall be the first day of the week.
913 *
914 * A WeekDay can be calculated by substracting 1 and calculating the rest of
915 * a division by 7 from the internal date represention
916 * which gives a 0 - 6 value for Monday - Sunday
917 *
918 * @see #IsLeapYear #WeekNumber
919 */
920
getWeeksInYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)921 sal_Int32 SAL_CALL ScaDateAddIn::getWeeksInYear(
922 const uno::Reference< beans::XPropertySet >& xOptions,
923 sal_Int32 nDate ) throw( uno::RuntimeException, lang::IllegalArgumentException )
924 {
925 sal_Int32 nNullDate = GetNullDate( xOptions );
926 sal_Int32 nDays = nDate + nNullDate;
927
928 sal_uInt16 nDay, nMonth, nYear;
929 DaysToDate(nDays,nDay,nMonth,nYear);
930
931 sal_Int32 nJan1WeekDay = ( DateToDays(1,1,nYear) - 1) % 7;
932
933 sal_Int32 nRet;
934 if ( nJan1WeekDay == 3 ) /* Thursday */
935 nRet = 53;
936 else if ( nJan1WeekDay == 2 ) /* Wednesday */
937 nRet = ( IsLeapYear(nYear) ? 53 : 52 );
938 else
939 nRet = 52;
940
941 return nRet;
942 }
943
944 /**
945 * Encrypt or decrypt a string using ROT13 algorithm
946 *
947 * This function rotates each character by 13 in the alphabet.
948 * Only the characters 'a' ... 'z' and 'A' ... 'Z' are modified.
949 */
950
getRot13(const OUString & aSrcString)951 OUString SAL_CALL ScaDateAddIn::getRot13( const OUString& aSrcString ) throw( uno::RuntimeException, lang::IllegalArgumentException )
952 {
953 OUStringBuffer aBuffer( aSrcString );
954 for( sal_Int32 nIndex = 0; nIndex < aBuffer.getLength(); nIndex++ )
955 {
956 sal_Unicode cChar = aBuffer.charAt( nIndex );
957 if( ((cChar >= 'a') && (cChar <= 'z') && ((cChar += 13) > 'z')) ||
958 ((cChar >= 'A') && (cChar <= 'Z') && ((cChar += 13) > 'Z')) )
959 cChar -= 26;
960 aBuffer.setCharAt( nIndex, cChar );
961 }
962 return aBuffer.makeStringAndClear();
963 }
964
965 //------------------------------------------------------------------
966
967