Use IdStrings for vendor equivalence detection.
[platform/upstream/libzypp.git] / zypp / VendorAttr.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /*
10   File:       VendorAttr.cc
11
12   Author:     Michael Andres <ma@suse.de>
13   Maintainer: Michael Andres <ma@suse.de>
14
15   Purpose: Manage vendor attributes
16
17 /-*/
18
19 #include <iostream>
20 #include <fstream>
21 #include <set>
22 #include <map>
23 #include <vector>
24
25 #include "zypp/base/LogTools.h"
26 #include "zypp/base/IOStream.h"
27 #include "zypp/base/String.h"
28
29 #include "zypp/PathInfo.h"
30 #include "zypp/VendorAttr.h"
31 #include "zypp/ZYppFactory.h"
32
33 #include "zypp/ZConfig.h"
34 #include "zypp/PathInfo.h"
35 #include "zypp/parser/IniDict.h"
36
37 using namespace std;
38
39 #undef  ZYPP_BASE_LOGGER_LOGGROUP
40 #define ZYPP_BASE_LOGGER_LOGGROUP "zypp::VendorAttr"
41
42 ///////////////////////////////////////////////////////////////////
43 namespace zypp
44 { /////////////////////////////////////////////////////////////////
45
46   ///////////////////////////////////////////////////////////////////
47   namespace
48   { /////////////////////////////////////////////////////////////////
49
50     typedef map<Vendor,unsigned int> VendorMap;
51     VendorMap _vendorMap;
52     unsigned int vendorGroupCounter;
53
54     /////////////////////////////////////////////////////////////////
55   } // namespace
56   ///////////////////////////////////////////////////////////////////
57
58   ///////////////////////////////////////////////////////////////////
59   namespace
60   { /////////////////////////////////////////////////////////////////
61     typedef DefaultIntegral<int,0>                              VendorMatchEntry;
62     typedef std::tr1::unordered_map<IdString, VendorMatchEntry> VendorMatch;
63     int         _nextId = -1;
64     VendorMatch _vendorMatch;
65
66     /** Reset match cache if global VendorMap was changed. */
67     inline void vendorMatchIdReset()
68     {
69       _nextId = -1;
70       _vendorMatch.clear();
71     }
72
73     /**
74      * Helper mapping vendor string to eqivalence class ID.
75      *
76      * \li Return the vendor strings eqivalence class ID stored in _vendorMatch.
77      * \li If not found, assign and return the eqivalence class ID of the lowercased string.
78      * \li If not found, assign and return a new ID (look into the predefined VendorMap (id>0),
79      *     otherwise create a new ID (<0)).
80      */
81     inline unsigned vendorMatchId( IdString vendor )
82     {
83       VendorMatchEntry & ent( _vendorMatch[vendor] );
84       if ( ! ent )
85       {
86         IdString lcvendor( str::toLower( vendor.asString() ) );
87         VendorMatchEntry & lcent( _vendorMatch[lcvendor] );
88         if ( ! lcent )
89         {
90           unsigned myid = 0;
91           // Compare this entry with the global vendor map.
92           // Reversed to get the longes prefix.
93           for ( VendorMap::reverse_iterator it = _vendorMap.rbegin(); it != _vendorMap.rend(); ++it )
94           {
95             if ( str::hasPrefix( lcvendor.c_str(), it->first ) )
96             {
97               myid = it->second;
98               break; // found
99             }
100           }
101           if ( ! myid )
102           {
103             myid = --_nextId; // get a new class ID
104           }
105           ent = lcent = myid; // remember the new DI
106         }
107         else
108         {
109           ent = lcent; // take the ID from the lowercased vendor string
110         }
111       }
112       return ent;
113     }
114     /////////////////////////////////////////////////////////////////
115   } // namespace
116   ///////////////////////////////////////////////////////////////////
117
118   const VendorAttr & VendorAttr::instance()
119   {
120       static VendorAttr _val;
121       return _val;
122   }
123
124   VendorAttr::VendorAttr ()
125   {
126       vendorGroupCounter = 1;
127       Pathname vendorPath (ZConfig::instance().vendorPath());
128       try
129       {
130           Target_Ptr trg( getZYpp()->target() );
131           if ( trg )
132               vendorPath = trg->root() / vendorPath;
133       }
134       catch ( ... )
135       {
136           // noop: Someone decided to let target() throw if the ptr is NULL ;(
137       }
138
139       // creating entries
140       addVendorDirectory (vendorPath);
141
142       // Checking if suse,opensuse has been defined, else create entries:
143       // - if both are defined we leve them as thay are.
144       // - if only one of them is defined, we add the other to the same group.
145       // - if both are undefined they make up a new group
146       VendorMap::const_iterator suseit( _vendorMap.find("suse") );
147       VendorMap::const_iterator opensuseit( _vendorMap.find("opensuse") );
148       if ( suseit == _vendorMap.end() )
149       {
150         if ( opensuseit == _vendorMap.end() )
151         {
152           // both are undefined
153           _vendorMap["suse"] = _vendorMap["opensuse"] = ++vendorGroupCounter;
154         }
155         else
156         {
157           // add suse to opensuse
158           _vendorMap["suse"] = opensuseit->second;
159         }
160       }
161       else if ( opensuseit == _vendorMap.end() )
162       {
163         // add opensuse to suse
164         _vendorMap["opensuse"] = suseit->second;
165       }
166
167       // Take care 'opensuse build service' gets it's own class.
168       VendorMap::const_iterator obsit( _vendorMap.find("opensuse build service") );
169       if ( obsit == _vendorMap.end() )
170       {
171         _vendorMap["opensuse build service"] = ++vendorGroupCounter;
172       }
173
174
175       MIL << *this << endl;
176   }
177
178   void VendorAttr::_addVendorList( VendorList & vendorList_r ) const
179   {
180     unsigned int nextId = vendorGroupCounter + 1;
181         // convert to lowercase and check if a vendor is already defined
182         // in an existing group.
183
184     for_( it, vendorList_r.begin(), vendorList_r.end() )
185     {
186       *it = str::toLower( *it );
187       if (_vendorMap.find(*it) != _vendorMap.end())
188       {
189         if (nextId != vendorGroupCounter + 1 &&
190             nextId != _vendorMap[*it])
191         {
192                     // We have at least 3 groups which has to be mixed --> mix the third group to the first
193           unsigned int moveID = _vendorMap[*it];
194           for_( itMap, _vendorMap.begin(), _vendorMap.end() )
195           {
196             if (itMap->second == moveID)
197               itMap->second = nextId;
198           }
199         }
200         else
201         {
202           nextId = _vendorMap[*it];
203           WAR << "Vendor " << *it << " is already used in another vendor group. --> mixing these groups" << endl;
204         }
205       }
206     }
207         // add new entries
208     for_( it, vendorList_r.begin(), vendorList_r.end() )
209     {
210       _vendorMap[*it] = nextId;
211     }
212
213     if (nextId == vendorGroupCounter + 1)
214       ++vendorGroupCounter;
215
216     // invalidate any match cache
217     vendorMatchIdReset();
218   }
219
220   bool VendorAttr::addVendorFile( const Pathname & filename ) const
221   {
222       parser::IniDict dict;
223
224       if ( PathInfo(filename).isExist())
225       {
226           InputStream is(filename);
227           dict.read(is);
228       }
229       else
230       {
231           MIL << filename << " not found." << endl;
232           return false;
233       }
234
235       for ( parser::IniDict::section_const_iterator sit = dict.sectionsBegin();
236             sit != dict.sectionsEnd();
237             ++sit )
238       {
239           string section(*sit);
240           //MIL << section << endl;
241           for ( parser::IniDict::entry_const_iterator it = dict.entriesBegin(*sit);
242                 it != dict.entriesEnd(*sit);
243                 ++it )
244           {
245               string entry(it->first);
246               string value(it->second);
247               if ( section == "main" )
248               {
249                   if ( entry == "vendors" )
250                   {
251                       VendorList vendorlist;
252                       str::split( value, back_inserter(vendorlist), "," );
253                       _addVendorList (vendorlist);
254                       break;
255                   }
256               }
257           }
258       }
259
260       return true;
261   }
262
263   bool VendorAttr::addVendorDirectory( const Pathname & dirname ) const
264   {
265       parser::IniDict dict;
266
267       if ( PathInfo(dirname).isExist())
268       {
269           InputStream is(dirname);
270           dict.read(is);
271       }
272       else
273       {
274           MIL << dirname << " not found." << endl;
275           return false;
276       }
277
278       list<Pathname> filenames;
279
280       filesystem::readdir( filenames,
281                            dirname, false );
282       for (list<Pathname>::iterator it = filenames.begin();
283            it != filenames.end(); ++it) {
284           MIL << "Adding file " << *it << endl;
285           addVendorFile( *it );
286       }
287       return true;
288   }
289
290   //////////////////////////////////////////////////////////////////
291   // vendor equivalence:
292   //////////////////////////////////////////////////////////////////
293
294   bool VendorAttr::equivalent( IdString lVendor, IdString rVendor ) const
295   {
296     if ( lVendor == rVendor )
297       return true;
298     return vendorMatchId( lVendor ) == vendorMatchId( rVendor );
299   }
300
301   bool VendorAttr::equivalent( const Vendor & lVendor, const Vendor & rVendor ) const
302   { return equivalent( IdString( lVendor ), IdString( rVendor ) );
303   }
304
305   bool VendorAttr::equivalent( sat::Solvable lVendor, sat::Solvable rVendor ) const
306   { return equivalent( lVendor.vendor(), rVendor.vendor() ); }
307
308   bool VendorAttr::equivalent( const PoolItem & lVendor, const PoolItem & rVendor ) const
309   { return equivalent( lVendor.satSolvable().vendor(), rVendor.satSolvable().vendor() ); }
310
311   //////////////////////////////////////////////////////////////////
312
313   std::ostream & operator<<( std::ostream & str, const VendorAttr & /*obj*/ )
314   {
315     str << "Equivalent vendors:";
316     for_( it, _vendorMap.begin(), _vendorMap.end() )
317     {
318       str << endl << "   [" << it->second << "] " << it->first;
319     }
320     return str;
321   }
322
323   /////////////////////////////////////////////////////////////////
324 } // namespace zypp
325 ///////////////////////////////////////////////////////////////////
326