Support PoolQuery for sub-structures attributes. (fate #305503)
[platform/upstream/libzypp.git] / zypp / sat / LookupAttr.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/sat/LookupAttr.cc
10  *
11 */
12 #include <iostream>
13 #include <sstream>
14
15 #include "zypp/base/LogTools.h"
16 #include "zypp/base/String.h"
17
18 #include "zypp/sat/detail/PoolImpl.h"
19
20 #include "zypp/sat/Pool.h"
21 #include "zypp/sat/LookupAttr.h"
22 #include "zypp/sat/AttrMatcher.h"
23
24 #include "zypp/CheckSum.h"
25
26 using std::endl;
27
28 ///////////////////////////////////////////////////////////////////
29 namespace zypp
30 { /////////////////////////////////////////////////////////////////
31   ///////////////////////////////////////////////////////////////////
32   namespace sat
33   { /////////////////////////////////////////////////////////////////
34
35     using detail::noSolvableId;
36
37     ///////////////////////////////////////////////////////////////////
38     //
39     //  CLASS NAME : LookupAttr::Impl
40     //
41     ///////////////////////////////////////////////////////////////////
42     /**
43      * LookupAttr implememtation.
44      *
45      * Repository and Solvable must not be set at the same time!
46      *
47      * \note When looking in pool or repo, \ref Solvable \c _solv is
48      * somewhat abused to store eiter \c Id \c 0 or \c SOLVID_META, which
49      * indicates whether the dataiterator should look into solvable or
50      * repository metadata. Remember that all \ref Solvables with an
51      * \e invalid \c Id, are treated as <tt>== Solvable::noSolvable</tt>,
52      * and in a boolean context evaluate to \c false. Thus \c noSolvable
53      * may have different \c Ids.
54      */
55     class LookupAttr::Impl
56     {
57       public:
58         Impl()
59         : _parent( SolvAttr::noAttr )
60         {}
61         Impl( SolvAttr attr_r, Location loc_r )
62         : _attr( attr_r ), _parent( attr_r.parent() ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
63         {}
64         Impl( SolvAttr attr_r, Repository repo_r, Location loc_r )
65         : _attr( attr_r ), _parent( attr_r.parent() ), _repo( repo_r ), _solv( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId )
66         {}
67         Impl( SolvAttr attr_r, Solvable solv_r )
68         : _attr( attr_r ), _parent( attr_r.parent() ), _solv( solv_r )
69         {}
70
71       public:
72         SolvAttr attr() const
73         { return _attr; }
74
75         void setAttr( SolvAttr attr_r )
76         {
77           _attr = attr_r;
78           SolvAttr p( _attr.parent() );
79           if ( p != SolvAttr::noAttr )
80             _parent = p;
81         }
82
83         const AttrMatcher & attrMatcher() const
84         { return _attrMatcher; }
85
86         void setAttrMatcher( const AttrMatcher & matcher_r )
87         {
88           matcher_r.compile();
89           _attrMatcher = matcher_r;
90         }
91
92       public:
93         bool pool() const
94         { return ! (_repo || _solv); }
95
96         void setPool( Location loc_r )
97         {
98           _repo = Repository::noRepository;
99           _solv = Solvable( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId );
100         }
101
102         Repository repo() const
103         { return _repo; }
104
105         void setRepo( Repository repo_r, Location loc_r  )
106         {
107           _repo = repo_r;
108           _solv = Solvable( loc_r == REPO_ATTR ? SOLVID_META : noSolvableId );
109         }
110
111         Solvable solvable() const
112         { return _solv; }
113
114         void setSolvable( Solvable solv_r )
115         {
116           _repo = Repository::noRepository;
117           _solv = solv_r;
118         }
119
120         SolvAttr parent() const
121         { return _parent; }
122
123         void setParent( SolvAttr attr_r )
124         { _parent = attr_r; }
125
126       public:
127         LookupAttr::iterator begin() const
128         {
129           if ( _attr == SolvAttr::noAttr || sat::Pool::instance().reposEmpty() )
130             return end();
131
132           detail::RepoIdType whichRepo = detail::noRepoId; // all repos
133           if ( _solv )
134             whichRepo = _solv.repository().id();
135           else if ( _repo )
136             whichRepo = _repo.id();
137
138           detail::DIWrap dip( whichRepo, _solv.id(), _attr.id(), _attrMatcher.searchstring(), _attrMatcher.flags().get() );
139           if ( _parent != SolvAttr::noAttr )
140             ::dataiterator_prepend_keyname( dip.get(), _parent.id() );
141
142           return iterator( dip ); // iterator takes over ownership!
143         }
144
145         LookupAttr::iterator end() const
146         { return iterator(); }
147
148       private:
149         SolvAttr   _attr;
150         SolvAttr   _parent;
151         Repository _repo;
152         Solvable   _solv;
153         AttrMatcher _attrMatcher;
154
155       private:
156         friend Impl * rwcowClone<Impl>( const Impl * rhs );
157         /** clone for RWCOW_pointer */
158         Impl * clone() const
159         { return new Impl( *this ); }
160     };
161
162     ///////////////////////////////////////////////////////////////////
163     //
164     //  CLASS NAME : LookupAttr
165     //
166     ///////////////////////////////////////////////////////////////////
167
168     LookupAttr::LookupAttr()
169       : _pimpl( new Impl )
170     {}
171
172     LookupAttr::LookupAttr( SolvAttr attr_r, Location loc_r )
173       : _pimpl( new Impl( attr_r, loc_r ) )
174     {}
175     LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Location loc_r )
176       : _pimpl( new Impl( attr_r, loc_r ) )
177     { _pimpl->setParent( parent_r ); }
178
179     LookupAttr::LookupAttr( SolvAttr attr_r, Repository repo_r, Location loc_r )
180       : _pimpl( new Impl( attr_r, repo_r, loc_r ) )
181     {}
182     LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Repository repo_r, Location loc_r )
183       : _pimpl( new Impl( attr_r, repo_r, loc_r ) )
184     { _pimpl->setParent( parent_r ); }
185
186     LookupAttr::LookupAttr( SolvAttr attr_r, Solvable solv_r )
187       : _pimpl( new Impl( attr_r, solv_r ) )
188     {}
189     LookupAttr::LookupAttr( SolvAttr attr_r, SolvAttr parent_r, Solvable solv_r )
190       : _pimpl( new Impl( attr_r, solv_r ) )
191     { _pimpl->setParent( parent_r ); }
192
193
194     ///////////////////////////////////////////////////////////////////
195
196     SolvAttr LookupAttr::attr() const
197     { return _pimpl->attr(); }
198
199     void LookupAttr::setAttr( SolvAttr attr_r )
200     { _pimpl->setAttr( attr_r ); }
201
202     const AttrMatcher & LookupAttr::attrMatcher() const
203     { return _pimpl->attrMatcher(); }
204
205     void LookupAttr::setAttrMatcher( const AttrMatcher & matcher_r )
206     { _pimpl->setAttrMatcher( matcher_r ); }
207
208     ///////////////////////////////////////////////////////////////////
209
210     bool LookupAttr::pool() const
211     { return _pimpl->pool(); }
212
213     void LookupAttr::setPool( Location loc_r )
214     { _pimpl->setPool( loc_r ); }
215
216     Repository LookupAttr::repo() const
217     { return _pimpl->repo(); }
218
219     void LookupAttr::setRepo( Repository repo_r, Location loc_r )
220     { _pimpl->setRepo( repo_r, loc_r ); }
221
222     Solvable LookupAttr::solvable() const
223     { return _pimpl->solvable(); }
224
225     void LookupAttr::setSolvable( Solvable solv_r )
226     { _pimpl->setSolvable( solv_r ); }
227
228     SolvAttr LookupAttr::parent() const
229     { return _pimpl->parent(); }
230
231     void LookupAttr::setParent( SolvAttr attr_r )
232     { _pimpl->setParent( attr_r ); }
233
234     ///////////////////////////////////////////////////////////////////
235
236     LookupAttr::iterator LookupAttr::begin() const
237     { return _pimpl->begin(); }
238
239     LookupAttr::iterator LookupAttr::end() const
240     { return _pimpl->end(); }
241
242     bool LookupAttr::empty() const
243     { return begin() == end(); }
244
245     LookupAttr::size_type LookupAttr::size() const
246     {
247       size_type c = 0;
248       for_( it, begin(), end() )
249         ++c;
250       return c;
251     }
252
253     ///////////////////////////////////////////////////////////////////
254
255     std::ostream & operator<<( std::ostream & str, const LookupAttr & obj )
256     {
257       if ( obj.attr() == SolvAttr::noAttr )
258         return str << "search nothing";
259
260       if ( obj.attr() )
261         str << "seach " << obj.attr() << " in ";
262       else
263         str << "seach ALL in ";
264
265       if ( obj.solvable() )
266         return str << obj.solvable();
267       if ( obj.repo() )
268         return str << obj.repo();
269       return str << "pool";
270     }
271
272     std::ostream & dumpOn( std::ostream & str, const LookupAttr & obj )
273     {
274       return dumpRange( str << obj, obj.begin(), obj.end() );
275     }
276
277     ///////////////////////////////////////////////////////////////////
278     //
279     //  CLASS NAME : LookupRepoAttr
280     //
281     ///////////////////////////////////////////////////////////////////
282
283     LookupRepoAttr::LookupRepoAttr( SolvAttr attr_r, Repository repo_r )
284       : LookupAttr( attr_r, repo_r, REPO_ATTR )
285     {}
286
287     void LookupRepoAttr::setRepo( Repository repo_r )
288     { LookupAttr::setRepo( repo_r, REPO_ATTR ); }
289
290     ///////////////////////////////////////////////////////////////////
291     //
292     //  CLASS NAME : detail::DIWrap
293     //
294     ///////////////////////////////////////////////////////////////////
295
296     namespace detail
297     {
298       DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
299                       const std::string & mstring_r, int flags_r )
300       : _dip( new ::Dataiterator )
301       , _mstring( mstring_r )
302       {
303         ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
304                              _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
305       }
306
307       DIWrap::DIWrap( RepoIdType repoId_r, SolvableIdType solvId_r, IdType attrId_r,
308                       const char * mstring_r, int flags_r )
309       : _dip( new ::Dataiterator )
310       , _mstring( mstring_r ? mstring_r : "" )
311       {
312         ::dataiterator_init( _dip, sat::Pool::instance().get(), repoId_r, solvId_r, attrId_r,
313                              _mstring.empty() ? 0 : _mstring.c_str(), flags_r );
314       }
315
316       DIWrap::DIWrap( const DIWrap & rhs )
317         : _dip( 0 )
318         , _mstring( rhs._mstring )
319       {
320         if ( rhs._dip )
321         {
322           _dip = new ::Dataiterator;
323           *_dip = *rhs._dip;
324           if ( _dip->nparents )
325           {
326             for ( int i = 1; i < _dip->nparents; ++i )
327             {
328               _dip->parents[i].kv.parent = &_dip->parents[i-1].kv;
329             }
330             _dip->kv.parent = &_dip->parents[_dip->nparents-1].kv;
331           }
332           // now we have to manually clone any allocated regex data matcher.
333           ::Datamatcher & matcher( _dip->matcher );
334           if ( matcher.match && ( matcher.flags & SEARCH_STRINGMASK ) == SEARCH_REGEX )
335           {
336             ::datamatcher_init( &matcher, _mstring.c_str(), matcher.flags );
337           }
338           else if ( matcher.match && matcher.match != _mstring.c_str() )
339           {
340             //SEC << "**" << rhs._dip << endl;
341             SEC << "r " << rhs._dip->matcher.match << endl;
342             SEC << "r " << rhs._dip->matcher.flags << endl;
343             SEC << "r " << (const void*)rhs._mstring.c_str() << "'" << rhs._mstring << "'" << endl;
344
345             SEC << "t " << matcher.match << endl;
346             SEC << "t " << matcher.flags << endl;
347             SEC << "t " << (const void*)_mstring.c_str() << "'" << _mstring << "'" <<  endl;
348             throw( "this cant be!" );
349           }
350         }
351       }
352
353       DIWrap::~DIWrap()
354       {
355         if ( _dip )
356         {
357           ::dataiterator_free( _dip );
358           delete _dip;
359         }
360       }
361
362       std::ostream & operator<<( std::ostream & str, const DIWrap & obj )
363       { return str << obj.get(); }
364     }
365
366     ///////////////////////////////////////////////////////////////////
367     //
368     //  CLASS NAME : LookupAttr::iterator
369     //
370     ///////////////////////////////////////////////////////////////////
371
372     ///////////////////////////////////////////////////////////////////
373     // position and moving
374     ///////////////////////////////////////////////////////////////////
375
376     Repository LookupAttr::iterator::inRepo() const
377     { return _dip ? Repository( _dip->repo ) : Repository::noRepository; }
378
379     Solvable LookupAttr::iterator::inSolvable() const
380     { return _dip ? Solvable( _dip->solvid ) : Solvable::noSolvable; }
381
382     SolvAttr LookupAttr::iterator::inSolvAttr() const
383     { return _dip ? SolvAttr( _dip->key->name ) : SolvAttr::noAttr; }
384
385     void LookupAttr::iterator::nextSkipSolvAttr()
386     { if ( _dip ) ::dataiterator_skip_attribute( _dip.get() ); }
387
388     void LookupAttr::iterator::nextSkipSolvable()
389     { if ( _dip ) ::dataiterator_skip_solvable( _dip.get() ); }
390
391     void LookupAttr::iterator::nextSkipRepo()
392     { if ( _dip ) ::dataiterator_skip_repo( _dip.get() ); }
393
394     void LookupAttr::iterator::stayInThisSolvable()
395     { if ( _dip ) { _dip.get()->repoid = -1; _dip.get()->flags |= SEARCH_THISSOLVID; } }
396
397     void LookupAttr::iterator::stayInThisRepo()
398     { if ( _dip ) { _dip.get()->repoid = -1; } }
399
400     ///////////////////////////////////////////////////////////////////
401     // attr value type test
402     ///////////////////////////////////////////////////////////////////
403
404     detail::IdType LookupAttr::iterator::solvAttrType() const
405     { return _dip ? _dip->key->type : detail::noId; }
406
407     bool LookupAttr::iterator::solvAttrNumeric() const
408     {
409       switch ( solvAttrType() )
410       {
411         case REPOKEY_TYPE_U32:
412         case REPOKEY_TYPE_NUM:
413         case REPOKEY_TYPE_CONSTANT:
414           return true;
415           break;
416       }
417       return false;
418     }
419
420     bool LookupAttr::iterator::solvAttrString() const
421     {
422       switch ( solvAttrType() )
423       {
424         case REPOKEY_TYPE_ID:
425         case REPOKEY_TYPE_IDARRAY:
426         case REPOKEY_TYPE_CONSTANTID:
427         case REPOKEY_TYPE_STR:
428         case REPOKEY_TYPE_DIRSTRARRAY:
429           return true;
430           break;
431       }
432       return false;
433     }
434
435     bool LookupAttr::iterator::solvAttrIdString() const
436     {
437       switch ( solvAttrType() )
438       {
439         case REPOKEY_TYPE_ID:
440         case REPOKEY_TYPE_IDARRAY:
441         case REPOKEY_TYPE_CONSTANTID:
442           return true;
443           break;
444       }
445       return false;
446     }
447
448     bool LookupAttr::iterator::solvAttrCheckSum() const
449     {
450       switch ( solvAttrType() )
451       {
452         case REPOKEY_TYPE_MD5:
453         case REPOKEY_TYPE_SHA1:
454         case REPOKEY_TYPE_SHA256:
455           return true;
456           break;
457       }
458       return false;
459     }
460
461     ///////////////////////////////////////////////////////////////////
462     namespace
463     {
464       enum SubType { ST_NONE,   // no sub-structure
465                      ST_FLEX,   // flexarray
466                      ST_SUB };  // inside sub-structure
467       SubType subType( const detail::DIWrap & dip )
468       {
469         if ( ! dip )
470           return ST_NONE;
471         if ( dip.get()->key->type == REPOKEY_TYPE_FLEXARRAY )
472           return ST_FLEX;
473         return dip.get()->kv.parent ? ST_SUB : ST_NONE;
474       }
475     }
476     ///////////////////////////////////////////////////////////////////
477
478     bool LookupAttr::iterator::solvAttrSubEntry() const
479     { return subType( _dip ) != ST_NONE; }
480
481     ///////////////////////////////////////////////////////////////////
482     // Iterate sub-structures.
483     ///////////////////////////////////////////////////////////////////
484
485     bool LookupAttr::iterator::subEmpty() const
486     { return( subBegin() == subEnd() ); }
487
488     LookupAttr::size_type LookupAttr::iterator::subSize() const
489     {
490       size_type c = 0;
491       for_( it, subBegin(), subEnd() )
492         ++c;
493       return c;
494     }
495
496     LookupAttr::iterator LookupAttr::iterator::subBegin() const
497     {
498       switch ( subType( _dip ) )
499       {
500         case ST_NONE:
501           return subEnd();
502           break;
503         case ST_FLEX:
504           ::dataiterator_setpos( _dip.get() );
505           break;
506         case ST_SUB:
507           ::dataiterator_setpos_parent( _dip.get() );
508           break;
509       }
510       // setup the new sub iterator with the remembered position
511       detail::DIWrap dip( 0, SOLVID_POS, 0, 0, 0 );
512       return iterator( dip ); // iterator takes over ownership!
513     }
514
515     LookupAttr::iterator LookupAttr::iterator::subEnd() const
516     {
517       return iterator();
518     }
519
520     LookupAttr::iterator LookupAttr::iterator::subFind( SolvAttr attr_r ) const
521     {
522       iterator it = subBegin();
523       if ( attr_r != sat::SolvAttr::allAttr )
524       {
525         while ( it != subEnd() && it.inSolvAttr() != attr_r )
526           ++it;
527       }
528       return it;
529     }
530
531     LookupAttr::iterator LookupAttr::iterator::subFind( const C_Str & attrname_r ) const
532     {
533       if ( attrname_r.empty() )
534         return subBegin();
535
536       SubType subtype( subType( _dip ) );
537       if ( subtype == ST_NONE )
538         return subBegin();
539
540       std::string subattr( inSolvAttr().asString() );
541       if ( subtype == ST_FLEX )
542       {
543         // append ":attrname"
544         subattr += ":";
545         subattr += attrname_r;
546       }
547       else
548       {
549         // replace "oldname" after ':' with "attrname"
550         std::string::size_type pos( subattr.rfind( ':' ) );
551         if ( pos != std::string::npos )
552         {
553           subattr.erase( pos+1 );
554           subattr += attrname_r;
555         }
556         else
557           subattr = attrname_r; // no ':' so replace all.
558       }
559       return subFind( SolvAttr( subattr ) );
560     }
561
562     ///////////////////////////////////////////////////////////////////
563     // attr value retrieval
564     ///////////////////////////////////////////////////////////////////
565
566     int LookupAttr::iterator::asInt() const
567     {
568       if ( _dip )
569       {
570         switch ( solvAttrType() )
571         {
572           case REPOKEY_TYPE_U32:
573           case REPOKEY_TYPE_NUM:
574           case REPOKEY_TYPE_CONSTANT:
575             return _dip->kv.num;
576             break;
577         }
578       }
579       return 0;
580     }
581
582     unsigned LookupAttr::iterator::asUnsigned() const
583     { return asInt(); }
584
585     bool LookupAttr::iterator::asBool() const
586     { return asInt(); }
587
588
589     const char * LookupAttr::iterator::c_str() const
590     {
591       if ( _dip )
592       {
593         switch ( solvAttrType() )
594         {
595           case REPOKEY_TYPE_ID:
596           case REPOKEY_TYPE_IDARRAY:
597           case REPOKEY_TYPE_CONSTANTID:
598             if ( _dip->data && _dip->data->localpool )
599               return ::stringpool_id2str( &_dip->data->spool, _dip->kv.id ); // in local pool
600             else
601               return IdString( _dip->kv.id ).c_str(); // in global pool
602             break;
603
604           case REPOKEY_TYPE_STR:
605             return _dip->kv.str;
606             break;
607
608           case REPOKEY_TYPE_DIRSTRARRAY:
609             return ::repodata_dir2str( _dip->data, _dip->kv.id, _dip->kv.str );
610             break;
611         }
612       }
613       return 0;
614     }
615
616     std::string LookupAttr::iterator::asString() const
617     {
618       if ( _dip )
619       {
620         switch ( solvAttrType() )
621         {
622           case REPOKEY_TYPE_ID:
623           case REPOKEY_TYPE_IDARRAY:
624           case REPOKEY_TYPE_CONSTANTID:
625             {
626               detail::IdType id = ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 );
627               return ISRELDEP(id) ? Capability( id ).asString()
628                                   : IdString( id ).asString();
629             }
630             break;
631
632           case REPOKEY_TYPE_STR:
633           case REPOKEY_TYPE_DIRSTRARRAY:
634             {
635               const char * ret( c_str() );
636               return ret ? ret : "";
637             }
638             break;
639
640           case REPOKEY_TYPE_U32:
641           case REPOKEY_TYPE_NUM:
642           case REPOKEY_TYPE_CONSTANT:
643             return str::numstring( asInt() );
644             break;
645
646           case REPOKEY_TYPE_MD5:
647           case REPOKEY_TYPE_SHA1:
648           case REPOKEY_TYPE_SHA256:
649             {
650               return asCheckSum().asString();
651             }
652             break;
653
654           case REPOKEY_TYPE_FLEXARRAY:
655             {
656               std::ostringstream str;
657               str << "{" << endl;
658               for_( it, subBegin(), subEnd() )
659               {
660                 str << "  " << it.inSolvAttr() << " = " << it.asString() << endl;
661               }
662               str << "}";
663              return str.str();
664             }
665             break;
666         }
667       }
668       return std::string();
669     }
670
671     IdString LookupAttr::iterator::idStr() const
672     {
673       if ( _dip )
674       {
675         switch ( solvAttrType() )
676         {
677           case REPOKEY_TYPE_ID:
678           case REPOKEY_TYPE_IDARRAY:
679           case REPOKEY_TYPE_CONSTANTID:
680             return IdString( ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 ) );
681             break;
682         }
683       }
684       return IdString();
685     }
686
687     CheckSum LookupAttr::iterator::asCheckSum() const
688     {
689       if ( _dip )
690       {
691         switch ( solvAttrType() )
692         {
693           case REPOKEY_TYPE_MD5:
694             return CheckSum::md5( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
695             break;
696
697           case REPOKEY_TYPE_SHA1:
698             return CheckSum::sha1( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
699             break;
700
701           case REPOKEY_TYPE_SHA256:
702             return CheckSum::sha256( ::repodata_chk2str( _dip->data, solvAttrType(), (unsigned char *)_dip->kv.str ) );
703             break;
704         }
705       }
706       return CheckSum();
707     }
708
709     ///////////////////////////////////////////////////////////////////
710     // internal stuff below
711     ///////////////////////////////////////////////////////////////////
712
713     LookupAttr::iterator::iterator()
714     : iterator_adaptor_( 0 )
715     {}
716
717     LookupAttr::iterator::iterator( const iterator & rhs )
718     : iterator_adaptor_( 0 )
719     , _dip( rhs._dip )
720     {
721       base_reference() = _dip.get();
722     }
723
724     LookupAttr::iterator::iterator( detail::DIWrap & dip_r )
725     : iterator_adaptor_( 0 )
726     {
727       _dip.swap( dip_r ); // take ownership!
728       base_reference() = _dip.get();
729       increment();
730     }
731
732     LookupAttr::iterator::~iterator()
733     {}
734
735     LookupAttr::iterator & LookupAttr::iterator::operator=( const iterator & rhs )
736     {
737       if ( &rhs != this )
738       {
739         _dip = rhs._dip;
740         base_reference() = _dip.get();
741       }
742       return *this;
743     }
744
745     ///////////////////////////////////////////////////////////////////
746
747     bool LookupAttr::iterator::dip_equal( const ::_Dataiterator & lhs, const ::_Dataiterator & rhs ) const
748     {
749       // Iterator equal is same position in same container.
750       // Here: same attribute in same solvable.
751       return( lhs.solvid == rhs.solvid && lhs.key->name == rhs.key->name );
752     }
753
754     detail::IdType LookupAttr::iterator::dereference() const
755     {
756       return _dip ? ::repodata_globalize_id( _dip->data, _dip->kv.id, 1 )
757                   : detail::noId;
758     }
759
760     void LookupAttr::iterator::increment()
761     {
762       if ( _dip && ! ::dataiterator_step( _dip.get() ) )
763       {
764         _dip.reset();
765         base_reference() = 0;
766       }
767     }
768
769     std::ostream & operator<<( std::ostream & str, const LookupAttr::iterator & obj )
770     {
771       const ::_Dataiterator * dip = obj.get();
772       if ( ! dip )
773         return str << "EndOfQuery";
774
775       if ( obj.inSolvable() )
776         str << obj.inSolvable();
777       else if ( obj.inRepo() )
778         str << obj.inRepo();
779
780       str << '<' << obj.inSolvAttr() << (obj.solvAttrSubEntry() ? ">(*" : ">(")
781           <<  IdString(obj.solvAttrType()) << ") = " << obj.asString();
782       return str;
783     }
784
785     template<> CheckSum LookupAttr::iterator::asType<CheckSum>() const
786     { return asCheckSum(); }
787
788     /////////////////////////////////////////////////////////////////
789   } // namespace sat
790   ///////////////////////////////////////////////////////////////////
791   /////////////////////////////////////////////////////////////////
792 } // namespace zypp
793 ///////////////////////////////////////////////////////////////////
794
795 std::ostream & operator<<( std::ostream & str, const ::_Dataiterator * obj )
796 {
797   str << "::_Dataiterator(";
798   if ( ! obj )
799   {
800     str << "NULL";
801   }
802   else
803   {
804     str << "|" << zypp::Repository(obj->repo);
805     str << "|" << zypp::sat::Solvable(obj->solvid);
806     str << "|" << zypp::IdString(obj->key->name);
807     str << "|" << zypp::IdString(obj->key->type);
808     str << "|" << obj->repodataid;
809     str << "|" << obj->repoid;
810   }
811   return str << ")";
812 }
813
814 ///////////////////////////////////////////////////////////////////