Imported Upstream version 17.21.0
[platform/upstream/libzypp.git] / zypp / ui / SelectableImpl.h
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/ui/SelectableImpl.h
10  *
11 */
12 #ifndef ZYPP_UI_SELECTABLEIMPL_H
13 #define ZYPP_UI_SELECTABLEIMPL_H
14
15 #include <iostream>
16 #include "zypp/base/LogTools.h"
17
18 #include "zypp/base/PtrTypes.h"
19
20 #include "zypp/ResPool.h"
21 #include "zypp/Resolver.h"
22 #include "zypp/ui/Selectable.h"
23 #include "zypp/ui/SelectableTraits.h"
24
25 using std::endl;
26
27 ///////////////////////////////////////////////////////////////////
28 namespace zypp
29 { /////////////////////////////////////////////////////////////////
30   ///////////////////////////////////////////////////////////////////
31   namespace ui
32   { /////////////////////////////////////////////////////////////////
33
34     ///////////////////////////////////////////////////////////////////
35     //
36     //  CLASS NAME : Selectable::Impl
37     //
38     /** Selectable implementation.
39      * \note Implementation is based in PoolItem, just the Selectable
40      * inteface restricts them to ResObject::constPtr.
41     */
42     struct Selectable::Impl
43     {
44     public:
45
46       typedef SelectableTraits::AvailableItemSet         AvailableItemSet;
47       typedef SelectableTraits::available_iterator       available_iterator;
48       typedef SelectableTraits::available_const_iterator available_const_iterator;
49       typedef SelectableTraits::available_size_type      available_size_type;
50
51       typedef SelectableTraits::InstalledItemSet         InstalledItemSet;
52       typedef SelectableTraits::installed_iterator       installed_iterator;
53       typedef SelectableTraits::installed_const_iterator installed_const_iterator;
54       typedef SelectableTraits::installed_size_type      installed_size_type;
55
56       typedef SelectableTraits::PickList                PickList;
57
58     public:
59       template <class TIterator>
60       Impl( const ResKind & kind_r,
61             const std::string & name_r,
62             TIterator begin_r,
63             TIterator end_r )
64       : _ident( sat::Solvable::SplitIdent( kind_r, name_r ).ident() )
65       , _kind( kind_r )
66       , _name( name_r )
67       {
68         for_( it, begin_r, end_r )
69         {
70           if ( it->status().isInstalled() )
71             _installedItems.insert( *it );
72           else
73             _availableItems.insert( *it );
74         }
75       }
76
77     public:
78       /**  */
79       IdString ident() const
80       { return _ident; }
81
82       /**  */
83       ResKind kind() const
84       { return _kind; }
85
86       /**  */
87       const std::string & name() const
88       { return _name; }
89
90       /**  */
91       Status status() const;
92
93       /**  */
94       bool setStatus( Status state_r, ResStatus::TransactByValue causer_r );
95
96       /** Installed object (transacting ot highest version). */
97       PoolItem installedObj() const
98       {
99         if ( installedEmpty() )
100           return PoolItem();
101         PoolItem ret( transactingInstalled() );
102         return ret ? ret : *_installedItems.begin();
103       }
104
105       /** Best among available objects.
106        * The transacting candidate or the one scheduled to receive
107        * the transact bit.
108       */
109       PoolItem candidateObj() const
110       {
111         PoolItem ret( transactingCandidate() );
112         if ( ! ret )
113           ret = _candidate ? _candidate : defaultCandidate();
114         return ret;
115       }
116
117       /** Set a userCandidate (out of available objects).
118        * \return The new userCandidate or NULL if choice was invalid
119        * (not among availableObjs).
120       */
121       PoolItem setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r );
122
123       /** The best candidate provided by a specific \ref Repository, if there is one.
124        * In contrary to \ref candidateObj, this may return no item even if
125        * there are available objects. This simply means the \ref Repository
126        * does not provide this object.
127        */
128       PoolItem candidateObjFrom( Repository repo_r ) const
129       {
130         for ( const PoolItem & pi : available() )
131         {
132           if ( pi.isRetracted() )
133             continue;
134           if ( pi.repository() == repo_r )
135             return pi;
136         }
137         return PoolItem();
138       }
139
140       /** The best candidate for update, if there is one.
141        * In contrary to \ref candidateObj, this may return no item even if
142        * there are available objects. This simply means the best object is
143        * already installed, and all available objects violate at least one
144        * update policy.
145        */
146       PoolItem updateCandidateObj() const
147       {
148         PoolItem defaultCand( defaultCandidate() );
149
150         // multiversionInstall: This returns the candidate for the last
151         // instance installed. Actually we'd need a list here.
152
153         if ( ! defaultCand || defaultCand.isRetracted() )
154           return PoolItem();
155
156         if ( installedEmpty() )
157           return defaultCand;
158         // Here: installed and defaultCand are non NULL and it's not a
159         //       multiversion install.
160
161         PoolItem installed( installedObj() );
162         // check vendor change
163         if ( ! ( ResPool::instance().resolver().allowVendorChange()
164                  || VendorAttr::instance().equivalent( defaultCand->vendor(), installed->vendor() ) ) )
165           return PoolItem();
166
167         // check arch change (arch noarch changes are allowed)
168         if ( defaultCand->arch() != installed->arch()
169            && ! ( defaultCand->arch() == Arch_noarch || installed->arch() == Arch_noarch ) )
170           return PoolItem();
171
172         // check greater edition
173         if ( defaultCand->edition() <= installed->edition() )
174           return PoolItem();
175
176         return defaultCand;
177       }
178
179       /** \copydoc Selectable::highestAvailableVersionObj()const */
180       PoolItem highestAvailableVersionObj() const
181       {
182         PoolItem ret;
183         bool retractedOk = false;
184         for ( const PoolItem & pi : available() )
185         {
186           if ( !retractedOk && pi.isRetracted() )
187           {
188             if ( ret )
189               break;    // prefer a not retracted candidate
190             retractedOk = true;
191           }
192           if ( !ret || pi.edition() > ret.edition() )
193             ret = pi;
194         }
195         return ret;
196       }
197
198       /** \copydoc Selectable::identIsAutoInstalled()const */
199       bool identIsAutoInstalled() const
200       { return sat::Solvable::identIsAutoInstalled( ident() ); }
201
202       /** \copydoc Selectable::identicalAvailable( const PoolItem & )const */
203       bool identicalAvailable( const PoolItem & rhs ) const
204       { return bool(identicalAvailableObj( rhs )); }
205
206       /** \copydoc Selectable::identicalInstalled( const PoolItem & )const */
207       bool identicalInstalled( const PoolItem & rhs ) const
208       { return bool(identicalInstalledObj( rhs )); }
209
210       /** \copydoc Selectable::identicalAvailableObj( const PoolItem & rhs ) const */
211       PoolItem identicalAvailableObj( const PoolItem & rhs ) const
212       {
213         if ( !availableEmpty() && rhs )
214         {
215           for_( it, _availableItems.begin(), _availableItems.end() )
216           {
217             if ( identical( *it, rhs ) )
218               return *it;
219           }
220         }
221         return PoolItem();
222       }
223
224       /** \copydoc Selectable::identicalInstalledObj( const PoolItem & rhs ) const */
225       PoolItem identicalInstalledObj( const PoolItem & rhs ) const
226       {
227         if ( !installedEmpty() && rhs )
228         {
229           for_( it, _installedItems.begin(), _installedItems.end() )
230           {
231             if ( identical( *it, rhs ) )
232               return *it;
233           }
234         }
235         return PoolItem();
236       }
237
238       /** Best among all objects. */
239       PoolItem theObj() const
240       {
241         PoolItem ret( candidateObj() );
242         if ( ret )
243           return ret;
244         return installedObj();
245       }
246
247       ////////////////////////////////////////////////////////////////////////
248
249       bool availableEmpty() const
250       { return _availableItems.empty(); }
251
252       available_size_type availableSize() const
253       { return _availableItems.size(); }
254
255       available_iterator availableBegin() const
256       { return _availableItems.begin(); }
257
258       available_iterator availableEnd() const
259       { return _availableItems.end(); }
260
261       inline Iterable<available_iterator>  available() const
262       { return makeIterable( availableBegin(), availableEnd() ); }
263
264       ////////////////////////////////////////////////////////////////////////
265
266       bool installedEmpty() const
267       { return _installedItems.empty(); }
268
269       installed_size_type installedSize() const
270       { return _installedItems.size(); }
271
272       installed_iterator installedBegin() const
273       { return _installedItems.begin(); }
274
275       installed_iterator installedEnd() const
276       { return _installedItems.end(); }
277
278       inline Iterable<installed_iterator>  installed() const
279       { return makeIterable( installedBegin(), installedEnd() ); }
280
281       ////////////////////////////////////////////////////////////////////////
282
283       const PickList & picklist() const
284       {
285         if ( ! _picklistPtr )
286         {
287           _picklistPtr.reset( new PickList );
288           // installed without identical avaialble first:
289           for ( const PoolItem & pi : installed() )
290           {
291             if ( ! identicalAvailable( pi ) )
292               _picklistPtr->push_back( pi );
293           }
294           _picklistPtr->insert( _picklistPtr->end(), availableBegin(), availableEnd() );
295         }
296         return *_picklistPtr;
297       }
298
299       bool picklistEmpty() const
300       { return picklist().empty(); }
301
302       picklist_size_type picklistSize() const
303       { return picklist().size(); }
304
305       picklist_iterator picklistBegin() const
306       { return picklist().begin(); }
307
308       picklist_iterator picklistEnd() const
309       { return picklist().end(); }
310
311       ////////////////////////////////////////////////////////////////////////
312
313       bool hasRetracted() const
314       { return !_availableItems.empty() && _availableItems.rbegin()->isRetracted(); }
315
316       bool hasRetractedInstalled() const
317       {
318         bool ret = false;
319         if ( hasRetracted() )
320         {
321           for ( const PoolItem & ipi : installed() )
322           {
323             PoolItem pi { identicalAvailableObj( ipi ) };
324             if ( pi && pi.isRetracted() )
325             {
326               ret = true;
327               break;
328             }
329           }
330         }
331         return ret;
332 // later if pool index is available:
333 //      for ( const PoolItem & pi : installed() )
334 //        if ( pi.isRetracted() )
335 //          return true;
336 //      return false;
337       }
338
339       bool isUnmaintained() const
340       { return availableEmpty(); }
341
342       bool multiversionInstall() const
343       {
344         for ( const PoolItem & pi : picklist() )
345         {
346           if ( pi.multiversionInstall() )
347             return true;
348         }
349         return false;
350       }
351
352       bool pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r );
353
354       bool pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r );
355
356       Status pickStatus( const PoolItem & pi_r ) const;
357
358       bool setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r );
359
360       ////////////////////////////////////////////////////////////////////////
361
362       bool isUndetermined() const
363       {
364         PoolItem cand( candidateObj() );
365         return ! cand || cand.isUndetermined();
366       }
367       bool isRelevant() const
368       {
369         PoolItem cand( candidateObj() );
370         return cand && cand.isRelevant();
371       }
372       bool isSatisfied() const
373        {
374         PoolItem cand( candidateObj() );
375         return cand && cand.isSatisfied();
376       }
377       bool isBroken() const
378       {
379         PoolItem cand( candidateObj() );
380         return cand && cand.isBroken();
381       }
382
383       /** Return who caused the modification. */
384       ResStatus::TransactByValue modifiedBy() const;
385
386       /** Return value of LicenceConfirmed bit. */
387       bool hasLicenceConfirmed() const
388       { return candidateObj() && candidateObj().status().isLicenceConfirmed(); }
389
390       /** Set LicenceConfirmed bit. */
391       void setLicenceConfirmed( bool val_r )
392       { if ( candidateObj() ) candidateObj().status().setLicenceConfirmed( val_r ); }
393
394       /** \copydoc Selectable::hasLocks()const */
395       bool hasLocks() const
396       {
397         for ( const PoolItem & pi : available() )
398         {
399           if ( pi.status().isLocked() )
400             return true;
401         }
402         for ( const PoolItem & pi : installed() )
403         {
404           if ( pi.status().isLocked() )
405             return true;
406         }
407         return false;
408       }
409
410     private:
411       PoolItem transactingInstalled() const
412       {
413         for ( const PoolItem & pi : installed() )
414           {
415             if ( pi.status().transacts() )
416               return pi;
417           }
418         return PoolItem();
419       }
420
421       PoolItem transactingCandidate() const
422       {
423         for ( const PoolItem & pi : available() )
424           {
425             if ( pi.status().transacts() )
426               return pi;
427           }
428         return PoolItem();
429       }
430
431       PoolItem defaultCandidate() const
432       {
433         if ( ! installedEmpty() )
434         {
435           // prefer the installed objects arch and vendor
436           bool solver_allowVendorChange( ResPool::instance().resolver().allowVendorChange() );
437           for ( const PoolItem & ipi : installed() )
438           {
439             PoolItem sameArch; // in case there's no same vendor at least stay with same arch.
440             for (  const PoolItem & api : available() )
441             {
442               // 'same arch' includes allowed changes to/from noarch.
443               if ( ipi.arch() == api.arch() || ipi.arch() == Arch_noarch || api.arch() == Arch_noarch )
444               {
445                 if ( ! solver_allowVendorChange )
446                 {
447                   if ( VendorAttr::instance().equivalent( ipi, api ) )
448                     return api;
449                   else if ( ! sameArch ) // remember best same arch in case no same vendor found
450                      sameArch = api;
451                 }
452                 else // same arch is sufficient
453                   return api;
454               }
455             }
456             if ( sameArch )
457               return sameArch;
458           }
459         }
460         if ( _availableItems.empty() )
461           return PoolItem();
462
463         return *_availableItems.begin();
464       }
465
466       bool allCandidatesLocked() const
467       {
468         for ( const PoolItem & pi : available() )
469           {
470             if ( ! pi.status().isLocked() )
471               return false;
472           }
473         return( ! _availableItems.empty() );
474       }
475
476       bool allInstalledLocked() const
477       {
478         for ( const PoolItem & pi : installed() )
479           {
480             if ( ! pi.status().isLocked() )
481               return false;
482           }
483         return( ! _installedItems.empty() );
484       }
485
486
487     private:
488       const IdString         _ident;
489       const ResKind          _kind;
490       const std::string      _name;
491       InstalledItemSet       _installedItems;
492       AvailableItemSet       _availableItems;
493       //! The object selected by setCandidateObj() method.
494       PoolItem               _candidate;
495       //! lazy initialized picklist
496       mutable scoped_ptr<PickList> _picklistPtr;
497     };
498     ///////////////////////////////////////////////////////////////////
499
500     /** \relates Selectable::Impl Stream output */
501     inline std::ostream & operator<<( std::ostream & str, const Selectable::Impl & obj )
502     {
503       return str << '[' << obj.kind() << ']' << obj.name() << ": " << obj.status()
504                  << " (I " << obj.installedSize() << ")"
505                  << " (A " << obj.availableSize() << ")"
506                  << obj.candidateObj();
507     }
508
509     /** \relates Selectable::Impl Stream output */
510     inline std::ostream & dumpOn( std::ostream & str, const Selectable::Impl & obj )
511     {
512       str << '[' << obj.kind() << ']' << obj.name() << ": " << obj.status()
513           << ( obj.multiversionInstall() ? " (multiversion)" : "") << endl;
514
515       if ( obj.installedEmpty() )
516         str << "   (I 0) {}" << endl << "   ";
517       else
518       {
519         PoolItem icand( obj.installedObj() );
520         str << "   (I " << obj.installedSize() << ") {" << endl;
521         for ( const PoolItem & pi : obj.installed() )
522         {
523           char t = ' ';
524           if ( pi == icand )
525           {
526             t = 'i';
527           }
528           str << " " << t << " " << pi << endl;
529         }
530         str << "}  ";
531       }
532
533       if ( obj.availableEmpty() )
534       {
535         str << "(A 0) {}" << endl << "   ";
536       }
537       else
538       {
539         PoolItem cand( obj.candidateObj() );
540         PoolItem up( obj.updateCandidateObj() );
541         str << "(A " << obj.availableSize() << ") {" << endl;
542         for ( const PoolItem & pi : obj.available() )
543         {
544           char t = ' ';
545           if ( pi == cand )
546           {
547             t = pi == up ? 'C' : 'c';
548           }
549           else if ( pi == up )
550           {
551             t = 'u';
552           }
553           str << " " << t << " " << pi << endl;
554         }
555         str << "}  ";
556       }
557
558       if ( obj.picklistEmpty() )
559       {
560         str << "(P 0) {}";
561       }
562       else
563       {
564         PoolItem cand( obj.candidateObj() );
565         PoolItem up( obj.updateCandidateObj() );
566         str << "(P " << obj.picklistSize() << ") {" << endl;
567         for ( const PoolItem & pi : obj.picklist() )
568         {
569           char t = ' ';
570           if ( pi == cand )
571           {
572             t = pi == up ? 'C' : 'c';
573           }
574           else if ( pi == up )
575           {
576             t = 'u';
577           }
578           str << " " << t << " " << pi << "\t" << obj.pickStatus( pi ) << endl;
579         }
580         str << "}  ";
581       }
582
583       return str;
584     }
585     /////////////////////////////////////////////////////////////////
586   } // namespace ui
587   ///////////////////////////////////////////////////////////////////
588   /////////////////////////////////////////////////////////////////
589 } // namespace zypp
590 ///////////////////////////////////////////////////////////////////
591 #endif // ZYPP_UI_SELECTABLEIMPL_H