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