17b76151acfa5cb92c6f4404a0a1e6e62234380f
[platform/upstream/libzypp.git] / zypp / PoolQuery.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/PoolQuery.cc
10  *
11 */
12 #include <iostream>
13 #include <sstream>
14
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"
21
22 #include "zypp/sat/Pool.h"
23 #include "zypp/sat/Solvable.h"
24 #include "zypp/base/StrMatcher.h"
25
26 #include "zypp/PoolQuery.h"
27
28 #undef ZYPP_BASE_LOGGER_LOGGROUP
29 #define ZYPP_BASE_LOGGER_LOGGROUP "PoolQuery"
30
31 using namespace std;
32 using namespace zypp::sat;
33
34 ///////////////////////////////////////////////////////////////////
35 namespace zypp
36 { /////////////////////////////////////////////////////////////////
37
38   ///////////////////////////////////////////////////////////////////
39   namespace
40   { /////////////////////////////////////////////////////////////////
41
42     /////////////////////////////////////////////////////////////////
43     // some Helpers and Predicates
44     /////////////////////////////////////////////////////////////////
45
46     bool isDependencyAttribute( sat::SolvAttr attr_r )
47     {
48       static sat::SolvAttr deps[] = {
49         SolvAttr::provides,
50         SolvAttr::requires,
51         SolvAttr::recommends,
52         SolvAttr::obsoletes,
53         SolvAttr::conflicts,
54         SolvAttr::suggests,
55         SolvAttr::supplements,
56         SolvAttr::enhances,
57       };
58       for_( it, arrayBegin(deps), arrayEnd(deps) )
59         if ( *it == attr_r )
60           return true;
61       return false;
62     }
63
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.
67      */
68     struct EditionRangePredicate
69     {
70       EditionRangePredicate( const Rel & op, const Edition & edition )
71         : _range( op, edition )
72         , _arch( Arch_empty )
73       {}
74       EditionRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
75         : _range( op, edition )
76         , _arch( arch )
77       {}
78
79       bool operator()( sat::LookupAttr::iterator iter_r )
80       {
81         if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
82           return false;
83
84         CapDetail cap( iter_r.id() );
85         if ( ! cap.isSimple() )
86           return false;
87         if ( cap.isNamed() ) // no range to match
88           return true;
89         return overlaps( Edition::MatchRange( cap.op(), cap.ed() ), _range );
90       }
91
92       std::string serialize() const
93       {
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() );
98         return ret;
99       }
100
101       Edition::MatchRange _range;
102       Arch                _arch;
103    };
104
105     /** Whether the current Solvables edition is within a given range and/or its arch matches. */
106     struct SolvableRangePredicate
107     {
108       SolvableRangePredicate( const Rel & op, const Edition & edition )
109         : _range( op, edition )
110         , _arch( Arch_empty )
111       {}
112
113       SolvableRangePredicate( const Rel & op, const Edition & edition, const Arch & arch )
114         : _range( op, edition )
115         , _arch( arch )
116       {}
117
118       bool operator()( sat::LookupAttr::iterator iter_r )
119       {
120         if ( !_arch.empty() && iter_r.inSolvable().arch() != _arch )
121           return false;
122         return overlaps( Edition::MatchRange( Rel::EQ, iter_r.inSolvable().edition() ), _range );
123       }
124
125       std::string serialize() const
126       {
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() );
131         return ret;
132       }
133
134       Edition::MatchRange _range;
135       Arch                _arch;
136     };
137
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.
141      */
142     struct CapabilityMatchPredicate
143     {
144       CapabilityMatchPredicate( Capability cap_r )
145         : _cap( cap_r )
146       {}
147
148       bool operator()( sat::LookupAttr::iterator iter_r ) const
149       {
150         return _cap.matches( iter_r.asType<Capability>() ) == CapMatch::yes;
151       }
152
153       std::string serialize() const
154       {
155         std::string ret( "CapabilityMatch" );
156         str::appendEscaped( ret, _cap.asString() );
157         return ret;
158       }
159
160       Capability _cap;
161     };
162
163     /////////////////////////////////////////////////////////////////
164     //
165     /////////////////////////////////////////////////////////////////
166     /** Match data per attribtue.
167      *
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.
172      *
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.
177      *
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).
181      *
182      * (bsc#1035729) If SolvAttr::name searches for an explicit \c kind:name,
183      * this \c kind is stored in \ref kindPredicate and will overwrite any
184      * 'global' kind restriction applied via \ref PoolQuery::addKind. This
185      * task can't be passed off to a predicate, as \ref PoolQueryMatcher::isAMatch
186      * must accept only explicit-kind-checking predicate matches, in case the
187      * 'global' kind restriction woudl otherwise discard the match.
188      *
189      * \note: \see \ref addPredicate for further constraints.
190      */
191     struct AttrMatchData
192     {
193       typedef function<bool(sat::LookupAttr::iterator)> Predicate;
194
195       static bool always( sat::LookupAttr::iterator ) { return true; }
196       static bool never( sat::LookupAttr::iterator ) { return false; }
197
198       AttrMatchData()
199       {}
200
201       AttrMatchData( sat::SolvAttr attr_r )
202         : attr( attr_r )
203       {}
204
205       AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r )
206         : attr( attr_r )
207         , strMatcher( strMatcher_r )
208       {}
209
210       AttrMatchData( sat::SolvAttr attr_r, const StrMatcher & strMatcher_r,
211                      const Predicate & predicate_r, const std::string & predicateStr_r )
212         : attr( attr_r )
213         , strMatcher( strMatcher_r )
214         , predicate( predicate_r )
215         , predicateStr( predicateStr_r )
216       {}
217
218       /** A usable Predicate must provide a string serialization.
219        * As there is no \c operator== for \ref Predicate, we compare it's
220        * string representation instead. If you add new predicated, check the
221        * deserialization code in \ref deserialize.
222        */
223       template<class _Predicate>
224       void addPredicate( const _Predicate & predicate_r )
225       {
226         predicate    = predicate_r;
227         predicateStr = predicate_r.serialize();
228       }
229
230       /** Dumb serialization.
231        * \code
232        *   AttrMatchData ATTRIBUTE SEARCHSTRING [C|X] SERIALIZED_PREDICATE
233        * \endcode
234       */
235       std::string serialize() const
236       {
237         std::string ret( "AttrMatchData" );
238         str::appendEscaped( ret, attr.asString() );
239         str::appendEscaped( ret, strMatcher.searchstring() );
240         // TODO: Actually the flag should be serialized too, but for PoolQuery
241         // it's by now sufficient to differ between mode OTHER and others,
242         // i.e. whether to compile or not compile.
243         str::appendEscaped( ret, strMatcher.flags().mode() == Match::OTHER ? "C" : "X" );
244         str::appendEscaped( ret, predicateStr );
245         return ret;
246       }
247
248        /** Dumb restore from serialized string.
249         * \throw Exception on parse error.
250         */
251       static AttrMatchData deserialize( const std::string & str_r )
252       {
253         std::vector<std::string> words;
254         str::splitEscaped( str_r, std::back_inserter(words) );
255         if ( words.empty() || words[0] != "AttrMatchData" )
256           ZYPP_THROW( Exception( str::Str() << "Expecting AttrMatchData: " << str_r ) );
257         if ( words.size() != 5 )
258           ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
259
260         AttrMatchData ret;
261         ret.attr = sat::SolvAttr( words[1] );
262         ret.strMatcher = StrMatcher( words[2] );
263         if ( words[3] == "C" )
264           ret.strMatcher.setFlags( Match::OTHER );
265         ret.predicateStr = words[4];
266
267         // now the predicate
268         words.clear();
269         str::splitEscaped( ret.predicateStr, std::back_inserter(words) );
270         if ( ! words.empty() )
271         {
272           if ( words[0] == "EditionRange" )
273           {
274             switch( words.size() )
275             {
276               case 3:
277                 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]) );
278                 break;
279               case 4:
280                 ret.predicate = EditionRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
281                 break;
282               default:
283                 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
284                 break;
285             }
286           }
287           else if ( words[0] == "SolvableRange" )
288           {
289             switch( words.size() )
290             {
291               case 3:
292                 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]) );
293                 break;
294               case 4:
295                 ret.predicate = SolvableRangePredicate( Rel(words[1]), Edition(words[2]), Arch(words[3]) );
296                 break;
297               default:
298                 ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
299                 break;
300             }
301           }
302           else if ( words[0] == "CapabilityMatch" )
303           {
304             if ( words.size() != 2 )
305               ZYPP_THROW( Exception( str::Str() << "Wrong number of words: " << str_r ) );
306             ret.predicate = CapabilityMatchPredicate( Capability(words[1]) );
307           }
308           else
309             ZYPP_THROW( Exception( str::Str() << "Unknown predicate: " << str_r ) );
310         }
311         return ret;
312      }
313
314       sat::SolvAttr    attr;
315       StrMatcher       strMatcher;
316       Predicate        predicate;
317       std::string      predicateStr;
318       ResKind          kindPredicate = ResKind::nokind; // holds the 'kind' part if SolvAttr:name looks for an explicit 'kind:name'
319     };
320
321     /** \relates AttrMatchData */
322     inline std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
323     {
324       str << obj.attr << ": " << obj.strMatcher;
325       if ( obj.kindPredicate )
326         str << " +(" << obj.kindPredicate << ")";
327       if ( obj.predicate )
328         str << " +(" << obj.predicateStr << ")";
329       return str;
330     }
331
332     /** \relates AttrMatchData */
333     inline bool operator==( const AttrMatchData & lhs, const AttrMatchData & rhs )
334     {
335       return ( lhs.attr == rhs.attr
336                && lhs.strMatcher == rhs.strMatcher
337                && lhs.predicateStr == rhs.predicateStr );
338     }
339
340     /** \relates AttrMatchData */
341     inline bool operator!=( const AttrMatchData & lhs, const AttrMatchData & rhs )
342     { return !( lhs == rhs ); }
343
344     /** \relates AttrMatchData Arbitrary order for std::container. */
345     inline bool operator<( const AttrMatchData & lhs, const AttrMatchData & rhs )
346     {
347       if ( lhs.attr != rhs.attr )
348         return (  lhs.attr < rhs.attr );
349       if ( lhs.strMatcher != rhs.strMatcher )
350         return (  lhs.strMatcher < rhs.strMatcher );
351       if ( lhs.predicateStr != rhs.predicateStr )
352         return (  lhs.predicateStr < rhs.predicateStr );
353       return false;
354     }
355
356     typedef std::list<AttrMatchData> AttrMatchList;
357
358
359   } /////////////////////////////////////////////////////////////////
360   // namespace
361   ///////////////////////////////////////////////////////////////////
362
363   ///////////////////////////////////////////////////////////////////
364   //
365   //  CLASS NAME : PoolQuery::Impl
366   //
367   /** */
368   class PoolQuery::Impl
369   {
370   public:
371     Impl()
372       : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
373       , _match_word(false)
374       , _status_flags(ALL)
375     {}
376
377     ~Impl()
378     {}
379
380   public:
381     /** String representation */
382     string asString() const;
383
384     /** \name Raw query options. */
385     //@{
386     /** Raw search strings. */
387     StrContainer _strings;
388     /** Raw attributes */
389     AttrRawStrMap _attrs;
390     /** Uncompiled attributes with predicate. */
391     std::set<AttrMatchData> _uncompiledPredicated;
392
393     /** Sat solver search flags */
394     Match _flags;
395     bool _match_word;
396
397     /** Sat solver status flags */
398     StatusFilter _status_flags;
399
400     /** Edition condition operand */
401     Edition _edition;
402     /** Operator for edition condition */
403     Rel _op;
404
405     /** Repos to search. */
406     StrContainer _repos;
407
408     /** Kinds to search */
409     Kinds _kinds;
410     //@}
411
412   public:
413
414     bool operator<( const PoolQuery::Impl & rhs ) const
415     {
416 #define OUTS(A) if ( A != rhs.A ) return A < rhs.A;
417       OUTS( _strings );
418       OUTS( _attrs );
419       OUTS( _uncompiledPredicated );
420       OUTS( _flags.get() );
421       OUTS( _match_word );
422       OUTS( _status_flags );
423       OUTS( _edition );
424       OUTS( _op.inSwitch() );
425       OUTS( _repos );
426       OUTS( _kinds );
427 #undef OUTS
428       return false;
429     }
430
431     bool operator==( const PoolQuery::Impl & rhs ) const
432     {
433       if ( _flags == rhs._flags
434         // bnc#792901: while libzypp uses exact match mode for a single
435         // package name lock, zypper always uses glob. :(
436         // We unify those two forms to enable zypper to remove zypp locks
437         // without need to actually evaluate the query (which would require
438         // repos to be loaded).
439         || ( ( ( _flags.isModeString() && rhs._flags.isModeGlob() )
440             || ( _flags.isModeGlob() && rhs._flags.isModeString() ) )
441           && _strings.empty()
442           && _attrs.size() == 1
443           && _attrs.begin()->first == sat::SolvAttr::name ) )
444       {
445         return ( _strings == rhs._strings
446               && _attrs == rhs._attrs
447               && _uncompiledPredicated == rhs._uncompiledPredicated
448               && _match_word == rhs._match_word
449               && _status_flags == rhs._status_flags
450               && _edition == rhs._edition
451               && _op == rhs._op
452               && _repos == rhs._repos
453               && _kinds == rhs._kinds );
454       }
455       return false;
456     }
457
458     bool operator!=( const PoolQuery::Impl & rhs ) const
459     { return ! operator==( rhs ); }
460
461   public:
462     /** Compile the regex.
463      * Basically building the \ref _attrMatchList from strings.
464      * \throws MatchException Any of the exceptions thrown by \ref StrMatcher::compile.
465      */
466     void compile() const;
467
468     /** StrMatcher per attribtue. */
469     mutable AttrMatchList _attrMatchList;
470
471   private:
472     /** Join patterns in \a container_r according to \a flags_r into a single \ref StrMatcher.
473      * The \ref StrMatcher returned will be a REGEX if more than one pattern was passed.
474      */
475     StrMatcher joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const;
476
477   private:
478     friend Impl * rwcowClone<Impl>( const Impl * rhs );
479     /** clone for RWCOW_pointer */
480     Impl * clone() const
481     { return new Impl( *this ); }
482   };
483
484   ///////////////////////////////////////////////////////////////////
485
486   struct MyInserter
487   {
488     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
489
490     bool operator()(const string & str)
491     {
492       _cont.insert(str);
493       return true;
494     }
495
496     PoolQuery::StrContainer & _cont;
497   };
498
499
500   struct EmptyFilter
501   {
502     bool operator()(const string & str)
503     {
504       return !str.empty();
505     }
506   };
507
508   void PoolQuery::Impl::compile() const
509   {
510     _attrMatchList.clear();
511
512     if ( _flags.mode() == Match::OTHER ) // this will never succeed...
513       ZYPP_THROW( MatchUnknownModeException( _flags ) );
514
515     // 'different'         - will have to iterate through all and match by ourselves (slow)
516     // 'same'              - will pass the compiled string to dataiterator_init
517     // 'one-attr'          - will pass it to dataiterator_init
518     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
519
520     // // NO ATTRIBUTE
521     // else
522     //   for all _strings
523     //     create regex; store in rcstrings; if more strings flag regex;
524     if (_attrs.empty())
525     {
526       ; // A default 'query-all' will be added after all sources are processed.
527     }
528
529     // // ONE ATTRIBUTE
530     // else if _attrs is not empty but it contains just one attr
531     //   for all _strings and _attr[key] strings
532     //     create regex; flag 'one-attr'; if more strings flag regex;
533     else if (_attrs.size() == 1)
534     {
535       StrContainer joined;
536       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
537       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
538
539       _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first, joinedStrMatcher( joined, _flags ) ) );
540     }
541
542     // // MULTIPLE ATTRIBUTES
543     else
544     {
545       // check whether there are any per-attribute strings
546       bool attrvals_empty = true;
547       for_( ai, _attrs.begin(), _attrs.end() )
548       {
549         if ( ai->second.empty() )
550           continue;
551         for_( it, ai->second.begin(), ai->second.end() )
552         {
553           if ( !it->empty() )
554           {
555             attrvals_empty = false;
556             break;
557           }
558         }
559         if ( ! attrvals_empty )
560           break;
561       }
562
563       // chceck whether the per-attribute strings are all the same
564       bool attrvals_thesame = true;
565       AttrRawStrMap::const_iterator ai = _attrs.begin();
566       const StrContainer & set1 = ai->second;
567       ++ai;
568       for (; ai != _attrs.end(); ++ai)
569       {
570         StrContainer result;
571         set_difference(
572           set1.begin(), set1.end(),
573           ai->second.begin(), ai->second.end(),
574           inserter(result, result.begin())/*, ltstr()*/);
575         if (!result.empty())
576         {
577           attrvals_thesame = false;
578           break;
579         }
580       }
581
582       // // THE SAME STRINGS FOR DIFFERENT ATTRS
583       // else if _attrs is not empty but it does not contain strings
584       //   for each key in _attrs take all _strings
585       //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
586       if (attrvals_empty || attrvals_thesame)
587       {
588         StrContainer joined;
589         if (attrvals_empty)
590         {
591           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
592         }
593         else
594         {
595           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
596           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
597         }
598
599         // May use the same StrMatcher for all
600         StrMatcher matcher( joinedStrMatcher( joined, _flags ) );
601         for_( ai, _attrs.begin(), _attrs.end() )
602         {
603           _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
604         }
605       }
606
607       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
608       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
609       //   for each key in _attrs take all _strings + all _attrs[key] strings
610       //     create regex; flag 'different'; if more strings flag regex;
611       else
612       {
613         for_(ai, _attrs.begin(), _attrs.end())
614         {
615           StrContainer joined;
616           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
617           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
618
619           _attrMatchList.push_back( AttrMatchData( ai->first, joinedStrMatcher( joined, _flags ) ) );
620         }
621       }
622     }
623
624     // Now handle any predicated queries
625     if ( ! _uncompiledPredicated.empty() )
626     {
627       StrContainer global;
628       invokeOnEach( _strings.begin(), _strings.end(), EmptyFilter(), MyInserter(global) );
629       for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
630       {
631         if ( it->strMatcher.flags().mode() == Match::OTHER )
632         {
633           // need to compile:
634           StrContainer joined( global );
635           const std::string & mstr( it->strMatcher.searchstring() );
636           if ( ! mstr.empty() )
637             joined.insert( mstr );
638
639           // copy and exchange the StrMatcher
640           AttrMatchData nattr( *it );
641           nattr.strMatcher = joinedStrMatcher( joined, _flags );
642           _attrMatchList.push_back( std::move(nattr) );
643         }
644         else
645         {
646           // copy matcher
647          _attrMatchList.push_back( *it );
648         }
649       }
650     }
651
652     // If no attributes defined at all, then add 'query all'
653     if ( _attrMatchList.empty() )
654     {
655       _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr, joinedStrMatcher( _strings, _flags ) ) );
656     }
657
658     // Finally check here, whether all involved regex compile.
659     for_( it, _attrMatchList.begin(), _attrMatchList.end() )
660     {
661       it->strMatcher.compile(); // throws on error
662     }
663     //DBG << asString() << endl;
664   }
665
666   ///////////////////////////////////////////////////////////////////
667   namespace
668   {
669     /** Escape \a str_r for use in a regex.
670      * \a flags_r determines whether the input string is interpreted
671      * as regex, glob or plain string.
672      */
673     std::string rxEscape( std::string str_r, const Match & flags_r )
674     {
675       if ( str_r.empty() || flags_r.isModeRegex() )
676         return str_r;
677
678       if ( flags_r.isModeGlob() )
679         return str::rxEscapeGlob( std::move(str_r) );
680
681       return str::rxEscapeStr( std::move(str_r) );
682     }
683   } // namespace
684   ///////////////////////////////////////////////////////////////////
685
686   StrMatcher PoolQuery::Impl::joinedStrMatcher( const StrContainer & container_r, const Match & flags_r ) const
687   {
688     if ( container_r.empty() )
689       return StrMatcher( std::string(), flags_r );
690
691     if ( container_r.size() == 1 && !_match_word )      // use RX to match words
692       return StrMatcher( *container_r.begin(), flags_r );
693
694     // Convert to a regex.
695     // Note: Modes STRING and GLOB match whole strings (anchored ^ $)
696     //       SUBSTRING and REGEX match substrings      (match_word anchores SUBSTRING \b)
697     Match retflags( flags_r );
698     retflags.setModeRegex();
699     str::Str ret;
700
701     if ( flags_r.isModeString() || flags_r.isModeGlob() )
702       ret << "^";
703     else if ( _match_word )
704       ret << "\\b";
705
706     // (..|..|..)
707     char sep = '(';
708     for ( const::std::string & s : container_r )
709     {
710       ret << sep << rxEscape( s, flags_r );
711       if ( sep == '(' )
712         sep = '|';
713     }
714     ret << ')';
715
716     if ( flags_r.isModeString() || flags_r.isModeGlob() )
717       ret << "$";
718     else if ( _match_word )
719       ret << "\\b";
720
721     return StrMatcher( ret, retflags );
722   }
723
724   string PoolQuery::Impl::asString() const
725   {
726     ostringstream o;
727
728     o << "kinds: ";
729     if ( _kinds.empty() )
730       o << "ALL";
731     else
732     {
733       for(Kinds::const_iterator it = _kinds.begin();
734           it != _kinds.end(); ++it)
735         o << *it << " ";
736     }
737     o << endl;
738
739     o << "repos: ";
740     if ( _repos.empty() )
741       o << "ALL";
742     else
743     {
744       for(StrContainer::const_iterator it = _repos.begin();
745           it != _repos.end(); ++it)
746         o << *it << " ";
747     }
748     o << endl;
749
750     o << "version: "<< _op << " " << _edition.asString() << endl;
751     o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
752                                        : "ALL" ) << endl;
753
754     o << "string match flags: " << Match(_flags) << endl;
755
756     // raw
757     o << "strings: ";
758     for(StrContainer::const_iterator it = _strings.begin();
759         it != _strings.end(); ++it)
760       o << *it << " ";
761     o << endl;
762
763     o << "attributes: " << endl;
764     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
765     {
766       o << "* " << ai->first << ": ";
767       for(StrContainer::const_iterator vi = ai->second.begin();
768           vi != ai->second.end(); ++vi)
769         o << *vi << " ";
770       o << endl;
771     }
772
773     o << "predicated: " << endl;
774     for_( it, _uncompiledPredicated.begin(), _uncompiledPredicated.end() )
775     {
776       o << "* " << *it << endl;
777     }
778
779     // compiled
780     o << "last attribute matcher compiled: " << endl;
781     if ( _attrMatchList.empty() )
782     {
783       o << "not yet compiled" << endl;
784     }
785     else
786     {
787       for_( it, _attrMatchList.begin(), _attrMatchList.end() )
788       {
789         o << "* " << *it << endl;
790       }
791     }
792     return o.str();
793   }
794
795   ///////////////////////////////////////////////////////////////////
796
797   ///////////////////////////////////////////////////////////////////
798   //
799   //    CLASS NAME : PoolQuery
800   //
801   ///////////////////////////////////////////////////////////////////
802
803   PoolQuery::PoolQuery()
804     : _pimpl(new Impl())
805   {}
806
807   PoolQuery::~PoolQuery()
808   {}
809
810   void PoolQuery::addRepo(const std::string &repoalias)
811   {
812     if (repoalias.empty())
813     {
814       WAR << "ignoring an empty repository alias" << endl;
815       return;
816     }
817     _pimpl->_repos.insert(repoalias);
818   }
819
820   void PoolQuery::addKind(const ResKind & kind)
821   { _pimpl->_kinds.insert(kind); }
822
823   void PoolQuery::addString(const string & value)
824   { _pimpl->_strings.insert(value); }
825
826   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
827   { _pimpl->_attrs[attr].insert(value); }
828
829   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition )
830   { return addDependency( attr, name, op, edition, Arch_empty ); }
831
832   void PoolQuery::addDependency( const sat::SolvAttr & attr, const std::string & name, const Rel & op, const Edition & edition, const Arch & arch )
833   {
834     // SolvAttr::name with explicit 'kind:name' will overwrite the default _kinds
835     ResKind explicitKind;
836     if ( attr == sat::SolvAttr::name ) explicitKind = ResKind::explicitBuiltin( name );
837
838     switch ( op.inSwitch() )
839     {
840       case Rel::ANY_e:  // no additional constraint on edition.
841         if ( arch.empty() && !explicitKind )    // no additional constraint on arch/kind
842         {
843           addAttribute( attr, name );
844           return;
845         }
846         break;
847
848       case Rel::NONE_e: // will never match.
849         return;
850
851       default: // go and add the predicated query (uncompiled)
852         break;
853     }
854
855     // Match::OTHER indicates need to compile
856     // (merge global search strings into name).
857     AttrMatchData attrMatchData( attr );
858     if ( !explicitKind )
859       attrMatchData.strMatcher = StrMatcher( name, Match::OTHER );
860     else
861     {
862       // ResKind::explicitBuiltin call above asserts the presence of the ':' in name
863       attrMatchData.strMatcher = StrMatcher( strchr( name.c_str(), ':')+1, Match::OTHER );
864       attrMatchData.kindPredicate = explicitKind;
865     }
866
867     if ( isDependencyAttribute( attr ) )
868       attrMatchData.addPredicate( EditionRangePredicate( op, edition, arch ) );
869     else
870       attrMatchData.addPredicate( SolvableRangePredicate( op, edition, arch ) );
871
872     _pimpl->_uncompiledPredicated.insert( attrMatchData );
873   }
874
875   void PoolQuery::addDependency( const sat::SolvAttr & attr, Capability cap_r )
876   {
877     CapDetail cap( cap_r );
878     if ( ! cap.isSimple() ) // will never match.
879       return;
880
881     // Matches STRING per default. (won't get compiled!)
882     AttrMatchData attrMatchData( attr, StrMatcher( cap.name().asString() ) );
883
884     if ( isDependencyAttribute( attr ) )
885       attrMatchData.addPredicate( CapabilityMatchPredicate( cap_r ) );
886     else
887       attrMatchData.addPredicate( SolvableRangePredicate( cap.op(), cap.ed() ) );
888
889     _pimpl->_uncompiledPredicated.insert( attrMatchData );
890   }
891
892   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
893   {
894     _pimpl->_edition = edition;
895     _pimpl->_op = op;
896   }
897
898   void PoolQuery::setMatchSubstring()   { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = false; }
899   void PoolQuery::setMatchExact()       { _pimpl->_flags.setModeString();       _pimpl->_match_word = false; }
900   void PoolQuery::setMatchRegex()       { _pimpl->_flags.setModeRegex();        _pimpl->_match_word = false; }
901   void PoolQuery::setMatchGlob()        { _pimpl->_flags.setModeGlob();         _pimpl->_match_word = false; }
902   void PoolQuery::setMatchWord()        { _pimpl->_flags.setModeSubstring();    _pimpl->_match_word = true; }
903
904   Match PoolQuery::flags() const
905   { return _pimpl->_flags; }
906   void PoolQuery::setFlags( const Match & flags )
907   { _pimpl->_flags = flags; }
908
909
910   void PoolQuery::setInstalledOnly()
911   { _pimpl->_status_flags = INSTALLED_ONLY; }
912   void PoolQuery::setUninstalledOnly()
913   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
914   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
915   { _pimpl->_status_flags = flags; }
916
917
918   const PoolQuery::StrContainer &
919   PoolQuery::strings() const
920   { return _pimpl->_strings; }
921
922   const PoolQuery::AttrRawStrMap &
923   PoolQuery::attributes() const
924   { return _pimpl->_attrs; }
925
926   const PoolQuery::StrContainer &
927   PoolQuery::attribute(const sat::SolvAttr & attr) const
928   {
929     static const PoolQuery::StrContainer nocontainer;
930     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
931     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
932   }
933
934   const Edition PoolQuery::edition() const
935   { return _pimpl->_edition; }
936   const Rel PoolQuery::editionRel() const
937   { return _pimpl->_op; }
938
939
940   const PoolQuery::Kinds &
941   PoolQuery::kinds() const
942   { return _pimpl->_kinds; }
943
944   const PoolQuery::StrContainer &
945   PoolQuery::repos() const
946   { return _pimpl->_repos; }
947
948
949   bool PoolQuery::caseSensitive() const
950   { return !_pimpl->_flags.test( Match::NOCASE ); }
951   void PoolQuery::setCaseSensitive( bool value )
952   { _pimpl->_flags.turn( Match::NOCASE, !value ); }
953
954   bool PoolQuery::filesMatchFullPath() const
955   { return _pimpl->_flags.test( Match::FILES ); }
956   void PoolQuery::setFilesMatchFullPath( bool value )
957   { _pimpl->_flags.turn( Match::FILES, value ); }
958
959   bool PoolQuery::matchExact() const            { return _pimpl->_flags.isModeString(); }
960   bool PoolQuery::matchSubstring() const        { return _pimpl->_flags.isModeSubstring() && !_pimpl->_match_word; }
961   bool PoolQuery::matchGlob() const             { return _pimpl->_flags.isModeGlob(); }
962   bool PoolQuery::matchRegex() const            { return _pimpl->_flags.isModeRegex(); }
963   bool PoolQuery::matchWord() const             { return _pimpl->_flags.isModeSubstring() && _pimpl->_match_word; }
964
965   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
966   { return _pimpl->_status_flags; }
967
968   bool PoolQuery::empty() const
969   {
970     try { return begin() == end(); }
971     catch (const Exception & ex) {}
972     return true;
973   }
974
975   PoolQuery::size_type PoolQuery::size() const
976   {
977     try
978     {
979       size_type count = 0;
980       for_( it, begin(), end() )
981         ++count;
982       return count;
983     }
984     catch (const Exception & ex) {}
985     return 0;
986   }
987
988   void PoolQuery::execute(ProcessResolvable fnc)
989   { invokeOnEach( begin(), end(), fnc); }
990
991
992   /*DEPRECATED LEGACY:*/void PoolQuery::setRequireAll( bool ) {}
993   /*DEPRECATED LEGACY:*/bool PoolQuery::requireAll() const    { return false; }
994
995   ///////////////////////////////////////////////////////////////////
996   //
997   //  CLASS NAME : PoolQuery::Attr
998   //
999   /**
1000    * represents all atributes in PoolQuery except SolvAtributes, which are
1001    * used as is (not needed extend anything if someone adds new solv attr)
1002    */
1003   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
1004   {
1005   private:
1006     friend class IdStringType<PoolQueryAttr>;
1007     IdString _str;
1008   public:
1009
1010     //noAttr
1011     PoolQueryAttr(){}
1012
1013     explicit PoolQueryAttr( const char* cstr_r )
1014         : _str( cstr_r )
1015       {}
1016
1017     explicit PoolQueryAttr( const std::string & str_r )
1018         : _str( str_r )
1019       {}
1020
1021     // unknown atributes
1022     static const PoolQueryAttr noAttr;
1023
1024     // PoolQuery's own attributes
1025     static const PoolQueryAttr repoAttr;
1026     static const PoolQueryAttr kindAttr;
1027     static const PoolQueryAttr stringAttr;
1028     static const PoolQueryAttr stringTypeAttr;
1029     static const PoolQueryAttr requireAllAttr;  // LEAGACY: attribute was defined but never implemented.
1030     static const PoolQueryAttr caseSensitiveAttr;
1031     static const PoolQueryAttr installStatusAttr;
1032     static const PoolQueryAttr editionAttr;
1033     static const PoolQueryAttr complexAttr;
1034   };
1035
1036   const PoolQueryAttr PoolQueryAttr::noAttr;
1037
1038   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
1039   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
1040   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
1041   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
1042   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");     // LEAGACY: attribute was defined but never implemented.
1043   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
1044   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
1045   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
1046   const PoolQueryAttr PoolQueryAttr::complexAttr("complex");
1047
1048   class StringTypeAttr : public IdStringType<PoolQueryAttr>
1049   {
1050     friend class IdStringType<StringTypeAttr>;
1051     IdString _str;
1052
1053   public:
1054     StringTypeAttr(){}
1055     explicit StringTypeAttr( const char* cstr_r )
1056             : _str( cstr_r ){}
1057     explicit StringTypeAttr( const std::string & str_r )
1058              : _str( str_r ){}
1059
1060     static const StringTypeAttr noAttr;
1061
1062     static const StringTypeAttr exactAttr;
1063     static const StringTypeAttr substringAttr;
1064     static const StringTypeAttr regexAttr;
1065     static const StringTypeAttr globAttr;
1066     static const StringTypeAttr wordAttr;
1067   };
1068
1069   const StringTypeAttr StringTypeAttr::noAttr;
1070
1071   const StringTypeAttr StringTypeAttr::exactAttr("exact");
1072   const StringTypeAttr StringTypeAttr::substringAttr("substring");
1073   const StringTypeAttr StringTypeAttr::regexAttr("regex");
1074   const StringTypeAttr StringTypeAttr::globAttr("glob");
1075   const StringTypeAttr StringTypeAttr::wordAttr("word");
1076
1077   ///////////////////////////////////////////////////////////////////
1078
1079
1080   //\TODO maybe ctor with stream can be usefull
1081   //\TODO let it throw, let it throw, let it throw.
1082   bool PoolQuery::recover( istream &str, char delim )
1083   {
1084     bool finded_something = false; //indicates some atributes is finded
1085     string s;
1086     do {
1087       if ( str.eof() )
1088         break;
1089
1090       getline( str, s, delim );
1091
1092       if ((!s.empty()) && s[0]=='#') //comment
1093       {
1094         continue;
1095       }
1096
1097       string::size_type pos = s.find(':');
1098       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
1099       {
1100         if (finded_something) //is first blank line after record?
1101         {
1102           break;
1103         }
1104         else
1105         {
1106           continue;
1107         }
1108       }
1109
1110       finded_something = true;
1111
1112       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
1113       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
1114
1115       PoolQueryAttr attribute( attrName );
1116
1117       if ( attribute==PoolQueryAttr::repoAttr )
1118       {
1119         addRepo( attrValue );
1120       }
1121       /* some backwards compatibility */
1122       else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
1123       {
1124         addKind( ResKind(attrValue) );
1125       }
1126       else if ( attribute==PoolQueryAttr::stringAttr
1127         || attribute=="global_string")
1128       {
1129         addString( attrValue );
1130       }
1131       else if ( attribute==PoolQueryAttr::stringTypeAttr
1132         || attribute=="string_type" )
1133       {
1134         StringTypeAttr s(attrValue);
1135         if( s == StringTypeAttr::regexAttr )
1136         {
1137           setMatchRegex();
1138         }
1139         else if ( s == StringTypeAttr::globAttr )
1140         {
1141           setMatchGlob();
1142         }
1143         else if ( s == StringTypeAttr::exactAttr )
1144         {
1145           setMatchExact();
1146         }
1147         else if ( s == StringTypeAttr::substringAttr )
1148         {
1149           setMatchSubstring();
1150         }
1151         else if ( s == StringTypeAttr::wordAttr )
1152         {
1153           setMatchWord();
1154         }
1155         else if ( s == StringTypeAttr::noAttr )
1156         {
1157           WAR << "unknown string type " << attrValue << endl;
1158         }
1159         else
1160         {
1161           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
1162         }
1163       }
1164       else if ( attribute==PoolQueryAttr::requireAllAttr )
1165       {
1166         // LEAGACY: attribute was defined but never implemented.
1167         // Actually it should not occur outside our testcases.
1168       }
1169       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
1170       {
1171         if ( str::strToTrue(attrValue) )
1172         {
1173           setCaseSensitive(true);
1174         }
1175         else if ( !str::strToFalse(attrValue) )
1176         {
1177           setCaseSensitive(false);
1178         }
1179         else
1180         {
1181           WAR << "unknown boolean value " << attrValue << endl;
1182         }
1183       }
1184       else if ( attribute==PoolQueryAttr::installStatusAttr )
1185       {
1186         if( attrValue == "all" )
1187         {
1188           setStatusFilterFlags( ALL );
1189         }
1190         else if( attrValue == "installed" )
1191         {
1192           setInstalledOnly();
1193         }
1194         else if( attrValue == "not-installed" )
1195         {
1196           setUninstalledOnly();
1197         }
1198         else
1199         {
1200           WAR << "Unknown value for install status " << attrValue << endl;
1201         }
1202       }
1203       else if ( attribute == PoolQueryAttr::editionAttr)
1204       {
1205         string::size_type pos;
1206         Rel rel("==");
1207         if (attrValue.find_first_of("=<>!") == 0)
1208         {
1209           pos = attrValue.find_last_of("=<>");
1210           rel = Rel(attrValue.substr(0, pos+1));
1211           attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
1212         }
1213
1214         setEdition(Edition(attrValue), rel);
1215       }
1216       else if ( attribute == PoolQueryAttr::complexAttr )
1217       {
1218         try
1219         {
1220           _pimpl->_uncompiledPredicated.insert( AttrMatchData::deserialize( attrValue ) );
1221         }
1222         catch ( const Exception & err )
1223         {
1224           WAR << "Unparsable value for complex: " << err.asUserHistory() << endl;
1225
1226         }
1227       }
1228       else if ( attribute==PoolQueryAttr::noAttr )
1229       {
1230         WAR << "empty attribute name" << endl;
1231       }
1232       else
1233       {
1234         string s = attrName;
1235         str::replaceAll( s,"_",":" );
1236         SolvAttr a(s);
1237         if ( a == SolvAttr::name || isDependencyAttribute( a ) )
1238         {
1239           Capability c( attrValue );
1240           CapDetail d( c );
1241           if ( d.isVersioned() )
1242             addDependency( a, d.name().asString(), d.op(), d.ed() );
1243           else
1244             addDependency( a, attrValue );
1245         }
1246         else
1247           addAttribute( a, attrValue );
1248       }
1249
1250     } while ( true );
1251
1252     // OLD STYLE VERSIONED LOCKS:
1253     //  solvable_name: kernel
1254     //  version: > 1
1255     //
1256     // NEW STYLE VERSIONED LOCKS:
1257     //  complex: AttrMatchData solvable:name kernel C SolvableRange\ >\ 1\ \"\"
1258     //   or
1259     //  solvable_name: kernel > 1
1260     //
1261     // Semantically equivalent as locks, but due to the different syntax
1262     // the complex lock is wrongly handled by zypper.
1263     //
1264     // bsc#1112911: Unfortunately all styles are found in real-life locks-files.
1265     // libzypp will try to make sure, when parsing the locks-file, that complex
1266     // locks are rewritten into to OLD STYLE queries zypper can handle.
1267     if ( !_pimpl->_attrs.count(SolvAttr::name) && _pimpl->_uncompiledPredicated.size() == 1 )
1268     {
1269       // No OLD STYLE lock for SolvAttr::name and exactly one complex lock...
1270       const AttrMatchData & attrmatch {  *_pimpl->_uncompiledPredicated.begin() };
1271       if ( attrmatch.attr == SolvAttr::name && attrmatch.strMatcher.flags().mode() == Match::OTHER )
1272       {
1273         // ...for SolvAttr::name and following the global search flags.
1274         // A candidate for a rewrite?
1275
1276         std::vector<std::string> words;
1277         str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
1278         if ( words.size() < 4 || words[3].empty() )
1279         {
1280           // We have _NO_ arch rule in the complex predicate, so we can simplify it.
1281           //
1282           // NOTE: AFAIK it's not possible to create (or have created) a complex lock
1283           // with arch rule with zypper means. Nevertheless, in case such a rule made it
1284           // into a locks file, it's better to have a strange looking 'zypper locks' list
1285           // than to lock the wrong packages.
1286           // (and remember that you can't use "addAttribute( SolvAttr::arch, ... )" because
1287           // attributes are `OR`ed)
1288
1289           // kind
1290           if ( attrmatch.kindPredicate )
1291           {
1292             _pimpl->_kinds.clear();     // an explicit kind overwrites any global one
1293             addKind( attrmatch.kindPredicate );
1294           }
1295
1296           // name
1297           addAttribute( SolvAttr::name, attrmatch.strMatcher.searchstring() );
1298
1299           // edition
1300           std::vector<std::string> words;
1301           str::splitEscaped( attrmatch.predicateStr, std::back_inserter(words) );
1302           if ( ! words.empty() )
1303           {
1304             if ( words[0] == "EditionRange" || words[0] == "SolvableRange" )
1305             {
1306               setEdition( Edition(words[2]), Rel(words[1]) );
1307             }
1308           }
1309
1310           // finally remove the complex lock
1311           _pimpl->_uncompiledPredicated.clear();
1312         }
1313       }
1314     }
1315
1316     return finded_something;
1317   }
1318
1319   void PoolQuery::serialize( ostream &str, char delim ) const
1320   {
1321     //separating delim
1322     str << delim;
1323     //iterate thrue all settings and write it
1324     static const zypp::PoolQuery q; //not save default options, so create default query example
1325
1326     for_( it, repos().begin(), repos().end() )
1327     {
1328       str << "repo: " << *it << delim ;
1329     }
1330
1331     for_( it, kinds().begin(), kinds().end() )
1332     {
1333       str << PoolQueryAttr::kindAttr.asString() << ": "
1334           << it->idStr() << delim ;
1335     }
1336
1337     if (editionRel() != Rel::ANY && edition() != Edition::noedition)
1338       str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
1339
1340     if (matchMode()!=q.matchMode())
1341     {
1342       switch( matchMode() )
1343       {
1344       case Match::STRING:
1345         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
1346         break;
1347       case Match::SUBSTRING:
1348         str << PoolQueryAttr::stringTypeAttr.asString()
1349             << ": substring" << delim;
1350         break;
1351       case Match::GLOB:
1352         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
1353         break;
1354       case Match::REGEX:
1355         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
1356         break;
1357       default:
1358         WAR << "unknown match type "  << matchMode() << endl;
1359       }
1360     }
1361
1362     if( caseSensitive() != q.caseSensitive() )
1363     {
1364       str << "case_sensitive: ";
1365       if (caseSensitive())
1366       {
1367         str << "on" << delim;
1368       }
1369       else
1370       {
1371         str << "off" << delim;
1372       }
1373     }
1374
1375     if( statusFilterFlags() != q.statusFilterFlags() )
1376     {
1377       switch( statusFilterFlags() )
1378       {
1379       case ALL:
1380         str << "install_status: all" << delim;
1381         break;
1382       case INSTALLED_ONLY:
1383         str << "install_status: installed" << delim;
1384         break;
1385       case UNINSTALLED_ONLY:
1386         str << "install_status: not-installed" << delim;
1387         break;
1388       }
1389     }
1390
1391     for_( it, strings().begin(), strings().end() )
1392     {
1393       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
1394     }
1395
1396     for_( it, attributes().begin(), attributes().end() )
1397     {
1398       string s = it->first.asString();
1399       str::replaceAll(s,":","_");
1400       for_( it2,it->second.begin(),it->second.end() )
1401       {
1402         str << s <<": "<< *it2 << delim;
1403       }
1404     }
1405
1406     for_( it, _pimpl->_uncompiledPredicated.begin(), _pimpl->_uncompiledPredicated.end() )
1407     {
1408       str << "complex: "<< it->serialize() << delim;
1409     }
1410
1411     //separating delim - protection
1412     str << delim;
1413   }
1414
1415   string PoolQuery::asString() const
1416   { return _pimpl->asString(); }
1417
1418   ostream & operator<<( ostream & str, const PoolQuery & obj )
1419   { return str << obj.asString(); }
1420
1421   std::ostream & dumpOn( std::ostream & str, const PoolQuery & obj )
1422   { return dumpRange( str << obj, obj.begin(), obj.end() ); }
1423
1424   bool PoolQuery::operator==( const PoolQuery & rhs ) const
1425   { return *_pimpl == *rhs._pimpl; }
1426
1427   bool PoolQuery::operator<( const PoolQuery & rhs ) const
1428   { return *_pimpl < *rhs._pimpl; }
1429
1430   ///////////////////////////////////////////////////////////////////
1431   namespace detail
1432   { /////////////////////////////////////////////////////////////////
1433
1434     ///////////////////////////////////////////////////////////////////
1435     //
1436     //  CLASS NAME : PoolQueryMatcher
1437     //
1438     /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1439      *
1440      * Basically the matcher performs a base query, which should preselect
1441      * candidates for a match. And has some filter conditions on top of it.
1442      * Query and fileter depend on the \ref PoolQuery settings.
1443      *
1444      * Matcher must be stateless, as it is shared between multiple
1445      * \ref PoolQueryIterator instances.
1446      *
1447      * If \ref base_iterator is at the \ref end, \ref advance moves it
1448      * to the first match. Otherwise advance moves to the next match, or
1449      * to the \ref end, if there is no more match.
1450      *
1451      * \note The original implementation treated an empty search string as
1452      * <it>"match always"</it>. We stay compatible.
1453      */
1454     class PoolQueryMatcher
1455     {
1456       public:
1457         typedef sat::LookupAttr::iterator base_iterator;
1458
1459       public:
1460         const base_iterator & end() const
1461         {
1462           static base_iterator _end;
1463           return _end;
1464         }
1465
1466         bool advance( base_iterator & base_r ) const
1467         {
1468           if ( base_r == end() )
1469             base_r = startNewQyery(); // first candidate
1470           else
1471           {
1472             base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1473             ++base_r; // advance to next candidate
1474           }
1475
1476           while ( base_r != end() )
1477           {
1478             if ( isAMatch( base_r ) )
1479               return true;
1480             // No match: try next
1481             ++base_r;
1482           }
1483           return false;
1484         }
1485
1486         /** Provide all matching attributes within this solvable.
1487          *
1488          */
1489         void matchDetail( const base_iterator & base_r, std::vector<base_iterator> & return_r ) const
1490         {
1491           if ( base_r == end() )
1492             return;
1493
1494           sat::Solvable inSolvable( base_r.inSolvable() );
1495
1496           if ( _attrMatchList.size() == 1 )
1497           {
1498             // base_r is already on the 1st matching attribute!
1499             // String matching is done by the base iterator. We must check the predicate here.
1500             // Let's see if there are more matches for this solvable:
1501             base_iterator base( base_r );
1502             base.stayInThisSolvable(); // avoid discarding matches we found far away from here.
1503             return_r.push_back( base );
1504
1505             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1506             for ( ++base; base.inSolvable() == inSolvable; ++base ) // safe even if base == end()
1507             {
1508               if ( ! predicate || predicate( base ) )
1509                 return_r.push_back( base );
1510             }
1511           }
1512           else
1513           {
1514             // Here: search all attributes ;(
1515             for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1516             {
1517               const AttrMatchData & matchData( *mi );
1518               sat::LookupAttr q( matchData.attr, inSolvable );
1519               if ( matchData.strMatcher ) // an empty searchstring matches always
1520                 q.setStrMatcher( matchData.strMatcher );
1521
1522               if ( ! q.empty() ) // there are matches.
1523               {
1524                 // now check any predicate:
1525                 const AttrMatchData::Predicate & predicate( matchData.predicate );
1526                 for_( it, q.begin(), q.end() )
1527                 {
1528                   if ( ! predicate || predicate( it ) )
1529                     return_r.push_back( it );
1530                 }
1531               }
1532             }
1533           }
1534         }
1535
1536       public:
1537         /** Ctor stores the \ref PoolQuery settings.
1538          * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1539          */
1540         PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1541         {
1542           query_r->compile();
1543
1544           // Repo restriction:
1545           sat::Pool satpool( sat::Pool::instance() );
1546
1547           for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1548           {
1549             Repository r( satpool.reposFind( *it ) );
1550             if ( r )
1551               _repos.insert( r );
1552             else
1553               _neverMatchRepo = true;
1554           }
1555           // _neverMatchRepo: we just need to catch the case that no repo
1556           // matched, so we'd interpret the empty list as 'take from all'
1557           if ( _neverMatchRepo && ! _repos.empty() )
1558             _neverMatchRepo = false;
1559
1560           // Kind restriction:
1561           _kinds = query_r->_kinds;
1562           // Edition restriction:
1563           _op      = query_r->_op;
1564           _edition = query_r->_edition;
1565           // Status restriction:
1566           _status_flags = query_r->_status_flags;
1567           // StrMatcher
1568           _attrMatchList = query_r->_attrMatchList;
1569         }
1570
1571         ~PoolQueryMatcher()
1572         {}
1573
1574       private:
1575         /** Initialize a new base query. */
1576         base_iterator startNewQyery() const
1577         {
1578           sat::LookupAttr q;
1579
1580           if ( _neverMatchRepo )
1581             return q.end();
1582
1583           // Repo restriction:
1584           if ( _repos.size() == 1 )
1585             q.setRepo( *_repos.begin() );
1586           // else: handled in isAMatch.
1587
1588           // Attribute restriction:
1589           if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1590           {
1591             const AttrMatchData & matchData( _attrMatchList.front() );
1592             q.setAttr( matchData.attr );
1593             if ( matchData.strMatcher ) // empty searchstring matches always
1594               q.setStrMatcher( matchData.strMatcher );
1595           }
1596           else // more than 1 attr (but not all)
1597           {
1598             // no restriction, it's all handled in isAMatch.
1599             q.setAttr( sat::SolvAttr::allAttr );
1600           }
1601
1602           return q.begin();
1603         }
1604
1605
1606         /** Check whether we are on a match.
1607          *
1608          * The check covers the whole Solvable, not just the current
1609          * attribute \c base_r points to. If there's no match, also
1610          * prepare \c base_r to advance appropriately. If there is
1611          * a match, simply return \c true. \ref advance always moves
1612          * to the next Solvable if there was a match.
1613          *
1614          * \note: Caller asserts we're not at \ref end.
1615         */
1616         bool isAMatch( base_iterator & base_r ) const
1617         {
1618           /////////////////////////////////////////////////////////////////////
1619           Repository inRepo( base_r.inRepo() );
1620           // Status restriction:
1621           if ( _status_flags
1622              && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1623           {
1624             base_r.nextSkipRepo();
1625             return false;
1626           }
1627           // Repo restriction:
1628           if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1629           {
1630             base_r.nextSkipRepo();
1631             return false;
1632           }
1633           /////////////////////////////////////////////////////////////////////
1634           sat::Solvable inSolvable( base_r.inSolvable() );
1635           // Edition restriction:
1636           if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1637           {
1638             base_r.nextSkipSolvable();
1639             return false;
1640           }
1641
1642           // Kind restriction:
1643           // Delay the decision to nextSkipSolvable and return false, as there may be
1644           // some explicit kind:name predicate which overrules the global kinds.
1645           bool globalKindOk =( _kinds.empty() || inSolvable.isKind( _kinds.begin(), _kinds.end() ) );
1646
1647           /////////////////////////////////////////////////////////////////////
1648           // string and predicate matching:
1649
1650           if ( _attrMatchList.size() == 1 )
1651           {
1652             // String matching was done by the base iterator.
1653             // Now check any predicate:
1654             const AttrMatchData & matchData( _attrMatchList.front() );
1655
1656             if ( matchData.kindPredicate )
1657             {
1658               if ( matchData.kindPredicate != inSolvable.kind() )
1659               {
1660                 base_r.nextSkipSolvable();      // this matchData will never match in this solvable
1661                 return false;
1662               }
1663             }
1664             else if ( !globalKindOk )
1665               return false;                     // only matching kindPredicate could overwrite this
1666
1667             if ( !matchData.predicate || matchData.predicate( base_r ) )
1668               return true;
1669
1670             return false; // no skip as there may be more occurrences in this solvable of this attr.
1671           }
1672
1673           // Here: search all attributes ;(
1674           for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1675           {
1676             const AttrMatchData & matchData( *mi );
1677
1678             if ( matchData.kindPredicate )
1679             {
1680               if ( matchData.kindPredicate != inSolvable.kind() )
1681                 continue;                       // this matchData does not apply
1682             }
1683             else if ( !globalKindOk )
1684               continue;                         // only matching kindPredicate could overwrite this
1685
1686             sat::LookupAttr q( matchData.attr, inSolvable );
1687             if ( matchData.strMatcher ) // an empty searchstring matches always
1688               q.setStrMatcher( matchData.strMatcher );
1689
1690             if ( ! q.empty() ) // there are matches.
1691             {
1692               // now check any predicate:
1693               const AttrMatchData::Predicate & predicate( matchData.predicate );
1694               if ( predicate )
1695               {
1696                 for_( it, q.begin(), q.end() )
1697                 {
1698                   if ( predicate( it ) )
1699                     return true;
1700                 }
1701               }
1702               else
1703                 return true;
1704             }
1705           }
1706           base_r.nextSkipSolvable();
1707           return false;
1708         }
1709
1710       private:
1711         /** Repositories include in the search. */
1712         std::set<Repository> _repos;
1713         DefaultIntegral<bool,false> _neverMatchRepo;
1714         /** Resolvable kinds to include. */
1715         std::set<ResKind> _kinds;
1716         /** Edition filter. */
1717         Rel _op;
1718         Edition _edition;
1719         /** Installed status filter flags. \see PoolQuery::StatusFilter */
1720         int _status_flags;
1721         /** StrMatcher per attribtue. */
1722         AttrMatchList _attrMatchList;
1723     };
1724     ///////////////////////////////////////////////////////////////////
1725
1726     void PoolQueryIterator::increment()
1727     {
1728       // matcher restarts if at end! It is called from the ctor
1729       // to get the 1st match. But if the end is reached, it should
1730       // be deleted, otherwise we'd start over again.
1731       if ( !_matcher )
1732         return; // at end
1733       if ( _matches )
1734         _matches.reset(); // invalidate old matches
1735       if ( ! _matcher->advance( base_reference() ) )
1736         _matcher.reset();
1737     }
1738
1739     const PoolQueryIterator::Matches & PoolQueryIterator::matches() const
1740     {
1741       if ( _matches )
1742         return *_matches;
1743
1744       if ( !_matcher )
1745       {
1746         // at end of query:
1747         static const Matches _none;
1748         return _none;
1749       }
1750
1751       _matches.reset( new Matches );
1752       _matcher->matchDetail( base_reference(), *_matches );
1753       return *_matches;
1754     }
1755
1756     std::ostream & dumpOn( std::ostream & str, const PoolQueryIterator & obj )
1757     {
1758       str << *obj;
1759       if ( ! obj.matchesEmpty() )
1760       {
1761         for_( it, obj.matchesBegin(), obj.matchesEnd() )
1762         {
1763           str << endl << "    " << it->inSolvAttr() << "\t" << it->asString();
1764         }
1765       }
1766       return str;
1767     }
1768
1769     ///////////////////////////////////////////////////////////////////
1770   } //namespace detail
1771   ///////////////////////////////////////////////////////////////////
1772
1773   detail::PoolQueryIterator PoolQuery::begin() const
1774   {
1775     return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1776   }
1777
1778   /////////////////////////////////////////////////////////////////
1779 } // namespace zypp
1780 ///////////////////////////////////////////////////////////////////
1781