Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / ui / SelectableImpl.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/ui/SelectableImpl.cc
10  *
11 */
12 #include <iostream>
13 //#include "zypp/base/Logger.h"
14
15 #include "zypp/ui/SelectableImpl.h"
16
17 using std::endl;
18
19 ///////////////////////////////////////////////////////////////////
20 namespace zypp
21 { /////////////////////////////////////////////////////////////////
22   ///////////////////////////////////////////////////////////////////
23   namespace ui
24   { /////////////////////////////////////////////////////////////////
25
26     /** Simple ResStatus backup stack.
27      * \ref restore simply rewinds all remembered status.
28     */
29     class StatusBackup
30     {
31       public:
32         typedef ResStatus::TransactByValue Causer;
33
34       public:
35         /** Backup status. */
36         ResStatus & backup( ResStatus & status_r )
37         {
38           _backup.push_back( status_r );
39           return status_r;
40         }
41         /** \overload */
42         ResStatus & backup( const PoolItem & pi_r )
43         { return backup( pi_r.status() ); }
44
45         /** Backup status. */
46         ResStatus & operator()( ResStatus & status_r )
47         { return backup( status_r ); }
48         /** \overload */
49         ResStatus & operator()( const PoolItem & pi_r )
50         { return backup( pi_r.status() ); }
51
52         /** Restore all status. */
53         bool restore()
54         {
55           for_( rit, _backup.rbegin(), _backup.rend() )
56             rit->replay();
57           return false; // restore is done on error - return restore();
58         }
59
60       public:
61         /** lowlevel \c ResStatus::setTransact */
62         bool setTransact( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
63         { return backup( pi_r ).setTransact( yesno_r, causer_r ); }
64
65         /** lowlevel \c ResStatus::setLock */
66         bool setLock( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
67         { return backup( pi_r ).setLock( yesno_r, causer_r ); }
68
69         /** lowlevel \c ResStatus::setTransact(true). */
70         bool setTransactTrue( const PoolItem & pi_r, Causer causer_r )
71         { return setTransact( pi_r, true, causer_r ); }
72
73         /** lowlevel \c ResStatus::setTransact(false). */
74         bool setTransactFalse( const PoolItem & pi_r, Causer causer_r )
75         { return setTransact( pi_r, false, causer_r ); }
76
77       public:
78         /** highevel set transact (force unlock). */
79         bool transact( const PoolItem & pi_r, Causer causer_r )
80         {
81           ResStatus & status( backup( pi_r ) );
82           if ( ! status.setLock( false, causer_r ) ) return false;
83           if ( ! status.setTransact( true, causer_r ) ) return false;
84           return true;
85         }
86
87         /** highlevel set locked. */
88         bool lock( const PoolItem & pi_r, Causer causer_r )
89         {
90           ResStatus & status( backup( pi_r ) );
91           if ( ! status.setTransact( false, causer_r ) ) return false;
92           if ( ! status.setLock( true, causer_r ) ) return false;
93           return true;
94         }
95
96         /** highlevel unlock (also unsets transact). */
97         bool unlock( const PoolItem & pi_r, Causer causer_r )
98         {
99           ResStatus & status( backup( pi_r ) );
100           if ( ! status.setTransact( false, causer_r ) ) return false;
101           if ( ! status.setLock( false, causer_r ) ) return false;
102           return true;
103         }
104
105         /** highlevel remove transact from non-multiversion packages. */
106         bool unsetNonMultiTransact(  const PoolItem & pi_r, Causer causer_r )
107         {
108           ResStatus & status( backup( pi_r ) );
109           if ( status.transacts() && ! pi_r.multiversionInstall() )
110           {
111             if ( ! status.setTransact( false, causer_r ) ) return false;
112           }
113           return true;
114         }
115
116         /** highlevel remove transact from multiversion packages. */
117         bool unsetMultiTransact(  const PoolItem & pi_r, Causer causer_r )
118         {
119           ResStatus & status( backup( pi_r ) );
120           if ( status.transacts() && pi_r.multiversionInstall() )
121           {
122             if ( ! status.setTransact( false, causer_r ) ) return false;
123           }
124           return true;
125         }
126
127         /** Highlevel action. */
128         typedef bool (StatusBackup::*Action)( const PoolItem &, Causer );
129
130         /** Highlevel action on range of items. */
131         template <class TIter>
132         bool forEach( TIter begin_r, TIter end_r, Action action_r, Causer causer_r )
133         {
134           for_( it, begin_r, end_r )
135             if ( ! (this->*action_r)( *it, causer_r ) )
136               return false;
137           return true;
138         }
139
140       private:
141         std::vector<resstatus::StatusBackup> _backup;
142     };
143
144     ///////////////////////////////////////////////////////////////////
145     //
146     //  CLASS NAME : StatusHelper
147     //
148     /** \todo Unify status and pickStatus.
149     */
150     struct StatusHelper
151     {
152       StatusHelper( const Selectable::Impl & impl, ResStatus::TransactByValue causer_r )
153       : _impl( impl )
154       , inst( impl.installedObj() )
155       , cand( impl.candidateObj() )
156       , causer( causer_r )
157       {}
158
159       typedef Selectable::Impl::available_const_iterator available_const_iterator;
160
161       //
162       // Queries
163       //
164       bool hasInstalled() const
165       { return bool(inst); }
166
167       bool hasCandidate() const
168       { return bool(cand); }
169
170       bool hasInstalledOnly() const
171       { return inst && !cand; }
172
173       bool hasCandidateOnly() const
174       { return cand && !inst; }
175
176       bool hasBoth() const
177       { return inst && cand; }
178
179       /** \name Topevel methods must restore status on failure. */
180       //@{
181       bool setInstall()
182       {
183         if ( cand )
184         {
185           if ( inst ) {
186             for_( it, _impl.installedBegin(), _impl.installedEnd() )
187             {
188               ResStatus & inststatus( backup( it->status() ) );
189               if ( ! inststatus.setTransact( false, causer ) ) return restore();
190               if ( ! inststatus.setLock    ( false, causer ) ) return restore();
191               if ( ! cand->multiversionInstall() )
192               {
193               // This is what the solver most probabely will do.
194               // If we are wrong the solver will correct it. But
195               // this way we will get a better disk usage result,
196               // even if no autosolving is on.
197                 inststatus.setTransact( true, ResStatus::SOLVER );
198               }
199             }
200           }
201           if ( ! unlockCandidates() ) return restore();
202           ResStatus & candstatus( backup( cand.status() ) );
203           if ( ! candstatus.setTransact( true, causer ) ) return restore();
204           return true;
205         }
206         return false;
207       }
208
209       bool setDelete()
210       {
211         if ( inst )
212         {
213           if ( ! resetTransactingCandidates() ) return restore();
214           for_( it, _impl.installedBegin(), _impl.installedEnd() )
215           {
216             ResStatus & inststatus( backup( it->status() ) );
217             if ( ! inststatus.setLock( false, causer ) ) return restore();
218             if ( ! inststatus.setTransact( true, causer ) ) return restore();
219           }
220           return true;
221         }
222         return false;
223       }
224
225       bool unset()
226       {
227         if ( inst )
228         {
229           for_( it, _impl.installedBegin(), _impl.installedEnd() )
230           {
231             ResStatus & inststatus( backup( it->status() ) );
232             if ( ! inststatus.setTransact( false, causer ) ) return restore();
233             if ( ! inststatus.setLock( false, causer ) ) return restore();
234           }
235         }
236         if ( ! unlockCandidates() ) return restore();
237         return true;
238       }
239
240       bool setProtected()
241       {
242         if ( causer != ResStatus::USER ) // by user only
243           return false;
244
245         if ( inst ) {
246           resetTransactingCandidates();
247           for_( it, _impl.installedBegin(), _impl.installedEnd() )
248           {
249             it->status().setTransact( false, causer );
250             it->status().setLock( true, causer );
251           }
252           return true;
253         } else
254           return false;
255       }
256
257       bool setTaboo()
258       {
259         if ( causer != ResStatus::USER ) // by user only
260           return false;
261
262         if ( cand ) {
263           lockCandidates();
264           return true;
265         } else
266           return false;
267       }
268       //@}
269
270     private:
271       /** \name Helper methods backup status but do not replay. */
272       //@{
273       bool resetTransactingCandidates()
274       {
275         for_( it, _impl.availableBegin(), _impl.availableEnd() )
276         {
277           ResStatus & status( backup( (*it).status() ) );
278           if ( ! status.setTransact( false, causer ) ) return false;
279         }
280         return true;
281       }
282       bool unlockCandidates()
283       {
284         for_( it, _impl.availableBegin(), _impl.availableEnd() )
285         {
286           ResStatus & status( backup( (*it).status() ) );
287           if ( ! status.setTransact( false, causer ) ) return false;
288           if ( ! status.setLock( false, causer ) ) return false;
289         }
290         return true;
291       }
292       bool lockCandidates()
293       {
294         for_( it, _impl.availableBegin(), _impl.availableEnd() )
295         {
296           ResStatus & status( backup( (*it).status() ) );
297           if ( ! status.setTransact( false, causer ) ) return false;
298           if ( ! status.setLock( true, causer ) ) return false;
299         }
300         return true;
301       }
302       //@}
303
304     private:
305       const Selectable::Impl & _impl;
306       PoolItem                   inst;
307       PoolItem                   cand;
308       ResStatus::TransactByValue causer;
309
310     private:
311       bool restore() { return backup.restore(); }
312       StatusBackup backup;
313     };
314     ///////////////////////////////////////////////////////////////////
315
316     ///////////////////////////////////////////////////////////////////
317     //
318     //  CLASS NAME : Selectable::Impl
319     //
320     ///////////////////////////////////////////////////////////////////
321
322     Status Selectable::Impl::status() const
323     {
324       PoolItem cand( candidateObj() );
325       if ( cand && cand.status().transacts() )
326         {
327           if ( cand.status().isByUser() )
328             return( installedObj() ? S_Update : S_Install );
329           else
330             return( installedObj() ? S_AutoUpdate : S_AutoInstall );
331         }
332
333       if ( installedObj() && installedObj().status().transacts() )
334         {
335           return( installedObj().status().isByUser() ? S_Del : S_AutoDel );
336         }
337
338       if ( installedObj() && allInstalledLocked() )
339           return S_Protected;
340
341       if ( !installedObj() && allCandidatesLocked() )
342           return S_Taboo;
343
344       // KEEP state:
345       if ( installedObj() )
346         return S_KeepInstalled;
347       // Report pseudo installed items as installed, if they are satisfied.
348       if ( traits::isPseudoInstalled( kind() )
349            && cand.status().isSatisfied() ) // no installed, so we must have candidate
350         return S_KeepInstalled;
351
352       return S_NoInst;
353     }
354
355     bool Selectable::Impl::setStatus( Status state_r, ResStatus::TransactByValue causer_r )
356     {
357       StatusHelper self( *this, causer_r );
358
359       switch ( state_r )
360         {
361         case S_Protected:
362             return self.setProtected();
363         case S_Taboo:
364             return self.setTaboo();
365         case S_AutoDel:
366         case S_AutoInstall:
367         case S_AutoUpdate:
368           // Auto level is SOLVER level. UI may query, but not
369           // set at this level.
370           break;
371
372         case S_Del:
373           return self.setDelete();
374           break;
375
376         case S_Install:
377           return self.hasCandidateOnly() && self.setInstall();
378           break;
379
380         case S_Update:
381           return self.hasBoth() && self.setInstall();
382           break;
383
384         case S_KeepInstalled:
385           return self.hasInstalled() && self.unset();
386           break;
387
388         case S_NoInst:
389           return !self.hasInstalled() && self.unset();
390           break;
391         }
392
393       return false;
394     }
395
396     PoolItem Selectable::Impl::setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
397     {
398       PoolItem newCandidate;
399
400       if ( newCandidate_r ) // must be in available list
401       {
402         for_( it, availableBegin(), availableEnd() )
403         {
404           if ( *it == newCandidate_r )
405           {
406             newCandidate = *it;
407             break;
408           }
409         }
410       }
411
412       if ( newCandidate )
413       {
414         PoolItem trans( transactingCandidate() );
415         if ( trans && trans != newCandidate )
416         {
417           // adjust transact to the new cancidate
418           if (    trans.status().maySetTransact( false, causer_r )
419                && newCandidate.status().maySetTransact( true, causer_r ) )
420           {
421             trans.status().setTransact( false, causer_r );
422             newCandidate.status().setTransact( true, causer_r );
423           }
424           else
425           {
426             // No permission to change a transacting candidate.
427             // Leave _candidate untouched and return NULL.
428             return PoolItem();
429           }
430         }
431       }
432
433       return _candidate = newCandidate;
434     }
435
436     ///////////////////////////////////////////////////////////////////
437
438     bool Selectable::Impl::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
439     {
440       if ( identicalInstalled( pi_r ) )
441         return setPickStatus( pi_r, ( yesno_r ? S_Update : S_KeepInstalled ), causer_r );
442       return setPickStatus( pi_r, ( yesno_r ? S_Install : S_NoInst ), causer_r );
443     }
444
445     bool Selectable::Impl::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
446     {
447       return setPickStatus( pi_r, ( yesno_r ? S_Del : S_KeepInstalled ), causer_r );
448     }
449
450     bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r )
451     {
452       if ( pi_r.ident() != ident() )
453         return false;  // not my PoolItem
454
455       StatusBackup backup;
456       std::vector<PoolItem> i;
457       std::vector<PoolItem> a;
458
459       for_( it, installedBegin(), installedEnd() )
460         if ( identical( *it, pi_r ) )
461           i.push_back( *it );
462       for_( it, availableBegin(), availableEnd() )
463         if ( identical( *it, pi_r ) )
464           a.push_back( *it );
465
466       switch ( state_r )
467       {
468         case S_Protected:
469           if ( causer_r == ResStatus::USER && ! i.empty() )
470           {
471             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
472             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
473             return true;
474           }
475           break;
476
477         case S_Taboo:
478           if ( causer_r == ResStatus::USER && ! a.empty() )
479           {
480             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
481             return true;
482           }
483           break;
484
485         case S_AutoDel:
486         case S_AutoInstall:
487         case S_AutoUpdate:
488           // Auto level is SOLVER level. UI may query, but not
489           // set at this level.
490           break;
491
492         case S_Del:
493           if ( ! i.empty() )
494           {
495             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::transact, causer_r ) ) return backup.restore();
496             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
497             return true;
498           }
499           break;
500
501         case S_Install:
502           if ( i.empty() && ! a.empty() )
503           {
504             const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r );
505             if ( cand.multiversionInstall() )
506             {
507               if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetNonMultiTransact, causer_r ) ) return backup.restore();
508               // maybe unlock candidate only?
509               if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
510               if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
511               return true;
512             }
513             else
514             {
515               // For non-multiversion use ordinary setStatus
516               // NOTE that S_Update/S_Install here depends on !installedEmpty()
517               // and not on picklists identicalInstalled.
518               if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetMultiTransact, causer_r ) ) return backup.restore();
519               if ( ! setCandidate( cand, causer_r ) )  return backup.restore();
520               if ( ! setStatus( installedEmpty() ? S_Install : S_Update, causer_r ) )  return backup.restore();
521               return true;
522             }
523           }
524           break;
525
526         case S_Update:
527           if ( ! i.empty() && ! a.empty() )
528           {
529             const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r );
530             if ( cand.multiversionInstall() )
531             {
532               if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
533               if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::setTransactTrue, ResStatus::SOLVER ) ) return backup.restore();
534               if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetNonMultiTransact, causer_r ) ) return backup.restore();
535               // maybe unlock candidate only?
536               if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
537               if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
538               return true;
539             }
540             else
541             {
542               // For non-multiversion use ordinary setStatus
543               // NOTE that S_Update/S_Install here depends on !installedEmpty()
544               // and not on picklists identicalInstalled.
545               if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetMultiTransact, causer_r ) ) return backup.restore();
546               if ( ! setCandidate( cand, causer_r ) )  return backup.restore();
547               if ( ! setStatus( installedEmpty() ? S_Install : S_Update, causer_r ) )  return backup.restore();
548               return true;
549             }
550           }
551           break;
552
553         case S_KeepInstalled:
554           if ( ! i.empty()  )
555           {
556             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
557             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
558             return true;
559           }
560           break;
561
562         case S_NoInst:
563           if ( i.empty()  )
564           {
565             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
566             return true;
567           }
568           break;
569       }
570       return false;
571     }
572
573     Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
574     {
575       if ( pi_r.satSolvable().ident() != ident() )
576         return Status(-1); // not my PoolItem
577
578       std::vector<PoolItem> i;
579       std::vector<PoolItem> a;
580       PoolItem ti;
581       PoolItem ta;
582
583       for_( it, installedBegin(), installedEnd() )
584         if ( identical( *it, pi_r ) )
585         {
586           i.push_back( *it );
587           if ( ! ti && it->status().transacts() )
588             ti = *it;
589         }
590
591       for_( it, availableBegin(), availableEnd() )
592         if ( identical( *it, pi_r ) )
593         {
594           a.push_back( *it );
595           if ( ! ta && it->status().transacts() )
596             ta = *it;
597         }
598
599       if ( ta )
600       {
601         if ( ta.status().isByUser() )
602           return( i.empty() ? S_Install : S_Update );
603         else
604           return( i.empty() ? S_AutoInstall : S_AutoUpdate );
605       }
606
607       if ( ti )
608       {
609         return( ti.status().isByUser() ? S_Del : S_AutoDel );
610       }
611
612       for_( it, i.begin(), i.end() )
613         if ( it->status().isLocked() )
614           return S_Protected;
615
616       if ( i.empty() )
617       {
618         bool allALocked = true;
619         for_( it, a.begin(), a.end() )
620           if ( ! it->status().isLocked() )
621           {
622             allALocked = false;
623             break;
624           }
625         if ( allALocked )
626           return S_Taboo;
627       }
628
629       // KEEP state:
630       if ( ! i.empty() )
631         return S_KeepInstalled;
632       // Report pseudo installed items as installed, if they are satisfied.
633       if ( traits::isPseudoInstalled( kind() )
634            && ( ta ? ta : *a.begin() ).status().isSatisfied() ) // no installed, so we must have candidate
635         return S_KeepInstalled;
636
637       return S_NoInst;
638     }
639
640     ///////////////////////////////////////////////////////////////////
641
642     ResStatus::TransactByValue Selectable::Impl::modifiedBy() const
643     {
644       PoolItem cand( candidateObj() );
645       if ( cand && cand.status().transacts() )
646         return cand.status().getTransactByValue();
647
648       if ( installedObj() && installedObj().status().transacts() )
649         return installedObj().status().getTransactByValue();
650
651       if ( cand )
652         return cand.status().getTransactByValue();
653
654       if ( installedObj() )
655         return installedObj().status().getTransactByValue();
656
657       return ResStatus::SOLVER;
658     }
659
660     /////////////////////////////////////////////////////////////////
661   } // namespace ui
662   ///////////////////////////////////////////////////////////////////
663   /////////////////////////////////////////////////////////////////
664 } // namespace zypp
665 ///////////////////////////////////////////////////////////////////