• Skip to content
  • Skip to link menu
KDE API Reference
  • KDE API Reference
  • kdepimlibs API Reference
  • KDE Home
  • Contact Us
 

akonadi

  • sources
  • kde-4.12
  • kdepimlibs
  • akonadi
typepluginloader.cpp
1 /*
2  Copyright (c) 2007 Till Adam <adam@kde.org>
3  Copyright (c) 2007 Volker Krause <vkrause@kde.org>
4 
5  This library is free software; you can redistribute it and/or modify it
6  under the terms of the GNU Library General Public License as published by
7  the Free Software Foundation; either version 2 of the License, or (at your
8  option) any later version.
9 
10  This library is distributed in the hope that it will be useful, but WITHOUT
11  ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
12  FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
13  License for more details.
14 
15  You should have received a copy of the GNU Library General Public License
16  along with this library; see the file COPYING.LIB. If not, write to the
17  Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18  02110-1301, USA.
19 */
20 
21 #include "typepluginloader_p.h"
22 
23 #include "item.h"
24 #include "itemserializer_p.h"
25 #include "itemserializerplugin.h"
26 
27 // KDE core
28 #include <kdebug.h>
29 #include <kmimetype.h>
30 #include <kglobal.h>
31 
32 // Qt
33 #include <QtCore/QHash>
34 #include <QtCore/QString>
35 #include <QtCore/QByteArray>
36 #include <QtCore/QStringList>
37 
38 #include <boost/graph/adjacency_list.hpp>
39 #include <boost/graph/topological_sort.hpp>
40 
41 // temporary
42 #include "pluginloader_p.h"
43 
44 #include <vector>
45 #include <cassert>
46 
47 static const char LEGACY_NAME[] = "legacy";
48 static const char DEFAULT_NAME[] = "default";
49 static const char _APPLICATION_OCTETSTREAM[] = "application/octet-stream";
50 
51 namespace Akonadi {
52 
53 K_GLOBAL_STATIC( DefaultItemSerializerPlugin, s_defaultItemSerializerPlugin )
54 
55 class PluginEntry
56 {
57  public:
58  PluginEntry()
59  : mPlugin( 0 )
60  {
61  }
62 
63  explicit PluginEntry( const QString &identifier, QObject *plugin = 0 )
64  : mIdentifier( identifier ), mPlugin( plugin )
65  {
66  }
67 
68  QObject* plugin() const
69  {
70  if ( mPlugin )
71  return mPlugin;
72 
73  QObject *object = PluginLoader::self()->createForName( mIdentifier );
74  if ( !object ) {
75  kWarning() << "ItemSerializerPluginLoader: "
76  << "plugin" << mIdentifier << "is not valid!" << endl;
77 
78  // we try to use the default in that case
79  mPlugin = s_defaultItemSerializerPlugin;
80  }
81 
82  mPlugin = object;
83  if ( !qobject_cast<ItemSerializerPlugin*>( mPlugin ) ) {
84  kWarning() << "ItemSerializerPluginLoader: "
85  << "plugin" << mIdentifier << "doesn't provide interface ItemSerializerPlugin!" << endl;
86 
87  // we try to use the default in that case
88  mPlugin = s_defaultItemSerializerPlugin;
89  }
90 
91  Q_ASSERT( mPlugin );
92 
93  return mPlugin;
94  }
95 
96  const char * pluginClassName() const
97  {
98  return plugin()->metaObject()->className();
99  }
100 
101  QString identifier() const
102  {
103  return mIdentifier;
104  }
105 
106  bool operator<( const PluginEntry &other ) const
107  {
108  return mIdentifier < other.mIdentifier;
109  }
110 
111  bool operator<( const QString &identifier ) const
112  {
113  return mIdentifier < identifier;
114  }
115 
116  private:
117  QString mIdentifier;
118  mutable QObject *mPlugin;
119 };
120 
121 static bool operator<( const QString &identifier, const PluginEntry &entry )
122 {
123  return identifier < entry.identifier();
124 }
125 
126 class MimeTypeEntry
127 {
128 public:
129  explicit MimeTypeEntry( const QString & mimeType )
130  : m_mimeType( mimeType ), m_plugins(), m_pluginsByMetaTypeId() {}
131 
132  QString type() const { return m_mimeType; }
133 
134  void add( const QByteArray & class_, const PluginEntry & entry ) {
135  m_pluginsByMetaTypeId.clear(); // iterators will be invalidated by next line
136  m_plugins.insert( class_, entry );
137  }
138 
139  const PluginEntry * plugin( const QByteArray & class_ ) const {
140  const QHash<QByteArray,PluginEntry>::const_iterator it = m_plugins.find( class_ );
141  return it == m_plugins.end() ? 0 : it.operator->() ;
142  }
143 
144  const PluginEntry * defaultPlugin() const {
145  // 1. If there's an explicit default plugin, use that one:
146  if ( const PluginEntry * pe = plugin( DEFAULT_NAME ) )
147  return pe;
148 
149  // 2. Otherwise, look through the already instantiated plugins,
150  // and return one of them (preferably not the legacy one):
151  bool sawZero = false;
152  for ( QMap<int,QHash<QByteArray,PluginEntry>::const_iterator>::const_iterator it = m_pluginsByMetaTypeId.constBegin(), end = m_pluginsByMetaTypeId.constEnd() ; it != end ; ++it )
153  if ( it.key() == 0 )
154  sawZero = true;
155  else
156  if ( *it != m_plugins.end() )
157  return it->operator->();
158 
159  // 3. Otherwise, look through the whole list (again, preferably not the legacy one):
160  for ( QHash<QByteArray,PluginEntry>::const_iterator it = m_plugins.constBegin(), end = m_plugins.constEnd() ; it != end ; ++it )
161  if ( it.key() == LEGACY_NAME )
162  sawZero = true;
163  else
164  return it.operator->() ;
165 
166  // 4. take the legacy one:
167  if ( sawZero )
168  return plugin( 0 );
169  return 0;
170  }
171 
172  const PluginEntry * plugin( int metaTypeId ) const {
173  const QMap<int,QHash<QByteArray,PluginEntry>::const_iterator> & c_pluginsByMetaTypeId = m_pluginsByMetaTypeId;
174  QMap<int,QHash<QByteArray,PluginEntry>::const_iterator>::const_iterator it = c_pluginsByMetaTypeId.find( metaTypeId );
175  if ( it == c_pluginsByMetaTypeId.end() )
176  it = QMap<int,QHash<QByteArray,PluginEntry>::const_iterator>::const_iterator( m_pluginsByMetaTypeId.insert( metaTypeId, m_plugins.find( metaTypeId ? QMetaType::typeName( metaTypeId ) : LEGACY_NAME ) ) );
177  return *it == m_plugins.end() ? 0 : it->operator->() ;
178  }
179 
180  const PluginEntry * plugin( const QVector<int> & metaTypeIds, int & chosen ) const {
181  bool sawZero = false;
182  for ( QVector<int>::const_iterator it = metaTypeIds.begin(), end = metaTypeIds.end() ; it != end ; ++it )
183  if ( *it == 0 ) {
184  sawZero = true; // skip the legacy type and see if we can find something else first
185  } else if ( const PluginEntry * const entry = plugin( *it ) ) {
186  chosen = *it;
187  return entry;
188  }
189  if ( sawZero ) {
190  chosen = 0;
191  return plugin( 0 );
192  }
193  return 0;
194  }
195 
196 private:
197  QString m_mimeType;
198  QHash< QByteArray/* class */, PluginEntry > m_plugins;
199  mutable QMap<int,QHash<QByteArray,PluginEntry>::const_iterator> m_pluginsByMetaTypeId;
200 };
201 
202 static bool operator<( const MimeTypeEntry & lhs, const MimeTypeEntry & rhs )
203 {
204  return lhs.type() < rhs.type() ;
205 }
206 
207 static bool operator<( const MimeTypeEntry & lhs, const QString & rhs )
208 {
209  return lhs.type() < rhs ;
210 }
211 
212 static bool operator<( const QString & lhs, const MimeTypeEntry & rhs )
213 {
214  return lhs < rhs.type();
215 }
216 
217 static QString format( const QString & mimeType, const QVector<int> & metaTypeIds ) {
218  if ( metaTypeIds.empty() )
219  return QLatin1String( "default for " ) + mimeType;
220  QStringList classTypes;
221  Q_FOREACH( int metaTypeId, metaTypeIds )
222  classTypes.push_back( QString::fromLatin1( metaTypeId ? QMetaType::typeName( metaTypeId ) : LEGACY_NAME ) );
223  return mimeType + QLatin1String("@{") + classTypes.join(QLatin1String(",")) + QLatin1Char('}');
224 }
225 
226 class PluginRegistry
227 {
228  public:
229  PluginRegistry()
230  : mDefaultPlugin( PluginEntry( QLatin1String( "application/octet-stream@QByteArray" ), s_defaultItemSerializerPlugin ) ),
231  mOverridePlugin( 0 )
232  {
233  const PluginLoader* pl = PluginLoader::self();
234  if ( !pl ) {
235  kWarning() << "Cannot instantiate plugin loader!" << endl;
236  return;
237  }
238  const QStringList names = pl->names();
239  kDebug() << "ItemSerializerPluginLoader: "
240  << "found" << names.size() << "plugins." << endl;
241  QMap<QString,MimeTypeEntry> map;
242  QRegExp rx( QLatin1String( "(.+)@(.+)" ) );
243  Q_FOREACH ( const QString & name, names )
244  if ( rx.exactMatch( name ) ) {
245  KMimeType::Ptr mime = KMimeType::mimeType( rx.cap(1), KMimeType::ResolveAliases );
246  if ( mime ) {
247  const QString mimeType = mime->name();
248  const QByteArray classType = rx.cap(2).toLatin1();
249  QMap<QString,MimeTypeEntry>::iterator it = map.find( mimeType );
250  if ( it == map.end() )
251  it = map.insert( mimeType, MimeTypeEntry( mimeType ) );
252  it->add( classType, PluginEntry( name ) );
253  }
254  } else {
255  kDebug() << "ItemSerializerPluginLoader: "
256  << "name" << name << "doesn't look like mimetype@classtype" << endl;
257  }
258  const QString APPLICATION_OCTETSTREAM = QLatin1String( _APPLICATION_OCTETSTREAM );
259  QMap<QString,MimeTypeEntry>::iterator it = map.find( APPLICATION_OCTETSTREAM );
260  if ( it == map.end() )
261  it = map.insert( APPLICATION_OCTETSTREAM, MimeTypeEntry( APPLICATION_OCTETSTREAM ) );
262  it->add( "QByteArray", mDefaultPlugin );
263  it->add( LEGACY_NAME, mDefaultPlugin );
264  const int size = map.size();
265  allMimeTypes.reserve( size );
266  std::copy( map.begin(), map.end(),
267  std::back_inserter( allMimeTypes ) );
268  }
269 
270  QObject * findBestMatch( const QString & type, const QVector<int> & metaTypeId, TypePluginLoader::Options opt ) {
271  if ( QObject * const plugin = findBestMatch( type, metaTypeId ) ) {
272  if ( ( opt & TypePluginLoader::NoDefault ) && plugin == mDefaultPlugin.plugin() )
273  return 0;
274  return plugin;
275  }
276  return 0;
277  }
278 
279  QObject * findBestMatch( const QString & type, const QVector<int> & metaTypeIds ) {
280  if ( mOverridePlugin ) {
281  return mOverridePlugin;
282  }
283  if ( QObject * const plugin = cacheLookup( type, metaTypeIds ) )
284  // plugin cached, so let's take that one
285  return plugin;
286  int chosen = -1;
287  QObject * const plugin = findBestMatchImpl( type, metaTypeIds, chosen );
288  if ( metaTypeIds.empty() )
289  if ( plugin )
290  cachedDefaultPlugins[type] = plugin;
291  if ( chosen >= 0 )
292  cachedPlugins[type][chosen] = plugin;
293  return plugin;
294  }
295 
296  void overrideDefaultPlugin( QObject *p ) {
297  mOverridePlugin = p;
298  }
299 
300 private:
301  QObject * findBestMatchImpl( const QString &type, const QVector<int> & metaTypeIds, int & chosen ) const
302  {
303  KMimeType::Ptr mimeType = KMimeType::mimeType( type, KMimeType::ResolveAliases );
304  if ( mimeType.isNull() )
305  return mDefaultPlugin.plugin();
306 
307  // step 1: find all plugins that match at all
308  QVector<int> matchingIndexes;
309  for ( int i = 0, end = allMimeTypes.size(); i < end; ++i ) {
310  if ( mimeType->is( allMimeTypes[i].type() ) )
311  matchingIndexes.append( i );
312  }
313 
314  // step 2: if we have more than one match, find the most specific one using topological sort
315  QVector<int> order;
316  if ( matchingIndexes.size() <= 1 ) {
317  order.push_back( 0 );
318  } else {
319  boost::adjacency_list<> graph( matchingIndexes.size() );
320  for ( int i = 0, end = matchingIndexes.size() ; i != end ; ++i ) {
321  KMimeType::Ptr mimeType = KMimeType::mimeType( allMimeTypes[matchingIndexes[i]].type(), KMimeType::ResolveAliases );
322  if ( mimeType.isNull() )
323  continue;
324  for ( int j = 0; j != end; ++j ) {
325  if ( i != j && mimeType->is( allMimeTypes[matchingIndexes[j]].type() ) )
326  boost::add_edge( j, i, graph );
327  }
328  }
329 
330  order.reserve( matchingIndexes.size() );
331  try {
332  boost::topological_sort( graph, std::back_inserter( order ) );
333  } catch ( boost::not_a_dag &e ) {
334  kWarning() << "Mimetype tree is not a DAG!";
335  return mDefaultPlugin.plugin();
336  }
337  }
338 
339  // step 3: ask each one in turn if it can handle any of the metaTypeIds:
340 // kDebug() << "Looking for " << format( type, metaTypeIds );
341  for ( QVector<int>::const_iterator it = order.constBegin(), end = order.constEnd() ; it != end ; ++it ) {
342 // kDebug() << " Considering serializer plugin for type" << allMimeTypes[matchingIndexes[*it]].type()
343 // // << "as the closest match";
344  const MimeTypeEntry & mt = allMimeTypes[matchingIndexes[*it]];
345  if ( metaTypeIds.empty() ) {
346  if ( const PluginEntry * const entry = mt.defaultPlugin() ) {
347 // kDebug() << " -> got " << entry->pluginClassName() << " and am happy with it.";
348  return entry->plugin();
349  } else {
350 // kDebug() << " -> no default plugin for this mime type, trying next";
351  }
352  } else if ( const PluginEntry * const entry = mt.plugin( metaTypeIds, chosen ) ) {
353 // kDebug() << " -> got " << entry->pluginClassName() << " and am happy with it.";
354  return entry->plugin();
355  } else {
356 // kDebug() << " -> can't handle any of the types, trying next";
357  }
358  }
359 
360 // kDebug() << " No further candidates, using default plugin";
361  // no luck? Use the default plugin
362  return mDefaultPlugin.plugin();
363  }
364 
365  std::vector<MimeTypeEntry> allMimeTypes;
366  QHash<QString, QMap<int,QObject*> > cachedPlugins;
367  QHash<QString, QObject*> cachedDefaultPlugins;
368 
369  // ### cache NULLs, too
370  QObject * cacheLookup( const QString & mimeType, const QVector<int> & metaTypeIds ) const {
371  if ( metaTypeIds.empty() ) {
372  const QHash<QString,QObject*>::const_iterator hit = cachedDefaultPlugins.find( mimeType );
373  if ( hit != cachedDefaultPlugins.end() )
374  return *hit;
375  }
376 
377  const QHash<QString,QMap<int,QObject*> >::const_iterator hit = cachedPlugins.find( mimeType );
378  if ( hit == cachedPlugins.end() )
379  return 0;
380  bool sawZero = false;
381  for ( QVector<int>::const_iterator it = metaTypeIds.begin(), end = metaTypeIds.end() ; it != end ; ++it )
382  if ( *it == 0 )
383  sawZero = true; // skip the legacy type and see if we can find something else first
384  else if ( QObject * const o = hit->value( *it ) )
385  return o;
386  if ( sawZero )
387  return hit->value( 0 );
388  return 0;
389  }
390 
391  private:
392  PluginEntry mDefaultPlugin;
393  QObject *mOverridePlugin;
394 };
395 
396 K_GLOBAL_STATIC( PluginRegistry, s_pluginRegistry )
397 
398 QObject* TypePluginLoader::objectForMimeTypeAndClass( const QString &mimetype, const QVector<int> & metaTypeIds, Options opt )
399 {
400  return s_pluginRegistry->findBestMatch( mimetype, metaTypeIds, opt );
401 }
402 
403 #if 0
404 QObject* TypePluginLoader::legacyObjectForMimeType( const QString &mimetype ) {
405  // ### impl specifically - only works b/c vector isn't used in impl ###
406  return objectForMimeTypeAndClass( mimetype, QVector<int>( 1, 0 ) );
407 }
408 #endif
409 
410 QObject* TypePluginLoader::defaultObjectForMimeType( const QString &mimetype ) {
411  return objectForMimeTypeAndClass( mimetype, QVector<int>() );
412 }
413 
414 ItemSerializerPlugin* TypePluginLoader::pluginForMimeTypeAndClass( const QString &mimetype, const QVector<int> &metaTypeIds, Options opt )
415 {
416  return qobject_cast<ItemSerializerPlugin*>( objectForMimeTypeAndClass( mimetype, metaTypeIds, opt ) );
417 }
418 
419 #if 0
420 ItemSerializerPlugin* TypePluginLoader::legacyPluginForMimeType( const QString &mimetype ) {
421  ItemSerializerPlugin* plugin = qobject_cast<ItemSerializerPlugin*>( legacyObjectForMimeType( mimetype ) );
422  Q_ASSERT( plugin );
423  return plugin;
424 }
425 #endif
426 
427 ItemSerializerPlugin* TypePluginLoader::defaultPluginForMimeType( const QString &mimetype ) {
428  ItemSerializerPlugin* plugin = qobject_cast<ItemSerializerPlugin*>( defaultObjectForMimeType( mimetype ) );
429  Q_ASSERT( plugin );
430  return plugin;
431 }
432 
433 void TypePluginLoader::overridePluginLookup( QObject *p ) {
434  s_pluginRegistry->overrideDefaultPlugin( p );
435 }
436 
437 }
Akonadi::TypePluginLoader::overridePluginLookup
void overridePluginLookup(QObject *plugin)
Override the plugin-lookup with plugin.
Definition: typepluginloader.cpp:433
Akonadi::TypePluginLoader::pluginForMimeTypeAndClass
ItemSerializerPlugin * pluginForMimeTypeAndClass(const QString &mimetype, const QVector< int > &metaTypeIds, Options options=NoOptions)
Returns the item serializer plugin that matches the given mimetype, and any of the classes described ...
Definition: typepluginloader.cpp:414
Akonadi::TypePluginLoader::defaultPluginForMimeType
ItemSerializerPlugin * defaultPluginForMimeType(const QString &mimetype)
Returns the default item serializer plugin that matches the given mimetype.
Definition: typepluginloader.cpp:427
Akonadi::TypePluginLoader::objectForMimeTypeAndClass
QObject * objectForMimeTypeAndClass(const QString &mimetype, const QVector< int > &metaTypeIds, Options options=NoOptions)
Returns the type plugin object that matches the given mimetype, and any of the classes described by m...
Definition: typepluginloader.cpp:398
Akonadi::TypePluginLoader::defaultObjectForMimeType
QObject * defaultObjectForMimeType(const QString &mimetype)
Returns the default type plugin object that matches the given mimetype.
Definition: typepluginloader.cpp:410
Akonadi::ItemSerializerPlugin
The base class for item type serializer plugins.
Definition: itemserializerplugin.h:119
This file is part of the KDE documentation.
Documentation copyright © 1996-2014 The KDE developers.
Generated on Tue Oct 14 2014 23:00:28 by doxygen 1.8.7 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.

akonadi

Skip menu "akonadi"
  • Main Page
  • Namespace List
  • Namespace Members
  • Alphabetical List
  • Class List
  • Class Hierarchy
  • Class Members
  • File List
  • Modules
  • Related Pages

kdepimlibs API Reference

Skip menu "kdepimlibs API Reference"
  • akonadi
  •   contact
  •   kmime
  •   socialutils
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kldap
  • kmbox
  • kmime
  • kpimidentities
  • kpimtextedit
  • kresources
  • ktnef
  • kxmlrpcclient
  • microblog

Search



Report problems with this website to our bug tracking system.
Contact the specific authors with questions and comments about the page contents.

KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal