Predicate support in PoolQueryMatcher.
[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/Logger.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/sat/AttrMatcher.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     /** Match data per attribtue.
43      *
44      * This includes the attribute itself, an optional \ref sat::AttrMatcher
45      * to restrict the query to certain string values, and an optional
46      * boolean \ref Predicate that may apply further restrictions that can
47      * not be expressed by the \ref attrMatcher.
48      *
49      * Example for such a \ref predicate would be an additional edition range
50      * check whan looking for dependencies. The \ref attrMatcher would
51      * find potential matches by looking at the dependencies name, the
52      * predicate will then check the edition ranges.
53      *
54      * As the \ref predicate takes an iterator pointing to the current
55      * match, it's also suitable for sub-structure (flexarray) inspection
56      * (\see \ref sat::LookupAttr::iterator::solvAttrSubEntry).
57      */
58     struct AttrMatchData
59     {
60       typedef function<bool(sat::LookupAttr::iterator)> Predicate;
61
62       static bool always( sat::LookupAttr::iterator ) { return true; }
63       static bool never( sat::LookupAttr::iterator ) { return false; }
64
65       AttrMatchData( sat::SolvAttr attr_r, const sat::AttrMatcher & attrMatcher_r )
66         : attr( attr_r )
67         , attrMatcher( attrMatcher_r )
68       {}
69
70       sat::SolvAttr    attr;
71       sat::AttrMatcher attrMatcher;
72       Predicate        predicate;
73     };
74
75     std::ostream & operator<<( std::ostream & str, const AttrMatchData & obj )
76     {
77       return str << obj.attr << ": " << obj.attrMatcher << ( obj.predicate ? " (+predicate)" : "" );
78     }
79
80     typedef std::list<AttrMatchData> AttrMatchList;
81
82   } /////////////////////////////////////////////////////////////////
83   // namespace
84   ///////////////////////////////////////////////////////////////////
85
86   ///////////////////////////////////////////////////////////////////
87   //
88   //  CLASS NAME : PoolQuery::Impl
89   //
90   /** */
91   class PoolQuery::Impl
92   {
93   public:
94     Impl()
95       : _flags( Match::SUBSTRING | Match::NOCASE | Match::SKIP_KIND )
96       , _match_word(false)
97       , _require_all(false)
98       , _status_flags(ALL)
99     {}
100
101     ~Impl()
102     {}
103
104   public:
105     /** String representation */
106     string asString() const;
107
108     /** \name Raw query options. */
109     //@{
110     /** Raw search strings. */
111     StrContainer _strings;
112     /** Raw attributes */
113     AttrRawStrMap _attrs;
114
115     /** Sat solver search flags */
116     Match _flags;
117     bool _match_word;
118     bool _require_all;
119
120     /** Sat solver status flags */
121     StatusFilter _status_flags;
122
123     /** Edition condition operand */
124     Edition _edition;
125     /** Operator for edition condition */
126     Rel _op;
127
128     /** Repos to search. */
129     StrContainer _repos;
130
131     /** Kinds to search */
132     Kinds _kinds;
133     //@}
134
135   public:
136     /** Compile the regex.
137      * Basically building the \ref _attrMatchList from strings.
138      * \throws MatchException Any of the exceptions thrown by \ref AttrMatcher::compile.
139      */
140     void compile() const;
141
142     /** AttrMatcher per attribtue. */
143     mutable AttrMatchList _attrMatchList;
144
145   private:
146     /** Pass flags from \ref compile, as they may have been changed. */
147     string createRegex( const StrContainer & container, const Match & flags ) const;
148
149   private:
150     friend Impl * rwcowClone<Impl>( const Impl * rhs );
151     /** clone for RWCOW_pointer */
152     Impl * clone() const
153     { return new Impl( *this ); }
154   };
155
156
157   struct MyInserter
158   {
159     MyInserter(PoolQuery::StrContainer & cont) : _cont(cont) {}
160
161     bool operator()(const string & str)
162     {
163       _cont.insert(str);
164       return true;
165     }
166
167     PoolQuery::StrContainer & _cont;
168   };
169
170
171   struct EmptyFilter
172   {
173     bool operator()(const string & str)
174     {
175       return !str.empty();
176     }
177   };
178
179   void PoolQuery::Impl::compile() const
180   {
181     _attrMatchList.clear();
182
183     Match cflags( _flags );
184     if ( cflags.mode() == Match::OTHER) // this will never succeed...
185       ZYPP_THROW( MatchUnknownModeException( cflags ) );
186
187     /** Compiled search strings. */
188     string rcstrings;
189
190
191     // 'different'         - will have to iterate through all and match by ourselves (slow)
192     // 'same'              - will pass the compiled string to dataiterator_init
193     // 'one-attr'          - will pass it to dataiterator_init
194     // 'one-non-regex-str' - will pass to dataiterator_init, set flag to SEARCH_STRING or SEARCH_SUBSTRING
195
196     // // NO ATTRIBUTE
197     // else
198     //   for all _strings
199     //     create regex; store in rcstrings; if more strings flag regex;
200     if (_attrs.empty())
201     {
202       rcstrings = createRegex(_strings, cflags);
203       if (_strings.size() > 1) // switch to regex for multiple strings
204         cflags.setModeRegex();
205       _attrMatchList.push_back( AttrMatchData( sat::SolvAttr::allAttr,
206                                 sat::AttrMatcher( rcstrings, cflags ) ) );
207     }
208
209     // // ONE ATTRIBUTE
210     // else if _attrs is not empty but it contains just one attr
211     //   for all _strings and _attr[key] strings
212     //     create regex; flag 'one-attr'; if more strings flag regex;
213     else if (_attrs.size() == 1)
214     {
215       StrContainer joined;
216       invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
217       invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
218       rcstrings = createRegex(joined, cflags);
219       if (joined.size() > 1) // switch to regex for multiple strings
220         cflags.setModeRegex();
221       _attrMatchList.push_back( AttrMatchData( _attrs.begin()->first,
222                                 sat::AttrMatcher( rcstrings, cflags ) ) );
223     }
224
225     // // MULTIPLE ATTRIBUTES
226     else
227     {
228       // check whether there are any per-attribute strings
229       bool attrvals_empty = true;
230       for (AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
231         if (!ai->second.empty())
232           for(StrContainer::const_iterator it = ai->second.begin();
233               it != ai->second.end(); it++)
234             if (!it->empty())
235             {
236               attrvals_empty = false;
237               goto attremptycheckend;
238             }
239 attremptycheckend:
240
241       // chceck whether the per-attribute strings are all the same
242       bool attrvals_thesame = true;
243       AttrRawStrMap::const_iterator ai = _attrs.begin();
244       const StrContainer & set1 = ai->second;
245       ++ai;
246       for (; ai != _attrs.end(); ++ai)
247       {
248         StrContainer result;
249         set_difference(
250           set1.begin(), set1.end(),
251           ai->second.begin(), ai->second.end(),
252           inserter(result, result.begin())/*, ltstr()*/);
253         if (!result.empty())
254         {
255           attrvals_thesame = false;
256           break;
257         }
258       }
259
260       // // THE SAME STRINGS FOR DIFFERENT ATTRS
261       // else if _attrs is not empty but it does not contain strings
262       //   for each key in _attrs take all _strings
263       //     create regex; store in rcstrings; flag 'same'; if more strings flag regex;
264       if (attrvals_empty || attrvals_thesame)
265       {
266         StrContainer joined;
267         if (attrvals_empty)
268         {
269           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
270           rcstrings = createRegex(joined, cflags);
271         }
272         else
273         {
274           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
275           invokeOnEach(_attrs.begin()->second.begin(), _attrs.begin()->second.end(), EmptyFilter(), MyInserter(joined));
276           rcstrings = createRegex(joined, cflags);
277         }
278         if (joined.size() > 1) // switch to regex for multiple strings
279           cflags.setModeRegex();
280         // May use the same AttrMatcher for all
281         sat::AttrMatcher matcher( rcstrings, cflags );
282         for_( ai, _attrs.begin(), _attrs.end() )
283         {
284           _attrMatchList.push_back( AttrMatchData( ai->first, matcher ) );
285         }
286       }
287
288       // // DIFFERENT STRINGS FOR DIFFERENT ATTRS
289       // if _attrs is not empty and it contains non-empty vectors with non-empty strings
290       //   for each key in _attrs take all _strings + all _attrs[key] strings
291       //     create regex; flag 'different'; if more strings flag regex;
292       else
293       {
294         for_(ai, _attrs.begin(), _attrs.end())
295         {
296           StrContainer joined;
297           invokeOnEach(_strings.begin(), _strings.end(), EmptyFilter(), MyInserter(joined));
298           invokeOnEach(ai->second.begin(), ai->second.end(), EmptyFilter(), MyInserter(joined));
299           string s = createRegex(joined, cflags);
300           if (joined.size() > 1) // switch to regex for multiple strings
301             cflags.setModeRegex();
302           _attrMatchList.push_back( AttrMatchData( ai->first,
303                                     sat::AttrMatcher( s, cflags ) ) );
304         }
305       }
306     }
307
308     // Check here, whether all involved regex compile.
309     for_( it, _attrMatchList.begin(), _attrMatchList.end() )
310     {
311       it->attrMatcher.compile(); // throws on error
312     }
313     //DBG << asString() << endl;
314   }
315
316
317   /**
318    * Converts '*' and '?' wildcards within str into their regex equivalents.
319    */
320   static string wildcards2regex(const string & str)
321   {
322     string regexed = str;
323
324     string r_all(".*"); // regex equivalent of '*'
325     string r_one(".");  // regex equivalent of '?'
326     string::size_type pos;
327
328     // replace all "*" in input with ".*"
329     for (pos = 0; (pos = regexed.find("*", pos)) != std::string::npos; pos+=2)
330       regexed = regexed.replace(pos, 1, r_all);
331
332     // replace all "?" in input with "."
333     for (pos = 0; (pos = regexed.find('?', pos)) != std::string::npos; ++pos)
334       regexed = regexed.replace(pos, 1, r_one);
335
336     return regexed;
337   }
338
339   string PoolQuery::Impl::createRegex( const StrContainer & container, const Match & flags ) const
340   {
341 //! macro for word boundary tags for regexes
342 #define WB (_match_word ? string("\\b") : string())
343     string rstr;
344
345     if (container.empty())
346       return rstr;
347
348     if (container.size() == 1)
349     {
350       return WB + *container.begin() + WB;
351     }
352
353     // multiple strings
354
355     bool use_wildcards = flags.isModeGlob();
356     StrContainer::const_iterator it = container.begin();
357     string tmp;
358
359     if (use_wildcards)
360       tmp = wildcards2regex(*it);
361     else
362       tmp = *it;
363
364     if (_require_all)
365     {
366       if ( ! flags.isModeString() ) // not match exact
367         tmp += ".*" + WB + tmp;
368       rstr = "(?=" + tmp + ")";
369     }
370     else
371     {
372       if ( flags.isModeString() || flags.isModeGlob() )
373         rstr = "^";
374       rstr += WB + "(" + tmp;
375     }
376
377     ++it;
378
379     for (; it != container.end(); ++it)
380     {
381       if (use_wildcards)
382         tmp = wildcards2regex(*it);
383       else
384         tmp = *it;
385
386       if (_require_all)
387       {
388         if ( ! flags.isModeString() ) // not match exact
389           tmp += ".*" + WB + tmp;
390         rstr += "(?=" + tmp + ")";
391       }
392       else
393       {
394         rstr += "|" + tmp;
395       }
396     }
397
398     if (_require_all)
399     {
400       if ( ! flags.isModeString() ) // not match exact
401         rstr += WB + ".*";
402     }
403     else
404     {
405       rstr += ")" + WB;
406       if ( flags.isModeString() || flags.isModeGlob() )
407         rstr += "$";
408     }
409
410     return rstr;
411 #undef WB
412   }
413
414   string PoolQuery::Impl::asString() const
415   {
416     ostringstream o;
417
418     o << "kinds: ";
419     if ( _kinds.empty() )
420       o << "ALL";
421     else
422     {
423       for(Kinds::const_iterator it = _kinds.begin();
424           it != _kinds.end(); ++it)
425         o << *it << " ";
426     }
427     o << endl;
428
429     o << "repos: ";
430     if ( _repos.empty() )
431       o << "ALL";
432     else
433     {
434       for(StrContainer::const_iterator it = _repos.begin();
435           it != _repos.end(); ++it)
436         o << *it << " ";
437     }
438     o << endl;
439
440     o << "version: "<< _op << " " << _edition.asString() << endl;
441     o << "status: " << ( _status_flags ? ( _status_flags == INSTALLED_ONLY ? "INSTALLED_ONLY" : "UNINSTALLED_ONLY" )
442                                        : "ALL" ) << endl;
443
444     o << "string match flags: " << Match(_flags) << endl;
445
446     // raw
447     o << "strings: ";
448     for(StrContainer::const_iterator it = _strings.begin();
449         it != _strings.end(); ++it)
450       o << *it << " ";
451     o << endl;
452
453     o << "attributes: " << endl;
454     for(AttrRawStrMap::const_iterator ai = _attrs.begin(); ai != _attrs.end(); ++ai)
455     {
456       o << "* " << ai->first << ": ";
457       for(StrContainer::const_iterator vi = ai->second.begin();
458           vi != ai->second.end(); ++vi)
459         o << *vi << " ";
460       o << endl;
461     }
462
463     // compiled
464     o << "last attribute matcher compiled: " << endl;
465     if ( _attrMatchList.empty() )
466     {
467       o << "not yet compiled" << endl;
468     }
469     else
470     {
471       for_( it, _attrMatchList.begin(), _attrMatchList.end() )
472       {
473         o << "* " << *it << endl;
474       }
475     }
476     return o.str();
477   }
478
479   ///////////////////////////////////////////////////////////////////
480
481   ///////////////////////////////////////////////////////////////////
482   //
483   //    CLASS NAME : PoolQuery
484   //
485   ///////////////////////////////////////////////////////////////////
486
487   PoolQuery::PoolQuery()
488     : _pimpl(new Impl())
489   {}
490
491   PoolQuery::~PoolQuery()
492   {}
493
494   void PoolQuery::addRepo(const std::string &repoalias)
495   {
496     if (repoalias.empty())
497     {
498       WAR << "ignoring an empty repository alias" << endl;
499       return;
500     }
501     _pimpl->_repos.insert(repoalias);
502   }
503
504
505   void PoolQuery::addKind(const ResKind & kind)
506   { _pimpl->_kinds.insert(kind); }
507
508
509   void PoolQuery::addString(const string & value)
510   { _pimpl->_strings.insert(value); }
511
512
513   void PoolQuery::addAttribute(const sat::SolvAttr & attr, const std::string & value)
514   { _pimpl->_attrs[attr].insert(value); }
515
516
517   void PoolQuery::setEdition(const Edition & edition, const Rel & op)
518   {
519     _pimpl->_edition = edition;
520     _pimpl->_op = op;
521   }
522
523   void PoolQuery::setMatchSubstring()   { _pimpl->_flags.setModeSubstring(); }
524   void PoolQuery::setMatchExact()       { _pimpl->_flags.setModeString(); }
525   void PoolQuery::setMatchRegex()       { _pimpl->_flags.setModeRegex(); }
526   void PoolQuery::setMatchGlob()        { _pimpl->_flags.setModeGlob(); }
527   void PoolQuery::setMatchWord()
528   {
529     _pimpl->_match_word = true;
530     _pimpl->_flags.setModeRegex();
531   }
532
533   Match PoolQuery::flags() const
534   { return _pimpl->_flags; }
535   void PoolQuery::setFlags( const Match & flags )
536   { _pimpl->_flags = flags; }
537
538
539   void PoolQuery::setInstalledOnly()
540   { _pimpl->_status_flags = INSTALLED_ONLY; }
541   void PoolQuery::setUninstalledOnly()
542   { _pimpl->_status_flags = UNINSTALLED_ONLY; }
543   void PoolQuery::setStatusFilterFlags( PoolQuery::StatusFilter flags )
544   { _pimpl->_status_flags = flags; }
545
546
547   void PoolQuery::setRequireAll(bool require_all)
548   { _pimpl->_require_all = require_all; }
549
550
551   const PoolQuery::StrContainer &
552   PoolQuery::strings() const
553   { return _pimpl->_strings; }
554
555   const PoolQuery::AttrRawStrMap &
556   PoolQuery::attributes() const
557   { return _pimpl->_attrs; }
558
559   const PoolQuery::StrContainer &
560   PoolQuery::attribute(const sat::SolvAttr & attr) const
561   {
562     static const PoolQuery::StrContainer nocontainer;
563     AttrRawStrMap::const_iterator it = _pimpl->_attrs.find(attr);
564     return it != _pimpl->_attrs.end() ? it->second : nocontainer;
565   }
566
567   const Edition PoolQuery::edition() const
568   { return _pimpl->_edition; }
569   const Rel PoolQuery::editionRel() const
570   { return _pimpl->_op; }
571
572
573   const PoolQuery::Kinds &
574   PoolQuery::kinds() const
575   { return _pimpl->_kinds; }
576
577   const PoolQuery::StrContainer &
578   PoolQuery::repos() const
579   { return _pimpl->_repos; }
580
581
582   bool PoolQuery::caseSensitive() const
583   { return !_pimpl->_flags.test( Match::NOCASE ); }
584   void PoolQuery::setCaseSensitive( bool value )
585   { _pimpl->_flags.turn( Match::NOCASE, !value ); }
586
587   bool PoolQuery::filesMatchFullPath() const
588   { return _pimpl->_flags.test( Match::FILES ); }
589   void PoolQuery::setFilesMatchFullPath( bool value )
590   { _pimpl->_flags.turn( Match::FILES, value ); }
591
592   bool PoolQuery::matchExact() const            { return _pimpl->_flags.isModeString(); }
593   bool PoolQuery::matchSubstring() const        { return _pimpl->_flags.isModeSubstring(); }
594   bool PoolQuery::matchGlob() const             { return _pimpl->_flags.isModeGlob(); }
595   bool PoolQuery::matchRegex() const            { return _pimpl->_flags.isModeRegex(); }
596
597   bool PoolQuery::matchWord() const
598   { return _pimpl->_match_word; }
599
600   bool PoolQuery::requireAll() const
601   { return _pimpl->_require_all; }
602
603   PoolQuery::StatusFilter PoolQuery::statusFilterFlags() const
604   { return _pimpl->_status_flags; }
605
606   bool PoolQuery::empty() const
607   {
608     try { return begin() == end(); }
609     catch (const Exception & ex) {}
610     return true;
611   }
612
613   PoolQuery::size_type PoolQuery::size() const
614   {
615     try
616     {
617       size_type count = 0;
618       for_( it, begin(), end() )
619         ++count;
620       return count;
621     }
622     catch (const Exception & ex) {}
623     return 0;
624   }
625
626   void PoolQuery::execute(ProcessResolvable fnc)
627   { invokeOnEach( begin(), end(), fnc); }
628
629
630   ///////////////////////////////////////////////////////////////////
631   //
632   //  CLASS NAME : PoolQuery::Attr
633   //
634   /**
635    * represents all atributes in PoolQuery except SolvAtributes, which are
636    * used as is (not needed extend anything if someone adds new solv attr)
637    */
638   struct PoolQueryAttr : public IdStringType<PoolQueryAttr>
639   {
640   private:
641     friend class IdStringType<PoolQueryAttr>;
642     IdString _str;
643   public:
644
645     //noAttr
646     PoolQueryAttr(){}
647
648     explicit PoolQueryAttr( const char* cstr_r )
649         : _str( cstr_r )
650       {}
651
652     explicit PoolQueryAttr( const std::string & str_r )
653         : _str( str_r )
654       {}
655
656     // unknown atributes
657     static const PoolQueryAttr noAttr;
658
659     // PoolQuery's own attributes
660     static const PoolQueryAttr repoAttr;
661     static const PoolQueryAttr kindAttr;
662     static const PoolQueryAttr stringAttr;
663     static const PoolQueryAttr stringTypeAttr;
664     static const PoolQueryAttr requireAllAttr;
665     static const PoolQueryAttr caseSensitiveAttr;
666     static const PoolQueryAttr installStatusAttr;
667     static const PoolQueryAttr editionAttr;
668   };
669
670   const PoolQueryAttr PoolQueryAttr::noAttr;
671
672   const PoolQueryAttr PoolQueryAttr::repoAttr( "repo" );
673   const PoolQueryAttr PoolQueryAttr::kindAttr( "type" );
674   const PoolQueryAttr PoolQueryAttr::stringAttr( "query_string" );
675   const PoolQueryAttr PoolQueryAttr::stringTypeAttr("match_type");
676   const PoolQueryAttr PoolQueryAttr::requireAllAttr("require_all");
677   const PoolQueryAttr PoolQueryAttr::caseSensitiveAttr("case_sensitive");
678   const PoolQueryAttr PoolQueryAttr::installStatusAttr("install_status");
679   const PoolQueryAttr PoolQueryAttr::editionAttr("version");
680
681   class StringTypeAttr : public IdStringType<PoolQueryAttr>
682   {
683     friend class IdStringType<StringTypeAttr>;
684     IdString _str;
685
686   public:
687     StringTypeAttr(){}
688     explicit StringTypeAttr( const char* cstr_r )
689             : _str( cstr_r ){}
690     explicit StringTypeAttr( const std::string & str_r )
691              : _str( str_r ){}
692
693     static const StringTypeAttr noAttr;
694
695     static const StringTypeAttr exactAttr;
696     static const StringTypeAttr substringAttr;
697     static const StringTypeAttr regexAttr;
698     static const StringTypeAttr globAttr;
699     static const StringTypeAttr wordAttr;
700   };
701
702   const StringTypeAttr StringTypeAttr::noAttr;
703
704   const StringTypeAttr StringTypeAttr::exactAttr("exact");
705   const StringTypeAttr StringTypeAttr::substringAttr("substring");
706   const StringTypeAttr StringTypeAttr::regexAttr("regex");
707   const StringTypeAttr StringTypeAttr::globAttr("glob");
708   const StringTypeAttr StringTypeAttr::wordAttr("word");
709
710   ///////////////////////////////////////////////////////////////////
711
712
713   //\TODO maybe ctor with stream can be usefull
714   bool PoolQuery::recover( istream &str, char delim )
715   {
716     bool finded_something = false; //indicates some atributes is finded
717     string s;
718     do {
719       if ( str.eof() )
720         break;
721
722       getline( str, s, delim );
723
724       if ((!s.empty()) && s[0]=='#') //comment
725       {
726         continue;
727       }
728
729       string::size_type pos = s.find(':');
730       if (s.empty() || pos == s.npos) // some garbage on line... act like blank line
731       {
732         if (finded_something) //is first blank line after record?
733         {
734           break;
735         }
736         else
737         {
738           continue;
739         }
740       }
741
742       finded_something = true;
743
744       string attrName(str::trim(string(s,0,pos))); // trimmed name of atribute
745       string attrValue(str::trim(string(s,pos+1,s.npos))); //trimmed value
746
747       PoolQueryAttr attribute( attrName );
748
749       MIL << "attribute name: " << attrName << endl;
750
751       if ( attribute==PoolQueryAttr::repoAttr )
752       {
753         addRepo( attrValue );
754       }
755       /* some backwards compatibility */
756       else if ( attribute==PoolQueryAttr::kindAttr || attribute=="kind" )
757       {
758         addKind( ResKind(attrValue) );
759       }
760       else if ( attribute==PoolQueryAttr::stringAttr
761         || attribute=="global_string")
762       {
763         addString( attrValue );
764       }
765       else if ( attribute==PoolQueryAttr::stringTypeAttr
766         || attribute=="string_type" )
767       {
768         StringTypeAttr s(attrValue);
769         if( s == StringTypeAttr::regexAttr )
770         {
771           setMatchRegex();
772         }
773         else if ( s == StringTypeAttr::globAttr )
774         {
775           setMatchGlob();
776         }
777         else if ( s == StringTypeAttr::exactAttr )
778         {
779           setMatchExact();
780         }
781         else if ( s == StringTypeAttr::substringAttr )
782         {
783           setMatchSubstring();
784         }
785         else if ( s == StringTypeAttr::wordAttr )
786         {
787           setMatchWord();
788         }
789         else if ( s == StringTypeAttr::noAttr )
790         {
791           WAR << "unknown string type " << attrValue << endl;
792         }
793         else
794         {
795           WAR << "forget recover some attribute defined as String type attribute: " << attrValue << endl;
796         }
797       }
798       else if ( attribute==PoolQueryAttr::requireAllAttr )
799       {
800         if ( str::strToTrue(attrValue) )
801         {
802           setRequireAll(true);
803         }
804         else if ( !str::strToFalse(attrValue) )
805         {
806           setRequireAll(false);
807         }
808         else
809         {
810           WAR << "unknown boolean value " << attrValue << endl;
811         }
812       }
813       else if ( attribute==PoolQueryAttr::caseSensitiveAttr )
814       {
815         if ( str::strToTrue(attrValue) )
816         {
817           setCaseSensitive(true);
818         }
819         else if ( !str::strToFalse(attrValue) )
820         {
821           setCaseSensitive(false);
822         }
823         else
824         {
825           WAR << "unknown boolean value " << attrValue << endl;
826         }
827       }
828       else if ( attribute==PoolQueryAttr::installStatusAttr )
829       {
830         if( attrValue == "all" )
831         {
832           setStatusFilterFlags( ALL );
833         }
834         else if( attrValue == "installed" )
835         {
836           setInstalledOnly();
837         }
838         else if( attrValue == "not-installed" )
839         {
840           setUninstalledOnly();
841         }
842         else
843         {
844           WAR << "Unknown value for install status " << attrValue << endl;
845         }
846       }
847       else if ( attribute == PoolQueryAttr::editionAttr)
848       {
849         string::size_type pos;
850         Rel rel("==");
851         if (attrValue.find_first_of("=<>!") == 0)
852         {
853           pos = attrValue.find_last_of("=<>");
854           rel = Rel(attrValue.substr(0, pos+1));
855           attrValue = str::trim(attrValue.substr(pos+1, attrValue.npos));
856         }
857
858         setEdition(Edition(attrValue), rel);
859       }
860       else if ( attribute==PoolQueryAttr::noAttr )
861       {
862         WAR << "empty attribute name" << endl;
863       }
864       else
865       {
866         string s = attrName;
867         str::replaceAll( s,"_",":" );
868         SolvAttr a(s);
869         addAttribute(a,attrValue);
870       }
871
872     } while ( true );
873
874     return finded_something;
875   }
876
877   void PoolQuery::serialize( ostream &str, char delim ) const
878   {
879     //separating delim
880     str << delim;
881     //iterate thrue all settings and write it
882     static const zypp::PoolQuery q; //not save default options, so create default query example
883
884     for_( it, repos().begin(), repos().end() )
885     {
886       str << "repo: " << *it << delim ;
887     }
888
889     for_( it, kinds().begin(), kinds().end() )
890     {
891       str << PoolQueryAttr::kindAttr.asString() << ": "
892           << it->idStr() << delim ;
893     }
894
895     if (editionRel() != Rel::ANY && edition() != Edition::noedition)
896       str << PoolQueryAttr::editionAttr.asString() << ": " << editionRel() << " " << edition() << delim;
897
898     if (matchMode()!=q.matchMode())
899     {
900       switch( matchMode() )
901       {
902       case Match::STRING:
903         str << PoolQueryAttr::stringTypeAttr.asString() << ": exact" << delim;
904         break;
905       case Match::SUBSTRING:
906         str << PoolQueryAttr::stringTypeAttr.asString()
907             << ": substring" << delim;
908         break;
909       case Match::GLOB:
910         str << PoolQueryAttr::stringTypeAttr.asString() << ": glob" << delim;
911         break;
912       case Match::REGEX:
913         str << PoolQueryAttr::stringTypeAttr.asString() << ": regex" << delim;
914         break;
915       default:
916         WAR << "unknown match type "  << matchMode() << endl;
917       }
918     }
919
920     if( caseSensitive() != q.caseSensitive() )
921     {
922       str << "case_sensitive: ";
923       if (caseSensitive())
924       {
925         str << "on" << delim;
926       }
927       else
928       {
929         str << "off" << delim;
930       }
931     }
932
933     if( requireAll() != q.requireAll() )
934     {
935       str << "require_all: ";
936       if (requireAll())
937       {
938         str << "on" << delim;
939       }
940       else
941       {
942         str << "off" << delim;
943       }
944     }
945
946     if( statusFilterFlags() != q.statusFilterFlags() )
947     {
948       switch( statusFilterFlags() )
949       {
950       case ALL:
951         str << "install_status: all" << delim;
952         break;
953       case INSTALLED_ONLY:
954         str << "install_status: installed" << delim;
955         break;
956       case UNINSTALLED_ONLY:
957         str << "install_status: not-installed" << delim;
958         break;
959       }
960     }
961
962     for_( it, strings().begin(), strings().end() )
963     {
964       str << PoolQueryAttr::stringAttr.asString()<< ": " << *it << delim;
965     }
966
967     for_( it, attributes().begin(), attributes().end() )
968     {
969       string s = it->first.asString();
970       str::replaceAll(s,":","_");
971       for_( it2,it->second.begin(),it->second.end() )
972       {
973         str << s <<": "<< *it2 << delim;
974       }
975     }
976
977     //separating delim - protection
978     str << delim;
979   }
980
981   string PoolQuery::asString() const
982   { return _pimpl->asString(); }
983
984   ostream & operator<<( ostream & str, const PoolQuery & obj )
985   { return str << obj.asString(); }
986
987   bool PoolQuery::operator==(const PoolQuery& a) const
988   {
989     if( flags() != a.flags() )
990       return false;
991     if( a.matchWord() != matchWord())
992       return false;
993     if( a.requireAll() != requireAll() )
994       return false;
995     if ( a.kinds() != kinds() )
996       return false;
997     if ( a.repos() != repos() )
998       return false;
999     if(a.edition() != edition())
1000       return false;
1001     if(a.editionRel() != editionRel())
1002       return false;
1003
1004     return true;
1005   }
1006
1007   ///////////////////////////////////////////////////////////////////
1008   namespace detail
1009   { /////////////////////////////////////////////////////////////////
1010
1011     ///////////////////////////////////////////////////////////////////
1012     //
1013     //  CLASS NAME : PoolQueryMatcher
1014     //
1015     /** Store \ref PoolQuery settings and assist \ref PoolQueryIterator.
1016      *
1017      * Basically the matcher performs a base query, which should preselect
1018      * candidates for a match. And has some filter conditions on top of it.
1019      * Query and fileter depend on the \ref PoolQuery settings.
1020      *
1021      * Matcher must be stateless, as it is shared between multiple
1022      * \ref PoolQueryIterator instances.
1023      *
1024      * If \ref base_iterator is at the \ref end, \ref advance moves it
1025      * to the first match. Otherwise advance moves to the next match, or
1026      * to the \ref end, if there is no more match.
1027      *
1028      * \note The original implementation treated an empty search string as
1029      * <it>"match always"</it>. We stay compatible.
1030      */
1031     class PoolQueryMatcher
1032     {
1033       public:
1034         typedef sat::LookupAttr::iterator base_iterator;
1035
1036       public:
1037         const base_iterator & end() const
1038         {
1039           static base_iterator _end;
1040           return _end;
1041         }
1042
1043         bool advance( base_iterator & base_r ) const
1044         {
1045           if ( base_r == end() )
1046             base_r = startNewQyery(); // first candidate
1047           else
1048             ++base_r; // advance to next candidate
1049
1050           while ( base_r != end() )
1051           {
1052             if ( isAMatch( base_r ) )
1053             {
1054               base_r.nextSkipSolvable(); // assert we don't visit this Solvable again
1055               return true;
1056             }
1057             // No match: try next
1058             ++base_r;
1059           }
1060           return false;
1061         }
1062
1063       public:
1064         /** Ctor stores the \ref PoolQuery settings.
1065          * \throw MatchException Any of the exceptions thrown by \ref PoolQuery::Impl::compile.
1066          */
1067         PoolQueryMatcher( const shared_ptr<const PoolQuery::Impl> & query_r )
1068         {
1069           query_r->compile();
1070
1071           // Repo restriction:
1072           sat::Pool satpool( sat::Pool::instance() );
1073           for_( it, query_r->_repos.begin(), query_r->_repos.end() )
1074           {
1075             Repository r( satpool.reposFind( *it ) );
1076             if ( r )
1077               _repos.insert( r );
1078           }
1079           // Kind restriction:
1080           _kinds = query_r->_kinds;
1081           // Edition restriction:
1082           _op      = query_r->_op;
1083           _edition = query_r->_edition;
1084           // Status restriction:
1085           _status_flags = query_r->_status_flags;
1086           // AttrMatcher
1087           _attrMatchList = query_r->_attrMatchList;
1088         }
1089
1090         ~PoolQueryMatcher()
1091         {}
1092
1093       private:
1094         /** Initialize a new base query. */
1095         base_iterator startNewQyery() const
1096         {
1097           sat::LookupAttr q;
1098
1099           // Repo restriction:
1100           if ( _repos.size() == 1 )
1101             q.setRepo( *_repos.begin() );
1102           // else: handled in isAMatch.
1103
1104           // Attribute restriction:
1105           if ( _attrMatchList.size() == 1 ) // all (SolvAttr::allAttr) or 1 attr
1106           {
1107             const AttrMatchData & matchData( _attrMatchList.front() );
1108             q.setAttr( matchData.attr );
1109             if ( matchData.attrMatcher ) // empty searchstring matches always
1110               q.setAttrMatcher( matchData.attrMatcher );
1111           }
1112           else // more than 1 attr (but not all)
1113           {
1114             // no restriction, it's all handled in isAMatch.
1115             q.setAttr( sat::SolvAttr::allAttr );
1116           }
1117
1118           return q.begin();
1119         }
1120
1121
1122         /** Check whether we are on a match.
1123          *
1124          * The check covers the whole Solvable, not just the current
1125          * attribute \c base_r points to. If there's no match, also
1126          * prepare \c base_r to advance appropriately. If there is
1127          * a match, simply return \c true. \ref advance always moves
1128          * to the next Solvable if there was a match.
1129          *
1130          * \note: Caller asserts we're not at \ref end.
1131         */
1132         bool isAMatch( base_iterator & base_r ) const
1133         {
1134           /////////////////////////////////////////////////////////////////////
1135           Repository inRepo( base_r.inRepo() );
1136           // Status restriction:
1137           if ( _status_flags
1138              && ( (_status_flags == PoolQuery::INSTALLED_ONLY) != inRepo.isSystemRepo() ) )
1139           {
1140             base_r.nextSkipRepo();
1141             return false;
1142           }
1143           // Repo restriction:
1144           if ( _repos.size() > 1 && _repos.find( inRepo ) == _repos.end() )
1145           {
1146             base_r.nextSkipRepo();
1147             return false;
1148           }
1149           /////////////////////////////////////////////////////////////////////
1150           sat::Solvable inSolvable( base_r.inSolvable() );
1151           // Kind restriction:
1152           if ( ! _kinds.empty() && ! inSolvable.isKind( _kinds.begin(), _kinds.end() ) )
1153           {
1154             base_r.nextSkipSolvable();
1155             return false;
1156           }
1157
1158           // Edition restriction:
1159           if ( _op != Rel::ANY && !compareByRel( _op, inSolvable.edition(), _edition, Edition::Match() ) )
1160           {
1161             base_r.nextSkipSolvable();
1162             return false;
1163           }
1164           /////////////////////////////////////////////////////////////////////
1165           // string and predicate matching:
1166
1167           if ( _attrMatchList.size() == 1 )
1168           {
1169             // String matching was done by the base iterator.
1170             // Now check any predicate:
1171             const AttrMatchData::Predicate & predicate( _attrMatchList.front().predicate );
1172             if ( ! predicate || predicate( base_r ) )
1173               return true;
1174
1175             base_r.nextSkipSolvable();
1176             return false;
1177           }
1178
1179           // Here: search all attributes ;(
1180           for_( mi, _attrMatchList.begin(), _attrMatchList.end() )
1181           {
1182             const AttrMatchData & matchData( *mi );
1183             sat::LookupAttr q( matchData.attr, inSolvable );
1184             if ( matchData.attrMatcher ) // an empty searchstring matches always
1185               q.setAttrMatcher( matchData.attrMatcher );
1186
1187             if ( ! q.empty() ) // there are matches.
1188             {
1189               // now check any predicate:
1190               const AttrMatchData::Predicate & predicate( matchData.predicate );
1191               if ( predicate )
1192               {
1193                 for_( it, q.begin(), q.end() )
1194                 {
1195                   if ( predicate( it ) )
1196                     return true;
1197                 }
1198               }
1199               else
1200                 return true;
1201             }
1202           }
1203           base_r.nextSkipSolvable();
1204           return false;
1205         }
1206
1207       private:
1208         /** Repositories include in the search. */
1209         std::set<Repository> _repos;
1210         /** Resolvable kinds to include. */
1211         std::set<ResKind> _kinds;
1212         /** Edition filter. */
1213         Rel _op;
1214         Edition _edition;
1215         /** Installed status filter flags. \see PoolQuery::StatusFilter */
1216         int _status_flags;
1217         /** AttrMatcher per attribtue. */
1218         AttrMatchList _attrMatchList;
1219     };
1220     ///////////////////////////////////////////////////////////////////
1221
1222     void PoolQueryIterator::increment()
1223     {
1224       // matcher restarts if at end! It is called from the ctor
1225       // to get the 1st match. But if the end is reached, it should
1226       // be deleted, otherwise we'd start over again.
1227       if ( _matcher && ! _matcher->advance( base_reference() ) )
1228         _matcher.reset();
1229     }
1230
1231     ///////////////////////////////////////////////////////////////////
1232   } //namespace detail
1233   ///////////////////////////////////////////////////////////////////
1234
1235   detail::PoolQueryIterator PoolQuery::begin() const
1236   {
1237     return shared_ptr<detail::PoolQueryMatcher>( new detail::PoolQueryMatcher( _pimpl.getPtr() ) );
1238   }
1239
1240   /////////////////////////////////////////////////////////////////
1241 } // namespace zypp
1242 ///////////////////////////////////////////////////////////////////
1243