1 /*---------------------------------------------------------------------\
3 | |__ / \ / / . \ . \ |
8 \---------------------------------------------------------------------*/
9 /** \file zypp/PoolQuery.cc
15 #include "zypp/base/Gettext.h"
16 #include "zypp/base/LogTools.h"
17 #include "zypp/base/Algorithm.h"
18 #include "zypp/base/String.h"
19 #include "zypp/repo/RepoException.h"
20 #include "zypp/RelCompare.h"
22 #include "zypp/sat/Pool.h"
23 #include "zypp/sat/Solvable.h"
24 #include "zypp/base/StrMatcher.h"
26 #include "zypp/PoolQuery.h"
28 #undef ZYPP_BASE_LOGGER_LOGGROUP
29 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
32 using namespace zypp::sat;
34 ///////////////////////////////////////////////////////////////////
36 { /////////////////////////////////////////////////////////////////
38 ///////////////////////////////////////////////////////////////////
40 { /////////////////////////////////////////////////////////////////
42 /////////////////////////////////////////////////////////////////
43 // some Helpers and Predicates
44 /////////////////////////////////////////////////////////////////
46 bool isDependencyAttribute( sat::SolvAttr attr_r )
48 static sat::SolvAttr deps[] = {
55 SolvAttr::supplements,
58 for_( it, arrayBegin(deps), arrayEnd(deps) )
64 /** Whether the current capabilities edition range ovelaps and/or its solvables arch matches.
65 * Query asserts \a iter_r points to a capability and we
66 * have to check the range only.
68 struct EditionRangePredicate
70 EditionRangePredicate( const Rel & op, const Edition & edition )
71 : _range( op, edition )
74 EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
75 : _range( op, edition )
79 bool operator()( sat::LookupAttr::iterator iter_r )
81 if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
84 CapDetail cap( iter_r.id() );
85 if ( ! cap.isSimple() )
87 if ( cap.isNamed() ) // no range to match
89 return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
92 std::string serialize() const
94 std::string ret( "EditionRange" );
95 str::appendEscaped( ret, _range.op.asString() );
96 str::appendEscaped( ret, _range.value.asString() );
97 str::appendEscaped( ret, _arch.asString() );
101 Edition::MatchRange _range;
105 /** Whether the current Solvables edition is within a given range and/or its arch matches. */
106 struct SolvableRangePredicate
108 SolvableRangePredicate( const Rel & op, const Edition & edition )
109 : _range( op, edition )
110 , _arch( Arch_empty )
113 SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
114 : _range( op, edition )
118 bool operator()( sat::LookupAttr::iterator iter_r )
120 if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
122 return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
125 std::string serialize() const
127 std::string ret( "SolvableRange" );
128 str::appendEscaped( ret, _range.op.asString() );
129 str::appendEscaped( ret, _range.value.asString() );
130 str::appendEscaped( ret, _arch.asString() );
134 Edition::MatchRange _range;
138 /** Whether the current capability matches a given one.
139 * Query asserts \a iter_r points to a capability and we
140 * have to check the match only.
142 struct CapabilityMatchPredicate
144 CapabilityMatchPredicate( Capability cap_r )
148 bool operator()( sat::LookupAttr::iterator iter_r ) const
150 return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
153 std::string serialize() const
155 std::string ret( "CapabilityMatch" );
156 str::appendEscaped( ret, _cap.asString() );
163 /////////////////////////////////////////////////////////////////
165 /////////////////////////////////////////////////////////////////
166 /** Match data per attribtue.
168 * This includes the attribute itself, an optional \ref StrMatcher
169 * to restrict the query to certain string values, and an optional
170 * boolean \ref Predicate that may apply further restrictions that can
171 * not be expressed by the \ref strMatcher.
173 * Example for such a \ref predicate would be an additional edition range
174 * check whan looking for dependencies. The \ref strMatcher would
175 * find potential matches by looking at the dependencies name, the
176 * predicate will then check the edition ranges.
178 * As the \ref predicate takes an iterator pointing to the current
179 * match, it's also suitable for sub-structure (flexarray) inspection
180 * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
182 * \note: \see \ref addPredicate for further constraints.
186 typedef function<bool(sat::LookupAttr::iterator)> Predicate;
188 static bool always( sat::LookupAttr::iterator ) { return true; }
189 static bool never( sat::LookupAttr::iterator ) { return false; }
194 AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
196 , strMatcher( strMatcher_r )
199 AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
200 const Predicate & predicate_r, const std::string & predicateStr_r )
202 , strMatcher( strMatcher_r )
203 , predicate( predicate_r )
204 , predicateStr( predicateStr_r )
207 /** A usable Predicate must provide a string serialization.
208 * As there is no \c operator== for \ref Predicate, we compare it's
209 * string representation instead. If you add new predicated, check the
210 * deserialization code in \ref deserialize.
212 template<class _Predicate>
213 void addPredicate( const _Predicate & predicate_r )
215 predicate = predicate_r;
216 predicateStr = predicate_r.serialize();
219 /** Dumb serialization.
221 * AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
224 std::string serialize() const
226 std::string ret( "AttrMatchData" );
227 str::appendEscaped( ret, attr.asString() );
228 str::appendEscaped( ret, strMatcher.searchstring() );
229 // TODO: Actually the flag should be serialized too, but for PoolQuery
230 // it's by now sufficient to differ between mode OTHER and others,
231 // i.e. whether to compile or not compile.
232 str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
233 str::appendEscaped( ret, predicateStr );
237 /** Dumb restore from serialized string.
238 * \throw Exception on parse error.
240 static AttrMatchData deserialize( const std::string & str_r )
242 std::vector<std::string> words;
243 str::splitEscaped( str_r, std::back_inserter(words) );
244 if ( words.empty() || words[0] != "AttrMatchData" )
245 ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
246 if ( words.size() != 5 )
247 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
250 ret.attr = sat::SolvAttr( words[1] );
251 ret.strMatcher = StrMatcher( words[2] );
252 if ( words[3] == "C" )
253 ret.strMatcher.setFlags( Match::OTHER );
254 ret.predicateStr = words[4];
258 str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
259 if ( ! words.empty() )
261 if ( words[0] == "EditionRange" )
263 switch( words.size() )
266 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
269 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
272 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
276 else if ( words[0] == "SolvableRange" )
278 switch( words.size() )
281 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
284 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
287 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
291 else if ( words[0] == "CapabilityMatch" )
293 if ( words.size() != 2 )
294 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
295 ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
298 ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
304 StrMatcher strMatcher;
306 std::string predicateStr;
309 /** \relates AttrMatchData */
310 inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
312 str << obj.attr << ": " << obj.strMatcher;
314 str << " +(" << obj.predicateStr << ")";
318 /** \relates AttrMatchData */
319 inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
321 return ( lhs.attr == rhs.attr
322 && lhs.strMatcher == rhs.strMatcher
323 && lhs.predicateStr == rhs.predicateStr );
326 /** \relates AttrMatchData */
327 inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
328 { return !( lhs == rhs ); }
330 /** \relates AttrMatchData Arbitrary order for std::container. */
331 inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
333 if ( lhs.attr != rhs.attr )
334 return ( lhs.attr < rhs.attr );
335 if ( lhs.strMatcher != rhs.strMatcher )
336 return ( lhs.strMatcher < rhs.strMatcher );
337 if ( lhs.predicateStr != rhs.predicateStr )
338 return ( lhs.predicateStr < rhs.predicateStr );
342 typedef std::list<AttrMatchData> AttrMatchList;
345 } /////////////////////////////////////////////////////////////////
347 ///////////////////////////////////////////////////////////////////
349 ///////////////////////////////////////////////////////////////////
351 // CLASS NAME : PoolQuery::Impl
354 class PoolQuery::Impl
358 : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
360 , _require_all(false)
368 /** String representation */
369 string asString() const;
371 /** \name Raw query options. */
373 /** Raw search strings. */
374 StrContainer _strings;
375 /** Raw attributes */
376 AttrRawStrMap _attrs;
377 /** Uncompiled attributes with predicate. */
378 std::set<AttrMatchData> _uncompiledPredicated;
380 /** Sat solver search flags */
385 /** Sat solver status flags */
386 StatusFilter _status_flags;
388 /** Edition condition operand */
390 /** Operator for edition condition */
393 /** Repos to search. */
396 /** Kinds to search */
402 bool operator==( const PoolQuery::Impl & rhs ) const
404 if ( _flags == rhs._flags
405 // bnc#792901: while libzypp uses exact match mode for a single
406 // package name lock, zypper always uses glob. :(
407 // We unify those two forms to enable zypper to remove zypp locks
408 // without need to actually evaluate the query (which would require
409 // repos to be loaded).
410 || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
411 || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
413 && _attrs.size() == 1
414 && _attrs.begin()->first == sat::SolvAttr::name ) )
416 return ( _strings == rhs._strings
417 && _attrs == rhs._attrs
418 && _uncompiledPredicated == rhs._uncompiledPredicated
419 && _match_word == rhs._match_word
420 && _require_all == rhs._require_all
421 && _status_flags == rhs._status_flags
422 && _edition == rhs._edition
424 && _repos == rhs._repos
425 && _kinds == rhs._kinds );
430 bool operator!=( const PoolQuery::Impl & rhs ) const
431 { return ! operator==( rhs ); }
434 /** Compile the regex.
435 * Basically building the \ref _attrMatchList from strings.
436 * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
438 void compile() const;
440 /** StrMatcher per attribtue. */
441 mutable AttrMatchList _attrMatchList;
444 /** Pass flags from \ref compile, as they may have been changed. */
445 string createRegex( const StrContainer & container, const Match & flags ) const;
448 friend Impl * rwcowClone<Impl>( const Impl * rhs );
449 /** clone for RWCOW_pointer */
451 { return new Impl( *this ); }
454 ///////////////////////////////////////////////////////////////////
458 MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
460 bool operator()(const string & str)
466 PoolQuery::StrContainer & _cont;
472 bool operator()(const string & str)
478 void PoolQuery::Impl::compile() const
480 _attrMatchList.clear();
482 Match cflags( _flags );
483 if ( cflags.mode() == Match::OTHER ) // this will never succeed...
484 ZYPP_THROW( MatchUnknownModeException( cflags ) );
486 /** Compiled search strings. */
490 // 'different' - will have to iterate through all and match by ourselves (slow)
491 // 'same' - will pass the compiled string to dataiterator_init
492 // 'one-attr' - will pass it to dataiterator_init
493 // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
498 // create regex; store in rcstrings; if more strings flag regex;
501 ; // A default 'query-all' will be added after all sources are processed.
505 // else if _attrs is not empty but it contains just one attr
506 // for all _strings and _attr[key] strings
507 // create regex; flag 'one-attr'; if more strings flag regex;
508 else if (_attrs.size() == 1)
511 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
512 invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
513 rcstrings = createRegex(joined, cflags);
514 if (joined.size() > 1) // switch to regex for multiple strings
515 cflags.setModeRegex();
516 _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
517 StrMatcher( rcstrings, cflags ) ) );
520 // // MULTIPLE ATTRIBUTES
523 // check whether there are any per-attribute strings
524 bool attrvals_empty = true;
525 for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
526 if (!ai->second.empty())
527 for(StrContainer::const_iterator it = ai->second.begin();
528 it != ai->second.end(); it++)
531 attrvals_empty = false;
532 goto attremptycheckend;
536 // chceck whether the per-attribute strings are all the same
537 bool attrvals_thesame = true;
538 AttrRawStrMap::const_iterator ai = _attrs.begin();
539 const StrContainer & set1 = ai->second;
541 for (; ai != _attrs.end(); ++ai)
545 set1.begin(), set1.end(),
546 ai->second.begin(), ai->second.end(),
547 inserter(result, result.begin())/*, ltstr()*/);
550 attrvals_thesame = false;
555 // // THE SAME STRINGS FOR DIFFERENT ATTRS
556 // else if _attrs is not empty but it does not contain strings
557 // for each key in _attrs take all _strings
558 // create regex; store in rcstrings; flag 'same'; if more strings flag regex;
559 if (attrvals_empty || attrvals_thesame)
564 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
565 rcstrings = createRegex(joined, cflags);
569 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
570 invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
571 rcstrings = createRegex(joined, cflags);
573 if (joined.size() > 1) // switch to regex for multiple strings
574 cflags.setModeRegex();
575 // May use the same StrMatcher for all
576 StrMatcher matcher( rcstrings, cflags );
577 for_( ai, _attrs.begin(), _attrs.end() )
579 _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
583 // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
584 // if _attrs is not empty and it contains non-empty vectors with non-empty strings
585 // for each key in _attrs take all _strings + all _attrs[key] strings
586 // create regex; flag 'different'; if more strings flag regex;
589 for_(ai, _attrs.begin(), _attrs.end())
592 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
593 invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
594 string s = createRegex(joined, cflags);
595 if (joined.size() > 1) // switch to regex for multiple strings
596 cflags.setModeRegex();
597 _attrMatchList.push_back( AttrMatchData( ai->first,
598 StrMatcher( s, cflags ) ) );
603 // Now handle any predicated queries
604 if ( ! _uncompiledPredicated.empty() )
607 invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
608 for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
610 if ( it->strMatcher.flags().mode() == Match::OTHER )
613 StrContainer joined( global );
614 const std::string & mstr( it->strMatcher.searchstring() );
615 if ( ! mstr.empty() )
616 joined.insert( mstr );
619 rcstrings = createRegex( joined, cflags );
620 if ( joined.size() > 1 ) // switch to regex for multiple strings
621 cflags.setModeRegex();
623 _attrMatchList.push_back( AttrMatchData( it->attr,
624 StrMatcher( rcstrings, cflags ),
625 it->predicate, it->predicateStr ) );
630 _attrMatchList.push_back( *it );
635 // If no attributes defined at all, then add 'query all'
636 if ( _attrMatchList.empty() )
639 rcstrings = createRegex( _strings, cflags );
640 if ( _strings.size() > 1 ) // switch to regex for multiple strings
641 cflags.setModeRegex();
642 _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
643 StrMatcher( rcstrings, cflags ) ) );
646 // Finally check here, whether all involved regex compile.
647 for_( it, _attrMatchList.begin(), _attrMatchList.end() )
649 it->strMatcher.compile(); // throws on error
651 //DBG << asString() << endl;
656 * Converts '*' and '?' wildcards within str into their regex equivalents.
658 static string wildcards2regex(const string & str)
660 string regexed = str;
662 string r_all(".*"); // regex equivalent of '*'
663 string r_one("."); // regex equivalent of '?'
664 string::size_type pos;
666 // replace all "*" in input with ".*"
667 for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
668 regexed = regexed.replace(pos, 1, r_all);
670 // replace all "?" in input with "."
671 for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
672 regexed = regexed.replace(pos, 1, r_one);
677 string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
679 //! macro for word boundary tags for regexes
680 #define WB (_match_word ? string("\\b") : string())
683 if (container.empty())
686 if (container.size() == 1)
688 return WB + *container.begin() + WB;
693 bool use_wildcards = flags.isModeGlob();
694 StrContainer::const_iterator it = container.begin();
698 tmp = wildcards2regex(*it);
704 if ( ! flags.isModeString() ) // not match exact
705 tmp += ".*" + WB + tmp;
706 rstr = "(?=" + tmp + ")";
710 if ( flags.isModeString() || flags.isModeGlob() )
712 rstr += WB + "(" + tmp;
717 for (; it != container.end(); ++it)
720 tmp = wildcards2regex(*it);
726 if ( ! flags.isModeString() ) // not match exact
727 tmp += ".*" + WB + tmp;
728 rstr += "(?=" + tmp + ")";
738 if ( ! flags.isModeString() ) // not match exact
744 if ( flags.isModeString() || flags.isModeGlob() )
752 string PoolQuery::Impl::asString() const
757 if ( _kinds.empty() )
761 for(Kinds::const_iterator it = _kinds.begin();
762 it != _kinds.end(); ++it)
768 if ( _repos.empty() )
772 for(StrContainer::const_iterator it = _repos.begin();
773 it != _repos.end(); ++it)
778 o << "version: "<< _op << " " << _edition.asString() << endl;
779 o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
782 o << "string match flags: " << Match(_flags) << endl;
786 for(StrContainer::const_iterator it = _strings.begin();
787 it != _strings.end(); ++it)
791 o << "attributes: " << endl;
792 for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
794 o << "* " << ai->first << ": ";
795 for(StrContainer::const_iterator vi = ai->second.begin();
796 vi != ai->second.end(); ++vi)
801 o << "predicated: " << endl;
802 for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
804 o << "* " << *it << endl;
808 o << "last attribute matcher compiled: " << endl;
809 if ( _attrMatchList.empty() )
811 o << "not yet compiled" << endl;
815 for_( it, _attrMatchList.begin(), _attrMatchList.end() )
817 o << "* " << *it << endl;
823 ///////////////////////////////////////////////////////////////////
825 ///////////////////////////////////////////////////////////////////
827 // CLASS NAME : PoolQuery
829 ///////////////////////////////////////////////////////////////////
831 PoolQuery::PoolQuery()
835 PoolQuery::~PoolQuery()
838 void PoolQuery::addRepo(const std::string &repoalias)
840 if (repoalias.empty())
842 WAR << "ignoring an empty repository alias" << endl;
845 _pimpl->_repos.insert(repoalias);
848 void PoolQuery::addKind(const ResKind & kind)
849 { _pimpl->_kinds.insert(kind); }
851 void PoolQuery::addString(const string & value)
852 { _pimpl->_strings.insert(value); }
854 void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
855 { _pimpl->_attrs[attr].insert(value); }
857 void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
858 { return addDependency( attr, name, op, edition, Arch_empty ); }
860 void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
862 switch ( op.inSwitch() )
864 case Rel::ANY_e: // no additional constraint on edition.
865 if ( arch.empty() ) // no additional constraint on arch.
867 addAttribute( attr, name );
872 case Rel::NONE_e: // will never match.
875 default: // go and add the predicated query (uncompiled)
879 // Match::OTHER indicates need to compile
880 // (merge global search strings into name).
881 AttrMatchData attrMatchData( attr, StrMatcher( name, Match::OTHER ) );
883 if ( isDependencyAttribute( attr ) )
884 attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
886 attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
888 _pimpl->_uncompiledPredicated.insert( attrMatchData );
891 void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
893 CapDetail cap( cap_r );
894 if ( ! cap.isSimple() ) // will never match.
897 // Matches STRING per default. (won't get compiled!)
898 AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
900 if ( isDependencyAttribute( attr ) )
901 attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
903 attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
905 _pimpl->_uncompiledPredicated.insert( attrMatchData );
908 void PoolQuery::setEdition(const Edition & edition, const Rel & op)
910 _pimpl->_edition = edition;
914 void PoolQuery::setMatchSubstring() { _pimpl->_flags.setModeSubstring(); }
915 void PoolQuery::setMatchExact() { _pimpl->_flags.setModeString(); }
916 void PoolQuery::setMatchRegex() { _pimpl->_flags.setModeRegex(); }
917 void PoolQuery::setMatchGlob() { _pimpl->_flags.setModeGlob(); }
918 void PoolQuery::setMatchWord()
920 _pimpl->_match_word = true;
921 _pimpl->_flags.setModeRegex();
924 Match PoolQuery::flags() const
925 { return _pimpl->_flags; }
926 void PoolQuery::setFlags( const Match & flags )
927 { _pimpl->_flags = flags; }
930 void PoolQuery::setInstalledOnly()
931 { _pimpl->_status_flags = INSTALLED_ONLY; }
932 void PoolQuery::setUninstalledOnly()
933 { _pimpl->_status_flags = UNINSTALLED_ONLY; }
934 void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
935 { _pimpl->_status_flags = flags; }
938 void PoolQuery::setRequireAll(bool require_all)
939 { _pimpl->_require_all = require_all; }
942 const PoolQuery::StrContainer &
943 PoolQuery::strings() const
944 { return _pimpl->_strings; }
946 const PoolQuery::AttrRawStrMap &
947 PoolQuery::attributes() const
948 { return _pimpl->_attrs; }
950 const PoolQuery::StrContainer &
951 PoolQuery::attribute(const sat::SolvAttr & attr) const
953 static const PoolQuery::StrContainer nocontainer;
954 AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
955 return it != _pimpl->_attrs.end() ? it->second : nocontainer;
958 const Edition PoolQuery::edition() const
959 { return _pimpl->_edition; }
960 const Rel PoolQuery::editionRel() const
961 { return _pimpl->_op; }
964 const PoolQuery::Kinds &
965 PoolQuery::kinds() const
966 { return _pimpl->_kinds; }
968 const PoolQuery::StrContainer &
969 PoolQuery::repos() const
970 { return _pimpl->_repos; }
973 bool PoolQuery::caseSensitive() const
974 { return !_pimpl->_flags.test( Match::NOCASE ); }
975 void PoolQuery::setCaseSensitive( bool value )
976 { _pimpl->_flags.turn( Match::NOCASE, !value ); }
978 bool PoolQuery::filesMatchFullPath() const
979 { return _pimpl->_flags.test( Match::FILES ); }
980 void PoolQuery::setFilesMatchFullPath( bool value )
981 { _pimpl->_flags.turn( Match::FILES, value ); }
983 bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); }
984 bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring(); }
985 bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
986 bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
988 bool PoolQuery::matchWord() const
989 { return _pimpl->_match_word; }
991 bool PoolQuery::requireAll() const
992 { return _pimpl->_require_all; }
994 PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
995 { return _pimpl->_status_flags; }
997 bool PoolQuery::empty() const
999 try { return begin() == end(); }
1000 catch (const Exception & ex) {}
1004 PoolQuery::size_type PoolQuery::size() const
1008 size_type count = 0;
1009 for_( it, begin(), end() )
1013 catch (const Exception & ex) {}
1017 void PoolQuery::execute(ProcessResolvable fnc)
1018 { invokeOnEach( begin(), end(), fnc); }
1021 ///////////////////////////////////////////////////////////////////
1023 // CLASS NAME : PoolQuery::Attr
1026 * represents all atributes in PoolQuery except SolvAtributes, which are
1027 * used as is (not needed extend anything if someone adds new solv attr)
1029 struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1032 friend class IdStringType<PoolQueryAttr>;
1039 explicit PoolQueryAttr( const char* cstr_r )
1043 explicit PoolQueryAttr( const std::string & str_r )
1047 // unknown atributes
1048 static const PoolQueryAttr noAttr;
1050 // PoolQuery's own attributes
1051 static const PoolQueryAttr repoAttr;
1052 static const PoolQueryAttr kindAttr;
1053 static const PoolQueryAttr stringAttr;
1054 static const PoolQueryAttr stringTypeAttr;
1055 static const PoolQueryAttr requireAllAttr;
1056 static const PoolQueryAttr caseSensitiveAttr;
1057 static const PoolQueryAttr installStatusAttr;
1058 static const PoolQueryAttr editionAttr;
1059 static const PoolQueryAttr complexAttr;
1062 const PoolQueryAttr PoolQueryAttr::noAttr;
1064 const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1065 const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1066 const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1067 const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1068 const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
1069 const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1070 const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1071 const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1072 const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1074 class StringTypeAttr : public IdStringType<PoolQueryAttr>
1076 friend class IdStringType<StringTypeAttr>;
1081 explicit StringTypeAttr( const char* cstr_r )
1083 explicit StringTypeAttr( const std::string & str_r )
1086 static const StringTypeAttr noAttr;
1088 static const StringTypeAttr exactAttr;
1089 static const StringTypeAttr substringAttr;
1090 static const StringTypeAttr regexAttr;
1091 static const StringTypeAttr globAttr;
1092 static const StringTypeAttr wordAttr;
1095 const StringTypeAttr StringTypeAttr::noAttr;
1097 const StringTypeAttr StringTypeAttr::exactAttr("exact");
1098 const StringTypeAttr StringTypeAttr::substringAttr("substring");
1099 const StringTypeAttr StringTypeAttr::regexAttr("regex");
1100 const StringTypeAttr StringTypeAttr::globAttr("glob");
1101 const StringTypeAttr StringTypeAttr::wordAttr("word");
1103 ///////////////////////////////////////////////////////////////////
1106 //\TODO maybe ctor with stream can be usefull
1107 //\TODO let it throw, let it throw, let it throw.
1108 bool PoolQuery::recover( istream &str, char delim )
1110 bool finded_something = false; //indicates some atributes is finded
1116 getline( str, s, delim );
1118 if ((!s.empty()) && s[0]=='#') //comment
1123 string::size_type pos = s.find(':');
1124 if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1126 if (finded_something) //is first blank line after record?
1136 finded_something = true;
1138 string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1139 string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1141 PoolQueryAttr attribute( attrName );
1143 if ( attribute==PoolQueryAttr::repoAttr )
1145 addRepo( attrValue );
1147 /* some backwards compatibility */
1148 else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1150 addKind( ResKind(attrValue) );
1152 else if ( attribute==PoolQueryAttr::stringAttr
1153 || attribute=="global_string")
1155 addString( attrValue );
1157 else if ( attribute==PoolQueryAttr::stringTypeAttr
1158 || attribute=="string_type" )
1160 StringTypeAttr s(attrValue);
1161 if( s == StringTypeAttr::regexAttr )
1165 else if ( s == StringTypeAttr::globAttr )
1169 else if ( s == StringTypeAttr::exactAttr )
1173 else if ( s == StringTypeAttr::substringAttr )
1175 setMatchSubstring();
1177 else if ( s == StringTypeAttr::wordAttr )
1181 else if ( s == StringTypeAttr::noAttr )
1183 WAR << "unknown string type " << attrValue << endl;
1187 WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1190 else if ( attribute==PoolQueryAttr::requireAllAttr )
1192 if ( str::strToTrue(attrValue) )
1194 setRequireAll(true);
1196 else if ( !str::strToFalse(attrValue) )
1198 setRequireAll(false);
1202 WAR << "unknown boolean value " << attrValue << endl;
1205 else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1207 if ( str::strToTrue(attrValue) )
1209 setCaseSensitive(true);
1211 else if ( !str::strToFalse(attrValue) )
1213 setCaseSensitive(false);
1217 WAR << "unknown boolean value " << attrValue << endl;
1220 else if ( attribute==PoolQueryAttr::installStatusAttr )
1222 if( attrValue == "all" )
1224 setStatusFilterFlags( ALL );
1226 else if( attrValue == "installed" )
1230 else if( attrValue == "not-installed" )
1232 setUninstalledOnly();
1236 WAR << "Unknown value for install status " << attrValue << endl;
1239 else if ( attribute == PoolQueryAttr::editionAttr)
1241 string::size_type pos;
1243 if (attrValue.find_first_of("=<>!") == 0)
1245 pos = attrValue.find_last_of("=<>");
1246 rel = Rel(attrValue.substr(0, pos+1));
1247 attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1250 setEdition(Edition(attrValue), rel);
1252 else if ( attribute == PoolQueryAttr::complexAttr )
1256 _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1258 catch ( const Exception & err )
1260 WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1264 else if ( attribute==PoolQueryAttr::noAttr )
1266 WAR << "empty attribute name" << endl;
1270 string s = attrName;
1271 str::replaceAll( s,"_",":" );
1273 if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1275 Capability c( attrValue );
1277 if ( d.isVersioned() )
1278 addDependency( a, d.name().asString(), d.op(), d.ed() );
1280 addDependency( a, attrValue );
1283 addAttribute( a, attrValue );
1288 return finded_something;
1291 void PoolQuery::serialize( ostream &str, char delim ) const
1295 //iterate thrue all settings and write it
1296 static const zypp::PoolQuery q; //not save default options, so create default query example
1298 for_( it, repos().begin(), repos().end() )
1300 str << "repo: " << *it << delim ;
1303 for_( it, kinds().begin(), kinds().end() )
1305 str << PoolQueryAttr::kindAttr.asString() << ": "
1306 << it->idStr() << delim ;
1309 if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1310 str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1312 if (matchMode()!=q.matchMode())
1314 switch( matchMode() )
1317 str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1319 case Match::SUBSTRING:
1320 str << PoolQueryAttr::stringTypeAttr.asString()
1321 << ": substring" << delim;
1324 str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1327 str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1330 WAR << "unknown match type " << matchMode() << endl;
1334 if( caseSensitive() != q.caseSensitive() )
1336 str << "case_sensitive: ";
1337 if (caseSensitive())
1339 str << "on" << delim;
1343 str << "off" << delim;
1347 if( requireAll() != q.requireAll() )
1349 str << "require_all: ";
1352 str << "on" << delim;
1356 str << "off" << delim;
1360 if( statusFilterFlags() != q.statusFilterFlags() )
1362 switch( statusFilterFlags() )
1365 str << "install_status: all" << delim;
1367 case INSTALLED_ONLY:
1368 str << "install_status: installed" << delim;
1370 case UNINSTALLED_ONLY:
1371 str << "install_status: not-installed" << delim;
1376 for_( it, strings().begin(), strings().end() )
1378 str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1381 for_( it, attributes().begin(), attributes().end() )
1383 string s = it->first.asString();
1384 str::replaceAll(s,":","_");
1385 for_( it2,it->second.begin(),it->second.end() )
1387 str << s <<": "<< *it2 << delim;
1391 for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1393 str << "complex: "<< it->serialize() << delim;
1396 //separating delim - protection
1400 string PoolQuery::asString() const
1401 { return _pimpl->asString(); }
1403 ostream & operator<<( ostream & str, const PoolQuery & obj )
1404 { return str << obj.asString(); }
1406 std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1407 { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1409 bool PoolQuery::operator==( const PoolQuery & rhs ) const
1410 { return *_pimpl == *rhs._pimpl; }
1412 ///////////////////////////////////////////////////////////////////
1414 { /////////////////////////////////////////////////////////////////
1416 ///////////////////////////////////////////////////////////////////
1418 // CLASS NAME : PoolQueryMatcher
1420 /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1422 * Basically the matcher performs a base query, which should preselect
1423 * candidates for a match. And has some filter conditions on top of it.
1424 * Query and fileter depend on the \ref PoolQuery settings.
1426 * Matcher must be stateless, as it is shared between multiple
1427 * \ref PoolQueryIterator instances.
1429 * If \ref base_iterator is at the \ref end, \ref advance moves it
1430 * to the first match. Otherwise advance moves to the next match, or
1431 * to the \ref end, if there is no more match.
1433 * \note The original implementation treated an empty search string as
1434 * <it>"match always"</it>. We stay compatible.
1436 class PoolQueryMatcher
1439 typedef sat::LookupAttr::iterator base_iterator;
1442 const base_iterator & end() const
1444 static base_iterator _end;
1448 bool advance( base_iterator & base_r ) const
1450 if ( base_r == end() )
1451 base_r = startNewQyery(); // first candidate
1454 base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1455 ++base_r; // advance to next candidate
1458 while ( base_r != end() )
1460 if ( isAMatch( base_r ) )
1462 // No match: try next
1468 /** Provide all matching attributes within this solvable.
1471 void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1473 if ( base_r == end() )
1476 sat::Solvable inSolvable( base_r.inSolvable() );
1478 if ( _attrMatchList.size() == 1 )
1480 // base_r is already on the 1st matching attribute!
1481 // String matching is done by the base iterator. We must check the predicate here.
1482 // Let's see if there are more matches for this solvable:
1483 base_iterator base( base_r );
1484 base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1485 return_r.push_back( base );
1487 const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1488 for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1490 if ( ! predicate || predicate( base ) )
1491 return_r.push_back( base );
1496 // Here: search all attributes ;(
1497 for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1499 const AttrMatchData & matchData( *mi );
1500 sat::LookupAttr q( matchData.attr, inSolvable );
1501 if ( matchData.strMatcher ) // an empty searchstring matches always
1502 q.setStrMatcher( matchData.strMatcher );
1504 if ( ! q.empty() ) // there are matches.
1506 // now check any predicate:
1507 const AttrMatchData::Predicate & predicate( matchData.predicate );
1508 for_( it, q.begin(), q.end() )
1510 if ( ! predicate || predicate( it ) )
1511 return_r.push_back( it );
1519 /** Ctor stores the \ref PoolQuery settings.
1520 * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1522 PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1526 // Repo restriction:
1527 sat::Pool satpool( sat::Pool::instance() );
1529 for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1531 Repository r( satpool.reposFind( *it ) );
1535 _neverMatchRepo = true;
1537 // _neverMatchRepo: we just need to catch the case that no repo
1538 // matched, so we'd interpret the empty list as 'take from all'
1539 if ( _neverMatchRepo && ! _repos.empty() )
1540 _neverMatchRepo = false;
1542 // Kind restriction:
1543 _kinds = query_r->_kinds;
1544 // Edition restriction:
1546 _edition = query_r->_edition;
1547 // Status restriction:
1548 _status_flags = query_r->_status_flags;
1550 _attrMatchList = query_r->_attrMatchList;
1557 /** Initialize a new base query. */
1558 base_iterator startNewQyery() const
1562 if ( _neverMatchRepo )
1565 // Repo restriction:
1566 if ( _repos.size() == 1 )
1567 q.setRepo( *_repos.begin() );
1568 // else: handled in isAMatch.
1570 // Attribute restriction:
1571 if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1573 const AttrMatchData & matchData( _attrMatchList.front() );
1574 q.setAttr( matchData.attr );
1575 if ( matchData.strMatcher ) // empty searchstring matches always
1576 q.setStrMatcher( matchData.strMatcher );
1578 else // more than 1 attr (but not all)
1580 // no restriction, it's all handled in isAMatch.
1581 q.setAttr( sat::SolvAttr::allAttr );
1588 /** Check whether we are on a match.
1590 * The check covers the whole Solvable, not just the current
1591 * attribute \c base_r points to. If there's no match, also
1592 * prepare \c base_r to advance appropriately. If there is
1593 * a match, simply return \c true. \ref advance always moves
1594 * to the next Solvable if there was a match.
1596 * \note: Caller asserts we're not at \ref end.
1598 bool isAMatch( base_iterator & base_r ) const
1600 /////////////////////////////////////////////////////////////////////
1601 Repository inRepo( base_r.inRepo() );
1602 // Status restriction:
1604 && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1606 base_r.nextSkipRepo();
1609 // Repo restriction:
1610 if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1612 base_r.nextSkipRepo();
1615 /////////////////////////////////////////////////////////////////////
1616 sat::Solvable inSolvable( base_r.inSolvable() );
1617 // Kind restriction:
1618 if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1620 base_r.nextSkipSolvable();
1624 // Edition restriction:
1625 if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1627 base_r.nextSkipSolvable();
1630 /////////////////////////////////////////////////////////////////////
1631 // string and predicate matching:
1633 if ( _attrMatchList.size() == 1 )
1635 // String matching was done by the base iterator.
1636 // Now check any predicate:
1637 const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1638 if ( ! predicate || predicate( base_r ) )
1641 return false; // no skip as there may be more occurrences od this attr.
1644 // Here: search all attributes ;(
1645 for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1647 const AttrMatchData & matchData( *mi );
1648 sat::LookupAttr q( matchData.attr, inSolvable );
1649 if ( matchData.strMatcher ) // an empty searchstring matches always
1650 q.setStrMatcher( matchData.strMatcher );
1652 if ( ! q.empty() ) // there are matches.
1654 // now check any predicate:
1655 const AttrMatchData::Predicate & predicate( matchData.predicate );
1658 for_( it, q.begin(), q.end() )
1660 if ( predicate( it ) )
1668 base_r.nextSkipSolvable();
1673 /** Repositories include in the search. */
1674 std::set<Repository> _repos;
1675 DefaultIntegral<bool,false> _neverMatchRepo;
1676 /** Resolvable kinds to include. */
1677 std::set<ResKind> _kinds;
1678 /** Edition filter. */
1681 /** Installed status filter flags. \see PoolQuery::StatusFilter */
1683 /** StrMatcher per attribtue. */
1684 AttrMatchList _attrMatchList;
1686 ///////////////////////////////////////////////////////////////////
1688 void PoolQueryIterator::increment()
1690 // matcher restarts if at end! It is called from the ctor
1691 // to get the 1st match. But if the end is reached, it should
1692 // be deleted, otherwise we'd start over again.
1696 _matches.reset(); // invalidate old matches
1697 if ( ! _matcher->advance( base_reference() ) )
1701 const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1709 static const Matches _none;
1713 _matches.reset( new Matches );
1714 _matcher->matchDetail( base_reference(), *_matches );
1718 std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1721 if ( ! obj.matchesEmpty() )
1723 for_( it, obj.matchesBegin(), obj.matchesEnd() )
1725 str << endl << " " << it->inSolvAttr() << "\t" << it->asString();
1731 ///////////////////////////////////////////////////////////////////
1732 } //namespace detail
1733 ///////////////////////////////////////////////////////////////////
1735 detail::PoolQueryIterator PoolQuery::begin() const
1737 return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1740 /////////////////////////////////////////////////////////////////
1742 ///////////////////////////////////////////////////////////////////