Imported Upstream version 16.3.2
[platform/upstream/libzypp.git] / zypp / ui / SelectableImpl.cc
index 5ecbfe5..5d2a9d6 100644 (file)
@@ -23,11 +23,129 @@ namespace zypp
   namespace ui
   { /////////////////////////////////////////////////////////////////
 
+    /** Simple ResStatus backup stack.
+     * \ref restore simply rewinds all remembered status.
+    */
+    class StatusBackup
+    {
+      public:
+        typedef ResStatus::TransactByValue Causer;
+
+      public:
+        /** Backup status. */
+        ResStatus & backup( ResStatus & status_r )
+        {
+          _backup.push_back( status_r );
+          return status_r;
+        }
+        /** \overload */
+        ResStatus & backup( const PoolItem & pi_r )
+        { return backup( pi_r.status() ); }
+
+        /** Backup status. */
+        ResStatus & operator()( ResStatus & status_r )
+        { return backup( status_r ); }
+        /** \overload */
+        ResStatus & operator()( const PoolItem & pi_r )
+        { return backup( pi_r.status() ); }
+
+        /** Restore all status. */
+        bool restore()
+        {
+          for_( rit, _backup.rbegin(), _backup.rend() )
+            rit->replay();
+          return false; // restore is done on error - return restore();
+        }
+
+      public:
+        /** lowlevel \c ResStatus::setTransact */
+        bool setTransact( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
+        { return backup( pi_r ).setTransact( yesno_r, causer_r ); }
+
+        /** lowlevel \c ResStatus::setLock */
+        bool setLock( const PoolItem & pi_r, bool yesno_r, Causer causer_r )
+        { return backup( pi_r ).setLock( yesno_r, causer_r ); }
+
+        /** lowlevel \c ResStatus::setTransact(true). */
+        bool setTransactTrue( const PoolItem & pi_r, Causer causer_r )
+        { return setTransact( pi_r, true, causer_r ); }
+
+        /** lowlevel \c ResStatus::setTransact(false). */
+        bool setTransactFalse( const PoolItem & pi_r, Causer causer_r )
+        { return setTransact( pi_r, false, causer_r ); }
+
+      public:
+        /** highevel set transact (force unlock). */
+        bool transact( const PoolItem & pi_r, Causer causer_r )
+        {
+          ResStatus & status( backup( pi_r ) );
+          if ( ! status.setLock( false, causer_r ) ) return false;
+          if ( ! status.setTransact( true, causer_r ) ) return false;
+          return true;
+        }
+
+        /** highlevel set locked. */
+        bool lock( const PoolItem & pi_r, Causer causer_r )
+        {
+          ResStatus & status( backup( pi_r ) );
+          if ( ! status.setTransact( false, causer_r ) ) return false;
+          if ( ! status.setLock( true, causer_r ) ) return false;
+          return true;
+        }
+
+        /** highlevel unlock (also unsets transact). */
+        bool unlock( const PoolItem & pi_r, Causer causer_r )
+        {
+          ResStatus & status( backup( pi_r ) );
+          if ( ! status.setTransact( false, causer_r ) ) return false;
+          if ( ! status.setLock( false, causer_r ) ) return false;
+          return true;
+        }
+
+        /** highlevel remove transact from non-multiversion packages. */
+        bool unsetNonMultiTransact(  const PoolItem & pi_r, Causer causer_r )
+       {
+         ResStatus & status( backup( pi_r ) );
+         if ( status.transacts() && ! pi_r.multiversionInstall() )
+         {
+           if ( ! status.setTransact( false, causer_r ) ) return false;
+         }
+         return true;
+       }
+
+        /** highlevel remove transact from multiversion packages. */
+        bool unsetMultiTransact(  const PoolItem & pi_r, Causer causer_r )
+       {
+         ResStatus & status( backup( pi_r ) );
+         if ( status.transacts() && pi_r.multiversionInstall() )
+         {
+           if ( ! status.setTransact( false, causer_r ) ) return false;
+         }
+         return true;
+       }
+
+        /** Highlevel action. */
+        typedef bool (StatusBackup::*Action)( const PoolItem &, Causer );
+
+        /** Highlevel action on range of items. */
+        template <class TIter>
+        bool forEach( TIter begin_r, TIter end_r, Action action_r, Causer causer_r )
+        {
+          for_( it, begin_r, end_r )
+            if ( ! (this->*action_r)( *it, causer_r ) )
+              return false;
+          return true;
+        }
+
+      private:
+        std::vector<resstatus::StatusBackup> _backup;
+    };
+
     ///////////////////////////////////////////////////////////////////
     //
     // CLASS NAME : StatusHelper
     //
-    /**
+    /** \todo Unify status and pickStatus.
     */
     struct StatusHelper
     {
@@ -44,10 +162,10 @@ namespace zypp
       // Queries
       //
       bool hasInstalled() const
-      { return inst; }
+      { return bool(inst); }
 
       bool hasCandidate() const
-      { return cand; }
+      { return bool(cand); }
 
       bool hasInstalledOnly() const
       { return inst && !cand; }
@@ -65,16 +183,19 @@ namespace zypp
         if ( cand )
         {
           if ( inst ) {
-            ResStatus & inststatus( backup( inst.status() ) );
-            if ( ! inststatus.setTransact( false, causer ) ) return restore();
-            if ( ! inststatus.setLock    ( false, causer ) ) return restore();
-            if ( ! cand->installOnly() )
+            for_( it, _impl.installedBegin(), _impl.installedEnd() )
             {
+              ResStatus & inststatus( backup( it->status() ) );
+              if ( ! inststatus.setTransact( false, causer ) ) return restore();
+              if ( ! inststatus.setLock    ( false, causer ) ) return restore();
+              if ( ! cand->multiversionInstall() )
+              {
               // This is what the solver most probabely will do.
               // If we are wrong the solver will correct it. But
               // this way we will get a better disk usage result,
               // even if no autosolving is on.
-              inststatus.setTransact( true, ResStatus::SOLVER );
+                inststatus.setTransact( true, ResStatus::SOLVER );
+              }
             }
           }
           if ( ! unlockCandidates() ) return restore();
@@ -90,9 +211,12 @@ namespace zypp
         if ( inst )
         {
           if ( ! resetTransactingCandidates() ) return restore();
-          ResStatus & inststatus( backup( inst.status() ) );
-          if ( ! inststatus.setLock( false, causer ) ) return restore();
-          if ( ! inststatus.setTransact( true, causer ) ) return restore();
+          for_( it, _impl.installedBegin(), _impl.installedEnd() )
+          {
+            ResStatus & inststatus( backup( it->status() ) );
+            if ( ! inststatus.setLock( false, causer ) ) return restore();
+            if ( ! inststatus.setTransact( true, causer ) ) return restore();
+          }
           return true;
         }
         return false;
@@ -102,9 +226,12 @@ namespace zypp
       {
         if ( inst )
         {
-          ResStatus & inststatus( backup( inst.status() ) );
-          if ( ! inststatus.setTransact( false, causer ) ) return restore();
-          if ( ! inststatus.setLock( false, causer ) ) return restore();
+          for_( it, _impl.installedBegin(), _impl.installedEnd() )
+          {
+            ResStatus & inststatus( backup( it->status() ) );
+            if ( ! inststatus.setTransact( false, causer ) ) return restore();
+            if ( ! inststatus.setLock( false, causer ) ) return restore();
+          }
         }
         if ( ! unlockCandidates() ) return restore();
         return true;
@@ -117,8 +244,12 @@ namespace zypp
 
         if ( inst ) {
           resetTransactingCandidates();
-          inst.status().setTransact( false, causer );
-          return inst.status().setLock( true, causer );
+          for_( it, _impl.installedBegin(), _impl.installedEnd() )
+          {
+            it->status().setTransact( false, causer );
+            it->status().setLock( true, causer );
+          }
+          return true;
         } else
           return false;
       }
@@ -177,29 +308,8 @@ namespace zypp
       ResStatus::TransactByValue causer;
 
     private:
-      // No backup replay needed if causer is user,
-      // because actions should always succeed.
-
-      ResStatus & backup( ResStatus & status_r )
-      {
-        if ( causer != ResStatus::USER )
-          _backup.push_back( status_r );
-        return status_r;
-      }
-
-      bool restore()
-      {
-        if ( causer != ResStatus::USER )
-        {
-          for_( rit, _backup.rbegin(), _backup.rend() )
-          {
-            rit->replay();
-          }
-        }
-        return false; // restore is done on error - return restore();
-      }
-
-      std::vector<resstatus::StatusBackup> _backup;
+      bool restore() { return backup.restore(); }
+      StatusBackup backup;
     };
     ///////////////////////////////////////////////////////////////////
 
@@ -225,7 +335,7 @@ namespace zypp
           return( installedObj().status().isByUser() ? S_Del : S_AutoDel );
         }
 
-      if ( installedObj() && installedObj().status().isLocked() )
+      if ( installedObj() && allInstalledLocked() )
          return S_Protected;
 
       if ( !installedObj() && allCandidatesLocked() )
@@ -242,7 +352,7 @@ namespace zypp
       return S_NoInst;
     }
 
-    bool Selectable::Impl::setStatus( const Status state_r, ResStatus::TransactByValue causer_r )
+    bool Selectable::Impl::setStatus( Status state_r, ResStatus::TransactByValue causer_r )
     {
       StatusHelper self( *this, causer_r );
 
@@ -325,80 +435,206 @@ namespace zypp
 
     ///////////////////////////////////////////////////////////////////
 
-    bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, const Status state_r, ResStatus::TransactByValue causer_r )
+    bool Selectable::Impl::pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
     {
-      return false;
+      if ( identicalInstalled( pi_r ) )
+        return setPickStatus( pi_r, ( yesno_r ? S_Update : S_KeepInstalled ), causer_r );
+      return setPickStatus( pi_r, ( yesno_r ? S_Install : S_NoInst ), causer_r );
     }
 
-    Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
+    bool Selectable::Impl::pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r )
     {
-      if ( pi_r.satSolvable().ident() == ident() )
+      return setPickStatus( pi_r, ( yesno_r ? S_Del : S_KeepInstalled ), causer_r );
+    }
+
+    bool Selectable::Impl::setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r )
+    {
+      if ( pi_r.ident() != ident() )
+        return false;  // not my PoolItem
+
+      StatusBackup backup;
+      std::vector<PoolItem> i;
+      std::vector<PoolItem> a;
+
+      for_( it, installedBegin(), installedEnd() )
+        if ( identical( *it, pi_r ) )
+          i.push_back( *it );
+      for_( it, availableBegin(), availableEnd() )
+        if ( identical( *it, pi_r ) )
+          a.push_back( *it );
+
+      switch ( state_r )
       {
-        if ( pi_r.satSolvable().isSystem() )
-        {
-          // have installed!
-          if ( pi_r.status().isLocked() )
-            return S_Protected;
+        case S_Protected:
+          if ( causer_r == ResStatus::USER && ! i.empty() )
+          {
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
 
-          // at least one identical available transacing?
-          for_( it, _availableItems.begin(), _availableItems.end() )
+        case S_Taboo:
+          if ( causer_r == ResStatus::USER && ! a.empty() )
           {
-            if ( identical( *it, pi_r ) )
-            {
-              if ( (*it).status().transacts() )
-                return( (*it).status().isByUser() ? S_Update : S_AutoUpdate );
-            }
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::lock, causer_r ) ) return backup.restore();
+            return true;
           }
+          break;
+
+        case S_AutoDel:
+        case S_AutoInstall:
+        case S_AutoUpdate:
+          // Auto level is SOLVER level. UI may query, but not
+          // set at this level.
+          break;
 
-          // no update, so maybe delete?
-          if ( pi_r.status().transacts() )
-            return ( pi_r.status().isByUser() ? S_Del : S_AutoDel );
+        case S_Del:
+          if ( ! i.empty() )
+          {
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::transact, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::setTransactFalse, causer_r ) ) return backup.restore();
+            return true;
+          }
+          break;
 
-          // keep
-          return S_KeepInstalled;
-        }
-        else
-        {
-          // have available!
-          if ( pi_r.status().isLocked() )
-            return S_Taboo;
+       case S_Install:
+         if ( i.empty() && ! a.empty() )
+         {
+           const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r );
+           if ( cand.multiversionInstall() )
+           {
+             if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetNonMultiTransact, causer_r ) ) return backup.restore();
+             // maybe unlock candidate only?
+             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+             if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
+             return true;
+           }
+           else
+           {
+             // For non-multiversion use ordinary setStatus
+             // NOTE that S_Update/S_Install here depends on !installedEmpty()
+             // and not on picklists identicalInstalled.
+             if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetMultiTransact, causer_r ) ) return backup.restore();
+             if ( ! setCandidate( cand, causer_r ) )  return backup.restore();
+             if ( ! setStatus( installedEmpty() ? S_Install : S_Update, causer_r ) )  return backup.restore();
+             return true;
+           }
+         }
+         break;
+
+       case S_Update:
+         if ( ! i.empty() && ! a.empty() )
+         {
+           const PoolItem & cand( pi_r.status().isInstalled() ? *a.begin() : pi_r );
+           if ( cand.multiversionInstall() )
+           {
+             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+             if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::setTransactTrue, ResStatus::SOLVER ) ) return backup.restore();
+             if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetNonMultiTransact, causer_r ) ) return backup.restore();
+             // maybe unlock candidate only?
+             if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+             if ( ! cand.status().setTransact( true, causer_r ) ) return backup.restore();
+             return true;
+           }
+           else
+           {
+             // For non-multiversion use ordinary setStatus
+             // NOTE that S_Update/S_Install here depends on !installedEmpty()
+             // and not on picklists identicalInstalled.
+             if ( ! backup.forEach( availableBegin(), availableEnd(), &StatusBackup::unsetMultiTransact, causer_r ) ) return backup.restore();
+             if ( ! setCandidate( cand, causer_r ) )  return backup.restore();
+             if ( ! setStatus( installedEmpty() ? S_Install : S_Update, causer_r ) )  return backup.restore();
+             return true;
+           }
+         }
+         break;
 
-          // have identical installed? (maybe transacting):
-          PoolItem inst;
-          for_( it, _installedItems.begin(), _installedItems.end() )
+        case S_KeepInstalled:
+          if ( ! i.empty()  )
           {
-            if ( identical( *it, pi_r ) )
-            {
-              if ( (*it).status().transacts() )
-              {
-                inst = *it;
-                break;
-              }
-              if ( !inst )
-                inst = *it;
-            }
+            if ( ! backup.forEach( i.begin(), i.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            return true;
           }
+          break;
 
-          // check for inst/update
-          if ( pi_r.status().transacts() )
+        case S_NoInst:
+          if ( i.empty()  )
           {
-            if ( inst )
-              return( pi_r.status().isByUser() ? S_Update : S_AutoUpdate );
-            else
-              return( pi_r.status().isByUser() ? S_Install : S_AutoInstall );
+            if ( ! backup.forEach( a.begin(), a.end(), &StatusBackup::unlock, causer_r ) ) return backup.restore();
+            return true;
           }
+          break;
+      }
+      return false;
+    }
+
+    Status Selectable::Impl::pickStatus( const PoolItem & pi_r ) const
+    {
+      if ( pi_r.satSolvable().ident() != ident() )
+        return Status(-1); // not my PoolItem
 
-          // no inst/update, so maybe delete?
-          if ( ! inst )
-            return  S_NoInst;
+      std::vector<PoolItem> i;
+      std::vector<PoolItem> a;
+      PoolItem ti;
+      PoolItem ta;
 
-          if ( inst.status().transacts() )
-            return( inst.status().isByUser() ? S_Del : S_AutoDel );
+      for_( it, installedBegin(), installedEnd() )
+        if ( identical( *it, pi_r ) )
+        {
+          i.push_back( *it );
+          if ( ! ti && it->status().transacts() )
+            ti = *it;
+        }
 
-          return S_KeepInstalled;
+      for_( it, availableBegin(), availableEnd() )
+        if ( identical( *it, pi_r ) )
+        {
+          a.push_back( *it );
+          if ( ! ta && it->status().transacts() )
+            ta = *it;
         }
+
+      if ( ta )
+      {
+        if ( ta.status().isByUser() )
+          return( i.empty() ? S_Install : S_Update );
+        else
+          return( i.empty() ? S_AutoInstall : S_AutoUpdate );
+      }
+
+      if ( ti )
+      {
+        return( ti.status().isByUser() ? S_Del : S_AutoDel );
+      }
+
+      for_( it, i.begin(), i.end() )
+        if ( it->status().isLocked() )
+          return S_Protected;
+
+      if ( i.empty() )
+      {
+        bool allALocked = true;
+        for_( it, a.begin(), a.end() )
+          if ( ! it->status().isLocked() )
+          {
+            allALocked = false;
+            break;
+          }
+        if ( allALocked )
+          return S_Taboo;
       }
-      return Status(-1); // not my PoolItem
+
+      // KEEP state:
+      if ( ! i.empty() )
+        return S_KeepInstalled;
+      // Report pseudo installed items as installed, if they are satisfied.
+      if ( traits::isPseudoInstalled( kind() )
+           && ( ta ? ta : *a.begin() ).status().isSatisfied() ) // no installed, so we must have candidate
+        return S_KeepInstalled;
+
+      return S_NoInst;
     }
 
     ///////////////////////////////////////////////////////////////////