Extend LookupAttr to support matching specific string patterns.
authorMichael Andres <ma@suse.de>
Wed, 22 Apr 2009 15:44:25 +0000 (17:44 +0200)
committerMichael Andres <ma@suse.de>
Mon, 27 Apr 2009 11:49:29 +0000 (13:49 +0200)
- May define an AttrMatcher to be used in LookupAttr.
- Fix iterator copy and resource handling when compiled regex are involved.

tests/sat/LookupAttr_test.cc
zypp/sat/LookupAttr.cc
zypp/sat/LookupAttr.h

index 46a45a2..ab8c545 100644 (file)
@@ -1,5 +1,6 @@
 #include "TestSetup.h"
 #include <zypp/sat/LookupAttr.h>
+#include <zypp/sat/AttrMatcher.h>
 #include <zypp/ResObjects.h>
 
 static TestSetup test( "/tmp/x", Arch_x86_64 );
@@ -54,6 +55,20 @@ BOOST_AUTO_TEST_CASE(LookupAttr_existingattr)
   BOOST_CHECK_NE( q.begin(), q.end() );
 }
 
+BOOST_AUTO_TEST_CASE(LookupAttr_existingattr_matcher)
+{
+  sat::LookupAttr q( sat::SolvAttr::name );
+
+  BOOST_CHECK_THROW( q.setAttrMatcher( sat::AttrMatcher("[]ypper",Match::REGEX) ), MatchInvalidRegexException );
+  BOOST_CHECK( ! q.attrMatcher() );
+  BOOST_CHECK_NO_THROW( q.setAttrMatcher( sat::AttrMatcher("[zZ]ypper",Match::REGEX) ) );
+  BOOST_CHECK( q.attrMatcher() );
+
+  BOOST_CHECK_EQUAL( q.size(), 9 );
+  for_(it,q.begin(),q.end())
+  { cout << it << endl;}
+}
+
 BOOST_AUTO_TEST_CASE(LookupAttr_iterate_solvables)
 {
   // sat::SolvAttr::name query should visit each solvable once.
index 1b43889..8e7961d 100644 (file)
 
 #include "zypp/sat/detail/PoolImpl.h"
 
+#include "zypp/sat/Pool.h"
 #include "zypp/sat/LookupAttr.h"
+#include "zypp/sat/AttrMatcher.h"
+
 #include "zypp/CheckSum.h"
-#include "zypp/sat/Pool.h"
 
 using std::endl;
 
@@ -72,6 +74,15 @@ namespace zypp
         void setAttr( SolvAttr attr_r )
         { _attr = attr_r; }
 
+        const AttrMatcher & attrMatcher() const
+        { return _attrMatcher; }
+
+        void setAttrMatcher( const AttrMatcher & matcher_r )
+        {
+          matcher_r.compile();
+          _attrMatcher = matcher_r;
+        }
+
       public:
         bool pool() const
         { return ! (_repo || _solv); }
@@ -102,29 +113,16 @@ namespace zypp
 
         LookupAttr::iterator begin() const
         {
-          if ( _attr == SolvAttr::noAttr )
+          if ( _attr == SolvAttr::noAttr || sat::Pool::instance().reposEmpty() )
             return end();
 
-#warning Need to call dataiterator_free, use Autodispose instead of scoped_ptr
-          scoped_ptr< ::_Dataiterator> dip( new ::Dataiterator );
-          // needed while LookupAttr::iterator::dip_equal does ::memcmp:
-          ::memset( dip.get(), 0, sizeof(::_Dataiterator) );
-
+          detail::RepoIdType whichRepo = detail::noRepoId; // all repos
           if ( _solv )
-          {
-            ::dataiterator_init( dip.get(), sat::Pool::instance().get(), _solv.repository().id(), _solv.id(), _attr.id(), 0, 0 );
-          }
+            whichRepo = _solv.repository().id();
           else if ( _repo )
-          {
-            ::dataiterator_init( dip.get(), sat::Pool::instance().get(),              _repo.id(), _solv.id(), _attr.id(), 0, 0 );
-          }
-          else if ( ! sat::Pool::instance().reposEmpty() )
-          {
-            ::dataiterator_init( dip.get(), sat::Pool::instance().get(),                       0, _solv.id(), _attr.id(), 0, 0 );
-          }
-          else
-            return end();
+            whichRepo = _repo.id();
 
+          detail::DIWrap dip( whichRepo, _solv.id(), _attr.id(), _attrMatcher.searchstring(), _attrMatcher.flags().get() );
           return iterator( dip ); // iterator takes over ownership!
         }
 
@@ -135,6 +133,7 @@ namespace zypp
         SolvAttr   _attr;
         Repository _repo;
         Solvable   _solv;
+        AttrMatcher _attrMatcher;
 
       private:
         friend Impl * rwcowClone<Impl>( const Impl * rhs );
@@ -173,6 +172,12 @@ namespace zypp
     void LookupAttr::setAttr( SolvAttr attr_r )
     { _pimpl->setAttr( attr_r ); }
 
+    const AttrMatcher & LookupAttr::attrMatcher() const
+    { return _pimpl->attrMatcher(); }
+
+    void LookupAttr::setAttrMatcher( const AttrMatcher & matcher_r )
+    { _pimpl->setAttrMatcher( matcher_r ); }
+
     ///////////////////////////////////////////////////////////////////
 
     bool LookupAttr::pool() const
@@ -251,6 +256,74 @@ namespace zypp
 
     ///////////////////////////////////////////////////////////////////
     //
+    //  CLASS NAME : detail::DIWrap
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    namespace detail
+    {
+      DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                      const std::string & mstring_r, int flags_r )
+      : _dip( new ::Dataiterator )
+      , _mstring( mstring_r )
+      {
+        ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
+                             _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
+      }
+
+      DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                      const char * mstring_r, int flags_r )
+      : _dip( new ::Dataiterator )
+      , _mstring( mstring_r ? mstring_r : "" )
+      {
+        ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
+                             _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
+      }
+
+      DIWrap::DIWrap( const DIWrap & rhs )
+        : _dip( 0 )
+        , _mstring( rhs._mstring )
+      {
+        if ( rhs._dip )
+        {
+          _dip = new ::Dataiterator;
+          *_dip = *rhs._dip;
+          // now we have to manually clone any allocated regex data matcher.
+          ::Datamatcher & matcher( _dip->matcher );
+          if ( matcher.match && ( matcher.flags & SEARCH_STRINGMASK ) == SEARCH_REGEX )
+          {
+            ::datamatcher_init( &matcher, _mstring.c_str(), matcher.flags );
+          }
+          else if ( matcher.match && matcher.match != _mstring.c_str() )
+          {
+            //SEC << "**" << rhs._dip << endl;
+            SEC << "r " << rhs._dip->matcher.match << endl;
+            SEC << "r " << rhs._dip->matcher.flags << endl;
+            SEC << "r " << (const void*)rhs._mstring.c_str() << "'" << rhs._mstring << "'" << endl;
+
+            SEC << "t " << matcher.match << endl;
+            SEC << "t " << matcher.flags << endl;
+            SEC << "t " << (const void*)_mstring.c_str() << "'" << _mstring << "'" <<  endl;
+            throw( "this cant be!" );
+          }
+        }
+      }
+
+      DIWrap::~DIWrap()
+      {
+        if ( _dip )
+        {
+          ::dataiterator_free( _dip );
+          delete _dip;
+        }
+      }
+
+      std::ostream & operator<<( std::ostream & str, const DIWrap & obj )
+      { return str << obj.get(); }
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
     // CLASS NAME : LookupAttr::iterator
     //
     ///////////////////////////////////////////////////////////////////
@@ -366,13 +439,8 @@ namespace zypp
       // remember this position
       ::dataiterator_setpos( _dip.get() );
 
-      // setup the new sub iterator
-      scoped_ptr< ::_Dataiterator> dip( new ::Dataiterator );
-      // needed while LookupAttr::iterator::dip_equal does ::memcmp:
-      ::memset( dip.get(), 0, sizeof(::_Dataiterator) );
-
-      ::dataiterator_init( dip.get(), sat::Pool::instance().get(), 0, SOLVID_POS, 0, 0, 0 );
-
+      // 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!
     }
 
@@ -547,50 +615,45 @@ namespace zypp
     // internal stuff below
     ///////////////////////////////////////////////////////////////////
 
-    LookupAttr::iterator::~iterator()
-    {}
-
     LookupAttr::iterator::iterator()
     : iterator_adaptor_( 0 )
     {}
 
     LookupAttr::iterator::iterator( const iterator & rhs )
-    : iterator_adaptor_( cloneFrom( rhs.base() ) )
-    , _dip( base() )
+    : iterator_adaptor_( 0 )
+    , _dip( rhs._dip )
+    {
+      base_reference() = _dip.get();
+    }
+
+    LookupAttr::iterator::iterator( detail::DIWrap & dip_r )
+    : iterator_adaptor_( 0 )
+    {
+      _dip.swap( dip_r ); // take ownership!
+      base_reference() = _dip.get();
+      increment();
+    }
+
+    LookupAttr::iterator::~iterator()
     {}
 
     LookupAttr::iterator & LookupAttr::iterator::operator=( const iterator & rhs )
     {
       if ( &rhs != this )
       {
-        _dip.reset( cloneFrom( rhs.base() ) );
+        _dip = rhs._dip;
         base_reference() = _dip.get();
       }
       return *this;
     }
 
-    LookupAttr::iterator::iterator( scoped_ptr< ::_Dataiterator> & dip_r )
-    : iterator_adaptor_( dip_r.get() )
-    {
-      _dip.swap( dip_r ); // take ownership!
-      increment();
-    }
-
     ///////////////////////////////////////////////////////////////////
 
-    ::_Dataiterator * LookupAttr::iterator::cloneFrom( const ::_Dataiterator * rhs )
-    {
-      if ( ! rhs )
-        return 0;
-      ::_Dataiterator * ret( new ::_Dataiterator );
-      *ret = *rhs;
-      return ret;
-    }
-
     bool LookupAttr::iterator::dip_equal( const ::_Dataiterator & lhs, const ::_Dataiterator & rhs ) const
     {
-      // requires ::memset in LookupAttr::begin
-      return ::memcmp( &lhs, &rhs, sizeof(::_Dataiterator) ) == 0;
+      // Iterator equal is same position in same container.
+      // Here: same attribute in same solvable.
+      return( lhs.solvid == rhs.solvid && lhs.key->name == rhs.key->name );
     }
 
     detail::IdType LookupAttr::iterator::dereference() const
index 1d052e8..c18a99e 100644 (file)
@@ -29,11 +29,15 @@ namespace zypp
 { /////////////////////////////////////////////////////////////////
 
   class CheckSum;
+  class Match;
+  class MatchException;
 
   ///////////////////////////////////////////////////////////////////
   namespace sat
   { /////////////////////////////////////////////////////////////////
 
+    class AttrMatcher;
+
     ///////////////////////////////////////////////////////////////////
     //
     // CLASS NAME : LookupAttr
@@ -92,6 +96,9 @@ namespace zypp
     class LookupAttr
     {
       public:
+        typedef MatchException Exception;
+
+      public:
         typedef unsigned size_type;
 
         /** Specify the where to look for the attribule. */
@@ -147,6 +154,28 @@ namespace zypp
         void setAttr( SolvAttr attr_r );
         //@}
 
+        /** \name Restrict attributes to match a pattern. */
+        //@{
+        /** The pattern to match.
+         * You can also evaluate \ref AttrMatcher in a boolean context,
+         * in order to test whether an \ref AttrMatcher is set:
+         * \code
+         *   LookupAttr q;
+         *   if ( q.attrMatcher() )
+         *     ...; // an AttrMatcher is set
+         * \endcode
+        */
+        const AttrMatcher & attrMatcher() const;
+
+        /** Set the pattern to match.
+         * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
+         */
+        void setAttrMatcher( const AttrMatcher & matcher_r );
+
+        /** Reset the pattern to match. */
+        void resetAttrMatcher();
+       //@}
+
       public:
         /** \name Where to search. */
         //@{
@@ -227,6 +256,68 @@ namespace zypp
     };
     ///////////////////////////////////////////////////////////////////
 
+    namespace detail
+    {
+      /** Wrapper around sat \c ::_Dataiterator.
+       *
+       * Manages copy and assign, and release of allocated
+       * resources like datamatcher inside the dataiterator.
+       * Also maintains a copy of the matchstring in order to
+       * keep the char* passed to the dataiterator valid.
+       */
+      class DIWrap : private base::SafeBool<DIWrap>
+      {
+        public:
+          /** \c NULL \c ::_Dataiterator */
+          DIWrap()
+          : _dip( 0 )
+          {}
+          /** Initializes */
+          DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                  const std::string & mstring_r = std::string(), int flags_r = 0 );
+          /** \overload to catch \c NULL \a mstring_r. */
+          DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
+                  const char * mstring_r, int flags_r = 0 );
+          DIWrap( const DIWrap & rhs );
+          ~DIWrap();
+        public:
+          void swap( DIWrap & rhs )
+          {
+            if ( &rhs != this ) // prevent self assign!
+            {
+              std::swap( _dip, rhs._dip );
+              std::swap( _mstring, rhs._mstring );
+            }
+          }
+          DIWrap & operator=( const DIWrap & rhs )
+          {
+            if ( &rhs != this ) // prevent self assign!
+              DIWrap( rhs ).swap( *this );
+            return *this;
+          }
+          void reset()
+          { DIWrap().swap( *this ); }
+        public:
+#ifndef SWIG // Swig treats it as syntax error
+          /** Evaluate in a boolean context <tt>( _dip != NULL )</tt>. */
+          using base::SafeBool<DIWrap>::operator bool_type;
+#endif
+        public:
+          ::_Dataiterator * operator->() const  { return _dip; }
+          ::_Dataiterator * get()        const  { return _dip; }
+          const std::string & getstr()   const  { return _mstring; }
+        private:
+          friend base::SafeBool<DIWrap>::operator bool_type() const;
+          bool boolTest() const
+          { return _dip; }
+        private:
+          ::_Dataiterator * _dip;
+          std::string _mstring;
+      };
+      /** \relates DIWrap Stream output. */
+      std::ostream & operator<<( std::ostream & str, const DIWrap & obj );
+    }
+
     ///////////////////////////////////////////////////////////////////
     //
     // CLASS NAME : LookupAttr::iterator
@@ -405,16 +496,14 @@ namespace zypp
 
       public:
         /**
-         * C-tor taking over ownership of the passed scoped _Dataiterator*
+         * C-tor taking over ownership of the passed \c ::_Dataiterator
          * and doing it's first iteration (::dataiterator_step)
          */
-        iterator( scoped_ptr< ::_Dataiterator> & dip_r );
+        iterator( detail::DIWrap & dip_r );
 
       private:
         friend class boost::iterator_core_access;
 
-        ::_Dataiterator * cloneFrom( const ::_Dataiterator * rhs );
-
         template <class OtherDerived, class OtherIterator, class V, class C, class R, class D>
         bool equal( const boost::iterator_adaptor<OtherDerived, OtherIterator, V, C, R, D> & rhs ) const
         {
@@ -433,7 +522,7 @@ namespace zypp
         ::_Dataiterator * get() const
         { return _dip.get(); }
       private:
-        scoped_ptr< ::_Dataiterator> _dip;
+        detail::DIWrap _dip;
     };
     ///////////////////////////////////////////////////////////////////