xref: /trunk/main/scaddins/source/datefunc/datefunc.cxx (revision 91144cd0085a7583d2099b982122deb2184ab956) !
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 SAL_DLLPUBLIC_EXPORT 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 SAL_DLLPUBLIC_EXPORT 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()
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 )
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 )
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()
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()
394 {
395     return getImplementationName_Static();
396 }
397 
supportsService(const OUString & aServiceName)398 sal_Bool SAL_CALL ScaDateAddIn::supportsService( const OUString& aServiceName )
399 {
400     return aServiceName.equalsAscii( ADDIN_SERVICE ) ||
401         aServiceName.equalsAscii( MY_SERVICE );
402 }
403 
getSupportedServiceNames()404 uno::Sequence< OUString > SAL_CALL ScaDateAddIn::getSupportedServiceNames()
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 )
412 {
413     aFuncLoc = eLocale;
414     InitData();     // change of locale invalidates resources!
415 }
416 
getLocale()417 lang::Locale SAL_CALL ScaDateAddIn::getLocale()
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& )
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 )
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 )
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 )
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 )
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 )
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 )
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 )
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 {
621     if( nDays < 0 )
622         throw lang::IllegalArgumentException();
623 
624     sal_Int32   nTempDays;
625     sal_Int32   i = 0;
626     sal_Bool    bCalc;
627 
628     do
629     {
630         nTempDays = nDays;
631         rYear = (sal_uInt16)((nTempDays / 365) - i);
632         nTempDays -= ((sal_Int32) rYear -1) * 365;
633         nTempDays -= (( rYear -1) / 4) - (( rYear -1) / 100) + ((rYear -1) / 400);
634         bCalc = sal_False;
635         if ( nTempDays < 1 )
636         {
637             i++;
638             bCalc = sal_True;
639         }
640         else
641         {
642             if ( nTempDays > 365 )
643             {
644                 if ( (nTempDays != 366) || !IsLeapYear( rYear ) )
645                 {
646                     i--;
647                     bCalc = sal_True;
648                 }
649             }
650         }
651     }
652     while ( bCalc );
653 
654     rMonth = 1;
655     while ( (sal_Int32)nTempDays > DaysInMonth( rMonth, rYear ) )
656     {
657         nTempDays -= DaysInMonth( rMonth, rYear );
658         rMonth++;
659     }
660     rDay = (sal_uInt16)nTempDays;
661 }
662 
663 /**
664  * Get the null date used by the spreadsheet document
665  *
666  * The internal representation of a Date used in this Addin
667  * is the number of days between 01/01/0001 and the date
668  * this function returns this internal Date value for the document null date
669  *
670  */
671 
GetNullDate(const uno::Reference<beans::XPropertySet> & xOptions)672 sal_Int32 GetNullDate( const uno::Reference< beans::XPropertySet >& xOptions )
673 {
674     if (xOptions.is())
675     {
676         try
677         {
678             uno::Any aAny = xOptions->getPropertyValue(
679                                         OUString::createFromAscii( "NullDate" ) );
680             util::Date aDate;
681             if ( aAny >>= aDate )
682                 return DateToDays( aDate.Day, aDate.Month, aDate.Year );
683         }
684         catch (uno::Exception&)
685         {
686         }
687     }
688 
689     // no null date available -> no calculations possible
690     throw uno::RuntimeException();
691 }
692 
693 // XDateFunctions
694 
695 /**
696  * Get week difference between 2 dates
697  *
698  * new Weeks(date1,date2,mode) function for StarCalc
699  *
700  * Two modes of operation are provided.
701  * The first is just a simple division by 7 calculation.
702  *
703  * The second calculates the difference by week of year.
704  *
705  * The International Standard IS-8601 has decreed that Monday
706  * shall be the first day of the week.
707  *
708  * A week that lies partly in one year and partly in another
709  * is assigned a number in the year in which most of its days lie.
710  *
711  * That means that week 1 of any year is the week that contains the 4. January
712  *
713  * The internal representation of a Date used in the Addin is the number of days based on 01/01/0001
714  *
715  * A WeekDay can be then calculated by subtracting 1 and calculating the rest of
716  * a division by 7, which gives a 0 - 6 value for Monday - Sunday
717  *
718  * Using the 4. January rule explained above the formula
719  *
720  *  nWeek1= ( nDays1 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
721  *
722  * calculates a number between 0-53 for each day which is in the same year as nJan4
723  * where 0 means that this week belonged to the year before.
724  *
725  * If a day in the same or another year is used in this formula this calculates
726  * an calendar week offset from a given 4. January
727  *
728  *  nWeek2 = ( nDays2 - nJan4 + ( (nJan4-1) % 7 ) ) / 7 + 1;
729  *
730  * The 4.January of first Date Argument can thus be used to calculate
731  * the week difference by calendar weeks which is then nWeek = nWeek2 - nWeek1
732  *
733  * which can be optimized to
734  *
735  * nWeek = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 )
736  *
737  * Note: All calculations are operating on the long integer data type
738  * % is the modulo operator in C which calculates the rest of an Integer division
739  *
740  *
741  * mode 0 is the interval between the dates in month, that is days / 7
742  *
743  * mode 1 is the difference by week of year
744  *
745  */
746 
getDiffWeeks(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)747 sal_Int32 SAL_CALL ScaDateAddIn::getDiffWeeks(
748         const uno::Reference< beans::XPropertySet >& xOptions,
749         sal_Int32 nStartDate, sal_Int32 nEndDate,
750         sal_Int32 nMode )
751 {
752     sal_Int32 nNullDate = GetNullDate( xOptions );
753 
754     sal_Int32 nDays1 = nStartDate + nNullDate;
755     sal_Int32 nDays2 = nEndDate + nNullDate;
756 
757     sal_Int32 nRet;
758 
759     if ( nMode == 1 )
760     {
761         sal_uInt16 nDay,nMonth,nYear;
762         DaysToDate( nDays1, nDay, nMonth, nYear );
763         sal_Int32 nJan4 = DateToDays( 4, 1, nYear );
764 
765         nRet = ( (nDays2-nJan4+((nJan4-1)%7))/7 ) - ( (nDays1-nJan4+((nJan4-1)%7))/7 );
766     }
767     else
768     {
769         nRet = (nDays2 - nDays1) / 7;
770     }
771     return nRet;
772 }
773 
774 /**
775  * Get month difference between 2 dates
776  * =Month(start, end, mode) Function for StarCalc
777  *
778  * two modes are provided
779  *
780  * mode 0 is the interval between the dates in month
781  *
782  * mode 1 is the difference in calendar month
783  */
784 
getDiffMonths(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)785 sal_Int32 SAL_CALL ScaDateAddIn::getDiffMonths(
786         const uno::Reference< beans::XPropertySet >& xOptions,
787         sal_Int32 nStartDate, sal_Int32 nEndDate,
788         sal_Int32 nMode )
789 {
790     sal_Int32 nNullDate = GetNullDate( xOptions );
791 
792     sal_Int32 nDays1 = nStartDate + nNullDate;
793     sal_Int32 nDays2 = nEndDate + nNullDate;
794 
795     sal_uInt16 nDay1,nMonth1,nYear1;
796     sal_uInt16 nDay2,nMonth2,nYear2;
797     DaysToDate(nDays1,nDay1,nMonth1,nYear1);
798     DaysToDate(nDays2,nDay2,nMonth2,nYear2);
799 
800     sal_Int32 nRet = nMonth2 - nMonth1 + (nYear2 - nYear1) * 12;
801     if ( nMode == 1 || nDays1 == nDays2 ) return nRet;
802 
803     if ( nDays1 < nDays2 )
804     {
805         if ( nDay1 > nDay2 )
806         {
807             nRet -= 1;
808         }
809     }
810     else
811     {
812         if ( nDay1 < nDay2 )
813         {
814             nRet += 1;
815         }
816     }
817 
818     return nRet;
819 }
820 
821 /**
822  * Get Year difference between 2 dates
823  *
824  * two modes are provided
825  *
826  * mode 0 is the interval between the dates in years
827  *
828  * mode 1 is the difference in calendar years
829  */
830 
getDiffYears(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nStartDate,sal_Int32 nEndDate,sal_Int32 nMode)831 sal_Int32 SAL_CALL ScaDateAddIn::getDiffYears(
832         const uno::Reference< beans::XPropertySet >& xOptions,
833         sal_Int32 nStartDate, sal_Int32 nEndDate,
834         sal_Int32 nMode )
835 {
836     if ( nMode != 1 )
837         return getDiffMonths( xOptions, nStartDate, nEndDate, nMode ) / 12;
838 
839     sal_Int32 nNullDate = GetNullDate( xOptions );
840 
841     sal_Int32 nDays1 = nStartDate + nNullDate;
842     sal_Int32 nDays2 = nEndDate + nNullDate;
843 
844     sal_uInt16 nDay1,nMonth1,nYear1;
845     sal_uInt16 nDay2,nMonth2,nYear2;
846     DaysToDate(nDays1,nDay1,nMonth1,nYear1);
847     DaysToDate(nDays2,nDay2,nMonth2,nYear2);
848 
849     return nYear2 - nYear1;
850 }
851 
852 /**
853  * Check if a Date is in a leap year in the Gregorian calendar
854  */
855 
getIsLeapYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)856 sal_Int32 SAL_CALL ScaDateAddIn::getIsLeapYear(
857         const uno::Reference< beans::XPropertySet >& xOptions,
858         sal_Int32 nDate )
859 {
860     sal_Int32 nNullDate = GetNullDate( xOptions );
861     sal_Int32 nDays = nDate + nNullDate;
862 
863     sal_uInt16 nDay, nMonth, nYear;
864     DaysToDate(nDays,nDay,nMonth,nYear);
865 
866     return (sal_Int32)IsLeapYear(nYear);
867 }
868 
869 /**
870  * Get the Number of Days in the month for a date
871  */
872 
getDaysInMonth(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)873 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInMonth(
874         const uno::Reference<beans::XPropertySet>& xOptions,
875         sal_Int32 nDate )
876 {
877     sal_Int32 nNullDate = GetNullDate( xOptions );
878     sal_Int32 nDays = nDate + nNullDate;
879 
880     sal_uInt16 nDay, nMonth, nYear;
881     DaysToDate(nDays,nDay,nMonth,nYear);
882 
883     return DaysInMonth( nMonth, nYear );
884 }
885 
886 /**
887  * Get number of days in the year of a date specified
888  */
889 
getDaysInYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)890 sal_Int32 SAL_CALL ScaDateAddIn::getDaysInYear(
891         const uno::Reference< beans::XPropertySet >& xOptions,
892         sal_Int32 nDate )
893 {
894     sal_Int32 nNullDate = GetNullDate( xOptions );
895     sal_Int32 nDays = nDate + nNullDate;
896 
897     sal_uInt16 nDay, nMonth, nYear;
898     DaysToDate(nDays,nDay,nMonth,nYear);
899 
900     return ( IsLeapYear(nYear) ? 366 : 365 );
901 }
902 
903 /**
904  * Get number of weeks in the year for a date
905  *
906  * Most years have 52 weeks, but years that start on a Thursday
907  * and leep years that start on a Wednesday have 53 weeks
908  *
909  * The International Standard IS-8601 has decreed that Monday
910  * shall be the first day of the week.
911  *
912  * A WeekDay can be calculated by subtracting 1 and calculating the rest of
913  * a division by 7 from the internal date represention
914  * which gives a 0 - 6 value for Monday - Sunday
915  *
916  * @see #IsLeapYear #WeekNumber
917  */
918 
getWeeksInYear(const uno::Reference<beans::XPropertySet> & xOptions,sal_Int32 nDate)919 sal_Int32 SAL_CALL ScaDateAddIn::getWeeksInYear(
920         const uno::Reference< beans::XPropertySet >& xOptions,
921         sal_Int32 nDate )
922 {
923     sal_Int32 nNullDate = GetNullDate( xOptions );
924     sal_Int32 nDays = nDate + nNullDate;
925 
926     sal_uInt16 nDay, nMonth, nYear;
927     DaysToDate(nDays,nDay,nMonth,nYear);
928 
929     sal_Int32 nJan1WeekDay = ( DateToDays(1,1,nYear) - 1) % 7;
930 
931     sal_Int32 nRet;
932     if ( nJan1WeekDay == 3 )        /* Thursday */
933         nRet = 53;
934     else if ( nJan1WeekDay == 2 )   /* Wednesday */
935         nRet = ( IsLeapYear(nYear) ? 53 : 52 );
936     else
937         nRet = 52;
938 
939     return nRet;
940 }
941 
942 /**
943  * Encrypt or decrypt a string using ROT13 algorithm
944  *
945  * This function rotates each character by 13 in the alphabet.
946  * Only the characters 'a' ... 'z' and 'A' ... 'Z' are modified.
947  */
948 
getRot13(const OUString & aSrcString)949 OUString SAL_CALL ScaDateAddIn::getRot13( const OUString& aSrcString )
950 {
951     OUStringBuffer aBuffer( aSrcString );
952     for( sal_Int32 nIndex = 0; nIndex < aBuffer.getLength(); nIndex++ )
953     {
954         sal_Unicode cChar = aBuffer.charAt( nIndex );
955         if( ((cChar >= 'a') && (cChar <= 'z') && ((cChar += 13) > 'z')) ||
956             ((cChar >= 'A') && (cChar <= 'Z') && ((cChar += 13) > 'Z')) )
957             cChar -= 26;
958         aBuffer.setCharAt( nIndex, cChar );
959     }
960     return aBuffer.makeStringAndClear();
961 }
962 
963 //------------------------------------------------------------------
964