- Create the cache directly from the schema (installed) file.
[platform/upstream/libzypp.git] / zypp / CapFactory.cc
1 /*---------------------------------------------------------------------\
2 |                          ____ _   __ __ ___                          |
3 |                         |__  / \ / / . \ . \                         |
4 |                           / / \ V /|  _/  _/                         |
5 |                          / /__ | | | | | |                           |
6 |                         /_____||_| |_| |_|                           |
7 |                                                                      |
8 \---------------------------------------------------------------------*/
9 /** \file       zypp/CapFactory.cc
10  *
11 */
12 #include <iostream>
13 #include <functional>
14 #include <set>
15 #include <map>
16
17 #include <ext/hash_set>
18 #include <ext/hash_fun.h>
19
20 #include "zypp/base/Logger.h"
21 #include "zypp/base/Exception.h"
22 #include "zypp/base/String.h"
23 #include "zypp/base/Counter.h"
24
25 #include "zypp/CapFactory.h"
26 #include "zypp/capability/Capabilities.h"
27
28 using std::endl;
29
30 ///////////////////////////////////////////////////////////////////
31 namespace
32 { /////////////////////////////////////////////////////////////////
33   using ::zypp::Resolvable;
34   using ::zypp::capability::CapabilityImpl;
35   using ::zypp::capability::CapImplOrder;
36
37   struct CapImplHashFun
38   {
39     size_t operator() ( const CapabilityImpl::Ptr & p ) const
40     {
41       return __gnu_cxx::hash<const char*>()( p->encode().c_str() );
42     }
43   };
44
45   struct CapImplHashEqual
46   {
47     bool operator() ( const CapabilityImpl::Ptr & lhs, const CapabilityImpl::Ptr & rhs ) const
48     {
49       return (    lhs->encode() == rhs->encode()
50                && lhs->kind()   == rhs->kind()
51                && lhs->refers() == rhs->refers() );
52     }
53   };
54
55   /** Set of unique CapabilityImpl. */
56   //typedef std::set<CapabilityImpl::Ptr,CapImplOrder> USet;
57   typedef __gnu_cxx::hash_set<CapabilityImpl::Ptr, CapImplHashFun, CapImplHashEqual> USet;
58
59
60   /** Set to unify created capabilities.
61    *
62    * This is to unify capabilities. Each CapabilityImpl created
63    * by CapFactory, must be inserted into _uset, and the returned
64    * CapabilityImpl::Ptr has to be uset to create the Capability.
65   */
66   USet _uset;
67
68   /** Each CapabilityImpl created in CapFactory \b must be wrapped.
69    *
70    * Immediately wrap \a allocated_r, and unified by inserting it into
71    * \c _uset. Each CapabilityImpl created by CapFactory, \b must be
72    * inserted into _uset, by calling usetInsert.
73    *
74    * \return CapabilityImpl_Ptr referencing \a allocated_r (or an
75    * eqal representation, allocated is deleted then).
76   */
77   CapabilityImpl::Ptr usetInsert( CapabilityImpl * allocated_r )
78   {
79     return *(_uset.insert( CapabilityImpl::Ptr(allocated_r) ).first);
80   }
81
82   /** Collect USet statistics.
83    * \ingroup DEBUG
84   */
85   struct USetStatsCollect : public std::unary_function<CapabilityImpl::constPtr, void>
86   {
87     typedef ::zypp::Counter<unsigned> Counter;
88
89     Counter _caps;
90     std::map<CapabilityImpl::Kind,Counter> _capKind;
91     std::map<Resolvable::Kind,Counter>     _capRefers;
92
93     void operator()( const CapabilityImpl::constPtr & cap_r )
94     {
95       //DBG << *cap_r << endl;
96       ++_caps;
97       ++(_capKind[cap_r->kind()]);
98       ++(_capRefers[cap_r->refers()]);
99     }
100
101     std::ostream & dumpOn( std::ostream & str ) const
102     {
103       str << "  Capabilities total: " << _caps << endl;
104       str << "  Capability kinds:" << endl;
105       for ( std::map<CapabilityImpl::Kind,Counter>::const_iterator it = _capKind.begin();
106             it != _capKind.end(); ++it )
107         {
108           str << "    " << it->first << '\t' << it->second << endl;
109         }
110       str << "  Capability refers:" << endl;
111       for ( std::map<Resolvable::Kind,Counter>::const_iterator it = _capRefers.begin();
112             it != _capRefers.end(); ++it )
113         {
114           str << "    " << it->first << '\t' << it->second << endl;
115         }
116       return str;
117     }
118   };
119
120   /////////////////////////////////////////////////////////////////
121 } // namespace
122 ///////////////////////////////////////////////////////////////////
123
124 ///////////////////////////////////////////////////////////////////
125 namespace zypp
126 { /////////////////////////////////////////////////////////////////
127
128   ///////////////////////////////////////////////////////////////////
129   //
130   //    CLASS NAME : CapFactoryImpl
131   //
132   /** CapFactory implementation.
133    *
134    * Provides various functions doing checks and log and \c throw.
135    * CapFactory::parse usually combines them, and if nothing fails,
136    * finaly builds the Capability.
137    *
138    * \attention Each CapabilityImpl created by CapFactory, \b must
139    * be inserted into ::_uset, by calling ::usetInsert, \b before
140    * the Capability is created.
141    *
142    * \li \c file:     /absolute/path
143    * \li \c split:    name:/absolute/path
144    * \li \c name:     name
145    * \li \c vers:     name op edition
146    * \li \c hal:      hal(string)
147    * \li \c modalias: modalias(string)
148   */
149   struct CapFactory::Impl
150   {
151     /** Assert a valid Resolvable::Kind. */
152     static void assertResKind( const Resolvable::Kind & refers_r )
153     {
154       if ( refers_r == Resolvable::Kind() )
155         ZYPP_THROW( Exception("Missing or empty  Resolvable::Kind in Capability") );
156     }
157
158     /** Check whether \a op_r and \a edition_r make a valid edition spec.
159      *
160      * Rel::NONE is not usefull thus forbidden. Rel::ANY can be ignored,
161      * so no VersionedCap is needed for this. Everything else requires
162      * a VersionedCap.
163      *
164      * \return Whether to build a VersionedCap (i.e. \a op_r
165      * is not Rel::ANY.
166     */
167     static bool isEditionSpec( Rel op_r, const Edition & edition_r )
168     {
169       switch ( op_r.inSwitch() )
170         {
171         case Rel::ANY_e:
172           if ( edition_r != Edition::noedition )
173             WAR << "Operator " << op_r << " causes Edition "
174             << edition_r << " to be ignored." << endl;
175           return false;
176           break;
177
178         case Rel::NONE_e:
179           ZYPP_THROW( Exception("Operator NONE is not allowed in Capability") );
180           break;
181
182         case Rel::EQ_e:
183         case Rel::NE_e:
184         case Rel::LT_e:
185         case Rel::LE_e:
186         case Rel::GT_e:
187         case Rel::GE_e:
188           return true;
189           break;
190         }
191       // SHOULD NOT GET HERE
192       ZYPP_THROW( Exception("Unknow Operator NONE is not allowed in Capability") );
193       return false; // not reached
194     }
195
196     /** Test for a FileCap. \a name_r starts with \c "/". */
197     static bool isFileSpec( const std::string & name_r )
198     {
199       return *name_r.c_str() == '/';
200     }
201
202     /** Test for a SplitCap. \a name_r constains \c ":/". */
203     static bool isSplitSpec( const std::string & name_r )
204     {
205       return name_r.find( ":/" ) != std::string::npos;
206     }
207
208     /** Test for a HalCap. \a name_r starts with  "hal(". */
209     static bool isHalSpec( const std::string & name_r )
210     {
211       return name_r.substr(0,4) == "hal(";
212     }
213
214     /** Test for a ModaliasCap. \a name_r starts with  "modalias(". */
215     static bool isModaliasSpec( const std::string & name_r )
216     {
217       return name_r.substr(0,9) == "modalias(";
218     }
219
220     static CapabilityImpl::Ptr buildFile( const Resolvable::Kind & refers_r,
221                                           const std::string & name_r )
222     {
223       // NullCap check first:
224       if ( name_r.empty() )
225         {
226           // Singleton, so no need to put it into _uset !?
227           return capability::NullCap::instance();
228         }
229
230       assertResKind( refers_r );
231
232       return usetInsert
233       ( new capability::FileCap( refers_r, name_r ) );
234     }
235
236     /** Try to build a non versioned cap from \a name_r .
237      *
238      * The CapabilityImpl is built here and inserted into _uset.
239      * The final Capability must be created by CapFactory, as it
240      * is a friend of Capability. Here we can't access the ctor.
241     */
242     static CapabilityImpl::Ptr buildNamed( const Resolvable::Kind & refers_r,
243                                            const std::string & name_r )
244     {
245       // NullCap check first:
246       if ( name_r.empty() )
247         {
248           // Singleton, so no need to put it into _uset !?
249           return capability::NullCap::instance();
250         }
251
252       assertResKind( refers_r );
253
254       // file:    /absolute/path
255       if ( isFileSpec( name_r ) )
256         return usetInsert
257         ( new capability::FileCap( refers_r, name_r ) );
258
259       //split:   name:/absolute/path
260       static const str::regex  rx( "([^/]*):(/.*)" );
261       str::smatch what;
262       if( str::regex_match( name_r.begin(), name_r.end(), what, rx ) )
263         {
264           return usetInsert
265           ( new capability::SplitCap( refers_r, what[1].str(), what[2].str() ) );
266         }
267
268       //name:    name
269       return usetInsert
270       ( new capability::NamedCap( refers_r, name_r ) );
271     }
272
273     /** Try to build a versioned cap from \a name_r .
274      *
275      * The CapabilityImpl is built here and inserted into _uset.
276      * The final Capability must be created by CapFactory, as it
277      * is a friend of Capability. Here we can't access the ctor.
278      *
279      * \todo Quick check for name not being filename or split.
280     */
281     static CapabilityImpl::Ptr buildVersioned( const Resolvable::Kind & refers_r,
282                                                const std::string & name_r,
283                                                Rel op_r,
284                                                const Edition & edition_r )
285     {
286       if ( Impl::isEditionSpec( op_r, edition_r ) )
287         {
288           assertResKind( refers_r );
289
290           // build a VersionedCap
291           return usetInsert
292           ( new capability::VersionedCap( refers_r, name_r, op_r, edition_r ) );
293         }
294       //else
295       // build a NamedCap
296
297       return buildNamed( refers_r, name_r );
298     }
299
300     /** Try to build a hal cap from \a name_r .
301      *
302      * The CapabilityImpl is built here and inserted into _uset.
303      * The final Capability must be created by CapFactory, as it
304      * is a friend of Capability. Here we can't access the ctor.
305      *
306      * \todo Fix incaccuracy.
307     */
308     static CapabilityImpl::Ptr buildHal( const Resolvable::Kind & refers_r,
309                                          const std::string & name_r,
310                                          Rel op_r = Rel::ANY,
311                                          const std::string & value_r = std::string() )
312     {
313       if ( op_r != Rel::ANY )
314         {
315           ZYPP_THROW( Exception("Unsupported kind of Hal Capability '" + op_r.asString() + "'") );
316         }
317
318       //split:   hal(name) [op string]
319       static const str::regex  rx( "hal\\(([^)]*)\\)" );
320       str::smatch what;
321       if( str::regex_match( name_r.begin(), name_r.end(), what, rx ) )
322         {
323           // Hal always refers to 'System' kind of Resolvable.
324           return usetInsert
325           ( new capability::HalCap( ResTraits<SystemResObject>::kind,
326                                     what[1].str() ) );
327         }
328       // otherwise
329       ZYPP_THROW( Exception("Unsupported kind of Hal Capability '" + name_r + "'") );
330       return NULL; // make gcc happy
331     }
332
333
334     /** Try to build a modalias cap from \a name_r .
335      *
336      * The CapabilityImpl is built here and inserted into _uset.
337      * The final Capability must be created by CapFactory, as it
338      * is a friend of Capability. Here we can't access the ctor.
339      *
340      * \todo Fix incaccuracy.
341     */
342     static CapabilityImpl::Ptr buildModalias( const Resolvable::Kind & refers_r,
343                                               const std::string & name_r,
344                                               Rel op_r = Rel::ANY,
345                                               const std::string & value_r = std::string() )
346     {
347       if ( op_r != Rel::ANY )
348         {
349           ZYPP_THROW( Exception("Unsupported kind of Modalias Capability  '" + op_r.asString() + "'") );
350         }
351
352       //split:   modalias(name) [op string]
353       static const str::regex  rx( "modalias\\(([^)]*)\\)" );
354       str::smatch what;
355       if( str::regex_match( name_r.begin(), name_r.end(), what, rx ) )
356         {
357           // Modalias always refers to 'System' kind of Resolvable.
358           return usetInsert
359           ( new capability::ModaliasCap( ResTraits<SystemResObject>::kind,
360                                          what[1].str() ) );
361         }
362       // otherwise
363       ZYPP_THROW( Exception("Unsupported kind of Modalias Capability'" + name_r + "'") );
364       return NULL; // make gcc happy
365     }
366   };
367   ///////////////////////////////////////////////////////////////////
368
369   ///////////////////////////////////////////////////////////////////
370
371   ///////////////////////////////////////////////////////////////////
372   //
373   //    CLASS NAME : CapFactory
374   //
375   ///////////////////////////////////////////////////////////////////
376
377   ///////////////////////////////////////////////////////////////////
378   //
379   //    METHOD NAME : CapFactory::CapFactory
380   //    METHOD TYPE : Ctor
381   //
382   CapFactory::CapFactory()
383   {}
384
385   ///////////////////////////////////////////////////////////////////
386   //
387   //    METHOD NAME : CapFactory::~CapFactory
388   //    METHOD TYPE : Dtor
389   //
390   CapFactory::~CapFactory()
391   {}
392
393   ///////////////////////////////////////////////////////////////////
394   //
395   //    METHOD NAME : CapFactory::parse
396   //    METHOD TYPE : Capability
397   //
398   Capability CapFactory::parse( const Resolvable::Kind & refers_r,
399                                 const std::string & strval_r ) const
400
401   try
402     {
403       if ( Impl::isHalSpec( strval_r ) )
404         {
405           return Capability( Impl::buildHal( refers_r, strval_r ) );
406         }
407       if ( Impl::isModaliasSpec( strval_r ) )
408         {
409           return Capability( Impl::buildModalias( refers_r, strval_r ) );
410         }
411       if ( Impl::isFileSpec( strval_r ) )
412         {
413           return Capability( Impl::buildFile( refers_r, strval_r ) );
414         }
415
416       // strval_r has at least two words which could make 'op edition'?
417       // improve regex!
418       static const str::regex  rx( "(.*[^ \t])([ \t]+)([^ \t]+)([ \t]+)([^ \t]+)" );
419       str::smatch what;
420       if( str::regex_match( strval_r.begin(), strval_r.end(),what, rx ) )
421         {
422           Rel op;
423           Edition edition;
424           try
425             {
426               op = Rel(what[3].str());
427               edition = Edition(what[5].str());
428             }
429           catch ( Exception & excpt )
430             {
431               // So they don't make valid 'op edition'
432               ZYPP_CAUGHT( excpt );
433               DBG << "Trying named cap for: " << strval_r << endl;
434               // See whether it makes a named cap.
435               return Capability( Impl::buildNamed( refers_r, strval_r ) );
436             }
437
438           // Valid 'op edition'
439           return Capability ( Impl::buildVersioned( refers_r,
440                                                     what[1].str(), op, edition ) );
441         }
442       //else
443       // not a VersionedCap
444       return Capability( Impl::buildNamed( refers_r, strval_r ) );
445     }
446   catch ( Exception & excpt )
447     {
448       ZYPP_RETHROW( excpt );
449       return Capability(); // not reached
450     }
451
452
453   ///////////////////////////////////////////////////////////////////
454   //
455   //    METHOD NAME : CapFactory::parse
456   //    METHOD TYPE : Capability
457   //
458   Capability CapFactory::parse( const Resolvable::Kind & refers_r,
459                                 const std::string & name_r,
460                                 const std::string & op_r,
461                                 const std::string & edition_r ) const
462   try
463     {
464       if ( Impl::isHalSpec( name_r ) )
465         {
466           return Capability( Impl::buildHal( refers_r, name_r, Rel(op_r), edition_r ) );
467         }
468       if ( Impl::isModaliasSpec( name_r ) )
469         {
470           return Capability( Impl::buildModalias( refers_r, name_r, Rel(op_r), edition_r ) );
471         }
472       // Try creating Rel and Edition, then parse
473       return parse( refers_r, name_r, Rel(op_r), Edition(edition_r) );
474     }
475   catch ( Exception & excpt )
476     {
477       ZYPP_RETHROW( excpt );
478       return Capability(); // not reached
479     }
480
481   ///////////////////////////////////////////////////////////////////
482   //
483   //    METHOD NAME : CapFactory::parse
484   //    METHOD TYPE : Capability
485   //
486   Capability CapFactory::parse( const Resolvable::Kind & refers_r,
487                                 const std::string & name_r,
488                                 Rel op_r,
489                                 const Edition & edition_r ) const
490   try
491     {
492       if ( Impl::isHalSpec( name_r ) )
493         {
494           return Capability( Impl::buildHal( refers_r, name_r, op_r, edition_r.asString() ) );
495         }
496       if ( Impl::isModaliasSpec( name_r ) )
497         {
498           return Capability( Impl::buildModalias( refers_r, name_r, op_r, edition_r.asString() ) );
499         }
500       return Capability( Impl::buildVersioned( refers_r, name_r, op_r, edition_r ) );
501     }
502   catch ( Exception & excpt )
503     {
504       ZYPP_RETHROW( excpt );
505       return Capability(); // not reached
506     }
507
508   ///////////////////////////////////////////////////////////////////
509   //
510   //    METHOD NAME : CapFactory::halEvalCap
511   //    METHOD TYPE : Capability
512   //
513   Capability CapFactory::halEvalCap() const
514   try
515     {
516       return Capability( Impl::buildHal( Resolvable::Kind(), "hal()" ) );
517     }
518   catch ( Exception & excpt )
519     {
520       ZYPP_RETHROW( excpt );
521       return Capability(); // not reached
522     }
523
524   ///////////////////////////////////////////////////////////////////
525   //
526   //    METHOD NAME : CapFactory::modaliasEvalCap
527   //    METHOD TYPE : Capability
528   //
529   Capability CapFactory::modaliasEvalCap() const
530   try
531     {
532       return Capability( Impl::buildModalias( Resolvable::Kind(), "modalias()" ) );
533     }
534   catch ( Exception & excpt )
535     {
536       ZYPP_RETHROW( excpt );
537       return Capability(); // not reached
538     }
539
540   ///////////////////////////////////////////////////////////////////
541   //
542   //    METHOD NAME : CapFactory::encode
543   //    METHOD TYPE : std::string
544   //
545   std::string CapFactory::encode( const Capability & cap_r ) const
546   {
547     return cap_r._pimpl->encode();
548   }
549
550   /******************************************************************
551   **
552   **    FUNCTION NAME : operator<<
553   **    FUNCTION TYPE : std::ostream &
554   */
555   std::ostream & operator<<( std::ostream & str, const CapFactory & obj )
556   {
557     str << "CapFactory stats:" << endl;
558
559     return for_each( _uset.begin(), _uset.end(), USetStatsCollect() ).dumpOn( str );
560   }
561
562   /////////////////////////////////////////////////////////////////
563 } // namespace zypp
564 ///////////////////////////////////////////////////////////////////