Provide Selectable::setPickStatus instead of pickInstall/Delete
[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
328     bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, const Status state_r, ResStatus::TransactByValue causer_r )
329     {
330       return false;
331     }
332
333     Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
334     {
335       if ( pi_r.satSolvable().ident() == ident() )
336       {
337         if ( pi_r.satSolvable().isSystem() )
338         {
339           // have installed!
340           if ( pi_r.status().isLocked() )
341             return S_Protected;
342
343           // at least one identical available transacing?
344           for_( it, _availableItems.begin(), _availableItems.end() )
345           {
346             if ( identical( *it, pi_r ) )
347             {
348               if ( (*it).status().transacts() )
349                 return( (*it).status().isByUser() ? S_Update : S_AutoUpdate );
350             }
351           }
352
353           // no update, so maybe delete?
354           if ( pi_r.status().transacts() )
355             return ( pi_r.status().isByUser() ? S_Del : S_AutoDel );
356
357           // keep
358           return S_KeepInstalled;
359         }
360         else
361         {
362           // have available!
363           if ( pi_r.status().isLocked() )
364             return S_Taboo;
365
366           // have identical installed? (maybe transacting):
367           PoolItem inst;
368           for_( it, _installedItems.begin(), _installedItems.end() )
369           {
370             if ( identical( *it, pi_r ) )
371             {
372               if ( (*it).status().transacts() )
373               {
374                 inst = *it;
375                 break;
376               }
377               if ( !inst )
378                 inst = *it;
379             }
380           }
381
382           // check for inst/update
383           if ( pi_r.status().transacts() )
384           {
385             if ( inst )
386               return( pi_r.status().isByUser() ? S_Update : S_AutoUpdate );
387             else
388               return( pi_r.status().isByUser() ? S_Install : S_AutoInstall );
389           }
390
391           // no inst/update, so maybe delete?
392           if ( ! inst )
393             return  S_NoInst;
394
395           if ( inst.status().transacts() )
396             return( inst.status().isByUser() ? S_Del : S_AutoDel );
397
398           return S_KeepInstalled;
399         }
400       }
401       return Status(-1); // not my PoolItem
402     }
403
404     ///////////////////////////////////////////////////////////////////
405
406     ResStatus::TransactByValue Selectable::Impl::modifiedBy() const
407     {
408       PoolItem cand( candidateObj() );
409       if ( cand && cand.status().transacts() )
410         return cand.status().getTransactByValue();
411
412       if ( installedObj() && installedObj().status().transacts() )
413         return installedObj().status().getTransactByValue();
414
415       if ( cand )
416         return cand.status().getTransactByValue();
417
418       if ( installedObj() )
419         return installedObj().status().getTransactByValue();
420
421       return ResStatus::SOLVER;
422     }
423
424     /////////////////////////////////////////////////////////////////
425   } // namespace ui
426   ///////////////////////////////////////////////////////////////////
427   /////////////////////////////////////////////////////////////////
428 } // namespace zypp
429 ///////////////////////////////////////////////////////////////////