xref: /trunk/main/ucb/source/cacher/cachedcontentresultsetstub.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 // MARKER(update_precomp.py): autogen include statement, do not remove
25 #include "precompiled_cacher.hxx"
26 
27 #include <cachedcontentresultsetstub.hxx>
28 #include <com/sun/star/sdbc/FetchDirection.hpp>
29 #include <com/sun/star/ucb/FetchError.hpp>
30 #include <osl/diagnose.h>
31 
32 using namespace com::sun::star::beans;
33 using namespace com::sun::star::lang;
34 using namespace com::sun::star::sdbc;
35 using namespace com::sun::star::ucb;
36 using namespace com::sun::star::uno;
37 using namespace com::sun::star::util;
38 using namespace cppu;
39 using namespace rtl;
40 
CachedContentResultSetStub(Reference<XResultSet> xOrigin)41 CachedContentResultSetStub::CachedContentResultSetStub( Reference< XResultSet > xOrigin )
42                 : ContentResultSetWrapper( xOrigin )
43                 , m_nColumnCount( 0 )
44                 , m_bColumnCountCached( sal_False )
45                 , m_bNeedToPropagateFetchSize( sal_True )
46                 , m_bFirstFetchSizePropagationDone( sal_False )
47                 , m_nLastFetchSize( 1 )//this value is not important at all
48                 , m_bLastFetchDirection( sal_True )//this value is not important at all
49                 , m_aPropertyNameForFetchSize( OUString::createFromAscii( "FetchSize" ) )
50                 , m_aPropertyNameForFetchDirection( OUString::createFromAscii( "FetchDirection" ) )
51 {
52     impl_init();
53 }
54 
~CachedContentResultSetStub()55 CachedContentResultSetStub::~CachedContentResultSetStub()
56 {
57     impl_deinit();
58 }
59 
60 //--------------------------------------------------------------------------
61 // XInterface methods.
62 //--------------------------------------------------------------------------
XINTERFACE_COMMON_IMPL(CachedContentResultSetStub)63 XINTERFACE_COMMON_IMPL( CachedContentResultSetStub )
64 
65 Any SAL_CALL CachedContentResultSetStub
66     ::queryInterface( const Type&  rType )
67 {
68     //list all interfaces inclusive baseclasses of interfaces
69 
70     Any aRet = ContentResultSetWrapper::queryInterface( rType );
71     if( aRet.hasValue() )
72         return aRet;
73 
74     aRet = cppu::queryInterface( rType
75                 , static_cast< XTypeProvider* >( this )
76                 , static_cast< XServiceInfo* >( this )
77                 , static_cast< XFetchProvider* >( this )
78                 , static_cast< XFetchProviderForContentAccess* >( this )
79                 );
80 
81     return aRet.hasValue() ? aRet : OWeakObject::queryInterface( rType );
82 }
83 
84 //--------------------------------------------------------------------------
85 // own methods.  ( inherited )
86 //--------------------------------------------------------------------------
87 
88 //virtual
89 void SAL_CALL CachedContentResultSetStub
impl_propertyChange(const PropertyChangeEvent & rEvt)90     ::impl_propertyChange( const PropertyChangeEvent& rEvt )
91 {
92     impl_EnsureNotDisposed();
93 
94     //don't notify events on fetchsize and fetchdirection to the above CachedContentResultSet
95     //because it will ignore them anyway and we can save this remote calls
96     if(    rEvt.PropertyName == m_aPropertyNameForFetchSize
97         || rEvt.PropertyName == m_aPropertyNameForFetchDirection )
98         return;
99 
100     PropertyChangeEvent aEvt( rEvt );
101     aEvt.Source = static_cast< XPropertySet * >( this );
102     aEvt.Further = sal_False;
103 
104     impl_notifyPropertyChangeListeners( aEvt );
105 }
106 
107 
108 //virtual
109 void SAL_CALL CachedContentResultSetStub
impl_vetoableChange(const PropertyChangeEvent & rEvt)110     ::impl_vetoableChange( const PropertyChangeEvent& rEvt )
111 {
112     impl_EnsureNotDisposed();
113 
114     //don't notify events on fetchsize and fetchdirection to the above CachedContentResultSet
115     //because it will ignore them anyway and we can save this remote calls
116     if(    rEvt.PropertyName == m_aPropertyNameForFetchSize
117         || rEvt.PropertyName == m_aPropertyNameForFetchDirection )
118         return;
119 
120     PropertyChangeEvent aEvt( rEvt );
121     aEvt.Source = static_cast< XPropertySet * >( this );
122     aEvt.Further = sal_False;
123 
124     impl_notifyVetoableChangeListeners( aEvt );
125 }
126 
127 //--------------------------------------------------------------------------
128 // XTypeProvider methods.
129 //--------------------------------------------------------------------------
130 
XTYPEPROVIDER_COMMON_IMPL(CachedContentResultSetStub)131 XTYPEPROVIDER_COMMON_IMPL( CachedContentResultSetStub )
132 //list all interfaces exclusive baseclasses
133 Sequence< Type > SAL_CALL CachedContentResultSetStub
134     ::getTypes()
135 {
136     static Sequence< Type >* pTypes = NULL;
137     if( !pTypes )
138     {
139         osl::Guard< osl::Mutex > aGuard( osl::Mutex::getGlobalMutex() );
140         if( !pTypes )
141         {
142             pTypes = new Sequence< Type >(13);
143             (*pTypes)[0] = CPPU_TYPE_REF( XTypeProvider );
144             (*pTypes)[1] = CPPU_TYPE_REF( XServiceInfo );
145             (*pTypes)[2] = CPPU_TYPE_REF( XComponent );
146             (*pTypes)[3] = CPPU_TYPE_REF( XCloseable );
147             (*pTypes)[4] = CPPU_TYPE_REF( XResultSetMetaDataSupplier );
148             (*pTypes)[5] = CPPU_TYPE_REF( XPropertySet );
149             (*pTypes)[6] = CPPU_TYPE_REF( XPropertyChangeListener );
150             (*pTypes)[7] = CPPU_TYPE_REF( XVetoableChangeListener );
151             (*pTypes)[8] = CPPU_TYPE_REF( XResultSet );
152             (*pTypes)[9] = CPPU_TYPE_REF( XContentAccess );
153             (*pTypes)[10] = CPPU_TYPE_REF( XRow );
154             (*pTypes)[11] = CPPU_TYPE_REF( XFetchProvider );
155             (*pTypes)[12] = CPPU_TYPE_REF( XFetchProviderForContentAccess );
156         }
157     }
158     return *pTypes;
159     /*
160     static cppu::OTypeCollection * pCollection = 0;
161     if (!pCollection)
162     {
163         osl::MutexGuard aGuard(osl::Mutex::getGlobalMutex());
164         if (!pCollection)
165         {
166             static cppu::OTypeCollection
167                 aTheCollection(
168                     getCppuType(
169                         static_cast< Reference< XTypeProvider >
170                                          const * >(
171                             0)),
172                     getCppuType(
173                         static_cast< Reference< XServiceInfo >
174                                          const * >(
175                             0)),
176                     getCppuType(
177                         static_cast< Reference< XComponent >
178                                          const * >(
179                             0)),
180                     getCppuType(
181                         static_cast< Reference< XCloseable >
182                                          const * >(
183                             0)),
184                     getCppuType(
185                         static_cast< Reference< XResultSetMetaDataSupplier >
186                                          const * >(
187                             0)),
188                     getCppuType(
189                         static_cast< Reference< XPropertySet >
190                                          const * >(
191                             0)),
192                     getCppuType(
193                         static_cast< Reference< XPropertyChangeListener >
194                                          const * >(
195                             0)),
196                     getCppuType(
197                         static_cast< Reference< XVetoableChangeListener >
198                                          const * >(
199                             0)),
200                     getCppuType(
201                         static_cast< Reference< XResultSet >
202                                          const * >(
203                             0)),
204                     getCppuType(
205                         static_cast< Reference< XContentAccess >
206                                          const * >(
207                             0)),
208                     getCppuType(
209                         static_cast< Reference< XRow >
210                                          const * >(
211                             0)),
212                     getCppuType(
213                         static_cast< Reference< XFetchProvider >
214                                          const * >(
215                             0)),
216                     getCppuType(
217                         static_cast< Reference< XFetchProviderForContentAccess >
218                                          const * >(
219                             0))
220                             );
221             pCollection = &aTheCollection;
222         }
223     }
224     return pCollection->getTypes();
225     */
226 }
227 
228 //--------------------------------------------------------------------------
229 // XServiceInfo methods.
230 //--------------------------------------------------------------------------
231 
232 XSERVICEINFO_NOFACTORY_IMPL_1( CachedContentResultSetStub,
233                        OUString::createFromAscii(
234                         "com.sun.star.comp.ucb.CachedContentResultSetStub" ),
235                        OUString::createFromAscii(
236                         CACHED_CRS_STUB_SERVICE_NAME ) );
237 
238 //-----------------------------------------------------------------
239 // XFetchProvider methods.
240 //-----------------------------------------------------------------
241 
242 #define FETCH_XXX( impl_loadRow, loadInterface ) \
243 impl_EnsureNotDisposed(); \
244 if( !m_xResultSetOrigin.is() ) \
245 { \
246     OSL_ENSURE( sal_False, "broadcaster was disposed already" ); \
247     throw RuntimeException(); \
248 } \
249 impl_propagateFetchSizeAndDirection( nRowCount, bDirection ); \
250 FetchResult aRet; \
251 aRet.StartIndex = nRowStartPosition; \
252 aRet.Orientation = bDirection; \
253 aRet.FetchError = FetchError::SUCCESS; /*ENDOFDATA, EXCEPTION*/ \
254 sal_Int32 nOldOriginal_Pos = m_xResultSetOrigin->getRow(); \
255 if( impl_isForwardOnly() ) \
256 { \
257     if( nOldOriginal_Pos != nRowStartPosition ) \
258     { \
259         /*@todo*/ \
260         aRet.FetchError = FetchError::EXCEPTION; \
261         return aRet; \
262     } \
263     if( nRowCount != 1 ) \
264         aRet.FetchError = FetchError::EXCEPTION; \
265  \
266     aRet.Rows.realloc( 1 ); \
267  \
268     try \
269     { \
270         impl_loadRow( aRet.Rows[0], loadInterface ); \
271     } \
272     catch( SQLException& ) \
273     { \
274         aRet.Rows.realloc( 0 ); \
275         aRet.FetchError = FetchError::EXCEPTION; \
276         return aRet; \
277     } \
278     return aRet; \
279 } \
280 aRet.Rows.realloc( nRowCount ); \
281 sal_Bool bOldOriginal_AfterLast = sal_False; \
282 if( !nOldOriginal_Pos ) \
283     bOldOriginal_AfterLast = m_xResultSetOrigin->isAfterLast(); \
284 sal_Int32 nN = 1; \
285 sal_Bool bValidNewPos = sal_False; \
286 try \
287 { \
288     try \
289     { \
290         /*if( nOldOriginal_Pos != nRowStartPosition )*/ \
291         bValidNewPos = m_xResultSetOrigin->absolute( nRowStartPosition ); \
292     } \
293     catch( SQLException& ) \
294     { \
295         aRet.Rows.realloc( 0 ); \
296         aRet.FetchError = FetchError::EXCEPTION; \
297         return aRet; \
298     } \
299     if( !bValidNewPos ) \
300     { \
301         aRet.Rows.realloc( 0 ); \
302         aRet.FetchError = FetchError::EXCEPTION; \
303  \
304         /*restore old position*/ \
305         if( nOldOriginal_Pos ) \
306             m_xResultSetOrigin->absolute( nOldOriginal_Pos ); \
307         else if( bOldOriginal_AfterLast ) \
308             m_xResultSetOrigin->afterLast(); \
309         else \
310             m_xResultSetOrigin->beforeFirst(); \
311  \
312         return aRet; \
313     } \
314     for( ; nN <= nRowCount; ) \
315     { \
316         impl_loadRow( aRet.Rows[nN-1], loadInterface ); \
317         nN++; \
318         if( nN <= nRowCount ) \
319         { \
320             if( bDirection ) \
321             { \
322                 if( !m_xResultSetOrigin->next() ) \
323                 { \
324                     aRet.Rows.realloc( nN-1 ); \
325                     aRet.FetchError = FetchError::ENDOFDATA; \
326                     break; \
327                 } \
328             } \
329             else \
330             { \
331                 if( !m_xResultSetOrigin->previous() ) \
332                 { \
333                     aRet.Rows.realloc( nN-1 ); \
334                     aRet.FetchError = FetchError::ENDOFDATA; \
335                     break; \
336                 } \
337             } \
338         } \
339     } \
340 } \
341 catch( SQLException& ) \
342 { \
343     aRet.Rows.realloc( nN-1 ); \
344     aRet.FetchError = FetchError::EXCEPTION; \
345 } \
346 /*restore old position*/ \
347 if( nOldOriginal_Pos ) \
348     m_xResultSetOrigin->absolute( nOldOriginal_Pos ); \
349 else if( bOldOriginal_AfterLast ) \
350     m_xResultSetOrigin->afterLast(); \
351 else \
352     m_xResultSetOrigin->beforeFirst(); \
353 return aRet;
354 
355 FetchResult SAL_CALL CachedContentResultSetStub
fetch(sal_Int32 nRowStartPosition,sal_Int32 nRowCount,sal_Bool bDirection)356     ::fetch( sal_Int32 nRowStartPosition
357     , sal_Int32 nRowCount, sal_Bool bDirection )
358 {
359     impl_init_xRowOrigin();
360     FETCH_XXX( impl_getCurrentRowContent, m_xRowOrigin );
361 }
362 
363 sal_Int32 SAL_CALL CachedContentResultSetStub
impl_getColumnCount()364     ::impl_getColumnCount()
365 {
366     sal_Int32 nCount;
367     sal_Bool bCached;
368     {
369         osl::Guard< osl::Mutex > aGuard( m_aMutex );
370         nCount = m_nColumnCount;
371         bCached = m_bColumnCountCached;
372     }
373     if( !bCached )
374     {
375         try
376         {
377             Reference< XResultSetMetaData > xMetaData = getMetaData();
378             if( xMetaData.is() )
379                 nCount = xMetaData->getColumnCount();
380         }
381         catch( SQLException& )
382         {
383             OSL_ENSURE( sal_False, "couldn't determine the column count" );
384             nCount = 0;
385         }
386     }
387     osl::Guard< osl::Mutex > aGuard( m_aMutex );
388     m_nColumnCount = nCount;
389     m_bColumnCountCached = sal_True;
390     return m_nColumnCount;
391 }
392 
393 void SAL_CALL CachedContentResultSetStub
impl_getCurrentRowContent(Any & rRowContent,Reference<XRow> xRow)394     ::impl_getCurrentRowContent( Any& rRowContent
395         , Reference< XRow > xRow )
396 {
397     sal_Int32 nCount = impl_getColumnCount();
398 
399     Sequence< Any > aContent( nCount );
400     for( sal_Int32 nN = 1; nN <= nCount; nN++ )
401     {
402         aContent[nN-1] = xRow->getObject( nN, NULL );
403     }
404 
405     rRowContent <<= aContent;
406 }
407 
408 void SAL_CALL CachedContentResultSetStub
impl_propagateFetchSizeAndDirection(sal_Int32 nFetchSize,sal_Bool bFetchDirection)409     ::impl_propagateFetchSizeAndDirection( sal_Int32 nFetchSize, sal_Bool bFetchDirection )
410 {
411     //this is done only for the case, that there is another CachedContentResultSet in the chain of underlying ResultSets
412 
413     //we do not propagate the property 'FetchSize' or 'FetchDirection' via 'setPropertyValue' from the above CachedContentResultSet to save remote calls
414 
415     //if the underlying ResultSet has a property FetchSize and FetchDirection,
416     //we will set these properties, if the new given parameters are different from the last ones
417 
418     if( !m_bNeedToPropagateFetchSize )
419         return;
420 
421     sal_Bool bNeedAction;
422     sal_Int32 nLastSize;
423     sal_Bool bLastDirection;
424     sal_Bool bFirstPropagationDone;
425     {
426         osl::Guard< osl::Mutex > aGuard( m_aMutex );
427         bNeedAction             = m_bNeedToPropagateFetchSize;
428         nLastSize               = m_nLastFetchSize;
429         bLastDirection          = m_bLastFetchDirection;
430         bFirstPropagationDone   = m_bFirstFetchSizePropagationDone;
431     }
432     if( bNeedAction )
433     {
434         if( nLastSize == nFetchSize
435             && bLastDirection == bFetchDirection
436             && bFirstPropagationDone == sal_True )
437             return;
438 
439         if(!bFirstPropagationDone)
440         {
441             //check whether the properties 'FetchSize' and 'FetchDirection' do exist
442 
443             Reference< XPropertySetInfo > xPropertySetInfo = getPropertySetInfo();
444             sal_Bool bHasSize = xPropertySetInfo->hasPropertyByName( m_aPropertyNameForFetchSize );
445             sal_Bool bHasDirection = xPropertySetInfo->hasPropertyByName( m_aPropertyNameForFetchDirection );
446 
447             if(!bHasSize || !bHasDirection)
448             {
449                 osl::Guard< osl::Mutex > aGuard( m_aMutex );
450                 m_bNeedToPropagateFetchSize = sal_False;
451                 return;
452             }
453         }
454 
455         sal_Bool bSetSize       = ( nLastSize       !=nFetchSize        ) || !bFirstPropagationDone;
456         sal_Bool bSetDirection  = ( bLastDirection  !=bFetchDirection   ) || !bFirstPropagationDone;
457 
458         {
459             osl::Guard< osl::Mutex > aGuard( m_aMutex );
460             m_bFirstFetchSizePropagationDone = sal_True;
461             m_nLastFetchSize        = nFetchSize;
462             m_bLastFetchDirection   = bFetchDirection;
463         }
464 
465         if( bSetSize )
466         {
467             Any aValue;
468             aValue <<= nFetchSize;
469             try
470             {
471                 setPropertyValue( m_aPropertyNameForFetchSize, aValue );
472             }
473             catch( com::sun::star::uno::Exception& ) {}
474         }
475         if( bSetDirection )
476         {
477             sal_Int32 nFetchDirection = FetchDirection::FORWARD;
478             if( !bFetchDirection )
479                 nFetchDirection = FetchDirection::REVERSE;
480             Any aValue;
481             aValue <<= nFetchDirection;
482             try
483             {
484                 setPropertyValue( m_aPropertyNameForFetchDirection, aValue );
485             }
486             catch( com::sun::star::uno::Exception& ) {}
487         }
488 
489     }
490 }
491 
492 //-----------------------------------------------------------------
493 // XFetchProviderForContentAccess methods.
494 //-----------------------------------------------------------------
495 
496 void SAL_CALL CachedContentResultSetStub
impl_getCurrentContentIdentifierString(Any & rAny,Reference<XContentAccess> xContentAccess)497     ::impl_getCurrentContentIdentifierString( Any& rAny
498         , Reference< XContentAccess > xContentAccess )
499 {
500     rAny <<= xContentAccess->queryContentIdentifierString();
501 }
502 
503 void SAL_CALL CachedContentResultSetStub
impl_getCurrentContentIdentifier(Any & rAny,Reference<XContentAccess> xContentAccess)504     ::impl_getCurrentContentIdentifier( Any& rAny
505         , Reference< XContentAccess > xContentAccess )
506 {
507     rAny <<= xContentAccess->queryContentIdentifier();
508 }
509 
510 void SAL_CALL CachedContentResultSetStub
impl_getCurrentContent(Any & rAny,Reference<XContentAccess> xContentAccess)511     ::impl_getCurrentContent( Any& rAny
512         , Reference< XContentAccess > xContentAccess )
513 {
514     rAny <<= xContentAccess->queryContent();
515 }
516 
517 //virtual
518 FetchResult SAL_CALL CachedContentResultSetStub
fetchContentIdentifierStrings(sal_Int32 nRowStartPosition,sal_Int32 nRowCount,sal_Bool bDirection)519     ::fetchContentIdentifierStrings( sal_Int32 nRowStartPosition
520         , sal_Int32 nRowCount, sal_Bool bDirection )
521 {
522     impl_init_xContentAccessOrigin();
523     FETCH_XXX( impl_getCurrentContentIdentifierString, m_xContentAccessOrigin );
524 }
525 
526 //virtual
527 FetchResult SAL_CALL CachedContentResultSetStub
fetchContentIdentifiers(sal_Int32 nRowStartPosition,sal_Int32 nRowCount,sal_Bool bDirection)528     ::fetchContentIdentifiers( sal_Int32 nRowStartPosition
529         , sal_Int32 nRowCount, sal_Bool bDirection )
530 {
531     impl_init_xContentAccessOrigin();
532     FETCH_XXX( impl_getCurrentContentIdentifier, m_xContentAccessOrigin );
533 }
534 
535 //virtual
536 FetchResult SAL_CALL CachedContentResultSetStub
fetchContents(sal_Int32 nRowStartPosition,sal_Int32 nRowCount,sal_Bool bDirection)537     ::fetchContents( sal_Int32 nRowStartPosition
538         , sal_Int32 nRowCount, sal_Bool bDirection )
539 {
540     impl_init_xContentAccessOrigin();
541     FETCH_XXX( impl_getCurrentContent, m_xContentAccessOrigin );
542 }
543 
544 //--------------------------------------------------------------------------
545 //--------------------------------------------------------------------------
546 // class CachedContentResultSetStubFactory
547 //--------------------------------------------------------------------------
548 //--------------------------------------------------------------------------
549 
CachedContentResultSetStubFactory(const Reference<XMultiServiceFactory> & rSMgr)550 CachedContentResultSetStubFactory::CachedContentResultSetStubFactory(
551         const Reference< XMultiServiceFactory > & rSMgr )
552 {
553     m_xSMgr = rSMgr;
554 }
555 
~CachedContentResultSetStubFactory()556 CachedContentResultSetStubFactory::~CachedContentResultSetStubFactory()
557 {
558 }
559 
560 //--------------------------------------------------------------------------
561 // CachedContentResultSetStubFactory XInterface methods.
562 //--------------------------------------------------------------------------
563 
564 XINTERFACE_IMPL_3( CachedContentResultSetStubFactory,
565                    XTypeProvider,
566                    XServiceInfo,
567                    XCachedContentResultSetStubFactory );
568 
569 //--------------------------------------------------------------------------
570 // CachedContentResultSetStubFactory XTypeProvider methods.
571 //--------------------------------------------------------------------------
572 
573 XTYPEPROVIDER_IMPL_3( CachedContentResultSetStubFactory,
574                       XTypeProvider,
575                       XServiceInfo,
576                       XCachedContentResultSetStubFactory );
577 
578 //--------------------------------------------------------------------------
579 // CachedContentResultSetStubFactory XServiceInfo methods.
580 //--------------------------------------------------------------------------
581 
582 XSERVICEINFO_IMPL_1( CachedContentResultSetStubFactory,
583                  OUString::createFromAscii(
584                     "com.sun.star.comp.ucb.CachedContentResultSetStubFactory" ),
585                  OUString::createFromAscii(
586                     CACHED_CRS_STUB_FACTORY_NAME ) );
587 
588 //--------------------------------------------------------------------------
589 // Service factory implementation.
590 //--------------------------------------------------------------------------
591 
592 ONE_INSTANCE_SERVICE_FACTORY_IMPL( CachedContentResultSetStubFactory );
593 
594 //--------------------------------------------------------------------------
595 // CachedContentResultSetStubFactory XCachedContentResultSetStubFactory methods.
596 //--------------------------------------------------------------------------
597 
598     //virtual
599 Reference< XResultSet > SAL_CALL CachedContentResultSetStubFactory
createCachedContentResultSetStub(const Reference<XResultSet> & xSource)600     ::createCachedContentResultSetStub(
601             const Reference< XResultSet > & xSource )
602 {
603     if( xSource.is() )
604     {
605         Reference< XResultSet > xRet;
606         xRet = new CachedContentResultSetStub( xSource );
607         return xRet;
608     }
609     return NULL;
610 }
611