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/sat/AttrMatcher.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 sat::AttrMatcher
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 attrMatcher.
173 * Example for such a \ref predicate would be an additional edition range
174 * check whan looking for dependencies. The \ref attrMatcher 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 sat::AttrMatcher & attrMatcher_r )
196 , attrMatcher( attrMatcher_r )
199 AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r,
200 const Predicate & predicate_r, const std::string & predicateStr_r )
202 , attrMatcher( attrMatcher_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, attrMatcher.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, attrMatcher.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.attrMatcher = sat::AttrMatcher( words[2] );
252 if ( words[3] == "C" )
253 ret.attrMatcher.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 sat::AttrMatcher attrMatcher;
306 std::string predicateStr;
309 /** \relates AttrMatchData */
310 inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
312 str << obj.attr << ": " << obj.attrMatcher;
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.attrMatcher == rhs.attrMatcher
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.attrMatcher != rhs.attrMatcher )
336 return ( lhs.attrMatcher < rhs.attrMatcher );
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 return ( _strings == rhs._strings
405 && _attrs == rhs._attrs
406 && _uncompiledPredicated == rhs._uncompiledPredicated
407 && _flags == rhs._flags
408 && _match_word == rhs._match_word
409 && _require_all == rhs._require_all
410 && _status_flags == rhs._status_flags
411 && _edition == rhs._edition
413 && _repos == rhs._repos
414 && _kinds == rhs._kinds );
417 bool operator!=( const PoolQuery::Impl & rhs ) const
418 { return ! operator==( rhs ); }
421 /** Compile the regex.
422 * Basically building the \ref _attrMatchList from strings.
423 * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
425 void compile() const;
427 /** AttrMatcher per attribtue. */
428 mutable AttrMatchList _attrMatchList;
431 /** Pass flags from \ref compile, as they may have been changed. */
432 string createRegex( const StrContainer & container, const Match & flags ) const;
435 friend Impl * rwcowClone<Impl>( const Impl * rhs );
436 /** clone for RWCOW_pointer */
438 { return new Impl( *this ); }
441 ///////////////////////////////////////////////////////////////////
445 MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
447 bool operator()(const string & str)
453 PoolQuery::StrContainer & _cont;
459 bool operator()(const string & str)
465 void PoolQuery::Impl::compile() const
467 _attrMatchList.clear();
469 Match cflags( _flags );
470 if ( cflags.mode() == Match::OTHER ) // this will never succeed...
471 ZYPP_THROW( MatchUnknownModeException( cflags ) );
473 /** Compiled search strings. */
477 // 'different' - will have to iterate through all and match by ourselves (slow)
478 // 'same' - will pass the compiled string to dataiterator_init
479 // 'one-attr' - will pass it to dataiterator_init
480 // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
485 // create regex; store in rcstrings; if more strings flag regex;
488 ; // A default 'query-all' will be added after all sources are processed.
492 // else if _attrs is not empty but it contains just one attr
493 // for all _strings and _attr[key] strings
494 // create regex; flag 'one-attr'; if more strings flag regex;
495 else if (_attrs.size() == 1)
498 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
499 invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
500 rcstrings = createRegex(joined, cflags);
501 if (joined.size() > 1) // switch to regex for multiple strings
502 cflags.setModeRegex();
503 _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
504 sat::AttrMatcher( rcstrings, cflags ) ) );
507 // // MULTIPLE ATTRIBUTES
510 // check whether there are any per-attribute strings
511 bool attrvals_empty = true;
512 for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
513 if (!ai->second.empty())
514 for(StrContainer::const_iterator it = ai->second.begin();
515 it != ai->second.end(); it++)
518 attrvals_empty = false;
519 goto attremptycheckend;
523 // chceck whether the per-attribute strings are all the same
524 bool attrvals_thesame = true;
525 AttrRawStrMap::const_iterator ai = _attrs.begin();
526 const StrContainer & set1 = ai->second;
528 for (; ai != _attrs.end(); ++ai)
532 set1.begin(), set1.end(),
533 ai->second.begin(), ai->second.end(),
534 inserter(result, result.begin())/*, ltstr()*/);
537 attrvals_thesame = false;
542 // // THE SAME STRINGS FOR DIFFERENT ATTRS
543 // else if _attrs is not empty but it does not contain strings
544 // for each key in _attrs take all _strings
545 // create regex; store in rcstrings; flag 'same'; if more strings flag regex;
546 if (attrvals_empty || attrvals_thesame)
551 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
552 rcstrings = createRegex(joined, cflags);
556 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
557 invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
558 rcstrings = createRegex(joined, cflags);
560 if (joined.size() > 1) // switch to regex for multiple strings
561 cflags.setModeRegex();
562 // May use the same AttrMatcher for all
563 sat::AttrMatcher matcher( rcstrings, cflags );
564 for_( ai, _attrs.begin(), _attrs.end() )
566 _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
570 // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
571 // if _attrs is not empty and it contains non-empty vectors with non-empty strings
572 // for each key in _attrs take all _strings + all _attrs[key] strings
573 // create regex; flag 'different'; if more strings flag regex;
576 for_(ai, _attrs.begin(), _attrs.end())
579 invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
580 invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
581 string s = createRegex(joined, cflags);
582 if (joined.size() > 1) // switch to regex for multiple strings
583 cflags.setModeRegex();
584 _attrMatchList.push_back( AttrMatchData( ai->first,
585 sat::AttrMatcher( s, cflags ) ) );
590 // Now handle any predicated queries
591 if ( ! _uncompiledPredicated.empty() )
594 invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
595 for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
597 if ( it->attrMatcher.flags().mode() == Match::OTHER )
600 StrContainer joined( global );
601 const std::string & mstr( it->attrMatcher.searchstring() );
602 if ( ! mstr.empty() )
603 joined.insert( mstr );
606 rcstrings = createRegex( joined, cflags );
607 if ( joined.size() > 1 ) // switch to regex for multiple strings
608 cflags.setModeRegex();
610 _attrMatchList.push_back( AttrMatchData( it->attr,
611 sat::AttrMatcher( rcstrings, cflags ),
612 it->predicate, it->predicateStr ) );
617 _attrMatchList.push_back( *it );
622 // If no attributes defined at all, then add 'query all'
623 if ( _attrMatchList.empty() )
626 rcstrings = createRegex( _strings, cflags );
627 if ( _strings.size() > 1 ) // switch to regex for multiple strings
628 cflags.setModeRegex();
629 _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
630 sat::AttrMatcher( rcstrings, cflags ) ) );
633 // Finally check here, whether all involved regex compile.
634 for_( it, _attrMatchList.begin(), _attrMatchList.end() )
636 it->attrMatcher.compile(); // throws on error
638 //DBG << asString() << endl;
643 * Converts '*' and '?' wildcards within str into their regex equivalents.
645 static string wildcards2regex(const string & str)
647 string regexed = str;
649 string r_all(".*"); // regex equivalent of '*'
650 string r_one("."); // regex equivalent of '?'
651 string::size_type pos;
653 // replace all "*" in input with ".*"
654 for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
655 regexed = regexed.replace(pos, 1, r_all);
657 // replace all "?" in input with "."
658 for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
659 regexed = regexed.replace(pos, 1, r_one);
664 string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
666 //! macro for word boundary tags for regexes
667 #define WB (_match_word ? string("\\b") : string())
670 if (container.empty())
673 if (container.size() == 1)
675 return WB + *container.begin() + WB;
680 bool use_wildcards = flags.isModeGlob();
681 StrContainer::const_iterator it = container.begin();
685 tmp = wildcards2regex(*it);
691 if ( ! flags.isModeString() ) // not match exact
692 tmp += ".*" + WB + tmp;
693 rstr = "(?=" + tmp + ")";
697 if ( flags.isModeString() || flags.isModeGlob() )
699 rstr += WB + "(" + tmp;
704 for (; it != container.end(); ++it)
707 tmp = wildcards2regex(*it);
713 if ( ! flags.isModeString() ) // not match exact
714 tmp += ".*" + WB + tmp;
715 rstr += "(?=" + tmp + ")";
725 if ( ! flags.isModeString() ) // not match exact
731 if ( flags.isModeString() || flags.isModeGlob() )
739 string PoolQuery::Impl::asString() const
744 if ( _kinds.empty() )
748 for(Kinds::const_iterator it = _kinds.begin();
749 it != _kinds.end(); ++it)
755 if ( _repos.empty() )
759 for(StrContainer::const_iterator it = _repos.begin();
760 it != _repos.end(); ++it)
765 o << "version: "<< _op << " " << _edition.asString() << endl;
766 o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
769 o << "string match flags: " << Match(_flags) << endl;
773 for(StrContainer::const_iterator it = _strings.begin();
774 it != _strings.end(); ++it)
778 o << "attributes: " << endl;
779 for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
781 o << "* " << ai->first << ": ";
782 for(StrContainer::const_iterator vi = ai->second.begin();
783 vi != ai->second.end(); ++vi)
788 o << "predicated: " << endl;
789 for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
791 o << "* " << *it << endl;
795 o << "last attribute matcher compiled: " << endl;
796 if ( _attrMatchList.empty() )
798 o << "not yet compiled" << endl;
802 for_( it, _attrMatchList.begin(), _attrMatchList.end() )
804 o << "* " << *it << endl;
810 ///////////////////////////////////////////////////////////////////
812 ///////////////////////////////////////////////////////////////////
814 // CLASS NAME : PoolQuery
816 ///////////////////////////////////////////////////////////////////
818 PoolQuery::PoolQuery()
822 PoolQuery::~PoolQuery()
825 void PoolQuery::addRepo(const std::string &repoalias)
827 if (repoalias.empty())
829 WAR << "ignoring an empty repository alias" << endl;
832 _pimpl->_repos.insert(repoalias);
835 void PoolQuery::addKind(const ResKind & kind)
836 { _pimpl->_kinds.insert(kind); }
838 void PoolQuery::addString(const string & value)
839 { _pimpl->_strings.insert(value); }
841 void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
842 { _pimpl->_attrs[attr].insert(value); }
844 void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
845 { return addDependency( attr, name, op, edition, Arch_empty ); }
847 void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
849 switch ( op.inSwitch() )
851 case Rel::ANY_e: // no additional constraint on edition.
852 if ( arch.empty() ) // no additional constraint on arch.
854 addAttribute( attr, name );
859 case Rel::NONE_e: // will never match.
862 default: // go and add the predicated query (uncompiled)
866 // Match::OTHER indicates need to compile
867 // (merge global search strings into name).
868 AttrMatchData attrMatchData( attr, sat::AttrMatcher( name, Match::OTHER ) );
870 if ( isDependencyAttribute( attr ) )
871 attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
873 attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
875 _pimpl->_uncompiledPredicated.insert( attrMatchData );
878 void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
880 CapDetail cap( cap_r );
881 if ( ! cap.isSimple() ) // will never match.
884 // Matches STRING per default. (won't get compiled!)
885 AttrMatchData attrMatchData( attr, sat::AttrMatcher( cap.name().asString() ) );
887 if ( isDependencyAttribute( attr ) )
888 attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
890 attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
892 _pimpl->_uncompiledPredicated.insert( attrMatchData );
895 void PoolQuery::setEdition(const Edition & edition, const Rel & op)
897 _pimpl->_edition = edition;
901 void PoolQuery::setMatchSubstring() { _pimpl->_flags.setModeSubstring(); }
902 void PoolQuery::setMatchExact() { _pimpl->_flags.setModeString(); }
903 void PoolQuery::setMatchRegex() { _pimpl->_flags.setModeRegex(); }
904 void PoolQuery::setMatchGlob() { _pimpl->_flags.setModeGlob(); }
905 void PoolQuery::setMatchWord()
907 _pimpl->_match_word = true;
908 _pimpl->_flags.setModeRegex();
911 Match PoolQuery::flags() const
912 { return _pimpl->_flags; }
913 void PoolQuery::setFlags( const Match & flags )
914 { _pimpl->_flags = flags; }
917 void PoolQuery::setInstalledOnly()
918 { _pimpl->_status_flags = INSTALLED_ONLY; }
919 void PoolQuery::setUninstalledOnly()
920 { _pimpl->_status_flags = UNINSTALLED_ONLY; }
921 void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
922 { _pimpl->_status_flags = flags; }
925 void PoolQuery::setRequireAll(bool require_all)
926 { _pimpl->_require_all = require_all; }
929 const PoolQuery::StrContainer &
930 PoolQuery::strings() const
931 { return _pimpl->_strings; }
933 const PoolQuery::AttrRawStrMap &
934 PoolQuery::attributes() const
935 { return _pimpl->_attrs; }
937 const PoolQuery::StrContainer &
938 PoolQuery::attribute(const sat::SolvAttr & attr) const
940 static const PoolQuery::StrContainer nocontainer;
941 AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
942 return it != _pimpl->_attrs.end() ? it->second : nocontainer;
945 const Edition PoolQuery::edition() const
946 { return _pimpl->_edition; }
947 const Rel PoolQuery::editionRel() const
948 { return _pimpl->_op; }
951 const PoolQuery::Kinds &
952 PoolQuery::kinds() const
953 { return _pimpl->_kinds; }
955 const PoolQuery::StrContainer &
956 PoolQuery::repos() const
957 { return _pimpl->_repos; }
960 bool PoolQuery::caseSensitive() const
961 { return !_pimpl->_flags.test( Match::NOCASE ); }
962 void PoolQuery::setCaseSensitive( bool value )
963 { _pimpl->_flags.turn( Match::NOCASE, !value ); }
965 bool PoolQuery::filesMatchFullPath() const
966 { return _pimpl->_flags.test( Match::FILES ); }
967 void PoolQuery::setFilesMatchFullPath( bool value )
968 { _pimpl->_flags.turn( Match::FILES, value ); }
970 bool PoolQuery::matchExact() const { return _pimpl->_flags.isModeString(); }
971 bool PoolQuery::matchSubstring() const { return _pimpl->_flags.isModeSubstring(); }
972 bool PoolQuery::matchGlob() const { return _pimpl->_flags.isModeGlob(); }
973 bool PoolQuery::matchRegex() const { return _pimpl->_flags.isModeRegex(); }
975 bool PoolQuery::matchWord() const
976 { return _pimpl->_match_word; }
978 bool PoolQuery::requireAll() const
979 { return _pimpl->_require_all; }
981 PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
982 { return _pimpl->_status_flags; }
984 bool PoolQuery::empty() const
986 try { return begin() == end(); }
987 catch (const Exception & ex) {}
991 PoolQuery::size_type PoolQuery::size() const
996 for_( it, begin(), end() )
1000 catch (const Exception & ex) {}
1004 void PoolQuery::execute(ProcessResolvable fnc)
1005 { invokeOnEach( begin(), end(), fnc); }
1008 ///////////////////////////////////////////////////////////////////
1010 // CLASS NAME : PoolQuery::Attr
1013 * represents all atributes in PoolQuery except SolvAtributes, which are
1014 * used as is (not needed extend anything if someone adds new solv attr)
1016 struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1019 friend class IdStringType<PoolQueryAttr>;
1026 explicit PoolQueryAttr( const char* cstr_r )
1030 explicit PoolQueryAttr( const std::string & str_r )
1034 // unknown atributes
1035 static const PoolQueryAttr noAttr;
1037 // PoolQuery's own attributes
1038 static const PoolQueryAttr repoAttr;
1039 static const PoolQueryAttr kindAttr;
1040 static const PoolQueryAttr stringAttr;
1041 static const PoolQueryAttr stringTypeAttr;
1042 static const PoolQueryAttr requireAllAttr;
1043 static const PoolQueryAttr caseSensitiveAttr;
1044 static const PoolQueryAttr installStatusAttr;
1045 static const PoolQueryAttr editionAttr;
1046 static const PoolQueryAttr complexAttr;
1049 const PoolQueryAttr PoolQueryAttr::noAttr;
1051 const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1052 const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1053 const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1054 const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1055 const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
1056 const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1057 const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1058 const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1059 const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1061 class StringTypeAttr : public IdStringType<PoolQueryAttr>
1063 friend class IdStringType<StringTypeAttr>;
1068 explicit StringTypeAttr( const char* cstr_r )
1070 explicit StringTypeAttr( const std::string & str_r )
1073 static const StringTypeAttr noAttr;
1075 static const StringTypeAttr exactAttr;
1076 static const StringTypeAttr substringAttr;
1077 static const StringTypeAttr regexAttr;
1078 static const StringTypeAttr globAttr;
1079 static const StringTypeAttr wordAttr;
1082 const StringTypeAttr StringTypeAttr::noAttr;
1084 const StringTypeAttr StringTypeAttr::exactAttr("exact");
1085 const StringTypeAttr StringTypeAttr::substringAttr("substring");
1086 const StringTypeAttr StringTypeAttr::regexAttr("regex");
1087 const StringTypeAttr StringTypeAttr::globAttr("glob");
1088 const StringTypeAttr StringTypeAttr::wordAttr("word");
1090 ///////////////////////////////////////////////////////////////////
1093 //\TODO maybe ctor with stream can be usefull
1094 //\TODO let it throw, let it throw, let it throw.
1095 bool PoolQuery::recover( istream &str, char delim )
1097 bool finded_something = false; //indicates some atributes is finded
1103 getline( str, s, delim );
1105 if ((!s.empty()) && s[0]=='#') //comment
1110 string::size_type pos = s.find(':');
1111 if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1113 if (finded_something) //is first blank line after record?
1123 finded_something = true;
1125 string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1126 string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1128 PoolQueryAttr attribute( attrName );
1130 MIL << "attribute name: " << attrName << endl;
1132 if ( attribute==PoolQueryAttr::repoAttr )
1134 addRepo( attrValue );
1136 /* some backwards compatibility */
1137 else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1139 addKind( ResKind(attrValue) );
1141 else if ( attribute==PoolQueryAttr::stringAttr
1142 || attribute=="global_string")
1144 addString( attrValue );
1146 else if ( attribute==PoolQueryAttr::stringTypeAttr
1147 || attribute=="string_type" )
1149 StringTypeAttr s(attrValue);
1150 if( s == StringTypeAttr::regexAttr )
1154 else if ( s == StringTypeAttr::globAttr )
1158 else if ( s == StringTypeAttr::exactAttr )
1162 else if ( s == StringTypeAttr::substringAttr )
1164 setMatchSubstring();
1166 else if ( s == StringTypeAttr::wordAttr )
1170 else if ( s == StringTypeAttr::noAttr )
1172 WAR << "unknown string type " << attrValue << endl;
1176 WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1179 else if ( attribute==PoolQueryAttr::requireAllAttr )
1181 if ( str::strToTrue(attrValue) )
1183 setRequireAll(true);
1185 else if ( !str::strToFalse(attrValue) )
1187 setRequireAll(false);
1191 WAR << "unknown boolean value " << attrValue << endl;
1194 else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1196 if ( str::strToTrue(attrValue) )
1198 setCaseSensitive(true);
1200 else if ( !str::strToFalse(attrValue) )
1202 setCaseSensitive(false);
1206 WAR << "unknown boolean value " << attrValue << endl;
1209 else if ( attribute==PoolQueryAttr::installStatusAttr )
1211 if( attrValue == "all" )
1213 setStatusFilterFlags( ALL );
1215 else if( attrValue == "installed" )
1219 else if( attrValue == "not-installed" )
1221 setUninstalledOnly();
1225 WAR << "Unknown value for install status " << attrValue << endl;
1228 else if ( attribute == PoolQueryAttr::editionAttr)
1230 string::size_type pos;
1232 if (attrValue.find_first_of("=<>!") == 0)
1234 pos = attrValue.find_last_of("=<>");
1235 rel = Rel(attrValue.substr(0, pos+1));
1236 attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1239 setEdition(Edition(attrValue), rel);
1241 else if ( attribute == PoolQueryAttr::complexAttr )
1245 _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1247 catch ( const Exception & err )
1249 WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1253 else if ( attribute==PoolQueryAttr::noAttr )
1255 WAR << "empty attribute name" << endl;
1259 string s = attrName;
1260 str::replaceAll( s,"_",":" );
1262 if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1264 Capability c( attrValue );
1266 if ( d.isVersioned() )
1267 addDependency( a, d.name().asString(), d.op(), d.ed() );
1269 addDependency( a, attrValue );
1272 addAttribute( a, attrValue );
1277 return finded_something;
1280 void PoolQuery::serialize( ostream &str, char delim ) const
1284 //iterate thrue all settings and write it
1285 static const zypp::PoolQuery q; //not save default options, so create default query example
1287 for_( it, repos().begin(), repos().end() )
1289 str << "repo: " << *it << delim ;
1292 for_( it, kinds().begin(), kinds().end() )
1294 str << PoolQueryAttr::kindAttr.asString() << ": "
1295 << it->idStr() << delim ;
1298 if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1299 str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1301 if (matchMode()!=q.matchMode())
1303 switch( matchMode() )
1306 str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1308 case Match::SUBSTRING:
1309 str << PoolQueryAttr::stringTypeAttr.asString()
1310 << ": substring" << delim;
1313 str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1316 str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1319 WAR << "unknown match type " << matchMode() << endl;
1323 if( caseSensitive() != q.caseSensitive() )
1325 str << "case_sensitive: ";
1326 if (caseSensitive())
1328 str << "on" << delim;
1332 str << "off" << delim;
1336 if( requireAll() != q.requireAll() )
1338 str << "require_all: ";
1341 str << "on" << delim;
1345 str << "off" << delim;
1349 if( statusFilterFlags() != q.statusFilterFlags() )
1351 switch( statusFilterFlags() )
1354 str << "install_status: all" << delim;
1356 case INSTALLED_ONLY:
1357 str << "install_status: installed" << delim;
1359 case UNINSTALLED_ONLY:
1360 str << "install_status: not-installed" << delim;
1365 for_( it, strings().begin(), strings().end() )
1367 str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1370 for_( it, attributes().begin(), attributes().end() )
1372 string s = it->first.asString();
1373 str::replaceAll(s,":","_");
1374 for_( it2,it->second.begin(),it->second.end() )
1376 str << s <<": "<< *it2 << delim;
1380 for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1382 str << "complex: "<< it->serialize() << delim;
1385 //separating delim - protection
1389 string PoolQuery::asString() const
1390 { return _pimpl->asString(); }
1392 ostream & operator<<( ostream & str, const PoolQuery & obj )
1393 { return str << obj.asString(); }
1395 std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1396 { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1398 bool PoolQuery::operator==( const PoolQuery & rhs ) const
1399 { return *_pimpl == *rhs._pimpl; }
1401 ///////////////////////////////////////////////////////////////////
1403 { /////////////////////////////////////////////////////////////////
1405 ///////////////////////////////////////////////////////////////////
1407 // CLASS NAME : PoolQueryMatcher
1409 /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1411 * Basically the matcher performs a base query, which should preselect
1412 * candidates for a match. And has some filter conditions on top of it.
1413 * Query and fileter depend on the \ref PoolQuery settings.
1415 * Matcher must be stateless, as it is shared between multiple
1416 * \ref PoolQueryIterator instances.
1418 * If \ref base_iterator is at the \ref end, \ref advance moves it
1419 * to the first match. Otherwise advance moves to the next match, or
1420 * to the \ref end, if there is no more match.
1422 * \note The original implementation treated an empty search string as
1423 * <it>"match always"</it>. We stay compatible.
1425 class PoolQueryMatcher
1428 typedef sat::LookupAttr::iterator base_iterator;
1431 const base_iterator & end() const
1433 static base_iterator _end;
1437 bool advance( base_iterator & base_r ) const
1439 if ( base_r == end() )
1440 base_r = startNewQyery(); // first candidate
1443 base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1444 ++base_r; // advance to next candidate
1447 while ( base_r != end() )
1449 if ( isAMatch( base_r ) )
1451 // No match: try next
1457 /** Provide all matching attributes within this solvable.
1460 void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1462 if ( base_r == end() )
1465 sat::Solvable inSolvable( base_r.inSolvable() );
1467 if ( _attrMatchList.size() == 1 )
1469 // base_r is already on the 1st matching attribute!
1470 // String matching is done by the base iterator. We must check the predicate here.
1471 // Let's see if there are more matches for this solvable:
1472 base_iterator base( base_r );
1473 base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1474 return_r.push_back( base );
1476 const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1477 for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1479 if ( ! predicate || predicate( base ) )
1480 return_r.push_back( base );
1485 // Here: search all attributes ;(
1486 for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1488 const AttrMatchData & matchData( *mi );
1489 sat::LookupAttr q( matchData.attr, inSolvable );
1490 if ( matchData.attrMatcher ) // an empty searchstring matches always
1491 q.setAttrMatcher( matchData.attrMatcher );
1493 if ( ! q.empty() ) // there are matches.
1495 // now check any predicate:
1496 const AttrMatchData::Predicate & predicate( matchData.predicate );
1497 for_( it, q.begin(), q.end() )
1499 if ( ! predicate || predicate( it ) )
1500 return_r.push_back( it );
1508 /** Ctor stores the \ref PoolQuery settings.
1509 * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1511 PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1515 // Repo restriction:
1516 sat::Pool satpool( sat::Pool::instance() );
1518 for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1520 Repository r( satpool.reposFind( *it ) );
1524 _neverMatchRepo = true;
1526 // _neverMatchRepo: we just need to catch the case that no repo
1527 // matched, so we'd interpret the empty list as 'take from all'
1528 if ( _neverMatchRepo && ! _repos.empty() )
1529 _neverMatchRepo = false;
1531 // Kind restriction:
1532 _kinds = query_r->_kinds;
1533 // Edition restriction:
1535 _edition = query_r->_edition;
1536 // Status restriction:
1537 _status_flags = query_r->_status_flags;
1539 _attrMatchList = query_r->_attrMatchList;
1546 /** Initialize a new base query. */
1547 base_iterator startNewQyery() const
1551 if ( _neverMatchRepo )
1554 // Repo restriction:
1555 if ( _repos.size() == 1 )
1556 q.setRepo( *_repos.begin() );
1557 // else: handled in isAMatch.
1559 // Attribute restriction:
1560 if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1562 const AttrMatchData & matchData( _attrMatchList.front() );
1563 q.setAttr( matchData.attr );
1564 if ( matchData.attrMatcher ) // empty searchstring matches always
1565 q.setAttrMatcher( matchData.attrMatcher );
1567 else // more than 1 attr (but not all)
1569 // no restriction, it's all handled in isAMatch.
1570 q.setAttr( sat::SolvAttr::allAttr );
1577 /** Check whether we are on a match.
1579 * The check covers the whole Solvable, not just the current
1580 * attribute \c base_r points to. If there's no match, also
1581 * prepare \c base_r to advance appropriately. If there is
1582 * a match, simply return \c true. \ref advance always moves
1583 * to the next Solvable if there was a match.
1585 * \note: Caller asserts we're not at \ref end.
1587 bool isAMatch( base_iterator & base_r ) const
1589 /////////////////////////////////////////////////////////////////////
1590 Repository inRepo( base_r.inRepo() );
1591 // Status restriction:
1593 && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1595 base_r.nextSkipRepo();
1598 // Repo restriction:
1599 if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1601 base_r.nextSkipRepo();
1604 /////////////////////////////////////////////////////////////////////
1605 sat::Solvable inSolvable( base_r.inSolvable() );
1606 // Kind restriction:
1607 if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1609 base_r.nextSkipSolvable();
1613 // Edition restriction:
1614 if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1616 base_r.nextSkipSolvable();
1619 /////////////////////////////////////////////////////////////////////
1620 // string and predicate matching:
1622 if ( _attrMatchList.size() == 1 )
1624 // String matching was done by the base iterator.
1625 // Now check any predicate:
1626 const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1627 if ( ! predicate || predicate( base_r ) )
1630 return false; // no skip as there may be more occurrences od this attr.
1633 // Here: search all attributes ;(
1634 for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1636 const AttrMatchData & matchData( *mi );
1637 sat::LookupAttr q( matchData.attr, inSolvable );
1638 if ( matchData.attrMatcher ) // an empty searchstring matches always
1639 q.setAttrMatcher( matchData.attrMatcher );
1641 if ( ! q.empty() ) // there are matches.
1643 // now check any predicate:
1644 const AttrMatchData::Predicate & predicate( matchData.predicate );
1647 for_( it, q.begin(), q.end() )
1649 if ( predicate( it ) )
1657 base_r.nextSkipSolvable();
1662 /** Repositories include in the search. */
1663 std::set<Repository> _repos;
1664 DefaultIntegral<bool,false> _neverMatchRepo;
1665 /** Resolvable kinds to include. */
1666 std::set<ResKind> _kinds;
1667 /** Edition filter. */
1670 /** Installed status filter flags. \see PoolQuery::StatusFilter */
1672 /** AttrMatcher per attribtue. */
1673 AttrMatchList _attrMatchList;
1675 ///////////////////////////////////////////////////////////////////
1677 void PoolQueryIterator::increment()
1679 // matcher restarts if at end! It is called from the ctor
1680 // to get the 1st match. But if the end is reached, it should
1681 // be deleted, otherwise we'd start over again.
1685 _matches.reset(); // invalidate old matches
1686 if ( ! _matcher->advance( base_reference() ) )
1690 const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1698 static const Matches _none;
1702 _matches.reset( new Matches );
1703 _matcher->matchDetail( base_reference(), *_matches );
1707 std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1710 if ( ! obj.matchesEmpty() )
1712 for_( it, obj.matchesBegin(), obj.matchesEnd() )
1714 str << endl << " " << it->inSolvAttr() << "\t" << it->asString();
1720 ///////////////////////////////////////////////////////////////////
1721 } //namespace detail
1722 ///////////////////////////////////////////////////////////////////
1724 detail::PoolQueryIterator PoolQuery::begin() const
1726 return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1729 /////////////////////////////////////////////////////////////////
1731 ///////////////////////////////////////////////////////////////////