Enhance LookupAttr to allow direct query of attributes within flexarrays.
authorMichael Andres <ma@suse.de>
Tue, 30 Jun 2009 14:38:23 +0000 (16:38 +0200)
committerMichael Andres <ma@suse.de>
Tue, 30 Jun 2009 14:38:23 +0000 (16:38 +0200)
tests/sat/LookupAttr_test.cc
zypp/sat/LookupAttr.cc
zypp/sat/LookupAttr.h

index ab8c545..6ee003d 100644 (file)
@@ -180,7 +180,35 @@ BOOST_AUTO_TEST_CASE(LookupAttr_solvable_attribute_substructure)
     BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceHref ),  res.subFind( "href" ) );
     BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceId ),    res.subFind( "id" ) );
     BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceTitle ), res.subFind( "title" ) );
+
+    // NOTE: Unfortunately the satsolver dataiterator loses constect information when
+    // entering a sub-structure. That's why one can't invoke e.g subBegin on an iterator
+    // that was retieved by subFind.
+    // The test below will fail, once libsatsolver fixes the dataiterator. The expected
+    // result then is, that subBegin brings you to the beginning again.
+    BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceType ).subBegin(),  res.subEnd() );
+    // BOOST_CHECK_EQUAL( res.subFind( sat::SolvAttr::updateReferenceType ).subBegin(),  res.subBegin() );
   }
+
+  // search substructure id without parent-structure won't work:
+  q = sat::LookupAttr( sat::SolvAttr::updateReferenceId );
+  BOOST_CHECK_EQUAL( q.size(), 0 );
+
+  // search id in parent-structure:
+  q = sat::LookupAttr( sat::SolvAttr::updateReferenceId, sat::SolvAttr::updateReference );
+  BOOST_CHECK_EQUAL( q.size(), 303 );
+
+  // search id in any parent-structure:
+  q = sat::LookupAttr( sat::SolvAttr::updateReferenceId, sat::SolvAttr::allAttr );
+  BOOST_CHECK_EQUAL( q.size(), 303 );
+
+  // search any id in parent-structure: (4 ids per updateReference)
+  q = sat::LookupAttr( sat::SolvAttr::allAttr, sat::SolvAttr::updateReference );
+  BOOST_CHECK_EQUAL( q.size(), 1212 );
+
+  // search any id in any parent-structure:
+  q = sat::LookupAttr( sat::SolvAttr::allAttr, sat::SolvAttr::allAttr );
+  BOOST_CHECK_EQUAL( q.size(), 10473 );
 }
 
 BOOST_AUTO_TEST_CASE(LookupAttr_repoattr)
index 750b081..71acfab 100644 (file)
@@ -58,13 +58,13 @@ namespace zypp
         Impl()
         {}
         Impl( SolvAttr attr_r, Location loc_r )
-        : _attr( attr_r ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
+        : _attr( attr_r ), _parent( SolvAttr::noAttr ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
         {}
         Impl( SolvAttr attr_r, Repository repo_r, Location loc_r )
-        : _attr( attr_r ), _repo( repo_r ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
+        : _attr( attr_r ), _parent( SolvAttr::noAttr ), _repo( repo_r ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
         {}
         Impl( SolvAttr attr_r, Solvable solv_r )
-        : _attr( attr_r ), _solv( solv_r )
+        : _attr( attr_r ), _parent( SolvAttr::noAttr ), _solv( solv_r )
         {}
 
       public:
@@ -111,6 +111,13 @@ namespace zypp
           _solv = solv_r;
         }
 
+        SolvAttr parent() const
+        { return _parent; }
+
+        void setParent( SolvAttr attr_r )
+        { _parent = attr_r; }
+
+      public:
         LookupAttr::iterator begin() const
         {
           if ( _attr == SolvAttr::noAttr || sat::Pool::instance().reposEmpty() )
@@ -123,6 +130,8 @@ namespace zypp
             whichRepo = _repo.id();
 
           detail::DIWrap dip( whichRepo, _solv.id(), _attr.id(), _attrMatcher.searchstring(), _attrMatcher.flags().get() );
+          if ( _parent != SolvAttr::noAttr )
+            ::dataiterator_prepend_keyname( dip.get(), _parent.id() );
           return iterator( dip ); // iterator takes over ownership!
         }
 
@@ -131,6 +140,7 @@ namespace zypp
 
       private:
         SolvAttr   _attr;
+        SolvAttr   _parent;
         Repository _repo;
         Solvable   _solv;
         AttrMatcher _attrMatcher;
@@ -155,14 +165,24 @@ namespace zypp
     LookupAttr::LookupAttr( SolvAttr attr_r, Location loc_r )
       : _pimpl( new Impl( attr_r, loc_r ) )
     {}
+    LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Location loc_r )
+      : _pimpl( new Impl( attr_r, loc_r ) )
+    { _pimpl->setParent( parent_r ); }
 
     LookupAttr::LookupAttr( SolvAttr attr_r, Repository repo_r, Location loc_r )
       : _pimpl( new Impl( attr_r, repo_r, loc_r ) )
     {}
+    LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Repository repo_r, Location loc_r )
+      : _pimpl( new Impl( attr_r, repo_r, loc_r ) )
+    { _pimpl->setParent( parent_r ); }
 
     LookupAttr::LookupAttr( SolvAttr attr_r, Solvable solv_r )
       : _pimpl( new Impl( attr_r, solv_r ) )
     {}
+    LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Solvable solv_r )
+      : _pimpl( new Impl( attr_r, solv_r ) )
+    { _pimpl->setParent( parent_r ); }
+
 
     ///////////////////////////////////////////////////////////////////
 
@@ -198,6 +218,12 @@ namespace zypp
     void LookupAttr::setSolvable( Solvable solv_r )
     { _pimpl->setSolvable( solv_r ); }
 
+    SolvAttr LookupAttr::parent() const
+    { return _pimpl->parent(); }
+
+    void LookupAttr::setParent( SolvAttr attr_r )
+    { _pimpl->setParent( attr_r ); }
+
     ///////////////////////////////////////////////////////////////////
 
     LookupAttr::iterator LookupAttr::begin() const
@@ -417,10 +443,25 @@ namespace zypp
       return false;
     }
 
-    bool LookupAttr::iterator::solvAttrSubEntry() const
+    ///////////////////////////////////////////////////////////////////
+    namespace
     {
-      return solvAttrType() == REPOKEY_TYPE_FLEXARRAY;
+      enum SubType { ST_NONE,  // no sub-structure
+                     ST_FLEX,  // flexarray
+                     ST_SUB }; // inside sub-structure
+      SubType subType( const detail::DIWrap & dip )
+      {
+        if ( ! dip )
+          return ST_NONE;
+        if ( dip.get()->key->type == REPOKEY_TYPE_FLEXARRAY )
+          return ST_FLEX;
+        return dip.get()->kv.parent ? ST_SUB : ST_NONE;
+      }
     }
+    ///////////////////////////////////////////////////////////////////
+
+    bool LookupAttr::iterator::solvAttrSubEntry() const
+    { return subType( _dip ) != ST_NONE; }
 
     ///////////////////////////////////////////////////////////////////
     // Iterate sub-structures.
@@ -439,12 +480,18 @@ namespace zypp
 
     LookupAttr::iterator LookupAttr::iterator::subBegin() const
     {
-      if ( ! solvAttrSubEntry() )
-        return subEnd();
-
-      // remember this position
-      ::dataiterator_setpos( _dip.get() );
-
+      switch ( subType( _dip ) )
+      {
+        case ST_NONE:
+          return subEnd();
+          break;
+        case ST_FLEX:
+          ::dataiterator_setpos( _dip.get() );
+          break;
+        case ST_SUB:
+          ::dataiterator_setpos_parent( _dip.get() );
+          break;
+      }
       // setup the new sub iterator with the remembered position
       detail::DIWrap dip( 0, SOLVID_POS, 0, 0, 0 );
       return iterator( dip ); // iterator takes over ownership!
@@ -471,9 +518,29 @@ namespace zypp
       if ( attrname_r.empty() )
         return subBegin();
 
+      SubType subtype( subType( _dip ) );
+      if ( subtype == ST_NONE )
+        return subBegin();
+
       std::string subattr( inSolvAttr().asString() );
-      subattr += ":";
-      subattr += attrname_r;
+      if ( subtype == ST_FLEX )
+      {
+        // append ":attrname"
+        subattr += ":";
+        subattr += attrname_r;
+      }
+      else
+      {
+        // replace "oldname" after ':' with "attrname"
+        std::string::size_type pos( subattr.rfind( ':' ) );
+        if ( pos != std::string::npos )
+        {
+          subattr.erase( pos+1 );
+          subattr += attrname_r;
+        }
+        else
+          subattr = attrname_r; // no ':' so replace all.
+      }
       return subFind( SolvAttr( subattr ) );
     }
 
@@ -577,7 +644,7 @@ namespace zypp
               {
                 str << "  " << it.inSolvAttr() << " = " << it.asString() << endl;
               }
-              str << "}" << endl;
+              str << "}";
              return str.str();
             }
             break;
@@ -695,8 +762,8 @@ namespace zypp
       else if ( obj.inRepo() )
         str << obj.inRepo();
 
-      str << '<' << obj.inSolvAttr()
-          << ">(" <<  IdString(obj.solvAttrType()) << ") = " << obj.asString();
+      str << '<' << obj.inSolvAttr() << (obj.solvAttrSubEntry() ? ">(*" : ">(")
+          <<  IdString(obj.solvAttrType()) << ") = " << obj.asString();
       return str;
     }
 
index 944563b..5af9688 100644 (file)
@@ -60,6 +60,20 @@ namespace zypp
      *
      * Use \ref SolvAttr::allAttr to search all attributes.
      *
+     * To search for attributes located in a sub-structure (flexarray)
+     * you also have to pass the sub-structures attribute as parent.
+     * Passing \ref SolvAttr::allAttr a parent will lookup the attribute
+     * in \c any sub-structure.
+     *
+     * \code
+     *    // Lookup all 'name' attributes:
+     *    sat::LookupAttr q( sat::SolvAttr::name );
+     *    // Lookup all 'name' attributes within a sub-structure 'data':
+     *    sat::LookupAttr q( sat::SolvAttr::name, sat::SolvAttr::data );
+     *    // Lookup all 'name' attributes within any sub-structure:
+     *    sat::LookupAttr q( sat::SolvAttr::name, sat::SolvAttr::allAttr );
+     * \endcode
+     *
      * \code
      *  // look for all attributes of one solvable
      *  void ditest( sat::Solvable slv_r )
@@ -113,12 +127,18 @@ namespace zypp
 
         /** Lookup \ref SolvAttr in \ref Pool (all repositories). */
         explicit LookupAttr( SolvAttr attr_r, Location = SOLV_ATTR );
+        /** \overload SolvAttr within sub-structure \a parent_r. */
+        LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Location = SOLV_ATTR );
 
         /** Lookup \ref SolvAttr in one\ref Repository. */
-        explicit LookupAttr( SolvAttr attr_r, Repository repo_r, Location = SOLV_ATTR );
+        LookupAttr( SolvAttr attr_r, Repository repo_r, Location = SOLV_ATTR );
+        /** \overload SolvAttr within sub-structure \a parent_r. */
+        LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Repository repo_r, Location = SOLV_ATTR );
 
         /** Lookup \ref SolvAttr in one \ref Solvable. */
         LookupAttr( SolvAttr attr_r, Solvable solv_r );
+        /** \overload SolvAttr within sub-structure \a parent_r. */
+        LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Solvable solv_r );
 
       public:
         /** \name Search result. */
@@ -196,6 +216,12 @@ namespace zypp
 
         /** Set search in one \ref Solvable. */
         void setSolvable( Solvable solv_r );
+
+        /** Whether to search within a sub-structure (\ref SolvAttr::noAttr if not) */
+        SolvAttr parent() const;
+
+        /** Set search within a sub-structure (\ref SolvAttr::noAttr for none) */
+        void setParent( SolvAttr attr_r );
         //@}
 
       private:
@@ -376,7 +402,7 @@ namespace zypp
         /** The current \ref SolvAttr. */
         SolvAttr inSolvAttr() const;
 
-        /** Whether this points to the end of a query. */
+        /** Whether this points to the end of a query (Iterator is invalid). */
         bool atEnd() const
         { return !_dip; }
         //@}
@@ -409,6 +435,13 @@ namespace zypp
          *
          * These are usable iff \ref solvAttrSubEntry is \c true.
          *
+         * \note Unfortunately the underlying satsolver dataiterator as returned
+         * by \ref subBegin and \ref subFind loses some context when being created.
+         * Thus it's not possible to invoke \ref subBegin and \ref subFind on an
+         * iterator that was previously returned by one of those methods. The result
+         * will be an \c end iterator. For the same reason it is not possible for an
+         * iterator to leave the sub-structure again.
+         *
          * \code
          * // Lookup all "update:reference" entries for a specific solvable
          * sat::LookupAttr q( sat::SolvAttr::updateReference, p->satSolvable() );