Imported Upstream version 17.21.0
[platform/upstream/libzypp.git] / zypp / ui / SelectableImpl.h
index 3ea2012..01de374 100644 (file)
 #ifndef ZYPP_UI_SELECTABLEIMPL_H
 #define ZYPP_UI_SELECTABLEIMPL_H
 
-#include <iosfwd>
+#include <iostream>
+#include "zypp/base/LogTools.h"
 
 #include "zypp/base/PtrTypes.h"
 
+#include "zypp/ResPool.h"
+#include "zypp/Resolver.h"
 #include "zypp/ui/Selectable.h"
 #include "zypp/ui/SelectableTraits.h"
 
+using std::endl;
+
 ///////////////////////////////////////////////////////////////////
 namespace zypp
 { /////////////////////////////////////////////////////////////////
@@ -36,8 +41,6 @@ namespace zypp
     */
     struct Selectable::Impl
     {
-      friend std::ostream & operator<<( std::ostream & str, const Selectable::Impl & obj );
-
     public:
 
       typedef SelectableTraits::AvailableItemSet         AvailableItemSet;
@@ -50,28 +53,16 @@ namespace zypp
       typedef SelectableTraits::installed_const_iterator installed_const_iterator;
       typedef SelectableTraits::installed_size_type      installed_size_type;
 
+      typedef SelectableTraits::PickList               PickList;
 
     public:
-      Impl( const ResObject::Kind & kind_r,
-            const std::string & name_r,
-            installed_const_iterator installedBegin_r,
-            installed_const_iterator installedEnd_r ,
-            available_const_iterator availableBegin_r,
-            available_const_iterator availableEnd_r )
-      : _kind( kind_r )
-      , _name( name_r )
-      , _installedItems( installedBegin_r, installedEnd_r )
-      , _availableItems( availableBegin_r, availableEnd_r )
-      {
-        setCandidate( NULL );
-      }
-
-      template <class _Iterator>
-      Impl( const ResObject::Kind & kind_r,
+      template <class TIterator>
+      Impl( const ResKind & kind_r,
             const std::string & name_r,
-            _Iterator begin_r,
-            _Iterator end_r )
-      : _kind( kind_r )
+            TIterator begin_r,
+            TIterator end_r )
+      : _ident( sat::Solvable::SplitIdent( kind_r, name_r ).ident() )
+      , _kind( kind_r )
       , _name( name_r )
       {
         for_( it, begin_r, end_r )
@@ -81,12 +72,15 @@ namespace zypp
           else
             _availableItems.insert( *it );
         }
-        setCandidate( NULL );
       }
 
     public:
       /**  */
-      ResObject::Kind kind() const
+      IdString ident() const
+      { return _ident; }
+
+      /**  */
+      ResKind kind() const
       { return _kind; }
 
       /**  */
@@ -97,14 +91,15 @@ namespace zypp
       Status status() const;
 
       /**  */
-      bool setStatus( const Status state_r );
+      bool setStatus( Status state_r, ResStatus::TransactByValue causer_r );
 
-      /** Installed object. */
+      /** Installed object (transacting ot highest version). */
       PoolItem installedObj() const
       {
-          if (!installedEmpty())
-              return *_installedItems.begin();
+        if ( installedEmpty() )
           return PoolItem();
+        PoolItem ret( transactingInstalled() );
+        return ret ? ret : *_installedItems.begin();
       }
 
       /** Best among available objects.
@@ -114,33 +109,141 @@ namespace zypp
       PoolItem candidateObj() const
       {
         PoolItem ret( transactingCandidate() );
-        if ( ret )
-          return ret;
-
-        if ( _candidate )
-          return _candidate;
-        return defaultCandidate();
+        if ( ! ret )
+         ret = _candidate ? _candidate : defaultCandidate();
+       return ret;
       }
 
       /** Set a userCandidate (out of available objects).
        * \return The new userCandidate or NULL if choice was invalid
        * (not among availableObjs).
       */
-      PoolItem setCandidate( ResObject::constPtr byUser_r );
+      PoolItem setCandidate( const PoolItem & newCandidate_r, ResStatus::TransactByValue causer_r );
+
+      /** The best candidate provided by a specific \ref Repository, if there is one.
+       * In contrary to \ref candidateObj, this may return no item even if
+       * there are available objects. This simply means the \ref Repository
+       * does not provide this object.
+       */
+      PoolItem candidateObjFrom( Repository repo_r ) const
+      {
+        for ( const PoolItem & pi : available() )
+        {
+         if ( pi.isRetracted() )
+           continue;
+          if ( pi.repository() == repo_r )
+            return pi;
+        }
+        return PoolItem();
+      }
 
-      /** Best among all objects. */
-      PoolItem theObj() const
+      /** The best candidate for update, if there is one.
+       * In contrary to \ref candidateObj, this may return no item even if
+       * there are available objects. This simply means the best object is
+       * already installed, and all available objects violate at least one
+       * update policy.
+       */
+      PoolItem updateCandidateObj() const
       {
-        PoolItem ret( candidateObj() );
-        if (ret)
-            return ret;
+       PoolItem defaultCand( defaultCandidate() );
+
+       // multiversionInstall: This returns the candidate for the last
+       // instance installed. Actually we'd need a list here.
+
+       if ( ! defaultCand || defaultCand.isRetracted() )
+         return PoolItem();
+
+        if ( installedEmpty() )
+          return defaultCand;
+        // Here: installed and defaultCand are non NULL and it's not a
+        //       multiversion install.
+
+        PoolItem installed( installedObj() );
+        // check vendor change
+        if ( ! ( ResPool::instance().resolver().allowVendorChange()
+                 || VendorAttr::instance().equivalent( defaultCand->vendor(), installed->vendor() ) ) )
+          return PoolItem();
+
+        // check arch change (arch noarch changes are allowed)
+        if ( defaultCand->arch() != installed->arch()
+           && ! ( defaultCand->arch() == Arch_noarch || installed->arch() == Arch_noarch ) )
+          return PoolItem();
+
+        // check greater edition
+        if ( defaultCand->edition() <= installed->edition() )
+          return PoolItem();
+
+        return defaultCand;
+      }
+
+      /** \copydoc Selectable::highestAvailableVersionObj()const */
+      PoolItem highestAvailableVersionObj() const
+      {
+        PoolItem ret;
+       bool retractedOk = false;
+        for ( const PoolItem & pi : available() )
+        {
+         if ( !retractedOk && pi.isRetracted() )
+         {
+           if ( ret )
+             break;    // prefer a not retracted candidate
+           retractedOk = true;
+         }
+          if ( !ret || pi.edition() > ret.edition() )
+            ret = pi;
+        }
+        return ret;
+      }
+
+      /** \copydoc Selectable::identIsAutoInstalled()const */
+      bool identIsAutoInstalled() const
+      { return sat::Solvable::identIsAutoInstalled( ident() ); }
 
-        if ( ! _installedItems.empty() )
-            return  (*_installedItems.begin());
+      /** \copydoc Selectable::identicalAvailable( const PoolItem & )const */
+      bool identicalAvailable( const PoolItem & rhs ) const
+      { return bool(identicalAvailableObj( rhs )); }
 
+      /** \copydoc Selectable::identicalInstalled( const PoolItem & )const */
+      bool identicalInstalled( const PoolItem & rhs ) const
+      { return bool(identicalInstalledObj( rhs )); }
+
+      /** \copydoc Selectable::identicalAvailableObj( const PoolItem & rhs ) const */
+      PoolItem identicalAvailableObj( const PoolItem & rhs ) const
+      {
+        if ( !availableEmpty() && rhs )
+        {
+          for_( it, _availableItems.begin(), _availableItems.end() )
+          {
+            if ( identical( *it, rhs ) )
+              return *it;
+          }
+        }
         return PoolItem();
       }
 
+      /** \copydoc Selectable::identicalInstalledObj( const PoolItem & rhs ) const */
+      PoolItem identicalInstalledObj( const PoolItem & rhs ) const
+      {
+        if ( !installedEmpty() && rhs )
+        {
+          for_( it, _installedItems.begin(), _installedItems.end() )
+          {
+            if ( identical( *it, rhs ) )
+              return *it;
+          }
+        }
+        return PoolItem();
+      }
+
+      /** Best among all objects. */
+      PoolItem theObj() const
+      {
+        PoolItem ret( candidateObj() );
+        if ( ret )
+          return ret;
+        return installedObj();
+      }
+
       ////////////////////////////////////////////////////////////////////////
 
       bool availableEmpty() const
@@ -149,13 +252,15 @@ namespace zypp
       available_size_type availableSize() const
       { return _availableItems.size(); }
 
-      available_const_iterator availableBegin() const
+      available_iterator availableBegin() const
       { return _availableItems.begin(); }
 
-
-      available_const_iterator availableEnd() const
+      available_iterator availableEnd() const
       { return _availableItems.end(); }
 
+      inline Iterable<available_iterator>  available() const
+      { return makeIterable( availableBegin(), availableEnd() ); }
+
       ////////////////////////////////////////////////////////////////////////
 
       bool installedEmpty() const
@@ -170,11 +275,111 @@ namespace zypp
       installed_iterator installedEnd() const
       { return _installedItems.end(); }
 
+      inline Iterable<installed_iterator>  installed() const
+      { return makeIterable( installedBegin(), installedEnd() ); }
+
+      ////////////////////////////////////////////////////////////////////////
+
+      const PickList & picklist() const
+      {
+        if ( ! _picklistPtr )
+        {
+          _picklistPtr.reset( new PickList );
+          // installed without identical avaialble first:
+          for ( const PoolItem & pi : installed() )
+          {
+            if ( ! identicalAvailable( pi ) )
+              _picklistPtr->push_back( pi );
+          }
+          _picklistPtr->insert( _picklistPtr->end(), availableBegin(), availableEnd() );
+        }
+        return *_picklistPtr;
+      }
+
+      bool picklistEmpty() const
+      { return picklist().empty(); }
+
+      picklist_size_type picklistSize() const
+      { return picklist().size(); }
+
+      picklist_iterator picklistBegin() const
+      { return picklist().begin(); }
+
+      picklist_iterator picklistEnd() const
+      { return picklist().end(); }
+
       ////////////////////////////////////////////////////////////////////////
 
+      bool hasRetracted() const
+      { return !_availableItems.empty() && _availableItems.rbegin()->isRetracted(); }
+
+      bool hasRetractedInstalled() const
+      {
+       bool ret = false;
+       if ( hasRetracted() )
+       {
+         for ( const PoolItem & ipi : installed() )
+         {
+           PoolItem pi { identicalAvailableObj( ipi ) };
+           if ( pi && pi.isRetracted() )
+           {
+             ret = true;
+             break;
+           }
+         }
+       }
+       return ret;
+// later if pool index is available:
+//     for ( const PoolItem & pi : installed() )
+//       if ( pi.isRetracted() )
+//         return true;
+//     return false;
+      }
+
       bool isUnmaintained() const
       { return availableEmpty(); }
 
+      bool multiversionInstall() const
+      {
+       for ( const PoolItem & pi : picklist() )
+       {
+         if ( pi.multiversionInstall() )
+           return true;
+       }
+       return false;
+      }
+
+      bool pickInstall( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r );
+
+      bool pickDelete( const PoolItem & pi_r, ResStatus::TransactByValue causer_r, bool yesno_r );
+
+      Status pickStatus( const PoolItem & pi_r ) const;
+
+      bool setPickStatus( const PoolItem & pi_r, Status state_r, ResStatus::TransactByValue causer_r );
+
+      ////////////////////////////////////////////////////////////////////////
+
+      bool isUndetermined() const
+      {
+        PoolItem cand( candidateObj() );
+        return ! cand || cand.isUndetermined();
+      }
+      bool isRelevant() const
+      {
+        PoolItem cand( candidateObj() );
+        return cand && cand.isRelevant();
+      }
+      bool isSatisfied() const
+       {
+        PoolItem cand( candidateObj() );
+        return cand && cand.isSatisfied();
+      }
+      bool isBroken() const
+      {
+        PoolItem cand( candidateObj() );
+        return cand && cand.isBroken();
+      }
+
       /** Return who caused the modification. */
       ResStatus::TransactByValue modifiedBy() const;
 
@@ -186,59 +391,109 @@ namespace zypp
       void setLicenceConfirmed( bool val_r )
       { if ( candidateObj() ) candidateObj().status().setLicenceConfirmed( val_r ); }
 
+      /** \copydoc Selectable::hasLocks()const */
+      bool hasLocks() const
+      {
+       for ( const PoolItem & pi : available() )
+       {
+         if ( pi.status().isLocked() )
+           return true;
+       }
+        for ( const PoolItem & pi : installed() )
+       {
+         if ( pi.status().isLocked() )
+           return true;
+       }
+       return false;
+      }
+
     private:
+      PoolItem transactingInstalled() const
+      {
+        for ( const PoolItem & pi : installed() )
+          {
+            if ( pi.status().transacts() )
+              return pi;
+          }
+        return PoolItem();
+      }
+
       PoolItem transactingCandidate() const
       {
-        for ( available_const_iterator it = availableBegin();
-              it != availableEnd(); ++it )
+        for ( const PoolItem & pi : available() )
           {
-            if ( (*it).status().transacts() )
-              return (*it);
+            if ( pi.status().transacts() )
+              return pi;
           }
         return PoolItem();
       }
 
       PoolItem defaultCandidate() const
       {
-        if ( !installedEmpty() )
+        if ( ! installedEmpty() )
+        {
+          // prefer the installed objects arch and vendor
+          bool solver_allowVendorChange( ResPool::instance().resolver().allowVendorChange() );
+          for ( const PoolItem & ipi : installed() )
           {
-            // prefer the installed objects arch.
-            for ( installed_const_iterator iit = installedBegin();
-                  iit != installedEnd(); ++iit )
+            PoolItem sameArch; // in case there's no same vendor at least stay with same arch.
+            for (  const PoolItem & api : available() )
             {
-                for ( available_const_iterator it = availableBegin();
-                      it != availableEnd(); ++it )
+              // 'same arch' includes allowed changes to/from noarch.
+              if ( ipi.arch() == api.arch() || ipi.arch() == Arch_noarch || api.arch() == Arch_noarch )
+              {
+                if ( ! solver_allowVendorChange )
                 {
-                    if ( (*iit)->arch() == (*it)->arch() )
-                    {
-                        return (*it);
-                    }
+                  if ( VendorAttr::instance().equivalent( ipi, api ) )
+                    return api;
+                  else if ( ! sameArch ) // remember best same arch in case no same vendor found
+                     sameArch = api;
                 }
+                else // same arch is sufficient
+                  return api;
+              }
             }
+            if ( sameArch )
+              return sameArch;
           }
+        }
         if ( _availableItems.empty() )
-            return PoolItem();
+          return PoolItem();
 
         return *_availableItems.begin();
       }
 
       bool allCandidatesLocked() const
       {
-        for ( available_const_iterator it = availableBegin();
-              it != availableEnd(); ++it )
+        for ( const PoolItem & pi : available() )
           {
-            if ( ! (*it).status().isLocked() )
+            if ( ! pi.status().isLocked() )
               return false;
           }
         return( ! _availableItems.empty() );
       }
 
+      bool allInstalledLocked() const
+      {
+        for ( const PoolItem & pi : installed() )
+          {
+            if ( ! pi.status().isLocked() )
+              return false;
+          }
+        return( ! _installedItems.empty() );
+      }
+
+
     private:
-      ResObject::Kind  _kind;
-      std::string      _name;
-      InstalledItemSet _installedItems;
-      AvailableItemSet _availableItems;
-      PoolItem         _candidate;
+      const IdString         _ident;
+      const ResKind          _kind;
+      const std::string      _name;
+      InstalledItemSet       _installedItems;
+      AvailableItemSet       _availableItems;
+      //! The object selected by setCandidateObj() method.
+      PoolItem               _candidate;
+      //! lazy initialized picklist
+      mutable scoped_ptr<PickList> _picklistPtr;
     };
     ///////////////////////////////////////////////////////////////////
 
@@ -246,11 +501,87 @@ namespace zypp
     inline std::ostream & operator<<( std::ostream & str, const Selectable::Impl & obj )
     {
       return str << '[' << obj.kind() << ']' << obj.name() << ": " << obj.status()
-                 << " (I " << obj._installedItems.size() << ")"
-                 << " (A " << obj._availableItems.size() << ")"
+                 << " (I " << obj.installedSize() << ")"
+                 << " (A " << obj.availableSize() << ")"
                  << obj.candidateObj();
     }
 
+    /** \relates Selectable::Impl Stream output */
+    inline std::ostream & dumpOn( std::ostream & str, const Selectable::Impl & obj )
+    {
+      str << '[' << obj.kind() << ']' << obj.name() << ": " << obj.status()
+          << ( obj.multiversionInstall() ? " (multiversion)" : "") << endl;
+
+      if ( obj.installedEmpty() )
+        str << "   (I 0) {}" << endl << "   ";
+      else
+      {
+        PoolItem icand( obj.installedObj() );
+        str << "   (I " << obj.installedSize() << ") {" << endl;
+        for ( const PoolItem & pi : obj.installed() )
+        {
+          char t = ' ';
+          if ( pi == icand )
+          {
+            t = 'i';
+          }
+          str << " " << t << " " << pi << endl;
+        }
+        str << "}  ";
+      }
+
+      if ( obj.availableEmpty() )
+      {
+        str << "(A 0) {}" << endl << "   ";
+      }
+      else
+      {
+        PoolItem cand( obj.candidateObj() );
+        PoolItem up( obj.updateCandidateObj() );
+        str << "(A " << obj.availableSize() << ") {" << endl;
+        for ( const PoolItem & pi : obj.available() )
+        {
+          char t = ' ';
+          if ( pi == cand )
+          {
+            t = pi == up ? 'C' : 'c';
+          }
+          else if ( pi == up )
+          {
+            t = 'u';
+          }
+          str << " " << t << " " << pi << endl;
+        }
+        str << "}  ";
+      }
+
+      if ( obj.picklistEmpty() )
+      {
+        str << "(P 0) {}";
+      }
+      else
+      {
+        PoolItem cand( obj.candidateObj() );
+        PoolItem up( obj.updateCandidateObj() );
+        str << "(P " << obj.picklistSize() << ") {" << endl;
+        for ( const PoolItem & pi : obj.picklist() )
+        {
+          char t = ' ';
+          if ( pi == cand )
+          {
+            t = pi == up ? 'C' : 'c';
+          }
+          else if ( pi == up )
+          {
+            t = 'u';
+          }
+          str << " " << t << " " << pi << "\t" << obj.pickStatus( pi ) << endl;
+        }
+        str << "}  ";
+      }
+
+      return str;
+    }
     /////////////////////////////////////////////////////////////////
   } // namespace ui
   ///////////////////////////////////////////////////////////////////