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

akonadi

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

Skip menu "kdepimlibs-4.9.4 API Reference"
  • akonadi
  •   contact
  •   kmime
  • kabc
  • kalarmcal
  • kblog
  • kcal
  • kcalcore
  • kcalutils
  • kholidays
  • kimap
  • kioslave
  •   imap4
  •   mbox
  •   nntp
  • kldap
  • kmbox
  • kmime
  • kontactinterface
  • kpimidentities
  • kpimtextedit
  •   richtextbuilders
  • kpimutils
  • kresources
  • ktnef
  • kxmlrpcclient
  • mailtransport
  • microblog
  • qgpgme
  • syndication
  •   atom
  •   rdf
  •   rss2
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