Warning, file /office/calligra/libs/main/KoFilterGraph.cpp was not indexed or was modified since last indexation (in which case cross-reference links may be missing, inaccurate or erroneous).
0001 /* This file is part of the Calligra libraries 0002 Copyright (C) 2001 Werner Trobin <trobin@kde.org> 0003 0004 This library is free software; you can redistribute it and/or 0005 modify it under the terms of the GNU Library General Public 0006 License as published by the Free Software Foundation; either 0007 version 2 of the License, or (at your option) any later version. 0008 0009 This library is distributed in the hope that it will be useful, 0010 but WITHOUT ANY WARRANTY; without even the implied warranty of 0011 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 0012 Library General Public License for more details. 0013 0014 You should have received a copy of the GNU Library General Public License 0015 along with this library; see the file COPYING.LIB. If not, write to 0016 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 0017 Boston, MA 02110-1301, USA. 0018 */ 0019 #include "KoFilterGraph.h" 0020 0021 #include "KoFilterManager.h" // KoFilterManager::filterAvailable, private API 0022 #include "KoDocumentEntry.h" 0023 #include "KoFilterEntry.h" 0024 #include "KoDocument.h" 0025 #include <KoConfig.h> // CALLIGRA_OLD_PLUGIN_METADATA 0026 0027 #include "PriorityQueue_p.h" 0028 #include "KoFilterEdge.h" 0029 #include "KoFilterChainLink.h" 0030 #include "KoFilterVertex.h" 0031 0032 #include <QPluginLoader> 0033 #include <QMetaMethod> 0034 #include <MainDebug.h> 0035 0036 #include <limits.h> // UINT_MAX 0037 0038 0039 namespace CalligraFilter { 0040 0041 Graph::Graph(const QByteArray& from) 0042 : m_from(from) 0043 , m_graphValid(false) 0044 , d(0) 0045 { 0046 buildGraph(); 0047 shortestPaths(); // Will return after a single lookup if "from" is invalid (->no check here) 0048 } 0049 0050 Graph::~Graph() 0051 { 0052 foreach(Vertex* vertex, m_vertices) { 0053 delete vertex; 0054 } 0055 m_vertices.clear(); 0056 } 0057 0058 void Graph::setSourceMimeType(const QByteArray& from) 0059 { 0060 if (from == m_from) 0061 return; 0062 m_from = from; 0063 m_graphValid = false; 0064 0065 // Initialize with "infinity" ... 0066 foreach(Vertex* vertex, m_vertices) { 0067 vertex->reset(); 0068 } 0069 // ...and re-run the shortest path search for the new source mime 0070 shortestPaths(); 0071 } 0072 0073 KoFilterChain::Ptr Graph::chain(const KoFilterManager* manager, QByteArray& to) const 0074 { 0075 if (!isValid() || !manager) 0076 return KoFilterChain::Ptr(); 0077 0078 if (to.isEmpty()) { // if the destination is empty we search the closest Calligra part 0079 to = findCalligraPart(); 0080 if (to.isEmpty()) // still empty? strange stuff... 0081 return KoFilterChain::Ptr(); 0082 } 0083 0084 const Vertex* vertex = m_vertices.value(to); 0085 if (!vertex || vertex->key() == UINT_MAX) 0086 return KoFilterChain::Ptr(); 0087 0088 KoFilterChain::Ptr ret(new KoFilterChain(manager)); 0089 0090 // Fill the filter chain with all filters on the path 0091 const Vertex* tmp = vertex->predecessor(); 0092 while (tmp) { 0093 const Edge* const edge = tmp->findEdge(vertex); 0094 Q_ASSERT(edge); 0095 ret->prependChainLink(edge->filterEntry(), tmp->mimeType(), vertex->mimeType()); 0096 vertex = tmp; 0097 tmp = tmp->predecessor(); 0098 } 0099 return ret; 0100 } 0101 0102 void Graph::dump() const 0103 { 0104 #ifndef NDEBUG 0105 debugFilter << "+++++++++ Graph::dump +++++++++"; 0106 debugFilter << "From:" << m_from; 0107 foreach(Vertex *vertex, m_vertices) { 0108 vertex->dump(" "); 0109 } 0110 debugFilter << "+++++++++ Graph::dump (done) +++++++++"; 0111 #endif 0112 } 0113 0114 // Query the trader and create the vertices and edges representing 0115 // available mime types and filters. 0116 void Graph::buildGraph() 0117 { 0118 // Make sure that all available parts are added to the graph 0119 const QList<KoDocumentEntry> parts(KoDocumentEntry::query()); 0120 0121 foreach(const KoDocumentEntry& part, parts) { 0122 0123 QJsonObject metaData = part.metaData(); 0124 #ifdef CALLIGRA_OLD_PLUGIN_METADATA 0125 QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); 0126 #else 0127 QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); 0128 #endif 0129 nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); 0130 0131 foreach(const QString& nativeMimeType, nativeMimeTypes) { 0132 const QByteArray key = nativeMimeType.toLatin1(); 0133 if (!m_vertices.contains(key)) 0134 m_vertices[key] = new Vertex(key); 0135 } 0136 } 0137 0138 // no constraint here - we want *all* :) 0139 const QList<KoFilterEntry::Ptr> filters(KoFilterEntry::query()); 0140 0141 foreach(KoFilterEntry::Ptr filter, filters) { 0142 0143 // First add the "starting points" to the dict 0144 foreach (const QString& import, filter->import) { 0145 const QByteArray key = import.toLatin1(); // latin1 is okay here (werner) 0146 // already there? 0147 if (!m_vertices.contains(key)) 0148 m_vertices.insert(key, new Vertex(key)); 0149 } 0150 0151 // Are we allowed to use this filter at all? 0152 if (KoFilterManager::filterAvailable(filter)) { 0153 0154 foreach(const QString& exportIt, filter->export_) { 0155 0156 // First make sure the export vertex is in place 0157 const QByteArray key = exportIt.toLatin1(); // latin1 is okay here 0158 Vertex* exp = m_vertices.value(key); 0159 if (!exp) { 0160 exp = new Vertex(key); 0161 m_vertices.insert(key, exp); 0162 } 0163 // Then create the appropriate edges 0164 foreach(const QString& import, filter->import) { 0165 m_vertices[import.toLatin1()]->addEdge(new Edge(exp, filter)); 0166 } 0167 } 0168 } else 0169 debugFilter << "Filter:" << filter->fileName() << " doesn't apply."; 0170 } 0171 } 0172 0173 // As all edges (=filters) are required to have a positive weight 0174 // we can use Dijkstra's shortest path algorithm from Cormen's 0175 // "Introduction to Algorithms" (p. 527) 0176 // Note: I did some adaptions as our data structures are slightly 0177 // different from the ones used in the book. Further we simply stop 0178 // the algorithm is we don't find any node with a weight != Infinity 0179 // (==UINT_MAX), as this means that the remaining nodes in the queue 0180 // aren't connected anyway. 0181 void Graph::shortestPaths() 0182 { 0183 // Is the requested start mime type valid? 0184 Vertex* from = m_vertices.value(m_from); 0185 if (!from) 0186 return; 0187 0188 // Inititalize start vertex 0189 from->setKey(0); 0190 0191 // Fill the priority queue with all the vertices 0192 PriorityQueue<Vertex> queue(m_vertices); 0193 0194 while (!queue.isEmpty()) { 0195 Vertex *min = queue.extractMinimum(); 0196 // Did we already relax all connected vertices? 0197 if (min->key() == UINT_MAX) 0198 break; 0199 min->relaxVertices(queue); 0200 } 0201 m_graphValid = true; 0202 } 0203 0204 QByteArray Graph::findCalligraPart() const 0205 { 0206 // Here we simply try to find the closest Calligra mimetype 0207 const QList<KoDocumentEntry> parts(KoDocumentEntry::query()); 0208 QList<KoDocumentEntry>::ConstIterator partIt(parts.constBegin()); 0209 QList<KoDocumentEntry>::ConstIterator partEnd(parts.constEnd()); 0210 0211 const Vertex *v = 0; 0212 0213 // Be sure that v gets initialized correctly 0214 while (!v && partIt != partEnd) { 0215 QJsonObject metaData = (*partIt).metaData(); 0216 #ifdef CALLIGRA_OLD_PLUGIN_METADATA 0217 QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); 0218 #else 0219 QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); 0220 #endif 0221 nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); 0222 QStringList::ConstIterator it = nativeMimeTypes.constBegin(); 0223 QStringList::ConstIterator end = nativeMimeTypes.constEnd(); 0224 for (; !v && it != end; ++it) 0225 if (!(*it).isEmpty()) 0226 v = m_vertices.value((*it).toLatin1()); 0227 ++partIt; 0228 } 0229 if (!v) 0230 return ""; 0231 0232 // Now we try to find the "cheapest" Calligra vertex 0233 while (partIt != partEnd) { 0234 QJsonObject metaData = (*partIt).metaData(); 0235 #ifdef CALLIGRA_OLD_PLUGIN_METADATA 0236 QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toString().split(','); 0237 #else 0238 QStringList nativeMimeTypes = metaData.value("X-KDE-ExtraNativeMimeTypes").toVariant().toStringList(); 0239 #endif 0240 nativeMimeTypes += metaData.value("X-KDE-NativeMimeType").toString(); 0241 QStringList::ConstIterator it = nativeMimeTypes.constBegin(); 0242 QStringList::ConstIterator end = nativeMimeTypes.constEnd(); 0243 for (; !v && it != end; ++it) { 0244 QString key = *it; 0245 if (!key.isEmpty()) { 0246 Vertex* tmp = m_vertices.value(key.toLatin1()); 0247 if (!v || (tmp && tmp->key() < v->key())) 0248 v = tmp; 0249 } 0250 } 0251 ++partIt; 0252 } 0253 0254 // It seems it already is a Calligra part 0255 if (v->key() == 0) 0256 return ""; 0257 0258 return v->mimeType(); 0259 } 0260 }