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 #ifndef _CPPUHELPER_INTERFACECONTAINER_H_
24 #define _CPPUHELPER_INTERFACECONTAINER_H_
25 
26 #include <vector>
27 #include <osl/mutex.hxx>
28 #include <rtl/alloc.h>
29 #include <com/sun/star/uno/Sequence.hxx>
30 #include <com/sun/star/uno/XInterface.hpp>
31 #ifndef _COM_SUN_STAR_LANG_EVENTOBJECT_HXX_
32 #include <com/sun/star/lang/EventObject.hpp>
33 #endif
34 
35 #ifndef _COM_SUN_STAR_LANG_DISPOSEDEXCEPTION_HXX_
36 #include "com/sun/star/lang/DisposedException.hpp"
37 #endif
38 
39 #include "cppuhelper/cppuhelperdllapi.h"
40 
41 /** */ //for docpp
42 namespace cppu
43 {
44 
45 namespace detail {
46 
47     union element_alias
48     {
49         ::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > *pAsSequence;
50         ::com::sun::star::uno::XInterface * pAsInterface;
element_alias()51         element_alias() : pAsInterface(0) {}
52     };
53 
54 }
55 
56 //===================================================================
57 class OInterfaceContainerHelper;
58 /**
59   This is the iterator of a InterfaceContainerHelper. Typically
60   one constructs an instance on the stack for one firing session.
61   It is not allowed to assign or copy an instance of this class.
62 
63   @see OInterfaceContainerHelper
64  */
65 class CPPUHELPER_DLLPUBLIC OInterfaceIteratorHelper
66 {
67 public:
68 	/**
69 	   Create an iterator over the elements of the container. The iterator
70 	   copies the elements of the conatainer. A change to the container
71        during the lifetime of an iterator is allowed and does not
72 	   affect the iterator-instance. The iterator and the container take cares
73        themself for concurrent access, no additional guarding is necessary.
74 
75 	   Remark: The copy is on demand. The iterator copy the elements only if the container
76 	   change the contents. It is not allowed to destroy the container as long
77        as an iterator exist.
78 
79 	   @param rCont	the container of the elements.
80 	 */
81 	OInterfaceIteratorHelper( OInterfaceContainerHelper & rCont ) SAL_THROW( () );
82 
83 	/**
84 	  Releases the connection to the container.
85 	 */
86 	~OInterfaceIteratorHelper() SAL_THROW( () );
87 
88 	/** Return sal_True, if there are more elements in the iterator. */
hasMoreElements() const89 	sal_Bool SAL_CALL hasMoreElements() const SAL_THROW( () )
90 		{ return nRemain != 0; }
91 	/** Return the next element of the iterator. Calling this method if
92 	    hasMoreElements() has returned sal_False, is an error. Cast the
93         returned pointer to the
94 	 */
95 	::com::sun::star::uno::XInterface *	SAL_CALL next() SAL_THROW( () );
96 
97 	/** Removes the current element (the last one returned by next())
98 	    from the underlying container. Calling this method before
99 		next() has been called or calling it twice with no next()
100 		inbetween is an error.
101 	*/
102 	void SAL_CALL remove() SAL_THROW( () );
103 
104 private:
105 	OInterfaceContainerHelper &	rCont;
106 	sal_Bool					bIsList;
107 
108     detail::element_alias aData;
109 
110 	sal_Int32					nRemain;
111 
112 	OInterfaceIteratorHelper( const OInterfaceIteratorHelper & ) SAL_THROW( () );
113 	OInterfaceIteratorHelper &	operator = ( const OInterfaceIteratorHelper & ) SAL_THROW( () );
114 };
115 
116 //===================================================================
117 /**
118   A container of interfaces. To access the elements use an iterator.
119   This implementation is thread save.
120 
121   @see OInterfaceIteratorHelper
122  */
123 class CPPUHELPER_DLLPUBLIC OInterfaceContainerHelper
124 {
125 public:
126 	// these are here to force memory de/allocation to sal lib.
operator new(size_t nSize)127 	inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW( () )
128 		{ return ::rtl_allocateMemory( nSize ); }
operator delete(void * pMem)129 	inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW( () )
130 		{ ::rtl_freeMemory( pMem ); }
operator new(size_t,void * pMem)131 	inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW( () )
132 		{ return pMem; }
operator delete(void *,void *)133 	inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW( () )
134 		{}
135 
136 	/**
137 	   Create an interface container.
138 
139 	   @param rMutex	the mutex to protect multi thread access.
140        The lifetime must be longer than the lifetime
141 	   of this object.
142 	 */
143 	OInterfaceContainerHelper( ::osl::Mutex & rMutex ) SAL_THROW( () );
144 	/**
145 	  Release all interfaces. All iterators must be destroyed before
146 	  the container is destructed.
147 	 */
148 	~OInterfaceContainerHelper() SAL_THROW( () );
149 	/**
150 	  Return the number of Elements in the container. Only useful if you have acquired
151 	  the mutex.
152 	 */
153 	sal_Int32 SAL_CALL getLength() const SAL_THROW( () );
154 
155 	/**
156 	  Return all interfaces added to this container.
157 	 **/
158 	::com::sun::star::uno::Sequence< ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > > SAL_CALL getElements() const SAL_THROW( () );
159 
160 	/** Inserts an element into the container.  The position is not specified, thus it is not
161         specified in which order events are fired.
162 
163         @attention
164         If you add the same interface more than once, then it will be added to the elements list
165         more than once and thus if you want to remove that interface from the list, you have to call
166         removeInterface() the same number of times.
167         In the latter case, you will also get events fired more than once (if the interface is a
168         listener interface).
169 
170         @param rxIFace
171                interface to be added; it is allowed to insert null or
172                the same interface more than once
173         @return
174                 the new count of elements in the container
175     */
176 	sal_Int32 SAL_CALL addInterface( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace ) SAL_THROW( () );
177 	/** Removes an element from the container.  It uses interface equality to remove the interface.
178 
179         @param rxIFace
180                interface to be removed
181         @return
182                 the new count of elements in the container
183     */
184 	sal_Int32 SAL_CALL removeInterface( const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace ) SAL_THROW( () );
185 	/**
186 	  Call disposing on all object in the container that
187 	  support XEventListener. Than clear the container.
188 	 */
189 	void SAL_CALL disposeAndClear( const ::com::sun::star::lang::EventObject & rEvt ) SAL_THROW( () );
190 	/**
191 	  Clears the container without calling disposing().
192 	 */
193 	void SAL_CALL clear() SAL_THROW( () );
194 
195     /** Executes a functor for each contained listener of specified type, e.g.
196         <code>forEach<awt::XPaintListener>(...</code>.
197 
198         If a com::sun::star::lang::DisposedException occurs which relates to
199         the called listener, then that listener is removed from the container.
200 
201         @tpl ListenerT listener type
202         @tpl FuncT unary functor type, let your compiler deduce this for you
203         @param func unary functor object expecting an argument of type
204                     ::com::sun::star::uno::Reference<ListenerT>
205     */
206     template <typename ListenerT, typename FuncT>
207     inline void forEach( FuncT const& func );
208 
209     /** Calls a UNO listener method for each contained listener.
210 
211         The listener method must take a single argument of type EventT,
212         and return <code>void</code>.
213 
214         If a com::sun::star::lang::DisposedException occurs which relates to
215         the called listener, then that listener is removed from the container.
216 
217         @tpl ListenerT UNO event listener type, let your compiler deduce this for you
218         @tpl EventT event type, let your compiler deduce this for you
219         @param NotificationMethod
220             Pointer to a method of a ListenerT interface.
221         @param Event
222             Event to notify to all contained listeners
223 
224         @example
225 <pre>
226     awt::PaintEvent aEvent( static_cast< ::cppu::OWeakObject* >( this ), ... );
227     listeners.notifyEach( &XPaintListener::windowPaint, aEvent );
228 </pre>
229     */
230     template< typename ListenerT, typename EventT >
231     inline void notifyEach( void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& ), const EventT& Event );
232 
233 private:
234 friend class OInterfaceIteratorHelper;
235 	/**
236 	  bIsList == TRUE -> aData.pAsSequence of type Sequence< XInterfaceSequence >,
237 	  otherwise aData.pAsInterface == of type (XEventListener *)
238 	 */
239     detail::element_alias   aData;
240 	::osl::Mutex &			rMutex;
241 	/** TRUE -> used by an iterator. */
242 	sal_Bool				bInUse;
243 	/** TRUE -> aData.pAsSequence is of type Sequence< XInterfaceSequence >. */
244 	sal_Bool				bIsList;
245 
246 	OInterfaceContainerHelper( const OInterfaceContainerHelper & ) SAL_THROW( () );
247 	OInterfaceContainerHelper & operator = ( const OInterfaceContainerHelper & ) SAL_THROW( () );
248 
249 	/*
250 	  Dulicate content of the conaitner and release the old one without destroying.
251 	  The mutex must be locked and the memberbInUse must be true.
252 	 */
253 	void copyAndResetInUse() SAL_THROW( () );
254 
255 private:
256     template< typename ListenerT, typename EventT >
257     class NotifySingleListener
258     {
259     private:
260         typedef void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& );
261         NotificationMethod  m_pMethod;
262         const EventT&       m_rEvent;
263     public:
NotifySingleListener(NotificationMethod method,const EventT & event)264         NotifySingleListener( NotificationMethod method, const EventT& event ) : m_pMethod( method ), m_rEvent( event ) { }
265 
operator ()(const::com::sun::star::uno::Reference<ListenerT> & listener) const266         void operator()( const ::com::sun::star::uno::Reference<ListenerT>& listener ) const
267         {
268             (listener.get()->*m_pMethod)( m_rEvent );
269         }
270     };
271 };
272 
273 template <typename ListenerT, typename FuncT>
forEach(FuncT const & func)274 inline void OInterfaceContainerHelper::forEach( FuncT const& func )
275 {
276     OInterfaceIteratorHelper iter( *this );
277     while (iter.hasMoreElements()) {
278         ::com::sun::star::uno::Reference<ListenerT> const xListener(
279             iter.next(), ::com::sun::star::uno::UNO_QUERY );
280         if (xListener.is()) {
281 #if defined(EXCEPTIONS_OFF)
282             func( xListener );
283 #else
284             try {
285                 func( xListener );
286             }
287             catch (::com::sun::star::lang::DisposedException const& exc) {
288                 if (exc.Context == xListener)
289                     iter.remove();
290             }
291 #endif
292         }
293     }
294 }
295 
296 template< typename ListenerT, typename EventT >
notifyEach(void (SAL_CALL ListenerT::* NotificationMethod)(const EventT &),const EventT & Event)297 inline void OInterfaceContainerHelper::notifyEach( void ( SAL_CALL ListenerT::*NotificationMethod )( const EventT& ), const EventT& Event )
298 {
299     forEach< ListenerT, NotifySingleListener< ListenerT, EventT > >( NotifySingleListener< ListenerT, EventT >( NotificationMethod, Event ) );
300 }
301 
302 //===================================================================
303 /**
304   A helper class to store interface references of different types.
305 
306   @see OInterfaceIteratorHelper
307   @see OInterfaceContainerHelper
308  */
309 template< class key , class hashImpl , class equalImpl >
310 class OMultiTypeInterfaceContainerHelperVar
311 {
312 public:
313 	// these are here to force memory de/allocation to sal lib.
operator new(size_t nSize)314 	inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW( () )
315 		{ return ::rtl_allocateMemory( nSize ); }
operator delete(void * pMem)316 	inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW( () )
317 		{ ::rtl_freeMemory( pMem ); }
operator new(size_t,void * pMem)318 	inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW( () )
319 		{ return pMem; }
operator delete(void *,void *)320 	inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW( () )
321 		{}
322 
323 	/**
324 	  Create a container of interface containers.
325 
326 	  @param rMutex	the mutex to protect multi thread access.
327 	 					The lifetime must be longer than the lifetime
328 	 					of this object.
329 	 */
330 	inline OMultiTypeInterfaceContainerHelperVar( ::osl::Mutex & ) SAL_THROW( () );
331 	/**
332 	  Deletes all containers.
333 	 */
334 	inline ~OMultiTypeInterfaceContainerHelperVar() SAL_THROW( () );
335 
336 	/**
337 	  Return all id's under which at least one interface is added.
338 	 */
339 	inline ::com::sun::star::uno::Sequence< key > SAL_CALL getContainedTypes() const SAL_THROW( () );
340 
341 	/**
342 	  Return the container created under this key.
343       The InterfaceContainerHelper exists until the whole MultiTypeContainer is destroyed.
344 	  @return the container created under this key. If the container
345 	 			was not created, null was returned.
346 	 */
347 	inline OInterfaceContainerHelper * SAL_CALL getContainer( const key & ) const SAL_THROW( () );
348 
349 	/** Inserts an element into the container with the specified key.
350         The position is not specified, thus it is not specified in which order events are fired.
351 
352         @attention
353         If you add the same interface more than once, then it will be added to the elements list
354         more than once and thus if you want to remove that interface from the list, you have to call
355         removeInterface() the same number of times.
356         In the latter case, you will also get events fired more than once (if the interface is a
357         listener interface).
358 
359         @param rKey
360                the id of the container
361         @param r
362                interface to be added; it is allowed, to insert null or
363                the same interface more than once
364         @return
365                 the new count of elements in the container
366     */
367 	inline sal_Int32 SAL_CALL addInterface(
368 		const key & rKey,
369 		const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & r )
370 		SAL_THROW( () );
371 
372 	/** Removes an element from the container with the specified key.
373         It uses interface equality to remove the interface.
374 
375         @param rKey
376                the id of the container
377         @param rxIFace
378                interface to be removed
379         @return
380                 the new count of elements in the container
381     */
382 	inline sal_Int32 SAL_CALL removeInterface(
383 		const key & rKey,
384 		const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace )
385 		SAL_THROW( () );
386 
387 	/**
388 	  Call disposing on all references in the container, that
389 	  support XEventListener. Then clears the container.
390       @param rEvt the event object which is passed during disposing() call
391 	 */
392 	inline void	SAL_CALL disposeAndClear( const ::com::sun::star::lang::EventObject & rEvt ) SAL_THROW( () );
393 	/**
394 	  Remove all elements of all containers. Does not delete the container.
395 	 */
396 	inline void SAL_CALL clear() SAL_THROW( () );
397 
398 	typedef key keyType;
399 private:
400     typedef ::std::vector< std::pair < key , void* > > InterfaceMap;
401     InterfaceMap *m_pMap;
402 	::osl::Mutex &	rMutex;
403 
find(const key & rKey) const404     inline typename InterfaceMap::iterator find(const key &rKey) const
405     {
406         typename InterfaceMap::iterator iter = m_pMap->begin();
407         typename InterfaceMap::iterator end = m_pMap->end();
408 
409     	while( iter != end )
410         {
411             equalImpl equal;
412             if( equal( iter->first, rKey ) )
413                 break;
414             iter++;
415         }
416         return iter;
417     }
418 
419 	inline OMultiTypeInterfaceContainerHelperVar( const OMultiTypeInterfaceContainerHelperVar & ) SAL_THROW( () );
420 	inline OMultiTypeInterfaceContainerHelperVar & operator = ( const OMultiTypeInterfaceContainerHelperVar & ) SAL_THROW( () );
421 };
422 
423 
424 
425 
426 /**
427   This struct contains the standard variables of a broadcaster. Helper
428   classes only know a reference to this struct instead of references
429   to the four members. The access to the members must be guarded with
430   rMutex.
431 
432   The additional template parameter keyType has been added, because gcc
433   can't compile addListener( const container::keyType &key ).
434  */
435 template < class container , class keyType >
436 struct OBroadcastHelperVar
437 {
438 	/** The shared mutex. */
439 	::osl::Mutex &						rMutex;
440 	/** ListenerContainer class is thread save. */
441 	container	aLC;
442 	/** Dispose call ready. */
443 	sal_Bool							bDisposed;
444 	/** In dispose call. */
445 	sal_Bool							bInDispose;
446 
447 	/**
448 	  Initialize the structur. bDispose and bInDispose are set to false.
449 	  @param rMutex the mutex reference.
450 	 */
451 	OBroadcastHelperVar( ::osl::Mutex & rMutex_ ) SAL_THROW( () )
452 		: rMutex( rMutex_ )
453 		, aLC( rMutex_ )
454 		, bDisposed( sal_False )
455 		, bInDispose( sal_False )
456 	{}
457 
458 	/**
459 	  adds a listener threadsafe.
460 	 **/
addListenercppu::OBroadcastHelperVar461 	inline void addListener(
462 		const keyType &key,
463 		const ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface > &r )
464 		SAL_THROW( () )
465 	{
466 		::osl::MutexGuard guard( rMutex );
467 		OSL_ENSURE( !bInDispose, "do not add listeners in the dispose call" );
468 		OSL_ENSURE( !bDisposed, "object is disposed" );
469 		if( ! bInDispose && ! bDisposed  )
470 			aLC.addInterface( key , r );
471 	}
472 
473 	/**
474 	  removes a listener threadsafe
475 	 **/
removeListenercppu::OBroadcastHelperVar476 	inline void removeListener(
477 		const keyType &key,
478 		const ::com::sun::star::uno::Reference < ::com::sun::star::uno::XInterface > & r )
479 		SAL_THROW( () )
480 	{
481 		::osl::MutexGuard guard( rMutex );
482 		OSL_ENSURE( !bDisposed, "object is disposed" );
483 		if( ! bInDispose && ! bDisposed  )
484 			aLC.removeInterface( key , r );
485 	}
486 
487 	/**
488 	  Return the container created under this key.
489 	  @return the container created under this key. If the container
490 	 		   was not created, null was returned. This can be used to optimize
491 	          performance ( construction of an event object can be avoided ).
492 	 ***/
getContainercppu::OBroadcastHelperVar493 	inline OInterfaceContainerHelper * SAL_CALL getContainer( const keyType &key ) const SAL_THROW( () )
494 		{ return aLC.getContainer( key ); }
495 };
496 
497 /*------------------------------------------
498 *
499 * In general, the above templates are used with a Type as key.
500 * Therefore a default declaration is given ( OMultiTypeInterfaceContainerHelper and OBroadcastHelper )
501 *
502 *------------------------------------------*/
503 
504 // helper function call class
505 struct hashType_Impl
506 {
operator ()cppu::hashType_Impl507 	size_t operator()(const ::com::sun::star::uno::Type & s) const SAL_THROW( () )
508 		{ return s.getTypeName().hashCode(); }
509 };
510 
511 
512 /** Specialized class for key type com::sun::star::uno::Type,
513     without explicit usage of STL symbols.
514 */
515 class CPPUHELPER_DLLPUBLIC OMultiTypeInterfaceContainerHelper
516 {
517 public:
518 	// these are here to force memory de/allocation to sal lib.
operator new(size_t nSize)519 	inline static void * SAL_CALL operator new( size_t nSize ) SAL_THROW( () )
520 		{ return ::rtl_allocateMemory( nSize ); }
operator delete(void * pMem)521 	inline static void SAL_CALL operator delete( void * pMem ) SAL_THROW( () )
522 		{ ::rtl_freeMemory( pMem ); }
operator new(size_t,void * pMem)523 	inline static void * SAL_CALL operator new( size_t, void * pMem ) SAL_THROW( () )
524 		{ return pMem; }
operator delete(void *,void *)525 	inline static void SAL_CALL operator delete( void *, void * ) SAL_THROW( () )
526 		{}
527 
528 	/**
529 	  Create a container of interface containers.
530 
531 	  @param rMutex	the mutex to protect multi thread access.
532 	 					The lifetime must be longer than the lifetime
533 	 					of this object.
534 	 */
535 	OMultiTypeInterfaceContainerHelper( ::osl::Mutex & ) SAL_THROW( () );
536 	/**
537 	  Delete all containers.
538 	 */
539 	~OMultiTypeInterfaceContainerHelper() SAL_THROW( () );
540 
541 	/**
542 	  Return all id's under which at least one interface is added.
543 	 */
544 	::com::sun::star::uno::Sequence< ::com::sun::star::uno::Type > SAL_CALL getContainedTypes() const SAL_THROW( () );
545 
546 	/**
547 	  Return the container created under this key.
548 	  @return the container created under this key. If the container
549 	 			was not created, null was returned.
550 	 */
551 	OInterfaceContainerHelper * SAL_CALL getContainer( const ::com::sun::star::uno::Type & rKey ) const SAL_THROW( () );
552 
553 	/** Inserts an element into the container with the specified key.
554         The position is not specified, thus it is not specified in which order events are fired.
555 
556         @attention
557         If you add the same interface more than once, then it will be added to the elements list
558         more than once and thus if you want to remove that interface from the list, you have to call
559         removeInterface() the same number of times.
560         In the latter case, you will also get events fired more than once (if the interface is a
561         listener interface).
562 
563         @param rKey
564                the id of the container
565         @param r
566                interface to be added; it is allowed, to insert null or
567                the same interface more than once
568         @return
569                 the new count of elements in the container
570     */
571 	sal_Int32 SAL_CALL addInterface(
572 		const ::com::sun::star::uno::Type & rKey,
573 		const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & r )
574 		SAL_THROW( () );
575 
576 	/** Removes an element from the container with the specified key.
577         It uses interface equality to remove the interface.
578 
579         @param rKey
580                the id of the container
581         @param rxIFace
582                interface to be removed
583         @return
584                 the new count of elements in the container
585     */
586 	sal_Int32 SAL_CALL removeInterface(
587 		const ::com::sun::star::uno::Type & rKey,
588 		const ::com::sun::star::uno::Reference< ::com::sun::star::uno::XInterface > & rxIFace )
589 		SAL_THROW( () );
590 
591 	/**
592 	  Call disposing on all object in the container that
593 	  support XEventListener. Than clear the container.
594 	 */
595 	void	SAL_CALL disposeAndClear( const ::com::sun::star::lang::EventObject & rEvt ) SAL_THROW( () );
596 	/**
597 	  Remove all elements of all containers. Does not delete the container.
598 	 */
599 	void SAL_CALL clear() SAL_THROW( () );
600 
601 	typedef ::com::sun::star::uno::Type keyType;
602 private:
603 	void *m_pMap;
604 	::osl::Mutex &	rMutex;
605 
606 	inline OMultiTypeInterfaceContainerHelper( const OMultiTypeInterfaceContainerHelper & ) SAL_THROW( () );
607 	inline OMultiTypeInterfaceContainerHelper & operator = ( const OMultiTypeInterfaceContainerHelper & ) SAL_THROW( () );
608 };
609 
610 typedef OBroadcastHelperVar< OMultiTypeInterfaceContainerHelper , OMultiTypeInterfaceContainerHelper::keyType > OBroadcastHelper;
611 
612 }
613 
614 #endif
615 
616