ebcfc3136df83c275bc4ce2940a9bc334706fe50
[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 action. */
106         typedef bool (StatusBackup::*Action)( const PoolItem &, Causer );
107
108         /** Highlevel action on range of items. */
109         template <class _Iter>
110         bool forEach( _Iter begin_r, _Iter end_r, Action action_r, Causer causer_r )
111         {
112           for_( it, begin_r, end_r )
113             if ( ! (this->*action_r)( *it, causer_r ) )
114               return false;
115           return true;
116         }
117
118       private:
119         std::vector<resstatus::StatusBackup> _backup;
120     };
121
122     ///////////////////////////////////////////////////////////////////
123     //
124     //  CLASS NAME : StatusHelper
125     //
126     /** \todo Unify status and pickStatus.
127     */
128     struct StatusHelper
129     {
130       StatusHelper( const Selectable::Impl & impl, ResStatus::TransactByValue causer_r )
131       : _impl( impl )
132       , inst( impl.installedObj() )
133       , cand( impl.candidateObj() )
134       , causer( causer_r )
135       {}
136
137       typedef Selectable::Impl::available_const_iterator available_const_iterator;
138
139       //
140       // Queries
141       //
142       bool hasInstalled() const
143       { return bool(inst); }
144
145       bool hasCandidate() const
146       { return bool(cand); }
147
148       bool hasInstalledOnly() const
149       { return inst && !cand; }
150
151       bool hasCandidateOnly() const
152       { return cand && !inst; }
153
154       bool hasBoth() const
155       { return inst && cand; }
156
157       /** \name Topevel methods must restore status on failure. */
158       //@{
159       bool setInstall()
160       {
161         if ( cand )
162         {
163           if ( inst ) {
164             for_( it, _impl.installedBegin(), _impl.installedEnd() )
165             {
166               ResStatus & inststatus( backup( it->status() ) );
167               if ( ! inststatus.setTransact( false, causer ) ) return restore();
168               if ( ! inststatus.setLock    ( false, causer ) ) return restore();
169               if ( ! cand->multiversionInstall() )
170               {
171               // This is what the solver most probabely will do.
172               // If we are wrong the solver will correct it. But
173               // this way we will get a better disk usage result,
174               // even if no autosolving is on.
175                 inststatus.setTransact( true, ResStatus::SOLVER );
176               }
177             }
178           }
179           if ( ! unlockCandidates() ) return restore();
180           ResStatus & candstatus( backup( cand.status() ) );
181           if ( ! candstatus.setTransact( true, causer ) ) return restore();
182           return true;
183         }
184         return false;
185       }
186
187       bool setDelete()
188       {
189         if ( inst )
190         {
191           if ( ! resetTransactingCandidates() ) return restore();
192           for_( it, _impl.installedBegin(), _impl.installedEnd() )
193           {
194             ResStatus & inststatus( backup( it->status() ) );
195             if ( ! inststatus.setLock( false, causer ) ) return restore();
196             if ( ! inststatus.setTransact( true, causer ) ) return restore();
197           }
198           return true;
199         }
200         return false;
201       }
202
203       bool unset()
204       {
205         if ( inst )
206         {
207           for_( it, _impl.installedBegin(), _impl.installedEnd() )
208           {
209             ResStatus & inststatus( backup( it->status() ) );
210             if ( ! inststatus.setTransact( false, causer ) ) return restore();
211             if ( ! inststatus.setLock( false, causer ) ) return restore();
212           }
213         }
214         if ( ! unlockCandidates() ) return restore();
215         return true;
216       }
217
218       bool setProtected()
219       {
220         if ( causer != ResStatus::USER ) // by user only
221           return false;
222
223         if ( inst ) {
224           resetTransactingCandidates();
225           for_( it, _impl.installedBegin(), _impl.installedEnd() )
226           {
227             it->status().setTransact( false, causer );
228             it->status().setLock( true, causer );
229           }
230           return true;
231         } else
232           return false;
233       }
234
235       bool setTaboo()
236       {
237         if ( causer != ResStatus::USER ) // by user only
238           return false;
239
240         if ( cand ) {
241           lockCandidates();
242           return true;
243         } else
244           return false;
245       }
246       //@}
247
248     private:
249       /** \name Helper methods backup status but do not replay. */
250       //@{
251       bool resetTransactingCandidates()
252       {
253         for_( it, _impl.availableBegin(), _impl.availableEnd() )
254         {
255           ResStatus & status( backup( (*it).status() ) );
256           if ( ! status.setTransact( false, causer ) ) return false;
257         }
258         return true;
259       }
260       bool unlockCandidates()
261       {
262         for_( it, _impl.availableBegin(), _impl.availableEnd() )
263         {
264           ResStatus & status( backup( (*it).status() ) );
265           if ( ! status.setTransact( false, causer ) ) return false;
266           if ( ! status.setLock( false, causer ) ) return false;
267         }
268         return true;
269       }
270       bool lockCandidates()
271       {
272         for_( it, _impl.availableBegin(), _impl.availableEnd() )
273         {
274           ResStatus & status( backup( (*it).status() ) );
275           if ( ! status.setTransact( false, causer ) ) return false;
276           if ( ! status.setLock( true, causer ) ) return false;
277         }
278         return true;
279       }
280       //@}
281
282     private:
283       const Selectable::Impl & _impl;
284       PoolItem                   inst;
285       PoolItem                   cand;
286       ResStatus::TransactByValue causer;
287
288     private:
289       bool restore() { return backup.restore(); }
290       StatusBackup backup;
291     };
292     ///////////////////////////////////////////////////////////////////
293
294     ///////////////////////////////////////////////////////////////////
295     //
296     //  CLASS NAME : Selectable::Impl
297     //
298     ///////////////////////////////////////////////////////////////////
299
300     Status Selectable::Impl::status() const
301     {
302       PoolItem cand( candidateObj() );
303       if ( cand && cand.status().transacts() )
304         {
305           if ( cand.status().isByUser() )
306             return( installedObj() ? S_Update : S_Install );
307           else
308             return( installedObj() ? S_AutoUpdate : S_AutoInstall );
309         }
310
311       if ( installedObj() && installedObj().status().transacts() )
312         {
313           return( installedObj().status().isByUser() ? S_Del : S_AutoDel );
314         }
315
316       if ( installedObj() && allInstalledLocked() )
317           return S_Protected;
318
319       if ( !installedObj() && allCandidatesLocked() )
320           return S_Taboo;
321
322       // KEEP state:
323       if ( installedObj() )
324         return S_KeepInstalled;
325       // Report pseudo installed items as installed, if they are satisfied.
326       if ( traits::isPseudoInstalled( kind() )
327            && cand.status().isSatisfied() ) // no installed, so we must have candidate
328         return S_KeepInstalled;
329
330       return S_NoInst;
331     }
332
333     bool Selectable::Impl::setStatus( Status state_r, ResStatus::TransactByValue causer_r )
334     {
335       StatusHelper self( *this, causer_r );
336
337       switch ( state_r )
338         {
339         case S_Protected:
340             return self.setProtected();
341         case S_Taboo:
342             return self.setTaboo();
343         case S_AutoDel:
344         case S_AutoInstall:
345         case S_AutoUpdate:
346           // Auto level is SOLVER level. UI may query, but not
347           // set at this level.
348           break;
349
350         case S_Del:
351           return self.setDelete();
352           break;
353
354         case S_Install:
355           return self.hasCandidateOnly() && self.setInstall();
356           break;
357
358         case S_Update:
359           return self.hasBoth() && self.setInstall();
360           break;
361
362         case S_KeepInstalled:
363           return self.hasInstalled() && self.unset();
364           break;
365
366         case S_NoInst:
367           return !self.hasInstalled() && self.unset();
368           break;
369         }
370
371       return false;
372     }
373
374     PoolItem Selectable::Impl::setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
375     {
376       PoolItem newCandidate;
377
378       if ( newCandidate_r ) // must be in available list
379       {
380         for_( it, availableBegin(), availableEnd() )
381         {
382           if ( *it == newCandidate_r )
383           {
384             newCandidate = *it;
385             break;
386           }
387         }
388       }
389
390       if ( newCandidate )
391       {
392         PoolItem trans( transactingCandidate() );
393         if ( trans && trans != newCandidate )
394         {
395           // adjust transact to the new cancidate
396           if (    trans.status().maySetTransact( false, causer_r )
397                && newCandidate.status().maySetTransact( true, causer_r ) )
398           {
399             trans.status().setTransact( false, causer_r );
400             newCandidate.status().setTransact( true, causer_r );
401           }
402           else
403           {
404             // No permission to change a transacting candidate.
405             // Leave _candidate untouched and return NULL.
406             return PoolItem();
407           }
408         }
409       }
410
411       return _candidate = newCandidate;
412     }
413
414     ///////////////////////////////////////////////////////////////////
415
416     bool Selectable::Impl::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
417     {
418       if ( identicalInstalled( pi_r ) )
419         return setPickStatus( pi_r, ( yesno_r ? S_Update : S_KeepInstalled ), causer_r );
420       return setPickStatus( pi_r, ( yesno_r ? S_Install : S_NoInst ), causer_r );
421     }
422
423     bool Selectable::Impl::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
424     {
425       return setPickStatus( pi_r, ( yesno_r ? S_Del : S_KeepInstalled ), causer_r );
426     }
427
428     bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r )
429     {
430       if ( pi_r.satSolvable().ident() != ident() )
431         return false;  // not my PoolItem
432
433       if ( ! multiversionInstall() )
434         return false;  // We're not yet ready for this.
435       // TODO: Without multiversionInstall take care at most ONE available is set
436       // to install. Upon install ALL installed get deleted. Only upon deletetion
437       // one might pick individual versions (but more than one would be an error here).
438
439       StatusBackup backup;
440       std::vector<PoolItem> i;
441       std::vector<PoolItem> a;
442
443       for_( it, installedBegin(), installedEnd() )
444         if ( identical( *it, pi_r ) )
445           i.push_back( *it );
446       for_( it, availableBegin(), availableEnd() )
447         if ( identical( *it, pi_r ) )
448           a.push_back( *it );
449
450       switch ( state_r )
451       {
452         case S_Protected:
453           if ( causer_r == ResStatus::USER && ! i.empty() )
454           {
455             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
456             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
457             return true;
458           }
459           break;
460
461         case S_Taboo:
462           if ( causer_r == ResStatus::USER && ! a.empty() )
463           {
464             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
465             return true;
466           }
467           break;
468
469         case S_AutoDel:
470         case S_AutoInstall:
471         case S_AutoUpdate:
472           // Auto level is SOLVER level. UI may query, but not
473           // set at this level.
474           break;
475
476         case S_Del:
477           if ( ! i.empty() )
478           {
479             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::transact, causer_r ) ) return backup.restore();
480             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
481             return true;
482           }
483           break;
484
485         case S_Install:
486           if ( i.empty() && ! a.empty() )
487           {
488             // maybe unlock candidate only?
489             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
490             const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r ); // status already backed up above
491             if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
492             return true;
493           }
494           break;
495
496         case S_Update:
497           if ( ! i.empty() && ! a.empty() )
498           {
499             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
500             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::setTransactTrue, ResStatus::SOLVER ) ) return backup.restore();
501             // maybe unlock candidate only?
502             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
503             const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r ); // status already backed up above
504             if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
505             return true;
506           }
507           break;
508
509         case S_KeepInstalled:
510           if ( ! i.empty()  )
511           {
512             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
513             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
514             return true;
515           }
516           break;
517
518         case S_NoInst:
519           if ( i.empty()  )
520           {
521             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
522             return true;
523           }
524           break;
525       }
526       return false;
527     }
528
529     Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
530     {
531       if ( pi_r.satSolvable().ident() != ident() )
532         return Status(-1); // not my PoolItem
533
534       std::vector<PoolItem> i;
535       std::vector<PoolItem> a;
536       PoolItem ti;
537       PoolItem ta;
538
539       for_( it, installedBegin(), installedEnd() )
540         if ( identical( *it, pi_r ) )
541         {
542           i.push_back( *it );
543           if ( ! ti && it->status().transacts() )
544             ti = *it;
545         }
546
547       for_( it, availableBegin(), availableEnd() )
548         if ( identical( *it, pi_r ) )
549         {
550           a.push_back( *it );
551           if ( ! ta && it->status().transacts() )
552             ta = *it;
553         }
554
555       if ( ta )
556       {
557         if ( ta.status().isByUser() )
558           return( i.empty() ? S_Install : S_Update );
559         else
560           return( i.empty() ? S_AutoInstall : S_AutoUpdate );
561       }
562
563       if ( ti )
564       {
565         return( ti.status().isByUser() ? S_Del : S_AutoDel );
566       }
567
568       for_( it, i.begin(), i.end() )
569         if ( it->status().isLocked() )
570           return S_Protected;
571
572       if ( i.empty() )
573       {
574         bool allALocked = true;
575         for_( it, a.begin(), a.end() )
576           if ( ! it->status().isLocked() )
577           {
578             allALocked = false;
579             break;
580           }
581         if ( allALocked )
582           return S_Taboo;
583       }
584
585       // KEEP state:
586       if ( ! i.empty() )
587         return S_KeepInstalled;
588       // Report pseudo installed items as installed, if they are satisfied.
589       if ( traits::isPseudoInstalled( kind() )
590            && ( ta ? ta : *a.begin() ).status().isSatisfied() ) // no installed, so we must have candidate
591         return S_KeepInstalled;
592
593       return S_NoInst;
594     }
595
596     ///////////////////////////////////////////////////////////////////
597
598     ResStatus::TransactByValue Selectable::Impl::modifiedBy() const
599     {
600       PoolItem cand( candidateObj() );
601       if ( cand && cand.status().transacts() )
602         return cand.status().getTransactByValue();
603
604       if ( installedObj() && installedObj().status().transacts() )
605         return installedObj().status().getTransactByValue();
606
607       if ( cand )
608         return cand.status().getTransactByValue();
609
610       if ( installedObj() )
611         return installedObj().status().getTransactByValue();
612
613       return ResStatus::SOLVER;
614     }
615
616     /////////////////////////////////////////////////////////////////
617   } // namespace ui
618   ///////////////////////////////////////////////////////////////////
619   /////////////////////////////////////////////////////////////////
620 } // namespace zypp
621 ///////////////////////////////////////////////////////////////////