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