1 /*
2 * This program source code file is part of KICAD, a free EDA CAD application.
3 *
4 * Copyright (C) 2016 Kicad Developers, see change_log.txt for contributors.
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, you may find one here:
18 * http://www.gnu.org/licenses/old-licenses/gpl-2.0.html
19 * or you may search the http://www.gnu.org website for the version 2 license,
20 * or you may write to the Free Software Foundation, Inc.,
21 * 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA
22 */
23 
24 #include "observable.h"
25 #include <algorithm>
26 
27 namespace UTIL {
28 
29     namespace DETAIL {
30 
31         template<typename T>
32         struct equals {
equalsUTIL::DETAIL::equals33             equals( const T& val ) : val_( val ) {}
34 
operator ()UTIL::DETAIL::equals35             bool operator()( const T& val ) const
36             {
37                 return val == val_;
38             }
39 
40         private:
41             const T& val_;
42         };
43 
44 
IMPL(OBSERVABLE_BASE * owned_by)45         OBSERVABLE_BASE::IMPL::IMPL( OBSERVABLE_BASE* owned_by )
46             : iteration_count_( 0 ), owned_by_( owned_by )
47         {}
48 
49 
is_shared() const50         bool OBSERVABLE_BASE::IMPL::is_shared() const
51         {
52             return owned_by_ == nullptr;
53         }
54 
55 
set_shared()56         void OBSERVABLE_BASE::IMPL::set_shared()
57         {
58             owned_by_ = nullptr;
59         }
60 
61 
~IMPL()62         OBSERVABLE_BASE::IMPL::~IMPL()
63         {
64         }
65 
66 
enter_iteration()67         void OBSERVABLE_BASE::IMPL::enter_iteration()
68         {
69             ++iteration_count_;
70         }
71 
72 
leave_iteration()73         void OBSERVABLE_BASE::IMPL::leave_iteration()
74         {
75             --iteration_count_;
76 
77             if( iteration_count_ == 0 )
78                 collect();
79         }
80 
81 
is_iterating() const82         bool OBSERVABLE_BASE::IMPL::is_iterating() const
83         {
84             return iteration_count_ != 0;
85         }
86 
87 
add_observer(void * observer)88         void OBSERVABLE_BASE::IMPL::add_observer( void* observer )
89         {
90             assert( !is_iterating() );
91             observers_.push_back( observer );
92         }
93 
94 
remove_observer(void * observer)95         void OBSERVABLE_BASE::IMPL::remove_observer( void* observer )
96         {
97             auto it = std::find( observers_.begin(), observers_.end(), observer );
98 
99             if( it == observers_.end() )
100             {
101                 assert( false );
102                 return;
103             }
104 
105             if( is_iterating() )
106                 *it = nullptr;
107             else
108                 observers_.erase( it );
109         }
110 
111 
collect()112         void OBSERVABLE_BASE::IMPL::collect()
113         {
114             auto it = std::remove_if( observers_.begin(), observers_.end(), DETAIL::equals<void*>( nullptr ) );
115             observers_.erase( it, observers_.end() );
116         }
117     }
118 
119 
LINK()120     LINK::LINK()
121         : observer_( nullptr )
122     {
123     }
124 
125 
LINK(std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token,void * observer)126     LINK::LINK( std::shared_ptr<DETAIL::OBSERVABLE_BASE::IMPL> token, void* observer )
127         : token_( std::move( token ) ), observer_( observer )
128     {
129     }
130 
131 
LINK(LINK && other)132     LINK::LINK( LINK&& other )
133         : token_( std::move( other.token_ ) ), observer_( other.observer_ )
134     {
135         other.token_.reset();
136     }
137 
138 
operator =(LINK && other)139     LINK& LINK::operator=( LINK&& other )
140     {
141         token_ = std::move( other.token_ );
142         other.token_.reset();
143         observer_ = other.observer_;
144         return *this;
145     }
146 
147 
operator bool() const148     LINK::operator bool() const
149     {
150         return token_ ? true : false;
151     }
152 
153 
~LINK()154     LINK::~LINK()
155     {
156         reset();
157     }
158 
159 
reset()160     void LINK::reset()
161     {
162         if(token_)
163         {
164             token_->remove_observer( observer_ );
165             token_.reset();
166         }
167     }
168 
169 
170     namespace DETAIL {
171 
OBSERVABLE_BASE()172         OBSERVABLE_BASE::OBSERVABLE_BASE()
173         {
174         }
175 
176 
OBSERVABLE_BASE(OBSERVABLE_BASE & other)177         OBSERVABLE_BASE::OBSERVABLE_BASE( OBSERVABLE_BASE& other )
178             : impl_( other.get_shared_impl() )
179         {
180         }
181 
182 
~OBSERVABLE_BASE()183         OBSERVABLE_BASE::~OBSERVABLE_BASE()
184         {
185         }
186 
187 
allocate_impl()188         void OBSERVABLE_BASE::allocate_impl()
189         {
190             if(!impl_)
191                 impl_ = std::make_shared<IMPL>( this );
192         }
193 
194 
allocate_shared_impl()195         void OBSERVABLE_BASE::allocate_shared_impl()
196         {
197             if(!impl_)
198                 impl_ = std::make_shared<IMPL>();
199             else
200                 impl_->set_shared();
201         }
202 
203 
deallocate_impl()204         void OBSERVABLE_BASE::deallocate_impl() {
205             impl_.reset();
206         }
207 
208 
get_shared_impl()209         std::shared_ptr<OBSERVABLE_BASE::IMPL> OBSERVABLE_BASE::get_shared_impl()
210         {
211             allocate_shared_impl();
212             return impl_;
213         }
214 
215 
add_observer(void * observer)216         void OBSERVABLE_BASE::add_observer( void* observer )
217         {
218             allocate_impl();
219             impl_->add_observer( observer );
220         }
221 
222 
remove_observer(void * observer)223         void OBSERVABLE_BASE::remove_observer( void* observer )
224         {
225             assert( impl_ );
226             impl_->remove_observer( observer );
227         }
228 
229 
enter_iteration()230         void OBSERVABLE_BASE::enter_iteration()
231         {
232             if( impl_ )
233                 impl_->enter_iteration();
234         }
235 
236 
leave_iteration()237         void OBSERVABLE_BASE::leave_iteration()
238         {
239             if( impl_)
240             {
241                 impl_->leave_iteration();
242 
243                 if( !impl_->is_iterating() && !impl_->is_shared() && impl_.use_count() == 1 )
244                     impl_.reset();
245             }
246         }
247 
248 
size() const249         size_t OBSERVABLE_BASE::size() const
250         {
251             if( impl_ )
252                 return impl_->observers_.size();
253             else
254                 return 0;
255         }
256 
257 
on_observers_empty()258         void OBSERVABLE_BASE::on_observers_empty()
259         {
260             // called by an impl that is owned by this, ie. it is a non-shared impl
261             // also it is not iterating
262             deallocate_impl();
263         }
264 
265     }
266 
267 }
268