Compute proper Selectable::pickStatus
[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     ///////////////////////////////////////////////////////////////////
27     //
28     //  CLASS NAME : StatusHelper
29     //
30     /**
31     */
32     struct StatusHelper
33     {
34       StatusHelper( const Selectable::Impl & impl, ResStatus::TransactByValue causer_r )
35       : _impl( impl )
36       , inst( impl.installedObj() )
37       , cand( impl.candidateObj() )
38       , causer( causer_r )
39       {}
40
41       typedef Selectable::Impl::available_const_iterator available_const_iterator;
42
43       //
44       // Queries
45       //
46       bool hasInstalled() const
47       { return inst; }
48
49       bool hasCandidate() const
50       { return cand; }
51
52       bool hasInstalledOnly() const
53       { return inst && !cand; }
54
55       bool hasCandidateOnly() const
56       { return cand && !inst; }
57
58       bool hasBoth() const
59       { return inst && cand; }
60
61       /** \name Topevel methods must restore status on failure. */
62       //@{
63       bool setInstall()
64       {
65         if ( cand )
66         {
67           if ( inst ) {
68             ResStatus & inststatus( backup( inst.status() ) );
69             if ( ! inststatus.setTransact( false, causer ) ) return restore();
70             if ( ! inststatus.setLock    ( false, causer ) ) return restore();
71             if ( ! cand->installOnly() )
72             {
73               // This is what the solver most probabely will do.
74               // If we are wrong the solver will correct it. But
75               // this way we will get a better disk usage result,
76               // even if no autosolving is on.
77               inststatus.setTransact( true, ResStatus::SOLVER );
78             }
79           }
80           if ( ! unlockCandidates() ) return restore();
81           ResStatus & candstatus( backup( cand.status() ) );
82           if ( ! candstatus.setTransact( true, causer ) ) return restore();
83           return true;
84         }
85         return false;
86       }
87
88       bool setDelete()
89       {
90         if ( inst )
91         {
92           if ( ! resetTransactingCandidates() ) return restore();
93           ResStatus & inststatus( backup( inst.status() ) );
94           if ( ! inststatus.setLock( false, causer ) ) return restore();
95           if ( ! inststatus.setTransact( true, causer ) ) return restore();
96           return true;
97         }
98         return false;
99       }
100
101       bool unset()
102       {
103         if ( inst )
104         {
105           ResStatus & inststatus( backup( inst.status() ) );
106           if ( ! inststatus.setTransact( false, causer ) ) return restore();
107           if ( ! inststatus.setLock( false, causer ) ) return restore();
108         }
109         if ( ! unlockCandidates() ) return restore();
110         return true;
111       }
112
113       bool setProtected()
114       {
115         if ( causer != ResStatus::USER ) // by user only
116           return false;
117
118         if ( inst ) {
119           resetTransactingCandidates();
120           inst.status().setTransact( false, causer );
121           return inst.status().setLock( true, causer );
122         } else
123           return false;
124       }
125
126       bool setTaboo()
127       {
128         if ( causer != ResStatus::USER ) // by user only
129           return false;
130
131         if ( cand ) {
132           lockCandidates();
133           return true;
134         } else
135           return false;
136       }
137       //@}
138
139     private:
140       /** \name Helper methods backup status but do not replay. */
141       //@{
142       bool resetTransactingCandidates()
143       {
144         for_( it, _impl.availableBegin(), _impl.availableEnd() )
145         {
146           ResStatus & status( backup( (*it).status() ) );
147           if ( ! status.setTransact( false, causer ) ) return false;
148         }
149         return true;
150       }
151       bool unlockCandidates()
152       {
153         for_( it, _impl.availableBegin(), _impl.availableEnd() )
154         {
155           ResStatus & status( backup( (*it).status() ) );
156           if ( ! status.setTransact( false, causer ) ) return false;
157           if ( ! status.setLock( false, causer ) ) return false;
158         }
159         return true;
160       }
161       bool lockCandidates()
162       {
163         for_( it, _impl.availableBegin(), _impl.availableEnd() )
164         {
165           ResStatus & status( backup( (*it).status() ) );
166           if ( ! status.setTransact( false, causer ) ) return false;
167           if ( ! status.setLock( true, causer ) ) return false;
168         }
169         return true;
170       }
171       //@}
172
173     private:
174       const Selectable::Impl & _impl;
175       PoolItem                   inst;
176       PoolItem                   cand;
177       ResStatus::TransactByValue causer;
178
179     private:
180       // No backup replay needed if causer is user,
181       // because actions should always succeed.
182
183       ResStatus & backup( ResStatus & status_r )
184       {
185         if ( causer != ResStatus::USER )
186           _backup.push_back( status_r );
187         return status_r;
188       }
189
190       bool restore()
191       {
192         if ( causer != ResStatus::USER )
193         {
194           for_( rit, _backup.rbegin(), _backup.rend() )
195           {
196             rit->replay();
197           }
198         }
199         return false; // restore is done on error - return restore();
200       }
201
202       std::vector<resstatus::StatusBackup> _backup;
203     };
204     ///////////////////////////////////////////////////////////////////
205
206     ///////////////////////////////////////////////////////////////////
207     //
208     //  CLASS NAME : Selectable::Impl
209     //
210     ///////////////////////////////////////////////////////////////////
211
212     Status Selectable::Impl::status() const
213     {
214       PoolItem cand( candidateObj() );
215       if ( cand && cand.status().transacts() )
216         {
217           if ( cand.status().isByUser() )
218             return( installedObj() ? S_Update : S_Install );
219           else
220             return( installedObj() ? S_AutoUpdate : S_AutoInstall );
221         }
222
223       if ( installedObj() && installedObj().status().transacts() )
224         {
225           return( installedObj().status().isByUser() ? S_Del : S_AutoDel );
226         }
227
228       if ( installedObj() && installedObj().status().isLocked() )
229           return S_Protected;
230
231       if ( !installedObj() && allCandidatesLocked() )
232           return S_Taboo;
233
234       // KEEP state:
235       if ( installedObj() )
236         return S_KeepInstalled;
237       // Report pseudo installed items as installed, if they are satisfied.
238       if ( traits::isPseudoInstalled( kind() )
239            && cand.status().isSatisfied() ) // no installed, so we must have candidate
240         return S_KeepInstalled;
241
242       return S_NoInst;
243     }
244
245     bool Selectable::Impl::setStatus( const Status state_r, ResStatus::TransactByValue causer_r )
246     {
247       StatusHelper self( *this, causer_r );
248
249       switch ( state_r )
250         {
251         case S_Protected:
252             return self.setProtected();
253         case S_Taboo:
254             return self.setTaboo();
255         case S_AutoDel:
256         case S_AutoInstall:
257         case S_AutoUpdate:
258           // Auto level is SOLVER level. UI may query, but not
259           // set at this level.
260           break;
261
262         case S_Del:
263           return self.setDelete();
264           break;
265
266         case S_Install:
267           return self.hasCandidateOnly() && self.setInstall();
268           break;
269
270         case S_Update:
271           return self.hasBoth() && self.setInstall();
272           break;
273
274         case S_KeepInstalled:
275           return self.hasInstalled() && self.unset();
276           break;
277
278         case S_NoInst:
279           return !self.hasInstalled() && self.unset();
280           break;
281         }
282
283       return false;
284     }
285
286     PoolItem Selectable::Impl::setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r )
287     {
288       PoolItem newCandidate;
289
290       if ( newCandidate_r ) // must be in available list
291       {
292         for_( it, availableBegin(), availableEnd() )
293         {
294           if ( *it == newCandidate_r )
295           {
296             newCandidate = *it;
297             break;
298           }
299         }
300       }
301
302       if ( newCandidate )
303       {
304         PoolItem trans( transactingCandidate() );
305         if ( trans && trans != newCandidate )
306         {
307           // adjust transact to the new cancidate
308           if (    trans.status().maySetTransact( false, causer_r )
309                && newCandidate.status().maySetTransact( true, causer_r ) )
310           {
311             trans.status().setTransact( false, causer_r );
312             newCandidate.status().setTransact( true, causer_r );
313           }
314           else
315           {
316             // No permission to change a transacting candidate.
317             // Leave _candidate untouched and return NULL.
318             return PoolItem();
319           }
320         }
321       }
322
323       return _candidate = newCandidate;
324     }
325
326
327     bool Selectable::Impl::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
328     {
329       if ( pi_r.satSolvable().ident() != ident() || pi_r.satSolvable().isSystem() )
330         return false; // not my PoolItem or an installed one
331
332       return pi_r.status().setTransact( yesno_r, causer_r );
333     }
334
335     bool Selectable::Impl::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
336     {
337       if ( pi_r.satSolvable().ident() != ident() || ! pi_r.satSolvable().isSystem() )
338         return false; // not my PoolItem or not installed
339
340       return pi_r.status().setTransact( yesno_r, causer_r );
341     }
342
343     ///////////////////////////////////////////////////////////////////
344
345     Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
346     {
347       if ( pi_r.satSolvable().ident() == ident() )
348       {
349         if ( pi_r.satSolvable().isSystem() )
350         {
351           // have installed!
352           if ( pi_r.status().isLocked() )
353             return S_Protected;
354
355           // at least one identical available transacing?
356           for_( it, _availableItems.begin(), _availableItems.end() )
357           {
358             if ( identical( *it, pi_r ) )
359             {
360               if ( (*it).status().transacts() )
361                 return( (*it).status().isByUser() ? S_Update : S_AutoUpdate );
362             }
363           }
364
365           // no update, so maybe delete?
366           if ( pi_r.status().transacts() )
367             return ( pi_r.status().isByUser() ? S_Del : S_AutoDel );
368
369           // keep
370           return S_KeepInstalled;
371         }
372         else
373         {
374           // have available!
375           if ( pi_r.status().isLocked() )
376             return S_Taboo;
377
378           // have identical installed? (maybe transacting):
379           PoolItem inst;
380           for_( it, _installedItems.begin(), _installedItems.end() )
381           {
382             if ( identical( *it, pi_r ) )
383             {
384               if ( (*it).status().transacts() )
385               {
386                 inst = *it;
387                 break;
388               }
389               if ( !inst )
390                 inst = *it;
391             }
392           }
393
394           // check for inst/update
395           if ( pi_r.status().transacts() )
396           {
397             if ( inst )
398               return( pi_r.status().isByUser() ? S_Update : S_AutoUpdate );
399             else
400               return( pi_r.status().isByUser() ? S_Install : S_AutoInstall );
401           }
402
403           // no inst/update, so maybe delete?
404           if ( ! inst )
405             return  S_NoInst;
406
407           if ( inst.status().transacts() )
408             return( inst.status().isByUser() ? S_Del : S_AutoDel );
409
410           return S_KeepInstalled;
411         }
412       }
413       return Status(-1); // not my PoolItem
414     }
415
416     ///////////////////////////////////////////////////////////////////
417
418     ResStatus::TransactByValue Selectable::Impl::modifiedBy() const
419     {
420       PoolItem cand( candidateObj() );
421       if ( cand && cand.status().transacts() )
422         return cand.status().getTransactByValue();
423
424       if ( installedObj() && installedObj().status().transacts() )
425         return installedObj().status().getTransactByValue();
426
427       if ( cand )
428         return cand.status().getTransactByValue();
429
430       if ( installedObj() )
431         return installedObj().status().getTransactByValue();
432
433       return ResStatus::SOLVER;
434     }
435
436     /////////////////////////////////////////////////////////////////
437   } // namespace ui
438   ///////////////////////////////////////////////////////////////////
439   /////////////////////////////////////////////////////////////////
440 } // namespace zypp
441 ///////////////////////////////////////////////////////////////////