New classes wraping satsolver datamatcher (Match and sat::AttrMatcher)
authorMichael Andres <ma@suse.de>
Mon, 20 Apr 2009 14:47:57 +0000 (16:47 +0200)
committerMichael Andres <ma@suse.de>
Mon, 27 Apr 2009 11:49:12 +0000 (13:49 +0200)
- zypp::Match - string matching mode and option bit flags
- zypp::sat::AttrMatcher - string matching functor supporting STRING|SUBSTRING|GLOB|REGEX

tests/sat/AttrMatcher_test.cc [new file with mode: 0644]
tests/sat/CMakeLists.txt
zypp/CMakeLists.txt
zypp/sat/AttrMatcher.cc [new file with mode: 0644]
zypp/sat/AttrMatcher.h [new file with mode: 0644]

diff --git a/tests/sat/AttrMatcher_test.cc b/tests/sat/AttrMatcher_test.cc
new file mode 100644 (file)
index 0000000..05e0d88
--- /dev/null
@@ -0,0 +1,131 @@
+#include "TestSetup.h"
+#include <zypp/sat/LookupAttr.h>
+#include <zypp/sat/AttrMatcher.h>
+#include <zypp/ResObjects.h>
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : Matcher
+//
+///////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(Match_default)
+{
+  Match m;
+  BOOST_CHECK( !m ); // eval in boolean context
+  BOOST_CHECK_EQUAL( m, Match::NOTHING );
+  BOOST_CHECK_EQUAL( m.get(), 0 );
+
+  // set the mode part
+  BOOST_CHECK_EQUAL( m |= Match::STRING,       Match::STRING );
+
+  m.setModeSubstring();
+  BOOST_CHECK_EQUAL( m,                                Match::SUBSTRING );
+
+  m.setMode( Match::GLOB );
+  BOOST_CHECK_EQUAL( m,                        Match::GLOB );
+
+  BOOST_CHECK_EQUAL( m = Match::REGEX,                 Match::REGEX );
+
+  BOOST_CHECK( m.isModeRegex() );
+  m |= Match::NOCASE | Match::FILES;
+  BOOST_CHECK_EQUAL( m, Match::REGEX | Match::NOCASE | Match::FILES );
+
+  BOOST_CHECK( m.testAnyOf( Match::SUBSTRING | Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( !m.test( Match::SUBSTRING | Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( m.test( Match::REGEX | Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( m.test( Match::NOCASE | Match::FILES ) );
+  BOOST_CHECK( m != (Match::NOCASE | Match::FILES) );
+  BOOST_CHECK_EQUAL( m.flags(),Match::NOCASE | Match::FILES );
+
+  m -= Match::NOCASE; // remove flags
+  BOOST_CHECK( m.test( Match::REGEX | Match::FILES ) );
+  m -= Match::REGEX;
+  BOOST_CHECK_EQUAL( m, Match::FILES );
+}
+
+BOOST_AUTO_TEST_CASE(Match_operator)
+{
+  // Test whether implicit conversions from enum Match::Mode to
+  // Matcher work. There must be no difference in using mode and flag
+  // constants. These tests usually fail at compiletime, if some operator
+  // overload is missing.
+  //
+  // E.G.:
+  // inline Match operator|( const Match & lhs, const Match & rhs )
+  //  this does not cover (REGEX|SUBSTRING), because if both arguments
+  //  are enum Mode the compiler might want to use operator|(int,int)
+  //  instead.
+
+  Match m( Match::GLOB );
+  m = Match::GLOB;
+
+  m |= Match::GLOB;
+  m = Match::SUBSTRING | Match::GLOB;
+
+  m -= Match::GLOB;
+  m = Match::SUBSTRING - Match::GLOB;
+}
+
+///////////////////////////////////////////////////////////////////
+//
+//     CLASS NAME : AttrMatcher
+//
+///////////////////////////////////////////////////////////////////
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_defaultconstructed)
+{
+  sat::AttrMatcher m;
+  BOOST_CHECK( !m );   // eval in boolean context
+  BOOST_CHECK( m.searchstring().empty() );
+  BOOST_CHECK_EQUAL( m.flags(), Match() );
+  // matches nothing:
+  BOOST_CHECK( !m( "" ) );
+  BOOST_CHECK( !m( " " ) );
+  BOOST_CHECK( !m( "a" ) );
+  BOOST_CHECK( !m( "default" ) );
+
+  m.setSearchstring( "fau" );
+  BOOST_CHECK( m );    // eval in boolean context
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_STRING)
+{
+  sat::AttrMatcher m( "fau" );
+  BOOST_CHECK_EQUAL( m.flags(), Match::STRING );
+  BOOST_CHECK( !m( "" ) );
+  BOOST_CHECK( !m( "a" ) );
+  BOOST_CHECK( m( "fau" ) );
+  BOOST_CHECK( !m( "default" ) );
+}
+
+BOOST_AUTO_TEST_CASE(AttrMatcher_REGEX)
+{
+  sat::AttrMatcher m( "fau" );
+
+  BOOST_CHECK( !m.isCompiled() );
+  BOOST_CHECK_NO_THROW( m.compile() );
+
+  m.setSearchstring( "wa[" );
+  BOOST_CHECK( !m.isCompiled() );
+  m.setFlags( Match::REGEX );
+  BOOST_CHECK( !m.isCompiled() );
+  BOOST_CHECK_THROW( m.compile(), MatchInvalidRegexException );
+  BOOST_CHECK( !m.isCompiled() );
+
+  m.setSearchstring( "wa[a]" );
+  BOOST_CHECK_NO_THROW( m.compile() );
+  BOOST_CHECK( m.isCompiled() );
+
+  BOOST_CHECK( !m( "was" ) );
+  BOOST_CHECK( !m( "qwasq" ) );
+  BOOST_CHECK( m( "qwaaq" ) );
+}
+
+#if 0
+BOOST_AUTO_TEST_CASE(AttrMatcher_)
+{
+  base::LogControl::TmpLineWriter shutUp( new log::FileLineWriter( "/tmp/YLOG" ) );
+  MIL << "GO" << endl;
+}
+#endif
index 60775d4..1e34860 100644 (file)
@@ -2,6 +2,6 @@
 # to find the KeyRingTest receiver
 INCLUDE_DIRECTORIES( ${LIBZYPP_SOURCE_DIR}/tests/zypp )
 
-ADD_TESTS(Solvable SolvParsing WhatProvides WhatObsoletes LookupAttr)
+ADD_TESTS(Solvable SolvParsing WhatProvides WhatObsoletes LookupAttr AttrMatcher)
 
 
index 68c0102..addd92d 100644 (file)
@@ -508,6 +508,7 @@ SET( zypp_sat_SRCS
   sat/LocaleSupport.cc
   sat/LookupAttr.cc
   sat/SolvAttr.cc
+  sat/AttrMatcher.cc
 )
 
 SET( zypp_sat_HEADERS
@@ -521,6 +522,7 @@ SET( zypp_sat_HEADERS
   sat/LookupAttr.h
   sat/LookupAttrTools.h
   sat/SolvAttr.h
+  sat/AttrMatcher.h
 )
 
 INSTALL(  FILES
diff --git a/zypp/sat/AttrMatcher.cc b/zypp/sat/AttrMatcher.cc
new file mode 100644 (file)
index 0000000..026f2ba
--- /dev/null
@@ -0,0 +1,318 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/AttrMatcher.cc
+ *
+*/
+extern "C"
+{
+#include "satsolver/repo.h"
+}
+
+#include <iostream>
+#include <sstream>
+#include <boost/mpl/int.hpp>
+
+#include "zypp/base/LogTools.h"
+#include "zypp/base/Gettext.h"
+#include "zypp/base/String.h"
+
+#include "zypp/sat/AttrMatcher.h"
+
+using std::endl;
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Match
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  const int Match::_modemask = SEARCH_STRINGMASK;
+  const int Match::_flagmask = ~_modemask;
+
+  // option flags
+  const Match Match::NOCASE            (SEARCH_NOCASE);
+  const Match Match::NO_STORAGE_SOLVABLE(SEARCH_NO_STORAGE_SOLVABLE);
+  const Match Match::SUB               (SEARCH_SUB);
+  const Match Match::ARRAYSENTINEL     (SEARCH_ARRAYSENTINEL);
+  const Match Match::SKIP_KIND         (SEARCH_SKIP_KIND);
+  const Match Match::FILES             (SEARCH_FILES);
+
+  Match::Mode Match::mode() const
+  {
+    switch ( modeval() )
+    {
+      case 0:                  return NOTHING;         break;
+      case SEARCH_STRING:      return STRING;          break;
+      case SEARCH_SUBSTRING:   return SUBSTRING;       break;
+      case SEARCH_GLOB:                return GLOB;            break;
+      case SEARCH_REGEX:       return REGEX;           break;
+    }
+    return OTHER;
+  }
+
+  int Match::modeval( Mode mode_r )
+  {
+    switch ( mode_r )
+    {
+      case NOTHING:    return 0;                       break;
+      case STRING:     return SEARCH_STRING;           break;
+      case SUBSTRING:  return SEARCH_SUBSTRING;        break;
+      case GLOB:       return SEARCH_GLOB;             break;
+      case REGEX:      return SEARCH_REGEX;            break;
+      case OTHER:      return SEARCH_STRINGMASK;       break;
+    }
+    return SEARCH_STRINGMASK;
+  }
+
+  std::string Match::asString() const
+  { std::ostringstream str; str << *this; return str.str(); }
+
+  std::ostream & operator<<( std::ostream & str, Match::Mode obj )
+  {
+    switch ( obj )
+    {
+#define OUTS(V) case Match::V: return str << #V; break
+      OUTS( NOTHING );
+      OUTS( STRING );
+      OUTS( SUBSTRING );
+      OUTS( GLOB );
+      OUTS( REGEX );
+      OUTS( OTHER );
+#undef OUTS
+    }
+    return str << "Match::Mode::UNKNOWN";
+  }
+
+  std::ostream & operator<<( std::ostream & str, const Match & obj )
+  {
+    if ( ! obj )
+      return str << "NOTHING";
+
+    const char * sep = "|";
+
+    int val = obj.modeval();
+    switch ( val )
+    {
+      case 0:                  sep = 0;                break;
+      case SEARCH_STRING:      str << "STRING";        break;
+      case SEARCH_SUBSTRING:   str << "SUBSTRING";     break;
+      case SEARCH_GLOB:                str << "GLOB";          break;
+      case SEARCH_REGEX:       str << "REGEX";         break;
+      default:
+        str << "OTHER("<<val<<")"; // check whether satsolver has introduced new modes!
+        break;
+    }
+
+    val = obj.flagval();
+    if ( val )
+    {
+#define OUTS(V) if ( val & Match::V.get() ) { val &= ~Match::V.get(); if ( sep ) str << sep; else sep = "|"; str << #V; }
+      OUTS( NOCASE );
+      OUTS( NO_STORAGE_SOLVABLE );
+      OUTS( SUB );
+      OUTS( ARRAYSENTINEL );
+      OUTS( SKIP_KIND );
+      OUTS( FILES );
+#undef OUTS
+      if ( val )
+      {
+        if ( sep ) str << sep;
+        str << zypp::str::hexstring( val ); // check whether satsolver has introduced new flags.
+      }
+    }
+    return str;
+  }
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : MatchException
+  //
+  ///////////////////////////////////////////////////////////////////
+
+  MatchUnknownModeException::MatchUnknownModeException( const Match & mode_r, const std::string & msg_r )
+  : MatchException( msg_r.empty() ? str::form(_("Unknown match mode '%s'"), mode_r.asString().c_str() )
+                                  : str::form(_("Unknown match mode '%s' for pattern '%s'"), mode_r.asString().c_str(), msg_r.c_str() ) )
+  {}
+
+  MatchInvalidRegexException::MatchInvalidRegexException( const std::string & regex_r, int regcomp_r )
+  : MatchException( regcomp_r ? str::form(_("Invalid regular expression '%s': regcomp returned %d"), regex_r.c_str(), regcomp_r )
+                              : str::form(_("Invalid regular expression '%s'"), regex_r.c_str() ) )
+  {}
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : AttrMatcher::Impl
+    //
+    /** AttrMatcher implementation.
+     *
+     * Take care to release any allocated regex by calling
+     * \c ::datamatcher_free.
+     */
+    struct AttrMatcher::Impl
+    {
+      Impl()
+      {}
+
+      Impl( const std::string & search_r, const Match & flags_r )
+        : _search( search_r )
+        , _flags( flags_r )
+      {}
+
+      ~Impl()
+      { invalidate(); }
+
+      /** Compile the pattern. */
+      void compile() const
+      {
+        if ( !_matcher )
+        {
+          if ( ! _flags.mode() == Match::OTHER )
+            ZYPP_THROW( MatchUnknownModeException( _flags, _search ) );
+
+          _matcher.reset( new ::_Datamatcher );
+          int res = ::datamatcher_init( _matcher.get(), _search.c_str(), _flags.get() );
+          if ( res )
+          {
+            _matcher.reset();
+            ZYPP_THROW( MatchInvalidRegexException( _search, res ) );
+          }
+        }
+      }
+
+      /** Whether the pattern is already compiled. */
+      bool isCompiled() const
+      { return _matcher; }
+
+      /** Return whether string matches. */
+      bool doMatch( const char * string_r ) const
+      {
+        compile(); // nop if already compiled.
+
+        if ( ! string_r )
+          return false; // NULL never matches
+        return ::datamatcher_match( _matcher.get(), string_r );
+      }
+
+      /** The current searchstring. */
+      const std::string & searchstring() const
+      { return _search; }
+
+      /** Set a new searchstring. */
+      void setSearchstring( const std::string & string_r )
+      { invalidate(); _search = string_r; }
+
+      /** The current search flags. */
+      const Match & flags() const
+      { return _flags; }
+
+      /** Set new search flags. */
+      void setFlags( const Match & flags_r )
+      { invalidate(); _flags = flags_r; }
+
+      private:
+        /** Has to be called if _search or _flags change. */
+        void invalidate()
+        {
+          if ( _matcher )
+            ::datamatcher_free( _matcher.get() );
+          _matcher.reset();
+        }
+
+      private:
+        std::string _search;
+        Match       _flags;
+        mutable scoped_ptr< ::_Datamatcher> _matcher;
+
+      private:
+        friend Impl * rwcowClone<Impl>( const Impl * rhs );
+        /** clone for RWCOW_pointer */
+        Impl * clone() const
+        { return new Impl( _search, _flags ); }
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates AttrMatcher::Impl Stream output */
+    inline std::ostream & operator<<( std::ostream & str, const AttrMatcher::Impl & obj )
+    {
+      return str << "\"" << obj.searchstring() << "\"{" << obj.flags() << "}";
+    }
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : AttrMatcher
+    //
+    ///////////////////////////////////////////////////////////////////
+
+    AttrMatcher::AttrMatcher()
+    : _pimpl( new Impl )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r )
+    : _pimpl( new Impl( search_r, Match::STRING ) )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r, const Match & flags_r )
+    : _pimpl( new Impl( search_r, flags_r ) )
+    {}
+
+    AttrMatcher::AttrMatcher( const std::string & search_r, int flags_r )
+    : _pimpl( new Impl( search_r, Match(flags_r) ) )
+    {}
+
+    void AttrMatcher::compile() const
+    { return _pimpl->compile(); }
+
+    bool AttrMatcher::isCompiled() const
+    { return _pimpl->isCompiled(); }
+
+    bool AttrMatcher::doMatch( const char * string_r ) const
+    { return _pimpl->doMatch( string_r ); }
+
+    const std::string & AttrMatcher::searchstring() const
+    { return _pimpl->searchstring(); }
+
+    void AttrMatcher::setSearchstring( const std::string & string_r )
+    { _pimpl->setSearchstring( string_r ); }
+
+    void AttrMatcher::setSearchstring( const std::string & string_r, const Match & flags_r )
+    {
+      _pimpl->setSearchstring( string_r );
+      _pimpl->setFlags( flags_r );
+    }
+
+    const Match & AttrMatcher::flags() const
+    { return _pimpl->flags(); }
+
+    void AttrMatcher::setFlags( const Match & flags_r )
+    { _pimpl->setFlags( flags_r ); }
+
+    /******************************************************************
+    **
+    ** FUNCTION NAME : operator<<
+    ** FUNCTION TYPE : std::ostream &
+    */
+    std::ostream & operator<<( std::ostream & str, const AttrMatcher & obj )
+    {
+      return str << *obj._pimpl;
+    }
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
diff --git a/zypp/sat/AttrMatcher.h b/zypp/sat/AttrMatcher.h
new file mode 100644 (file)
index 0000000..155733a
--- /dev/null
@@ -0,0 +1,396 @@
+/*---------------------------------------------------------------------\
+|                          ____ _   __ __ ___                          |
+|                         |__  / \ / / . \ . \                         |
+|                           / / \ V /|  _/  _/                         |
+|                          / /__ | | | | | |                           |
+|                         /_____||_| |_| |_|                           |
+|                                                                      |
+\---------------------------------------------------------------------*/
+/** \file      zypp/sat/AttrMatcher.h
+ *
+*/
+#ifndef ZYPP_SAT_ATTRMATCHER_H
+#define ZYPP_SAT_ATTRMATCHER_H
+
+extern "C"
+{
+struct _Datamatcher;
+}
+
+#include <iosfwd>
+#include <string>
+
+#include "zypp/base/PtrTypes.h"
+#include "zypp/base/SafeBool.h"
+#include "zypp/base/Exception.h"
+
+///////////////////////////////////////////////////////////////////
+namespace zypp
+{ /////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : Match
+  //
+  /** String matching option flags as used e.g. by \ref sat::AttrMatcher.
+   *
+   *
+   *
+   * \code
+   * Match mode( Match::GLOB | Match::NOCASE );
+   * \endcode
+   */
+  class Match : private base::SafeBool<Match>
+  {
+    private:
+      static const int _modemask;
+      static const int _flagmask;
+
+    public:
+      /** Mode flags (mutual exclusive). */
+      enum Mode
+      {
+        NOTHING,       //!< Match nothing
+        STRING,                //!< Excat matching
+        SUBSTRING,     //!< Match substring
+        GLOB,          //!< Glob
+        REGEX,         //!< Regular Expression
+        OTHER          //!< Something else.
+      };
+
+      /** \name Option flags
+       * Some flags are actually \ref sat::LookupAttr specific, as they tell
+       * how to retrieve the attribute values. The plain \ref sat::AttrMatcher
+       * will ignore those flags and use the ones related to string matching only
+       * (like \ref NOCASE).
+       */
+      //@{
+      /** If set, match case insensitive. */
+      static const Match NOCASE;
+      /** internal */
+      static const Match NO_STORAGE_SOLVABLE;
+      /** internal */
+      static const Match SUB;
+      /** internal */
+      static const Match ARRAYSENTINEL;
+      /** If set, skip any \c kind: prefix when looking at a \ref Solvable name. */
+      static const Match SKIP_KIND;
+      /**  If set, match full path when matching in filelists, otherwise just the basenames. */
+      static const Match FILES;
+      //@}
+
+    public:
+      /** Default ctor \c 0 or \ref NOTHING. */
+      Match()
+      : _val( 0 )
+      {}
+
+      /** Ctor from \ref Mode value. */
+      Match( Mode val_r )
+      : _val( modeval( val_r ) )
+      {}
+
+      /** Just in case one needs it. */
+      explicit Match( int val_r )
+      : _val( val_r )
+      {}
+
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( != 0 )</tt>. */
+      using base::SafeBool<Match>::operator bool_type;
+#endif
+
+    public:
+      /** Test whether \c all of the \a rhs bits are set (same mode if \a rhs has one). */
+      bool test( const Match & rhs ) const
+      { return ( ( flagval() & rhs.flagval() ) == rhs.flagval() )
+          && ( !rhs.modeval() || rhs.modeval() == modeval() ); }
+
+      /** Whether at least one of the \a rhs bits is set (or the same mode). */
+      bool testAnyOf( const Match & rhs ) const
+      { return ( flagval() & rhs.flagval() )
+          || ( rhs.modeval() && rhs.modeval() == modeval() ); }
+
+      /** Set all of the \a rhs bits (setting a new mode if \a rhs has one). */
+      void set( const Match & rhs )
+      {
+        if ( rhs.modeval() )
+          _val = rhs._val | flagval(); // also set the rhs mode
+        else
+          _val |= rhs._val; // just set the flags
+      }
+
+      /** Unset all of the \a rhs bits (unsets mode if the same as \a rhs). */
+      void unset( const Match & rhs )
+      {
+        if ( modeval() == rhs.modeval() )
+          _val = flagval() & ~rhs.flagval(); // also unset mode
+        else
+          _val &= ~rhs.flagval(); // just unset falgs
+      }
+
+      /** Depending on the value of \a onoff, set or unset flags. */
+      void turn( const Match & rhs, bool onoff )
+      { onoff ? set( rhs ) : unset( rhs ); }
+
+      /** Add flags. */
+      Match & operator|=( const Match & rhs )
+      { set( rhs ); return *this; }
+
+      /** Remove flags.*/
+      Match & operator-=( const Match & rhs )
+      { unset( rhs ); return *this; }
+
+    public:
+      /** Return the \c mode part. */
+      Mode mode() const;
+
+      /** Return the \c flags part. */
+      Match flags() const
+      { return Match( flagval() ); }
+
+    public:
+      /** \name Low level integer representation. */
+      //@{
+      /** Return the integer representation. */
+      int get() const          { return _val; }
+      /** Return the modes integer representation. */
+      int modeval() const      { return _val & _modemask; }
+      /** Return the flags integer representation. */
+      int flagval() const      { return _val & _flagmask; }
+      //@}
+
+    public:
+      /** \name Mode flag manip/query convenience. */
+      //@{
+      /** Whether this has mode \a rhs */
+      bool isMode( Mode rhs ) const
+      { return modeval() == modeval( rhs ); }
+      /** Whether this has mode \ref STRING. */
+      bool isModeString() const
+      { return isMode( STRING ); }
+      /** Whether this has mode \ref SUBSTRING. */
+      bool isModeSubstring() const
+      { return isMode( SUBSTRING ); }
+      /** Whether this has mode \ref GLOB. */
+      bool isModeGlob() const
+      { return isMode( GLOB ); }
+      /** Whether this has mode \ref REGEX. */
+      bool isModeRegex() const
+      { return isMode( REGEX ); }
+
+      /** Set the mode part to \a rhs . */
+      void setMode( Mode rhs )
+      { _val = modeval( rhs ) | flagval(); }
+      /** Set the mode \ref STRING. */
+      void setModeString()
+      { setMode( STRING ); }
+      /** Set the mode \ref SUBSTRING. */
+      void setModeSubstring()
+      { setMode( SUBSTRING ); }
+      /** Set the mode \ref GLOB. */
+      void setModeGlob()
+      { setMode( GLOB ); }
+      /** Set the mode \ref REGEX. */
+      void setModeRegex()
+      { setMode( REGEX ); }
+      //@}
+
+      /** String representation. */
+      std::string asString() const;
+
+    private:
+      friend base::SafeBool<Match>::operator bool_type() const;
+      bool boolTest() const    { return _val; }
+
+      /** Numeric value for enum (short for <tt>Match(m).get()</tt>). */
+      static int modeval( Mode mode_r );
+
+    private:
+      int _val;
+  };
+
+  /** \relates Match */
+  inline bool operator==( const Match & lhs, const Match & rhs )
+  { return lhs.get() == rhs.get(); }
+  /** \relates Match */
+  inline bool operator!=( const Match & lhs, const Match & rhs )
+  { return lhs.get() != rhs.get(); }
+
+  /** \relates Match */
+  inline Match operator|( const Match & lhs, const Match & rhs )
+  { return Match(lhs) |= rhs; }
+  /** \relates Match \overload to disambiguate 'int|int'. */
+  inline Match operator|( Match::Mode lhs, Match::Mode rhs )
+  { return Match(lhs) |= rhs; }
+
+  /** \relates Match */
+  inline Match operator-( const Match & lhs, const Match & rhs )
+  { return Match(lhs) -= rhs; }
+  /** \relates Match \overload to disambiguate 'int-int'. */
+  inline Match operator-( Match::Mode lhs, Match::Mode rhs )
+  { return Match(lhs) -= rhs; }
+
+  /** \relates Match::Mode Stream output */
+  std::ostream & operator<<( std::ostream & str, Match::Mode obj );
+
+  /** \relates Match Stream output */
+  std::ostream & operator<<( std::ostream & str, const Match & obj );
+
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  //
+  //   CLASS NAME : MatchException
+  //
+  /** Exceptions thrown from attribute matching. */
+  struct MatchException : public Exception
+  {
+    /** Supplied message. */
+    explicit MatchException( const std::string & msg_r ) : Exception( msg_r ) {}
+  };
+
+  /** Unknown match mode. */
+  struct MatchUnknownModeException : public MatchException
+  {
+    /** Supplied message. */
+    explicit MatchUnknownModeException( const std::string & msg_r ) : MatchException( msg_r ) {}
+
+    /** Build message including the \a mode and optional the pattern string. */
+    MatchUnknownModeException( const Match & mode_r, const std::string & msg_r = std::string() );
+  };
+
+  /** Invalid regular expression (failed ::regcomp). */
+  struct MatchInvalidRegexException : public MatchException
+  {
+    /** Supplied message. */
+    explicit MatchInvalidRegexException( const std::string & msg_r ) : MatchException( msg_r ) {}
+
+    /** Build message including the \a regex and \c ::regcomp returncode (use \c 0 if unknown). */
+    MatchInvalidRegexException( const std::string & regex_r, int regcomp_r );
+  };
+
+  ///////////////////////////////////////////////////////////////////
+
+  ///////////////////////////////////////////////////////////////////
+  namespace sat
+  { /////////////////////////////////////////////////////////////////
+
+    ///////////////////////////////////////////////////////////////////
+    //
+    // CLASS NAME : AttrMatcher
+    //
+    /** String matching (STRING|SUBSTRING|GLOB|REGEX).
+     *
+     * Used by e.g. \ref PoolQuery and \ref LookupAttr for queries,
+     * but it can also be used for matching arbitrary strings.
+     *
+     * \code
+     *  AttrMatcher matches( "foo" );
+     *  for_( it, stringlist.begin(), stringlist().end() )
+     *  {
+     *    if ( matches( *it ) )
+     *      cout << *it << " has substring 'foo'" << endl;
+     *  }
+     * \endcode
+     *
+     * \Note Those flags are always set: <tt>REG_EXTENDED | REG_NOSUB | REG_NEWLINE</tt>
+     */
+    class AttrMatcher : private base::SafeBool<AttrMatcher>
+    {
+      friend std::ostream & operator<<( std::ostream & str, const AttrMatcher & obj );
+
+      public:
+        typedef MatchException Exception;
+
+      public:
+        /** Implementation  */
+        class Impl;
+
+      public:
+        /** Default ctor matches nothing. */
+        AttrMatcher();
+
+        /** Ctor from string matches in \ref Match::STRING mode per default. */
+        AttrMatcher( const std::string & search_r );
+
+        /** Ctor taking string and \ref Match flags. */
+        AttrMatcher( const std::string & search_r, const Match & flags_r );
+
+        /** Low level interface wraps \a flags into \ref Match. */
+        AttrMatcher( const std::string & search_r, int flags_r );
+
+#ifndef SWIG // Swig treats it as syntax error
+      /** Evaluate in a boolean context <tt>( ! searchstring().empty() )</tt>. */
+      using base::SafeBool<AttrMatcher>::operator bool_type;
+#endif
+
+      public:
+        /** Return whether string matches.
+         * You can use it with any class that impements \c c_str.
+         * (\c std::string, \ref Pathname, \ref IdString, ...).
+         * \Note \c NULL never matches.
+         */
+        template<class _Tp>
+            bool operator()( const _Tp & string_r ) const
+        { return doMatch( string_r.c_str() ); }
+        /** \overload */
+        bool operator()( const char * string_r ) const
+        { return doMatch( string_r ); }
+
+      public:
+        /** The current searchstring. */
+        const std::string & searchstring() const;
+
+        /** Set a new searchstring. */
+        void setSearchstring( const std::string & string_r );
+
+        /** Set a new searchstring and flags. */
+        void setSearchstring( const std::string & string_r, const Match & flags_r );
+
+        /** The current search flags. */
+        const Match & flags() const;
+
+        /** Set new search flags. */
+        void setFlags( const Match & flags_r );
+
+      public:
+        /** Compile the pattern e.g. in case of \c REGEX.
+         * \throws MatchUnknownModeException If the \ref Match flag more than
+         *         one mode bit set.
+         * \throws MatchInvalidRegexException If \ref Match::REGEX is set
+         *         and \ref searchstring is not a valid regular expression.
+         */
+        void compile() const;
+
+        /** Whether the \ref AttrMatcher is already compiled. */
+        bool isCompiled() const;
+
+        /** Return whether string matches.
+         * Compiles the \ref AttrMatcher if this was not yet done.
+         * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
+         */
+        bool doMatch( const char * string_r ) const;
+
+      private:
+        friend base::SafeBool<AttrMatcher>::operator bool_type() const;
+        bool boolTest() const
+        { return !searchstring().empty(); }
+
+      private:
+        /** Pointer to implementation */
+        RWCOW_pointer<Impl> _pimpl;
+    };
+    ///////////////////////////////////////////////////////////////////
+
+    /** \relates AttrMatcher Stream output */
+    std::ostream & operator<<( std::ostream & str, const AttrMatcher & obj );
+
+    /////////////////////////////////////////////////////////////////
+  } // namespace sat
+  ///////////////////////////////////////////////////////////////////
+  /////////////////////////////////////////////////////////////////
+} // namespace zypp
+///////////////////////////////////////////////////////////////////
+#endif // ZYPP_SAT_ATTRMATCHER_H