*/
#include <iostream>
#include <sstream>
-#include <list>
-#include <vector>
-#include <algorithm>
-#include <fnmatch.h>
-
-#include "zypp/base/Logger.h"
-#include "zypp/base/PtrTypes.h"
-#include "zypp/base/DefaultIntegral.h"
-#include "zypp/base/Regex.h"
+
+#include "zypp/base/Gettext.h"
+#include "zypp/base/LogTools.h"
#include "zypp/base/Algorithm.h"
-#include "zypp/base/UserRequestException.h"
+#include "zypp/base/String.h"
#include "zypp/repo/RepoException.h"
+#include "zypp/RelCompare.h"
#include "zypp/sat/Pool.h"
#include "zypp/sat/Solvable.h"
-#include "zypp/sat/SolvAttr.h"
-#include "zypp/sat/detail/PoolImpl.h"
-
-extern "C"
-{
-#include "satsolver/repo.h"
-}
+#include "zypp/base/StrMatcher.h"
#include "zypp/PoolQuery.h"
+#undef ZYPP_BASE_LOGGER_LOGGROUP
+#define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
+
using namespace std;
using namespace zypp::sat;
{ /////////////////////////////////////////////////////////////////
///////////////////////////////////////////////////////////////////
+ namespace
+ { /////////////////////////////////////////////////////////////////
+
+ /////////////////////////////////////////////////////////////////
+ // some Helpers and Predicates
+ /////////////////////////////////////////////////////////////////
+
+ bool isDependencyAttribute( sat::SolvAttr attr_r )
+ {
+ static sat::SolvAttr deps[] = {
+ SolvAttr::provides,
+ SolvAttr::requires,
+ SolvAttr::recommends,
+ SolvAttr::obsoletes,
+ SolvAttr::conflicts,
+ SolvAttr::suggests,
+ SolvAttr::supplements,
+ SolvAttr::enhances,
+ };
+ for_( it, arrayBegin(deps), arrayEnd(deps) )
+ if ( *it == attr_r )
+ return true;
+ return false;
+ }
+
+ /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
+ * Query asserts \a iter_r points to a capability and we
+ * have to check the range only.
+ */
+ struct EditionRangePredicate
+ {
+ EditionRangePredicate( const Rel & op, const Edition & edition )
+ : _range( op, edition )
+ , _arch( Arch_empty )
+ {}
+ EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+ : _range( op, edition )
+ , _arch( arch )
+ {}
+
+ bool operator()( sat::LookupAttr::iterator iter_r )
+ {
+ if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+ return false;
+
+ CapDetail cap( iter_r.id() );
+ if ( ! cap.isSimple() )
+ return false;
+ if ( cap.isNamed() ) // no range to match
+ return true;
+ return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
+ }
+
+ std::string serialize() const
+ {
+ std::string ret( "EditionRange" );
+ str::appendEscaped( ret, _range.op.asString() );
+ str::appendEscaped( ret, _range.value.asString() );
+ str::appendEscaped( ret, _arch.asString() );
+ return ret;
+ }
+
+ Edition::MatchRange _range;
+ Arch _arch;
+ };
+
+ /** Whether the current Solvables edition is within a given range and/or its arch matches. */
+ struct SolvableRangePredicate
+ {
+ SolvableRangePredicate( const Rel & op, const Edition & edition )
+ : _range( op, edition )
+ , _arch( Arch_empty )
+ {}
+
+ SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
+ : _range( op, edition )
+ , _arch( arch )
+ {}
+
+ bool operator()( sat::LookupAttr::iterator iter_r )
+ {
+ if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
+ return false;
+ return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
+ }
+
+ std::string serialize() const
+ {
+ std::string ret( "SolvableRange" );
+ str::appendEscaped( ret, _range.op.asString() );
+ str::appendEscaped( ret, _range.value.asString() );
+ str::appendEscaped( ret, _arch.asString() );
+ return ret;
+ }
+
+ Edition::MatchRange _range;
+ Arch _arch;
+ };
+
+ /** Whether the current capability matches a given one.
+ * Query asserts \a iter_r points to a capability and we
+ * have to check the match only.
+ */
+ struct CapabilityMatchPredicate
+ {
+ CapabilityMatchPredicate( Capability cap_r )
+ : _cap( cap_r )
+ {}
+
+ bool operator()( sat::LookupAttr::iterator iter_r ) const
+ {
+ return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
+ }
+
+ std::string serialize() const
+ {
+ std::string ret( "CapabilityMatch" );
+ str::appendEscaped( ret, _cap.asString() );
+ return ret;
+ }
+
+ Capability _cap;
+ };
+
+ /////////////////////////////////////////////////////////////////
+ //
+ /////////////////////////////////////////////////////////////////
+ /** Match data per attribtue.
+ *
+ * This includes the attribute itself, an optional \ref StrMatcher
+ * to restrict the query to certain string values, and an optional
+ * boolean \ref Predicate that may apply further restrictions that can
+ * not be expressed by the \ref strMatcher.
+ *
+ * Example for such a \ref predicate would be an additional edition range
+ * check whan looking for dependencies. The \ref strMatcher would
+ * find potential matches by looking at the dependencies name, the
+ * predicate will then check the edition ranges.
+ *
+ * As the \ref predicate takes an iterator pointing to the current
+ * match, it's also suitable for sub-structure (flexarray) inspection
+ * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
+ *
+ * \note: \see \ref addPredicate for further constraints.
+ */
+ struct AttrMatchData
+ {
+ typedef function<bool(sat::LookupAttr::iterator)> Predicate;
+
+ static bool always( sat::LookupAttr::iterator ) { return true; }
+ static bool never( sat::LookupAttr::iterator ) { return false; }
+
+ AttrMatchData()
+ {}
+
+ AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
+ : attr( attr_r )
+ , strMatcher( strMatcher_r )
+ {}
+
+ AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
+ const Predicate & predicate_r, const std::string & predicateStr_r )
+ : attr( attr_r )
+ , strMatcher( strMatcher_r )
+ , predicate( predicate_r )
+ , predicateStr( predicateStr_r )
+ {}
+
+ /** A usable Predicate must provide a string serialization.
+ * As there is no \c operator== for \ref Predicate, we compare it's
+ * string representation instead. If you add new predicated, check the
+ * deserialization code in \ref deserialize.
+ */
+ template<class TPredicate>
+ void addPredicate( const TPredicate & predicate_r )
+ {
+ predicate = predicate_r;
+ predicateStr = predicate_r.serialize();
+ }
+
+ /** Dumb serialization.
+ * \code
+ * AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
+ * \endcode
+ */
+ std::string serialize() const
+ {
+ std::string ret( "AttrMatchData" );
+ str::appendEscaped( ret, attr.asString() );
+ str::appendEscaped( ret, strMatcher.searchstring() );
+ // TODO: Actually the flag should be serialized too, but for PoolQuery
+ // it's by now sufficient to differ between mode OTHER and others,
+ // i.e. whether to compile or not compile.
+ str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
+ str::appendEscaped( ret, predicateStr );
+ return ret;
+ }
+
+ /** Dumb restore from serialized string.
+ * \throw Exception on parse error.
+ */
+ static AttrMatchData deserialize( const std::string & str_r )
+ {
+ std::vector<std::string> words;
+ str::splitEscaped( str_r, std::back_inserter(words) );
+ if ( words.empty() || words[0] != "AttrMatchData" )
+ ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
+ if ( words.size() != 5 )
+ ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+
+ AttrMatchData ret;
+ ret.attr = sat::SolvAttr( words[1] );
+ ret.strMatcher = StrMatcher( words[2] );
+ if ( words[3] == "C" )
+ ret.strMatcher.setFlags( Match::OTHER );
+ ret.predicateStr = words[4];
+
+ // now the predicate
+ words.clear();
+ str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
+ if ( ! words.empty() )
+ {
+ if ( words[0] == "EditionRange" )
+ {
+ switch( words.size() )
+ {
+ case 3:
+ ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
+ break;
+ case 4:
+ ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+ break;
+ default:
+ ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+ break;
+ }
+ }
+ else if ( words[0] == "SolvableRange" )
+ {
+ switch( words.size() )
+ {
+ case 3:
+ ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
+ break;
+ case 4:
+ ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
+ break;
+ default:
+ ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+ break;
+ }
+ }
+ else if ( words[0] == "CapabilityMatch" )
+ {
+ if ( words.size() != 2 )
+ ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
+ ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
+ }
+ else
+ ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
+ }
+ return ret;
+ }
+
+ sat::SolvAttr attr;
+ StrMatcher strMatcher;
+ Predicate predicate;
+ std::string predicateStr;
+ };
+
+ /** \relates AttrMatchData */
+ inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
+ {
+ str << obj.attr << ": " << obj.strMatcher;
+ if ( obj.predicate )
+ str << " +(" << obj.predicateStr << ")";
+ return str;
+ }
+
+ /** \relates AttrMatchData */
+ inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
+ {
+ return ( lhs.attr == rhs.attr
+ && lhs.strMatcher == rhs.strMatcher
+ && lhs.predicateStr == rhs.predicateStr );
+ }
+
+ /** \relates AttrMatchData */
+ inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
+ { return !( lhs == rhs ); }
+
+ /** \relates AttrMatchData Arbitrary order for std::container. */
+ inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
+ {
+ if ( lhs.attr != rhs.attr )
+ return ( lhs.attr < rhs.attr );
+ if ( lhs.strMatcher != rhs.strMatcher )
+ return ( lhs.strMatcher < rhs.strMatcher );
+ if ( lhs.predicateStr != rhs.predicateStr )
+ return ( lhs.predicateStr < rhs.predicateStr );
+ return false;
+ }
+
+ typedef std::list<AttrMatchData> AttrMatchList;
+
+
+ } /////////////////////////////////////////////////////////////////
+ // namespace
+ ///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
//
// CLASS NAME : PoolQuery::Impl
//
+ /** */
class PoolQuery::Impl
{
public:
Impl()
- : _flags( SEARCH_ALL_REPOS | SEARCH_NOCASE | SEARCH_SUBSTRING )
- , _status_flags(ALL)
- , _match_word(false)
+ : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
+ , _match_word(false)
, _require_all(false)
- , _compiled(false)
+ , _status_flags(ALL)
{}
~Impl()
{}
public:
-/*
- static int repo_search_cb(void *cbdata, ::Solvable *s, ::Repodata *data, ::Repokey *key, ::KeyValue *kv)
- {
- PoolQuery *me = (PoolQuery*) cbdata;
-
- bool r = false;
-
- sat::Solvable solvable(s - sat::Pool::instance().get()->solvables);
-
- // now filter by kind here (we cant do it before)
- if ( ! me->_pimpl->_kinds.empty() )
- {
- // the user wants to filter by kind.
- if ( find( me->_pimpl->_kinds.begin(),
- me->_pimpl->_kinds.end(),
- solvable.kind() )
- == me->_pimpl->_kinds.end() )
- {
- // we did not find the kind in the list
- // so this is not a result.
- return SEARCH_NEXT_SOLVABLE;
- }
- }
-
- if (me->_pimpl->_fnc)
- r = me->_pimpl->_fnc( solvable );//makeResObject(solvable) );
-
- if (!r)
- return SEARCH_STOP;
- return SEARCH_NEXT_SOLVABLE;
- }
- */
- ResultIterator begin();
- ResultIterator end();
-
+ /** String representation */
string asString() const;
-
- private:
- void compile();
- string createRegex(vector<string> & container);
- public:
+ /** \name Raw query options. */
+ //@{
/** Raw search strings. */
StrContainer _strings;
- /** Regex-compiled search strings. */
- string _rcstrings;
/** Raw attributes */
- AttrMap _attrs;
- /** Regex-compiled attributes */
- CompiledAttrMap _rcattrs;
+ AttrRawStrMap _attrs;
+ /** Uncompiled attributes with predicate. */
+ std::set<AttrMatchData> _uncompiledPredicated;
+
+ /** Sat solver search flags */
+ Match _flags;
+ bool _match_word;
+ bool _require_all;
+
+ /** Sat solver status flags */
+ StatusFilter _status_flags;
+
+ /** Edition condition operand */
+ Edition _edition;
+ /** Operator for edition condition */
+ Rel _op;
/** Repos to search. */
StrContainer _repos;
+
/** Kinds to search */
Kinds _kinds;
+ //@}
- /** Sat solver search flags */
- int _flags;
- /** Backup of search flags. compile() may change the flags if needed, so
- * in order to reuse the query, the original flags need to be stored
- * at the start of compile() */
- int _flags_old;
- /** Sat solver status flags */
- int _status_flags;
+ public:
- bool _match_word;
+ bool operator==( const PoolQuery::Impl & rhs ) const
+ {
+ if ( _flags == rhs._flags
+ // bnc#792901: while libzypp uses exact match mode for a single
+ // package name lock, zypper always uses glob. :(
+ // We unify those two forms to enable zypper to remove zypp locks
+ // without need to actually evaluate the query (which would require
+ // repos to be loaded).
+ || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
+ || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
+ && _strings.empty()
+ && _attrs.size() == 1
+ && _attrs.begin()->first == sat::SolvAttr::name ) )
+ {
+ return ( _strings == rhs._strings
+ && _attrs == rhs._attrs
+ && _uncompiledPredicated == rhs._uncompiledPredicated
+ && _match_word == rhs._match_word
+ && _require_all == rhs._require_all
+ && _status_flags == rhs._status_flags
+ && _edition == rhs._edition
+ && _op == rhs._op
+ && _repos == rhs._repos
+ && _kinds == rhs._kinds );
+ }
+ return false;
+ }
- bool _require_all;
+ bool operator!=( const PoolQuery::Impl & rhs ) const
+ { return ! operator==( rhs ); }
+
+ public:
+ /** Compile the regex.
+ * Basically building the \ref _attrMatchList from strings.
+ * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
+ */
+ void compile() const;
- /** Sat solver Dataiterator structure */
- ::_Dataiterator _rdit;
+ /** StrMatcher per attribtue. */
+ mutable AttrMatchList _attrMatchList;
- bool _compiled;
+ private:
+ /** Pass flags from \ref compile, as they may have been changed. */
+ string createRegex( const StrContainer & container, const Match & flags ) const;
- /** Function for processing found solvables. Used in execute(). */
- mutable PoolQuery::ProcessResolvable _fnc;
private:
friend Impl * rwcowClone<Impl>( const Impl * rhs );
/** clone for RWCOW_pointer */
Impl * clone() const
{ return new Impl( *this ); }
};
-/*
- template <class _OutputIterator>
- struct CollectNonEmpty
+
+ ///////////////////////////////////////////////////////////////////
+
+ struct MyInserter
{
- CollectNonEmpty( _OutputIterator iter_r ) : _iter( iter_r ) {}
+ MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
- template<class _Tp>
- bool operator()( const _Tp & value_r ) const
+ bool operator()(const string & str)
{
- if (value_r.empty())
- return true;
- *_iter++ = value_r;
+ _cont.insert(str);
return true;
}
- private:
- mutable _OutputIterator _iter;
+ PoolQuery::StrContainer & _cont;
};
-*/
- void PoolQuery::Impl::compile()
+
+
+ struct EmptyFilter
+ {
+ bool operator()(const string & str)
+ {
+ return !str.empty();
+ }
+ };
+
+ void PoolQuery::Impl::compile() const
{
- // backup the flags
- _flags_old = _flags;
+ _attrMatchList.clear();
+
+ Match cflags( _flags );
+ if ( cflags.mode() == Match::OTHER ) // this will never succeed...
+ ZYPP_THROW( MatchUnknownModeException( cflags ) );
+
+ /** Compiled search strings. */
+ string rcstrings;
+
// 'different' - will have to iterate through all and match by ourselves (slow)
// 'same' - will pass the compiled string to dataiterator_init
// 'one-attr' - will pass it to dataiterator_init
// 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
-
+
// // NO ATTRIBUTE
// else
// for all _strings
- // create regex; store in _rcstrings; if more strings flag regex;
+ // create regex; store in rcstrings; if more strings flag regex;
if (_attrs.empty())
{
- _rcstrings = createRegex(_strings);
- if (_strings.size() > 1)
- _flags = (_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;//setMatchRegex();
+ ; // A default 'query-all' will be added after all sources are processed.
}
- // // ONE ATTRIBUTE
+ // // ONE ATTRIBUTE
// else if _attrs is not empty but it contains just one attr
// for all _strings and _attr[key] strings
- // create regex; store in _rcattrs; flag 'one-attr'; if more strings flag regex;
+ // create regex; flag 'one-attr'; if more strings flag regex;
else if (_attrs.size() == 1)
{
- vector<string> joined;
- for(vector<string>::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
- if (!it->empty())
- joined.push_back(*it);
- for(vector<string>::const_iterator it = _attrs.begin()->second.begin(); it != _attrs.begin()->second.end(); ++it)
- if (!it->empty())
- joined.push_back(*it);
- _rcstrings = createRegex(joined);
- _rcattrs.insert(pair<sat::SolvAttr, string>(_attrs.begin()->first, string()));
+ StrContainer joined;
+ invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+ invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
+ rcstrings = createRegex(joined, cflags);
+ if (joined.size() > 1) // switch to regex for multiple strings
+ cflags.setModeRegex();
+ _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
+ StrMatcher( rcstrings, cflags ) ) );
}
-
// // MULTIPLE ATTRIBUTES
else
{
+ // check whether there are any per-attribute strings
bool attrvals_empty = true;
- for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+ for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
if (!ai->second.empty())
- for(vector<string>::const_iterator it = ai->second.begin();
+ for(StrContainer::const_iterator it = ai->second.begin();
it != ai->second.end(); it++)
if (!it->empty())
{
attrvals_empty = false;
goto attremptycheckend;
}
-
attremptycheckend:
+ // chceck whether the per-attribute strings are all the same
bool attrvals_thesame = true;
- for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+ AttrRawStrMap::const_iterator ai = _attrs.begin();
+ const StrContainer & set1 = ai->second;
+ ++ai;
+ for (; ai != _attrs.end(); ++ai)
{
-
+ StrContainer result;
+ set_difference(
+ set1.begin(), set1.end(),
+ ai->second.begin(), ai->second.end(),
+ inserter(result, result.begin())/*, ltstr()*/);
+ if (!result.empty())
+ {
+ attrvals_thesame = false;
+ break;
+ }
}
// // THE SAME STRINGS FOR DIFFERENT ATTRS
// else if _attrs is not empty but it does not contain strings
// for each key in _attrs take all _strings
- // create regex; store in _rcattrs and _rcstrings; flag 'same'; if more strings flag regex;
+ // create regex; store in rcstrings; flag 'same'; if more strings flag regex;
if (attrvals_empty || attrvals_thesame)
{
+ StrContainer joined;
if (attrvals_empty)
{
- // compile the search string
- vector<string> joined;
- for(vector<string>::const_iterator it = _strings.begin(); it != _strings.end(); ++it)
- if (!it->empty())
- joined.push_back(*it);
- _rcstrings = createRegex(joined);
-
- // copy the _attrs keys to _rcattrs
- for (AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
- _rcattrs.insert(pair<sat::SolvAttr, string>(ai->first, string()));
+ invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+ rcstrings = createRegex(joined, cflags);
+ }
+ else
+ {
+ invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+ invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
+ rcstrings = createRegex(joined, cflags);
+ }
+ if (joined.size() > 1) // switch to regex for multiple strings
+ cflags.setModeRegex();
+ // May use the same StrMatcher for all
+ StrMatcher matcher( rcstrings, cflags );
+ for_( ai, _attrs.begin(), _attrs.end() )
+ {
+ _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
}
}
+
// // DIFFERENT STRINGS FOR DIFFERENT ATTRS
// if _attrs is not empty and it contains non-empty vectors with non-empty strings
// for each key in _attrs take all _strings + all _attrs[key] strings
- // create regex; store in _rcattrs; flag 'different'; if more strings flag regex;
+ // create regex; flag 'different'; if more strings flag regex;
else
{
-
+ for_(ai, _attrs.begin(), _attrs.end())
+ {
+ StrContainer joined;
+ invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
+ invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
+ string s = createRegex(joined, cflags);
+ if (joined.size() > 1) // switch to regex for multiple strings
+ cflags.setModeRegex();
+ _attrMatchList.push_back( AttrMatchData( ai->first,
+ StrMatcher( s, cflags ) ) );
+ }
}
}
- // tell the Dataiterator to search only in one repo if only one specified
- if (_repos.size() == 1)
- _flags &= ~SEARCH_ALL_REPOS;
+ // Now handle any predicated queries
+ if ( ! _uncompiledPredicated.empty() )
+ {
+ StrContainer global;
+ invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
+ for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
+ {
+ if ( it->strMatcher.flags().mode() == Match::OTHER )
+ {
+ // need to compile:
+ StrContainer joined( global );
+ const std::string & mstr( it->strMatcher.searchstring() );
+ if ( ! mstr.empty() )
+ joined.insert( mstr );
+
+ cflags = _flags;
+ rcstrings = createRegex( joined, cflags );
+ if ( joined.size() > 1 ) // switch to regex for multiple strings
+ cflags.setModeRegex();
+
+ _attrMatchList.push_back( AttrMatchData( it->attr,
+ StrMatcher( rcstrings, cflags ),
+ it->predicate, it->predicateStr ) );
+ }
+ else
+ {
+ // copy matcher
+ _attrMatchList.push_back( *it );
+ }
+ }
+ }
+
+ // If no attributes defined at all, then add 'query all'
+ if ( _attrMatchList.empty() )
+ {
+ cflags = _flags;
+ rcstrings = createRegex( _strings, cflags );
+ if ( _strings.size() > 1 ) // switch to regex for multiple strings
+ cflags.setModeRegex();
+ _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
+ StrMatcher( rcstrings, cflags ) ) );
+ }
- _compiled = true;
-
- DBG << asString() << endl;
+ // Finally check here, whether all involved regex compile.
+ for_( it, _attrMatchList.begin(), _attrMatchList.end() )
+ {
+ it->strMatcher.compile(); // throws on error
+ }
+ //DBG << asString() << endl;
}
+
/**
* Converts '*' and '?' wildcards within str into their regex equivalents.
*/
{
string regexed = str;
- str::regex all("\\*"); // regex to search for '*'
- str::regex one("\\?"); // regex to search for '?'
string r_all(".*"); // regex equivalent of '*'
string r_one("."); // regex equivalent of '?'
string::size_type pos;
for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
regexed = regexed.replace(pos, 1, r_one);
- DBG << " -> " << regexed << endl;
-
return regexed;
}
+ string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
+ {
//! macro for word boundary tags for regexes
#define WB (_match_word ? string("\\b") : string())
-
- string PoolQuery::Impl::createRegex(vector<string> & container)
- {
string rstr;
if (container.empty())
if (container.size() == 1)
{
- if (_match_word)
- return ".*" + WB + *container.begin() + WB + ".*";
-
- return *container.begin();
+ return WB + *container.begin() + WB;
}
// multiple strings
- bool _use_wildcards = (_flags & SEARCH_STRINGMASK) == SEARCH_GLOB;
- vector<string>::const_iterator it = container.begin();
+ bool use_wildcards = flags.isModeGlob();
+ StrContainer::const_iterator it = container.begin();
string tmp;
- if (_use_wildcards)
+ if (use_wildcards)
tmp = wildcards2regex(*it);
+ else
+ tmp = *it;
if (_require_all)
{
- if (!(_flags & SEARCH_STRING)) // not match exact
+ if ( ! flags.isModeString() ) // not match exact
tmp += ".*" + WB + tmp;
rstr = "(?=" + tmp + ")";
}
else
{
- if (_flags & SEARCH_STRING) // match exact
+ if ( flags.isModeString() || flags.isModeGlob() )
rstr = "^";
- else
- rstr = ".*" + WB;
-
- rstr += "(" + tmp;
+ rstr += WB + "(" + tmp;
}
++it;
for (; it != container.end(); ++it)
{
- if (_use_wildcards)
+ if (use_wildcards)
tmp = wildcards2regex(*it);
+ else
+ tmp = *it;
if (_require_all)
{
- if (!(_flags & SEARCH_STRING)) // not match exact
+ if ( ! flags.isModeString() ) // not match exact
tmp += ".*" + WB + tmp;
rstr += "(?=" + tmp + ")";
}
if (_require_all)
{
- if (!(_flags & SEARCH_STRING)) // not match exact
+ if ( ! flags.isModeString() ) // not match exact
rstr += WB + ".*";
}
else
{
- rstr += ")";
- if (_flags & SEARCH_STRING) // match exact
+ rstr += ")" + WB;
+ if ( flags.isModeString() || flags.isModeGlob() )
rstr += "$";
- else
- rstr += WB + ".*";
}
return rstr;
+#undef WB
}
-
- PoolQuery::ResultIterator PoolQuery::Impl::begin()
+ string PoolQuery::Impl::asString() const
{
- compile();
+ ostringstream o;
- // if only one repository has been specified, find it in the pool
- sat::Pool pool(sat::Pool::instance());
- sat::Pool::RepositoryIterator itr = pool.reposBegin();
- if (!(_flags & SEARCH_ALL_REPOS) && _repos.size() == 1)
+ o << "kinds: ";
+ if ( _kinds.empty() )
+ o << "ALL";
+ else
{
- string theone = *_repos.begin();
- for (; itr->info().alias() != theone && itr != pool.reposEnd(); ++itr);
- if (itr == pool.reposEnd())
- {
- RepoInfo info; info.setAlias(theone);
- ERR << "Repository not found in sat pool." << endl;
- ZYPP_THROW(repo::RepoNotFoundException(info));
- }
+ for(Kinds::const_iterator it = _kinds.begin();
+ it != _kinds.end(); ++it)
+ o << *it << " ";
}
+ o << endl;
- DBG << "_flags:" << _flags << endl;
-
- if (_rcattrs.empty())
- {
- ::dataiterator_init(&_rdit,
- _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
- 0, // search all solvables
- 0, // attribute id - only if 1 attr key specified
- _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
- _flags);
- }
- else if (_rcattrs.size() == 1)
- {
- ::dataiterator_init(&_rdit,
- _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), // repository \todo fix this
- 0, // search all solvables
- _rcattrs.begin()->first.id(), // keyname - attribute id - only if 1 attr key specified
- _rcstrings.empty() ? 0 : _rcstrings.c_str(), // compiled search string
- _flags);
- }
+ o << "repos: ";
+ if ( _repos.empty() )
+ o << "ALL";
else
{
- ::dataiterator_init(&_rdit,
- _flags & SEARCH_ALL_REPOS ? pool.get()->repos[0] : itr->get(), /* repository - switch to next at the end of current one in increment() */
- 0, /*search all resolvables */
- 0, /*keyname - if only 1 attr key specified, pass it here, otherwise do more magic */
- 0, //qs.empty() ? 0 : qs.c_str(), /* create regex, pass it here */
- _flags);
+ for(StrContainer::const_iterator it = _repos.begin();
+ it != _repos.end(); ++it)
+ o << *it << " ";
}
+ o << endl;
- PoolQuery::ResultIterator it(this);
- it.increment();
- return it;
- }
-
- PoolQuery::ResultIterator PoolQuery::Impl::end()
- {
- INT << "end" << endl;
- return PoolQuery::ResultIterator();
- }
-
-
- string PoolQuery::Impl::asString() const
- {
- ostringstream o;
-
- o << "compiled: " << _compiled << endl;
+ o << "version: "<< _op << " " << _edition.asString() << endl;
+ o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
+ : "ALL" ) << endl;
- o << "string match flags:" << endl;
- o << "* string/substring/glob/regex: " << (_flags & SEARCH_STRINGMASK) << endl;
- o << "* SEARCH_NOCASE: " << ((_flags & SEARCH_NOCASE) ? "yes" : "no") << endl;
- o << "* SEARCH_ALL_REPOS: " << ((_flags & SEARCH_ALL_REPOS) ? "yes" : "no") << endl;
- o << "status filter flags:" << _status_flags << endl;
+ o << "string match flags: " << Match(_flags) << endl;
// raw
-
o << "strings: ";
- for(vector<string>::const_iterator it = _strings.begin();
+ for(StrContainer::const_iterator it = _strings.begin();
it != _strings.end(); ++it)
o << *it << " ";
o << endl;
o << "attributes: " << endl;
- for(AttrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
+ for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
{
o << "* " << ai->first << ": ";
- for(vector<string>::const_iterator vi = ai->second.begin();
+ for(StrContainer::const_iterator vi = ai->second.begin();
vi != ai->second.end(); ++vi)
o << *vi << " ";
o << endl;
}
- // compiled
-
- o << "regex compiled strings: " << _rcstrings << endl;
- o << "regex compiled attributes:" << endl;
- for (CompiledAttrMap::const_iterator ai = _rcattrs.begin(); ai != _rcattrs.end(); ++ai)
- o << "* " << ai->first << ": " << ai->second << endl;
-
- return o.str();
- }
-
- /** \relates PoolQuery::Impl Stream output *//*
- inline std::ostream & operator<<( std::ostream & str, const PoolQuery::Impl & obj )
- {
- return str << "PoolQuery::Impl";
- }
- */
- ///////////////////////////////////////////////////////////////////
-
- ///////////////////////////////////////////////////////////////////
- //
- // CLASS NAME : PoolQuery::ResultIterator
- //
- ///////////////////////////////////////////////////////////////////
-
- PoolQuery::ResultIterator::ResultIterator(Impl * pqimpl)
- : PoolQuery::ResultIterator::iterator_adaptor_(pqimpl ? &pqimpl->_rdit : 0)
- , _rdit(pqimpl ? &pqimpl->_rdit : 0)
- , _pqimpl(pqimpl)
- , _sid(0)
- , _has_next(true)
- , _attrs(pqimpl->_rcattrs)
- , _do_matching(false)
- , _pool((sat::Pool::instance()))
- {
- if (_attrs.size() > 1)
- _do_matching = true;
- }
-
- void PoolQuery::ResultIterator::increment()
- {
- if (!_rdit)
- return;
-
- bool got_match = false;
- if (_has_next)
+ o << "predicated: " << endl;
+ for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
{
- DBG << "last: " << _sid << endl;
- while (_has_next && !(got_match = matchSolvable()));
+ o << "* " << *it << endl;
}
- // no more solvables and the last did not match
- if (!got_match && !_has_next)
+ // compiled
+ o << "last attribute matcher compiled: " << endl;
+ if ( _attrMatchList.empty() )
{
- base_reference() = 0;
- _sid = 0;
+ o << "not yet compiled" << endl;
}
-
- DBG << "next: " << _sid << endl;
- }
-
- bool PoolQuery::ResultIterator::matchSolvable()
- {
- _sid = _rdit->solvid;
-
- bool new_solvable = true;
- bool matches = !_do_matching;
- bool in_repo;
- bool drop_by_kind_status = false;
- do
+ else
{
- //! \todo FIXME Dataiterator returning resolvables belonging to current repo?
- in_repo = _sid >= _rdit->repo->start;
-
- if (in_repo && new_solvable)
+ for_( it, _attrMatchList.begin(), _attrMatchList.end() )
{
- while(1)
- {
- drop_by_kind_status = false;
-
- // whether to drop an uninstalled (repo) solvable
- if ( (_pqimpl->_status_flags & INSTALLED_ONLY) &&
- _rdit->repo->name != _pool.systemRepoName() )
- {
- drop_by_kind_status = true;
- break;
- }
-
- // whether to drop an installed (target) solvable
- if ((_pqimpl->_status_flags & UNINSTALLED_ONLY) &&
- _rdit->repo->name == _pool.systemRepoName())
- {
- drop_by_kind_status = true;
- break;
- }
-
- // whether to drop unwanted kind
- if (!_pqimpl->_kinds.empty())
- {
- sat::Solvable s(_sid);
- // the user wants to filter by kind.
- if (_pqimpl->_kinds.find(s.kind()) == _pqimpl->_kinds.end())
- drop_by_kind_status = true;
- }
-
- break;
- }
-
- matches = matches && !drop_by_kind_status;
- }
-
- if (_do_matching && !drop_by_kind_status)
- {
- if (!matches && in_repo)
- {
- SolvAttr attr(_rdit->key->name);
- CompiledAttrMap::const_iterator ai = _attrs.find(attr);
- if (ai != _attrs.end())
- {
- const string & sstr =
- _pqimpl->_rcstrings.empty() ? ai->second : _pqimpl->_rcstrings;
- const IdString & value =
- IdString(_rdit->kv.id);
-
- //cout << "solvid: " << _sid << " attr: " << attr.asString() << " val: " << value.asString() << endl;
-
- //string v2(::id2str(_rdit->repo->pool/*_pool.get()*/, _rdit->kv.id));
- //if (value.asString() != v2)
- // cout << "ha! " << value.asString() << "!=" << v2 << endl;
-
- switch(_pqimpl->_flags & SEARCH_STRINGMASK)
- {
- case SEARCH_STRING:
- if (_pqimpl->_flags & SEARCH_NOCASE)
- matches = ! str::compareCI(sstr.c_str(), value.c_str());
- else
- matches = (sstr == value.asString());
- break;
- case SEARCH_SUBSTRING:
- if (_pqimpl->_flags & SEARCH_NOCASE)
- matches = ::strcasestr(value.c_str(), sstr.c_str());
- else
- matches = (value.asString().find(sstr) != string::npos);
- break;
- case SEARCH_GLOB:
- matches = !::fnmatch(sstr.c_str(), value.c_str(),
- (_pqimpl->_flags & SEARCH_NOCASE) ? FNM_CASEFOLD : 0);
- break;
- case SEARCH_REGEX:
- matches = false;
- break;
- default:
- matches = false;
- ERR << "ivalid string matching type: "
- << (_pqimpl->_flags & SEARCH_STRINGMASK) << endl;
- }
- if (matches)
- INT << "value: " << value.asString() << endl
- << " mstr: " << sstr << endl;
- }
- }
- }
-
- if ((_has_next = ::dataiterator_step(_rdit)))
- {
- new_solvable = _rdit->solvid != _sid;
- if (!in_repo)
- {
- INT << "repo start: " << _rdit->repo->start << endl;
- _sid = _rdit->solvid;
- }
- }
- // no more attributes/solvables, return
- else
- {
- // did the last solvable match conditions?
- return matches && in_repo;
+ o << "* " << *it << endl;
}
}
- while (!new_solvable || !in_repo);
-
- return matches;
+ return o.str();
}
///////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
//
// CLASS NAME : PoolQuery
//
: _pimpl(new Impl())
{}
-
PoolQuery::~PoolQuery()
{}
-
void PoolQuery::addRepo(const std::string &repoalias)
{
- _pimpl->_repos.push_back(repoalias);
- _pimpl->_flags &= ~SEARCH_ALL_REPOS;
+ if (repoalias.empty())
+ {
+ WAR << "ignoring an empty repository alias" << endl;
+ return;
+ }
+ _pimpl->_repos.insert(repoalias);
}
-
- void PoolQuery::addKind(const Resolvable::Kind &kind)
+ void PoolQuery::addKind(const ResKind & kind)
{ _pimpl->_kinds.insert(kind); }
-
void PoolQuery::addString(const string & value)
- { _pimpl->_strings.push_back(value); }
-
+ { _pimpl->_strings.insert(value); }
void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
- { _pimpl->_attrs[attr].push_back(value); }
+ { _pimpl->_attrs[attr].insert(value); }
+ void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
+ { return addDependency( attr, name, op, edition, Arch_empty ); }
- void PoolQuery::setCaseSensitive(const bool value)
+ void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
{
- if (value)
- _pimpl->_flags &= ~SEARCH_NOCASE;
+ switch ( op.inSwitch() )
+ {
+ case Rel::ANY_e: // no additional constraint on edition.
+ if ( arch.empty() ) // no additional constraint on arch.
+ {
+ addAttribute( attr, name );
+ return;
+ }
+ break;
+
+ case Rel::NONE_e: // will never match.
+ return;
+
+ default: // go and add the predicated query (uncompiled)
+ break;
+ }
+
+ // Match::OTHER indicates need to compile
+ // (merge global search strings into name).
+ AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
+
+ if ( isDependencyAttribute( attr ) )
+ attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
else
- _pimpl->_flags |= SEARCH_NOCASE;
+ attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
+
+ _pimpl->_uncompiledPredicated.insert( attrMatchData );
}
+ void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
+ {
+ CapDetail cap( cap_r );
+ if ( ! cap.isSimple() ) // will never match.
+ return;
+
+ // Matches STRING per default. (won't get compiled!)
+ AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
+
+ if ( isDependencyAttribute( attr ) )
+ attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
+ else
+ attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
+
+ _pimpl->_uncompiledPredicated.insert( attrMatchData );
+ }
+
+ void PoolQuery::setEdition(const Edition & edition, const Rel & op)
+ {
+ _pimpl->_edition = edition;
+ _pimpl->_op = op;
+ }
- void PoolQuery::setMatchSubstring()
- { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_SUBSTRING; }
- void PoolQuery::setMatchExact()
- { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_STRING; }
- void PoolQuery::setMatchRegex()
- { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX; }
- void PoolQuery::setMatchGlob()
- { _pimpl->_flags |= (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_GLOB; }
+ void PoolQuery::setMatchSubstring() { _pimpl->_flags.setModeSubstring(); }
+ void PoolQuery::setMatchExact() { _pimpl->_flags.setModeString(); }
+ void PoolQuery::setMatchRegex() { _pimpl->_flags.setModeRegex(); }
+ void PoolQuery::setMatchGlob() { _pimpl->_flags.setModeGlob(); }
void PoolQuery::setMatchWord()
{
_pimpl->_match_word = true;
- _pimpl->_flags = (_pimpl->_flags & ~SEARCH_STRINGMASK) | SEARCH_REGEX;
+ _pimpl->_flags.setModeRegex();
}
- void PoolQuery::setFlags(int flags)
+ Match PoolQuery::flags() const
+ { return _pimpl->_flags; }
+ void PoolQuery::setFlags( const Match & flags )
{ _pimpl->_flags = flags; }
{ _pimpl->_status_flags = INSTALLED_ONLY; }
void PoolQuery::setUninstalledOnly()
{ _pimpl->_status_flags = UNINSTALLED_ONLY; }
- void PoolQuery::setStatusFilterFlags( int flags )
+ void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
{ _pimpl->_status_flags = flags; }
- void PoolQuery::setRequireAll(const bool require_all)
+ void PoolQuery::setRequireAll(bool require_all)
{ _pimpl->_require_all = require_all; }
PoolQuery::strings() const
{ return _pimpl->_strings; }
- const PoolQuery::AttrMap &
+ const PoolQuery::AttrRawStrMap &
PoolQuery::attributes() const
{ return _pimpl->_attrs; }
+ const PoolQuery::StrContainer &
+ PoolQuery::attribute(const sat::SolvAttr & attr) const
+ {
+ static const PoolQuery::StrContainer nocontainer;
+ AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
+ return it != _pimpl->_attrs.end() ? it->second : nocontainer;
+ }
+
+ const Edition PoolQuery::edition() const
+ { return _pimpl->_edition; }
+ const Rel PoolQuery::editionRel() const
+ { return _pimpl->_op; }
+
+
const PoolQuery::Kinds &
PoolQuery::kinds() const
{ return _pimpl->_kinds; }
PoolQuery::repos() const
{ return _pimpl->_repos; }
+
bool PoolQuery::caseSensitive() const
- { return _pimpl->_flags & SEARCH_NOCASE; }
-
- bool PoolQuery::matchExact() const
- { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_STRING; }
- bool PoolQuery::matchSubstring() const
- { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_SUBSTRING; }
- bool PoolQuery::matchGlob() const
- { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_GLOB; }
- bool PoolQuery::matchRegex() const
- { return (_pimpl->_flags & SEARCH_STRINGMASK) == SEARCH_REGEX; }
- int PoolQuery::matchType() const
- { return _pimpl->_flags & SEARCH_STRINGMASK; }
+ { return !_pimpl->_flags.test( Match::NOCASE ); }
+ void PoolQuery::setCaseSensitive( bool value )
+ { _pimpl->_flags.turn( Match::NOCASE, !value ); }
+
+ bool PoolQuery::filesMatchFullPath() const
+ { return _pimpl->_flags.test( Match::FILES ); }
+ void PoolQuery::setFilesMatchFullPath( bool value )
+ { _pimpl->_flags.turn( Match::FILES, value ); }
+
+ bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); }
+ bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring(); }
+ bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
+ bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
bool PoolQuery::matchWord() const
{ return _pimpl->_match_word; }
bool PoolQuery::requireAll() const
{ return _pimpl->_require_all; }
+ PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
+ { return _pimpl->_status_flags; }
-
- PoolQuery::ResultIterator PoolQuery::begin()
- { return _pimpl->begin(); }
-
-
- PoolQuery::ResultIterator PoolQuery::end()
- { return _pimpl->end(); }
-
-
- bool PoolQuery::empty()
- { return _pimpl->begin() == _pimpl->end(); }
-
- //! \todo collect the result, reuse if not dirty
- PoolQuery::size_type PoolQuery::size()
+ bool PoolQuery::empty() const
{
- size_type count = 0;
- for(ResultIterator it = _pimpl->begin(); it != _pimpl->end(); ++it, ++count);
- return count;
+ try { return begin() == end(); }
+ catch (const Exception & ex) {}
+ return true;
}
-
- void PoolQuery::execute(ProcessResolvable fnc)
+ PoolQuery::size_type PoolQuery::size() const
{
- invokeOnEach(_pimpl->begin(), _pimpl->end(), fnc);
- /*
- _pimpl->_fnc = fnc;
- string term;
- if (!_pimpl->_strings.empty())
- term = *_pimpl->_strings.begin();
-
- sat::Pool pool(sat::Pool::instance());
- for ( sat::Pool::RepositoryIterator itr = pool.reposBegin();
- itr != pool.reposEnd();
- ++itr )
- {
- // filter by installed uninstalled
- if ( ( _pimpl->_status_flags & INSTALLED_ONLY ) && (itr->name() != sat::Pool::instance().systemRepoName()) )
- continue;
-
- if ( ( _pimpl->_status_flags & UNINSTALLED_ONLY ) && (itr->name() == sat::Pool::instance().systemRepoName()) )
- continue;
-
- // is this repo in users repos?
- bool included = ( find(_pimpl->_repos.begin(), _pimpl->_repos.end(), itr->name()) != _pimpl->_repos.end() );
-
- // only look in user repos filter if the filter is not empty
- // in this case we search in all
- if ( _pimpl->_repos.empty() || included )
- {
- repo_search( itr->get(), 0, 0, term.c_str(), _pimpl->_flags, Impl::repo_search_cb, (void*) (this));
- }
-
+ try
+ {
+ size_type count = 0;
+ for_( it, begin(), end() )
+ ++count;
+ return count;
}
- */
+ catch (const Exception & ex) {}
+ return 0;
}
+ void PoolQuery::execute(ProcessResolvable fnc)
+ { invokeOnEach( begin(), end(), fnc); }
+
+
///////////////////////////////////////////////////////////////////
//
- // CLASS NAME : PoolQuery::Impl
+ // CLASS NAME : PoolQuery::Attr
//
/**
* represents all atributes in PoolQuery except SolvAtributes, which are
*/
struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
{
- private:
- friend class IdStringType<PoolQueryAttr>;
- IdString _str;
+ private:
+ friend class IdStringType<PoolQueryAttr>;
+ IdString _str;
+ public:
- public:
-
//noAttr
- PoolQueryAttr():isSolvAttr(false){}
+ PoolQueryAttr(){}
explicit PoolQueryAttr( const char* cstr_r )
- : _str( cstr_r ),isSolvAttr(false){}
+ : _str( cstr_r )
+ {}
explicit PoolQueryAttr( const std::string & str_r )
- : _str( str_r ),isSolvAttr(false)
- {
- if( _str==noAttr ){
- sat::SolvAttr sa(str_r);
- if( sa != sat::SolvAttr::noAttr )
- {
- isSolvAttr = true;
- }
- }
- }
+ : _str( str_r )
+ {}
- //unknown atributes
+ // unknown atributes
static const PoolQueryAttr noAttr;
- // own attributes
- static const PoolQueryAttr nameAttr;
+ // PoolQuery's own attributes
static const PoolQueryAttr repoAttr;
static const PoolQueryAttr kindAttr;
-
- // exported attributes from SolvAtributes
- bool isSolvAttr;
+ static const PoolQueryAttr stringAttr;
+ static const PoolQueryAttr stringTypeAttr;
+ static const PoolQueryAttr requireAllAttr;
+ static const PoolQueryAttr caseSensitiveAttr;
+ static const PoolQueryAttr installStatusAttr;
+ static const PoolQueryAttr editionAttr;
+ static const PoolQueryAttr complexAttr;
};
const PoolQueryAttr PoolQueryAttr::noAttr;
- const PoolQueryAttr PoolQueryAttr::nameAttr( "name" );
const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
- const PoolQueryAttr PoolQueryAttr::kindAttr( "kind" );
+ const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
+ const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
+ const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
+ const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
+ const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
+ const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
+ const PoolQueryAttr PoolQueryAttr::editionAttr("version");
+ const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
+
+ class StringTypeAttr : public IdStringType<PoolQueryAttr>
+ {
+ friend class IdStringType<StringTypeAttr>;
+ IdString _str;
+
+ public:
+ StringTypeAttr(){}
+ explicit StringTypeAttr( const char* cstr_r )
+ : _str( cstr_r ){}
+ explicit StringTypeAttr( const std::string & str_r )
+ : _str( str_r ){}
+
+ static const StringTypeAttr noAttr;
+
+ static const StringTypeAttr exactAttr;
+ static const StringTypeAttr substringAttr;
+ static const StringTypeAttr regexAttr;
+ static const StringTypeAttr globAttr;
+ static const StringTypeAttr wordAttr;
+ };
+
+ const StringTypeAttr StringTypeAttr::noAttr;
+
+ const StringTypeAttr StringTypeAttr::exactAttr("exact");
+ const StringTypeAttr StringTypeAttr::substringAttr("substring");
+ const StringTypeAttr StringTypeAttr::regexAttr("regex");
+ const StringTypeAttr StringTypeAttr::globAttr("glob");
+ const StringTypeAttr StringTypeAttr::wordAttr("word");
///////////////////////////////////////////////////////////////////
//\TODO maybe ctor with stream can be usefull
+ //\TODO let it throw, let it throw, let it throw.
bool PoolQuery::recover( istream &str, char delim )
{
bool finded_something = false; //indicates some atributes is finded
finded_something = true;
- string atrName(str::trim(string(s,0,pos))); // trimmed name of atribute
- string atrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
+ string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
+ string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
- PoolQueryAttr attribute( atrName );
+ PoolQueryAttr attribute( attrName );
- if ( attribute==PoolQueryAttr::nameAttr)
+ if ( attribute==PoolQueryAttr::repoAttr )
{
- //setName...maybe some regex test
- break;
+ addRepo( attrValue );
}
- else if ( attribute==PoolQueryAttr::repoAttr )
+ /* some backwards compatibility */
+ else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
{
- addRepo( atrValue );
+ addKind( ResKind(attrValue) );
}
- else if ( attribute==PoolQueryAttr::kindAttr )
+ else if ( attribute==PoolQueryAttr::stringAttr
+ || attribute=="global_string")
{
- addKind( Resolvable::Kind(atrValue) );
+ addString( attrValue );
}
- else if ( attribute==PoolQueryAttr::noAttr )
+ else if ( attribute==PoolQueryAttr::stringTypeAttr
+ || attribute=="string_type" )
{
- if (attribute.isSolvAttr)
+ StringTypeAttr s(attrValue);
+ if( s == StringTypeAttr::regexAttr )
+ {
+ setMatchRegex();
+ }
+ else if ( s == StringTypeAttr::globAttr )
+ {
+ setMatchGlob();
+ }
+ else if ( s == StringTypeAttr::exactAttr )
+ {
+ setMatchExact();
+ }
+ else if ( s == StringTypeAttr::substringAttr )
{
- //setAtribute
+ setMatchSubstring();
+ }
+ else if ( s == StringTypeAttr::wordAttr )
+ {
+ setMatchWord();
+ }
+ else if ( s == StringTypeAttr::noAttr )
+ {
+ WAR << "unknown string type " << attrValue << endl;
}
else
{
- //log unknwon atribute
+ WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
+ }
+ }
+ else if ( attribute==PoolQueryAttr::requireAllAttr )
+ {
+ if ( str::strToTrue(attrValue) )
+ {
+ setRequireAll(true);
}
+ else if ( !str::strToFalse(attrValue) )
+ {
+ setRequireAll(false);
+ }
+ else
+ {
+ WAR << "unknown boolean value " << attrValue << endl;
+ }
+ }
+ else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
+ {
+ if ( str::strToTrue(attrValue) )
+ {
+ setCaseSensitive(true);
+ }
+ else if ( !str::strToFalse(attrValue) )
+ {
+ setCaseSensitive(false);
+ }
+ else
+ {
+ WAR << "unknown boolean value " << attrValue << endl;
+ }
+ }
+ else if ( attribute==PoolQueryAttr::installStatusAttr )
+ {
+ if( attrValue == "all" )
+ {
+ setStatusFilterFlags( ALL );
+ }
+ else if( attrValue == "installed" )
+ {
+ setInstalledOnly();
+ }
+ else if( attrValue == "not-installed" )
+ {
+ setUninstalledOnly();
+ }
+ else
+ {
+ WAR << "Unknown value for install status " << attrValue << endl;
+ }
+ }
+ else if ( attribute == PoolQueryAttr::editionAttr)
+ {
+ string::size_type pos;
+ Rel rel("==");
+ if (attrValue.find_first_of("=<>!") == 0)
+ {
+ pos = attrValue.find_last_of("=<>");
+ rel = Rel(attrValue.substr(0, pos+1));
+ attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
+ }
+
+ setEdition(Edition(attrValue), rel);
+ }
+ else if ( attribute == PoolQueryAttr::complexAttr )
+ {
+ try
+ {
+ _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
+ }
+ catch ( const Exception & err )
+ {
+ WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
+
+ }
+ }
+ else if ( attribute==PoolQueryAttr::noAttr )
+ {
+ WAR << "empty attribute name" << endl;
}
else
{
- //some forget handle new atribute
- ;
+ string s = attrName;
+ str::replaceAll( s,"_",":" );
+ SolvAttr a(s);
+ if ( a == SolvAttr::name || isDependencyAttribute( a ) )
+ {
+ Capability c( attrValue );
+ CapDetail d( c );
+ if ( d.isVersioned() )
+ addDependency( a, d.name().asString(), d.op(), d.ed() );
+ else
+ addDependency( a, attrValue );
+ }
+ else
+ addAttribute( a, attrValue );
}
-
+
} while ( true );
return finded_something;
void PoolQuery::serialize( ostream &str, char delim ) const
{
//separating delim
- str << delim;
+ str << delim;
//iterate thrue all settings and write it
-
- for_( it, _pimpl->_repos.begin(), _pimpl->_repos.end() )
+ static const zypp::PoolQuery q; //not save default options, so create default query example
+
+ for_( it, repos().begin(), repos().end() )
{
str << "repo: " << *it << delim ;
}
- for_( it, _pimpl->_kinds.begin(), _pimpl->_kinds.end() )
+ for_( it, kinds().begin(), kinds().end() )
{
- str << "kind: " << it->idStr() << delim ;
+ str << PoolQueryAttr::kindAttr.asString() << ": "
+ << it->idStr() << delim ;
}
- //separating delim - protection
- str << delim;
+ if (editionRel() != Rel::ANY && edition() != Edition::noedition)
+ str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
- }
+ if (matchMode()!=q.matchMode())
+ {
+ switch( matchMode() )
+ {
+ case Match::STRING:
+ str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
+ break;
+ case Match::SUBSTRING:
+ str << PoolQueryAttr::stringTypeAttr.asString()
+ << ": substring" << delim;
+ break;
+ case Match::GLOB:
+ str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
+ break;
+ case Match::REGEX:
+ str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
+ break;
+ default:
+ WAR << "unknown match type " << matchMode() << endl;
+ }
+ }
+ if( caseSensitive() != q.caseSensitive() )
+ {
+ str << "case_sensitive: ";
+ if (caseSensitive())
+ {
+ str << "on" << delim;
+ }
+ else
+ {
+ str << "off" << delim;
+ }
+ }
+
+ if( requireAll() != q.requireAll() )
+ {
+ str << "require_all: ";
+ if (requireAll())
+ {
+ str << "on" << delim;
+ }
+ else
+ {
+ str << "off" << delim;
+ }
+ }
+
+ if( statusFilterFlags() != q.statusFilterFlags() )
+ {
+ switch( statusFilterFlags() )
+ {
+ case ALL:
+ str << "install_status: all" << delim;
+ break;
+ case INSTALLED_ONLY:
+ str << "install_status: installed" << delim;
+ break;
+ case UNINSTALLED_ONLY:
+ str << "install_status: not-installed" << delim;
+ break;
+ }
+ }
+
+ for_( it, strings().begin(), strings().end() )
+ {
+ str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
+ }
+
+ for_( it, attributes().begin(), attributes().end() )
+ {
+ string s = it->first.asString();
+ str::replaceAll(s,":","_");
+ for_( it2,it->second.begin(),it->second.end() )
+ {
+ str << s <<": "<< *it2 << delim;
+ }
+ }
+
+ for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
+ {
+ str << "complex: "<< it->serialize() << delim;
+ }
+
+ //separating delim - protection
+ str << delim;
+ }
string PoolQuery::asString() const
{ return _pimpl->asString(); }
-
ostream & operator<<( ostream & str, const PoolQuery & obj )
{ return str << obj.asString(); }
- bool operator==(const PoolQuery& a, const PoolQuery& b)
- {
- return equal(a,b);
- }
+ std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
+ { return dumpRange( str << obj, obj.begin(), obj.end() ); }
- //internal matching two containers O(n^2)
- template <class Container>
- bool equalContainers(const Container& a, const Container& b)
- {
- for_(it,a.begin(),a.end())
+ bool PoolQuery::operator==( const PoolQuery & rhs ) const
+ { return *_pimpl == *rhs._pimpl; }
+
+ ///////////////////////////////////////////////////////////////////
+ namespace detail
+ { /////////////////////////////////////////////////////////////////
+
+ ///////////////////////////////////////////////////////////////////
+ //
+ // CLASS NAME : PoolQueryMatcher
+ //
+ /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
+ *
+ * Basically the matcher performs a base query, which should preselect
+ * candidates for a match. And has some filter conditions on top of it.
+ * Query and fileter depend on the \ref PoolQuery settings.
+ *
+ * Matcher must be stateless, as it is shared between multiple
+ * \ref PoolQueryIterator instances.
+ *
+ * If \ref base_iterator is at the \ref end, \ref advance moves it
+ * to the first match. Otherwise advance moves to the next match, or
+ * to the \ref end, if there is no more match.
+ *
+ * \note The original implementation treated an empty search string as
+ * <it>"match always"</it>. We stay compatible.
+ */
+ class PoolQueryMatcher
{
- bool finded = false;
- for_( it2, b.begin(),b.end() )
- {
- if (*it==*it2)
+ public:
+ typedef sat::LookupAttr::iterator base_iterator;
+
+ public:
+ const base_iterator & end() const
+ {
+ static base_iterator _end;
+ return _end;
+ }
+
+ bool advance( base_iterator & base_r ) const
+ {
+ if ( base_r == end() )
+ base_r = startNewQyery(); // first candidate
+ else
+ {
+ base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
+ ++base_r; // advance to next candidate
+ }
+
+ while ( base_r != end() )
+ {
+ if ( isAMatch( base_r ) )
+ return true;
+ // No match: try next
+ ++base_r;
+ }
+ return false;
+ }
+
+ /** Provide all matching attributes within this solvable.
+ *
+ */
+ void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
{
- finded = true;
- break;
+ if ( base_r == end() )
+ return;
+
+ sat::Solvable inSolvable( base_r.inSolvable() );
+
+ if ( _attrMatchList.size() == 1 )
+ {
+ // base_r is already on the 1st matching attribute!
+ // String matching is done by the base iterator. We must check the predicate here.
+ // Let's see if there are more matches for this solvable:
+ base_iterator base( base_r );
+ base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
+ return_r.push_back( base );
+
+ const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+ for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
+ {
+ if ( ! predicate || predicate( base ) )
+ return_r.push_back( base );
+ }
+ }
+ else
+ {
+ // Here: search all attributes ;(
+ for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
+ {
+ const AttrMatchData & matchData( *mi );
+ sat::LookupAttr q( matchData.attr, inSolvable );
+ if ( matchData.strMatcher ) // an empty searchstring matches always
+ q.setStrMatcher( matchData.strMatcher );
+
+ if ( ! q.empty() ) // there are matches.
+ {
+ // now check any predicate:
+ const AttrMatchData::Predicate & predicate( matchData.predicate );
+ for_( it, q.begin(), q.end() )
+ {
+ if ( ! predicate || predicate( it ) )
+ return_r.push_back( it );
+ }
+ }
+ }
+ }
}
+
+ public:
+ /** Ctor stores the \ref PoolQuery settings.
+ * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
+ */
+ PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
+ {
+ query_r->compile();
+
+ // Repo restriction:
+ sat::Pool satpool( sat::Pool::instance() );
+
+ for_( it, query_r->_repos.begin(), query_r->_repos.end() )
+ {
+ Repository r( satpool.reposFind( *it ) );
+ if ( r )
+ _repos.insert( r );
+ else
+ _neverMatchRepo = true;
+ }
+ // _neverMatchRepo: we just need to catch the case that no repo
+ // matched, so we'd interpret the empty list as 'take from all'
+ if ( _neverMatchRepo && ! _repos.empty() )
+ _neverMatchRepo = false;
+
+ // Kind restriction:
+ _kinds = query_r->_kinds;
+ // Edition restriction:
+ _op = query_r->_op;
+ _edition = query_r->_edition;
+ // Status restriction:
+ _status_flags = query_r->_status_flags;
+ // StrMatcher
+ _attrMatchList = query_r->_attrMatchList;
+ }
+
+ ~PoolQueryMatcher()
+ {}
+
+ private:
+ /** Initialize a new base query. */
+ base_iterator startNewQyery() const
+ {
+ sat::LookupAttr q;
+
+ if ( _neverMatchRepo )
+ return q.end();
+
+ // Repo restriction:
+ if ( _repos.size() == 1 )
+ q.setRepo( *_repos.begin() );
+ // else: handled in isAMatch.
+
+ // Attribute restriction:
+ if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
+ {
+ const AttrMatchData & matchData( _attrMatchList.front() );
+ q.setAttr( matchData.attr );
+ if ( matchData.strMatcher ) // empty searchstring matches always
+ q.setStrMatcher( matchData.strMatcher );
+ }
+ else // more than 1 attr (but not all)
+ {
+ // no restriction, it's all handled in isAMatch.
+ q.setAttr( sat::SolvAttr::allAttr );
+ }
+
+ return q.begin();
+ }
+
+
+ /** Check whether we are on a match.
+ *
+ * The check covers the whole Solvable, not just the current
+ * attribute \c base_r points to. If there's no match, also
+ * prepare \c base_r to advance appropriately. If there is
+ * a match, simply return \c true. \ref advance always moves
+ * to the next Solvable if there was a match.
+ *
+ * \note: Caller asserts we're not at \ref end.
+ */
+ bool isAMatch( base_iterator & base_r ) const
+ {
+ /////////////////////////////////////////////////////////////////////
+ Repository inRepo( base_r.inRepo() );
+ // Status restriction:
+ if ( _status_flags
+ && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
+ {
+ base_r.nextSkipRepo();
+ return false;
+ }
+ // Repo restriction:
+ if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
+ {
+ base_r.nextSkipRepo();
+ return false;
+ }
+ /////////////////////////////////////////////////////////////////////
+ sat::Solvable inSolvable( base_r.inSolvable() );
+ // Kind restriction:
+ if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
+ {
+ base_r.nextSkipSolvable();
+ return false;
+ }
+
+ // Edition restriction:
+ if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
+ {
+ base_r.nextSkipSolvable();
+ return false;
+ }
+ /////////////////////////////////////////////////////////////////////
+ // string and predicate matching:
+
+ if ( _attrMatchList.size() == 1 )
+ {
+ // String matching was done by the base iterator.
+ // Now check any predicate:
+ const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
+ if ( ! predicate || predicate( base_r ) )
+ return true;
+
+ return false; // no skip as there may be more occurrences od this attr.
+ }
+
+ // Here: search all attributes ;(
+ for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
+ {
+ const AttrMatchData & matchData( *mi );
+ sat::LookupAttr q( matchData.attr, inSolvable );
+ if ( matchData.strMatcher ) // an empty searchstring matches always
+ q.setStrMatcher( matchData.strMatcher );
+
+ if ( ! q.empty() ) // there are matches.
+ {
+ // now check any predicate:
+ const AttrMatchData::Predicate & predicate( matchData.predicate );
+ if ( predicate )
+ {
+ for_( it, q.begin(), q.end() )
+ {
+ if ( predicate( it ) )
+ return true;
+ }
+ }
+ else
+ return true;
+ }
+ }
+ base_r.nextSkipSolvable();
+ return false;
+ }
+
+ private:
+ /** Repositories include in the search. */
+ std::set<Repository> _repos;
+ DefaultIntegral<bool,false> _neverMatchRepo;
+ /** Resolvable kinds to include. */
+ std::set<ResKind> _kinds;
+ /** Edition filter. */
+ Rel _op;
+ Edition _edition;
+ /** Installed status filter flags. \see PoolQuery::StatusFilter */
+ int _status_flags;
+ /** StrMatcher per attribtue. */
+ AttrMatchList _attrMatchList;
+ };
+ ///////////////////////////////////////////////////////////////////
+
+ void PoolQueryIterator::increment()
+ {
+ // matcher restarts if at end! It is called from the ctor
+ // to get the 1st match. But if the end is reached, it should
+ // be deleted, otherwise we'd start over again.
+ if ( !_matcher )
+ return; // at end
+ if ( _matches )
+ _matches.reset(); // invalidate old matches
+ if ( ! _matcher->advance( base_reference() ) )
+ _matcher.reset();
+ }
+
+ const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
+ {
+ if ( _matches )
+ return *_matches;
+
+ if ( !_matcher )
+ {
+ // at end of query:
+ static const Matches _none;
+ return _none;
}
- if (!finded)
- return false;
+ _matches.reset( new Matches );
+ _matcher->matchDetail( base_reference(), *_matches );
+ return *_matches;
}
- return true;
- }
- bool equal(const PoolQuery& a, const PoolQuery& b)
- {
- if( a.matchType()!=b.matchType() )
- return false;
- if( a.matchWord()!=b.matchWord())
- return false;
- if( a.requireAll()!=b.requireAll() )
- return false;
- if(!equalContainers(a.strings(), b.strings()))
- return false;
- if(!equalContainers(a.kinds(), b.kinds()))
- return false;
- if(!equalContainers(a.repos(), b.repos()))
- return false;
- if(!equalContainers(a.attributes(), b.attributes()))
- return false;
+ std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
+ {
+ str << *obj;
+ if ( ! obj.matchesEmpty() )
+ {
+ for_( it, obj.matchesBegin(), obj.matchesEnd() )
+ {
+ str << endl << " " << it->inSolvAttr() << "\t" << it->asString();
+ }
+ }
+ return str;
+ }
- return true;
- }
+ ///////////////////////////////////////////////////////////////////
+ } //namespace detail
+ ///////////////////////////////////////////////////////////////////
+ detail::PoolQueryIterator PoolQuery::begin() const
+ {
+ return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
+ }
/////////////////////////////////////////////////////////////////
} // namespace zypp