Imported Upstream version 16.3.1
[platform/upstream/libzypp.git] / zypp / base / SetTracker.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/base/SetTracker.h
10  */
11 #ifndef ZYPP_BASE_SETTRACKER_H
12 #define ZYPP_BASE_SETTRACKER_H
13
14 #include <iosfwd>
15 #include <utility>
16 #include <algorithm>
17
18 ///////////////////////////////////////////////////////////////////
19 namespace zypp
20 {
21   ///////////////////////////////////////////////////////////////////
22   namespace base
23   {
24     ///////////////////////////////////////////////////////////////////
25     /// \class SetTracker
26     /// \brief Track added/removed set items based on an initial set.
27     ///
28     /// The class maintains the \ref current set of items and also records
29     /// the changes compared to the initial set (\ref added and \ref removed
30     /// items) if you use the tracking API.
31     ///
32     /// It is also possible to directly manipulate the three sets.
33     ///
34     /// \note The tracking API expects the template arg to have set semantic.
35     ///////////////////////////////////////////////////////////////////
36     template <class TSet>
37     struct SetTracker
38     {
39       typedef  TSet                     set_type;
40       typedef typename TSet::key_type   key_type;
41       typedef typename TSet::value_type value_type;
42
43       /** Default Ctor: empty set */
44       SetTracker()
45       {}
46
47       /** Ctor taking an initial set */
48       SetTracker( set_type initial_r )
49       : _current( std::move(initial_r) )
50       {}
51
52       /// \name Tracking API
53       //@{
54       /** (Re-)Start tracking the current set (discards previously tracked changes).
55        * \return \c False (set did not change)
56        */
57       bool setInitial()
58       { _added.clear(); _removed.clear(); return false; }
59
60       /** Start tracking a new set (discards previously tracked changes).
61        * \return Whether the set did change (new!=current)
62        */
63       bool setInitial( set_type new_r )
64       {
65         setInitial();
66         bool changed = ( new_r != _current );
67         if ( changed )
68         {
69           _current = std::move(new_r);
70         }
71         return changed;
72       }
73
74
75       /** Set a \a new_r set and track changes.
76        * \return Whether the set has changed
77        */
78       bool set( set_type new_r )
79       {
80         bool changed = ( new_r != _current );
81         if ( changed )
82         {
83           // build the initial (cur-add+rem) set in _current
84           setDifference( _current, _added, _removed );
85           _current.swap( _removed );
86           _added.clear();
87           _removed.clear();
88
89           const set_type & initial( _current );
90           setDifference( initial, new_r, _removed );
91           setDifference( new_r, initial, _added );
92           _current.swap( new_r );
93         }
94         return changed;
95       }
96
97       /** Add an element to the set and track changes.
98        * \return Whether the set has changed
99        */
100       bool add( const value_type & val_r )
101       {
102         bool done = _current.insert( val_r ).second;
103         if ( done )
104         {
105           if ( ! _removed.erase( val_r ) )
106             _added.insert( val_r );
107         }
108         return done;
109       }
110
111       /** Remove an element from the set and track changes.
112        * \return Whether the set has changed
113        */
114       bool remove( const value_type & val_r )
115       {
116         bool done = _current.erase( val_r );
117         if ( done )
118         {
119           if ( ! _added.erase( val_r ) )
120             _removed.insert( val_r );
121         }
122         return done;
123       }
124       //@}
125
126       /// \name Query and retrieval
127       //@{
128       /** Whether \a val_r is in the set. */
129       bool contains( const key_type & key_r ) const     { return find( _current, key_r ); }
130
131       /** Whether \a val_r is tracked as added. */
132       bool wasAdded( const key_type & key_r ) const     { return find( _added, key_r ); }
133
134       /** Whether \a val_r is tracked as removed. */
135       bool wasRemoved( const key_type & key_r ) const   { return find( _removed, key_r ); }
136
137
138       /** Return the current set. */
139       const set_type & current() const                  { return _current; }
140
141       /** Return the set of added items. */
142       const set_type & added() const                    { return _added; }
143
144       /** Return the set of removed items. */
145       const set_type & removed() const                  { return _removed; }
146       //@}
147
148       /// \name Direct manipulation
149       //@{
150       /** Return the current set. */
151       set_type & current()                              { return _current; }
152
153       /** Return the set of added items. */
154       set_type & added()                                { return _added; }
155
156       /** Return the set of removed items. */
157       set_type & removed()                              { return _removed; }
158       //@}
159
160     private:
161
162       static bool find( const set_type & set_r, const key_type & key_r )
163       { return set_r.find( key_r ) != set_r.end(); }
164
165       template <class TORDERED_SET, typename enable_if = typename TORDERED_SET::key_compare>
166       static void setDifference( const TORDERED_SET & lhs, const TORDERED_SET & rhs, TORDERED_SET & result_r )
167       {
168         // std::set_difference requires ordered sets!
169         std::set_difference( lhs.begin(), lhs.end(), rhs.begin(), rhs.end(),
170                              std::inserter( result_r, result_r.end() ),
171                              typename TORDERED_SET::key_compare() );
172       }
173
174       template <class TUNORDERED_SET, typename enable_if = typename TUNORDERED_SET::hasher, typename = void>
175       static void setDifference( const TUNORDERED_SET & lhs, const TUNORDERED_SET & rhs, TUNORDERED_SET & result_r )
176       {
177         // std::set_difference requires ordered sets!
178         for ( const auto & l : lhs )
179         { if ( rhs.find( l ) == rhs.end() ) result_r.insert( l ); }
180       }
181
182     private:
183       set_type _current;
184       set_type _added;
185       set_type _removed;
186     };
187
188     /** \relates SetTracker Stream output */
189     template <class TSet>
190     std::ostream & operator<<( std::ostream & str, const SetTracker<TSet> & obj )
191     { return str << "set(" << obj.current().size() << "|+" << obj.added().size() << "|-" << obj.removed().size() << ')'; }
192
193   } // namespace base
194   ///////////////////////////////////////////////////////////////////
195 } // namespace zypp
196 ///////////////////////////////////////////////////////////////////
197 #endif // ZYPP_BASE_SETTRACKER_H