• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KDED

kbuildmimetypefactory.cpp

Go to the documentation of this file.
00001 /*  This file is part of the KDE libraries
00002  *  Copyright 1999-2007 David Faure <faure@kde.org>
00003  *
00004  *  This library is free software; you can redistribute it and/or
00005  *  modify it under the terms of the GNU Library General Public
00006  *  License as published by the Free Software Foundation; either
00007  *  version 2 of the License, or (at your option) any later version.
00008  *
00009  *  This library is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00012  *  Library General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU Library General Public License
00015  *  along with this library; see the file COPYING.LIB.  If not, write to
00016  *  the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  */
00019 
00020 #include "kbuildmimetypefactory.h"
00021 #include "ksycoca.h"
00022 #include "kfoldermimetype.h"
00023 #include "ksycocadict.h"
00024 #include "kresourcelist.h"
00025 
00026 #include <kglobal.h>
00027 #include <kstandarddirs.h>
00028 #include <kdebug.h>
00029 #include <klocale.h>
00030 #include <assert.h>
00031 #include <kdesktopfile.h>
00032 #include <QtCore/QHash>
00033 #include <QtCore/QFile>
00034 #include <QtXml/QDomAttr>
00035 
00036 KBuildMimeTypeFactory::KBuildMimeTypeFactory() :
00037     KMimeTypeFactory(), m_parser(this),
00038     m_oldOtherPatternOffset(0)
00039 {
00040     m_resourceList = new KSycocaResourceList;
00041     // We want all xml files under xdgdata-mime - but not packages/*.xml
00042     m_resourceList->add( "xdgdata-mime", "*.xml" );
00043 
00044     m_fastPatternDict = new KSycocaDict();
00045 }
00046 
00047 // return all resource types for this factory
00048 // i.e. first arguments to m_resourceList->add() above
00049 QStringList KBuildMimeTypeFactory::resourceTypes()
00050 {
00051     return QStringList() << "xdgdata-mime";
00052 }
00053 
00054 KBuildMimeTypeFactory::~KBuildMimeTypeFactory()
00055 {
00056     delete m_resourceList;
00057 }
00058 
00059 KMimeType::Ptr KBuildMimeTypeFactory::findMimeTypeByName(const QString &_name, KMimeType::FindByNameOption options)
00060 {
00061     assert (KSycoca::self()->isBuilding());
00062 
00063     QString name = _name;
00064     if (options & KMimeType::ResolveAliases) {
00065         AliasesMap::const_iterator it = aliases().constFind(_name);
00066         if (it != aliases().constEnd())
00067             name = *it;
00068     }
00069 
00070     // We're building a database - the mime type must be in memory
00071     KSycocaEntry::Ptr servType = m_entryDict->value( name );
00072     return KMimeType::Ptr::staticCast( servType );
00073 }
00074 
00075 KSycocaEntry::List KBuildMimeTypeFactory::allEntries() const
00076 {
00077     assert (KSycoca::self()->isBuilding());
00078     KSycocaEntry::List lst;
00079     KSycocaEntryDict::Iterator itmime = m_entryDict->begin();
00080     const KSycocaEntryDict::Iterator endmime = m_entryDict->end();
00081     for( ; itmime != endmime ; ++itmime )
00082         lst.append( *itmime );
00083     return lst;
00084 }
00085 
00086 KSycocaEntry* KBuildMimeTypeFactory::createEntry(const QString &file, const char *resource) const
00087 {
00088     // file=text/plain.xml  ->  name=plain.xml dirName=text
00089     const int pos = file.lastIndexOf('/');
00090     if (pos == -1) // huh?
00091         return 0;
00092     const QString dirName = file.left(pos);
00093     if (dirName == "packages") // special subdir
00094         return 0;
00095 
00096     QString name;
00097     QString userIcon;
00098     QString comment;
00099     QString mainPattern;
00100     QMap<QString, QString> commentsByLanguage;
00101 
00102     const QStringList mimeFiles = KGlobal::dirs()->findAllResources(resource, file);
00103     if (mimeFiles.isEmpty()) {
00104         kWarning() << "No file found for" << file << ", even though the file appeared in a directory listing.";
00105         kWarning() << "Either it was just removed, or the directory doesn't have executable permission...";
00106         return 0;
00107     }
00108     QListIterator<QString> mimeFilesIter(mimeFiles);
00109     mimeFilesIter.toBack();
00110     while (mimeFilesIter.hasPrevious()) { // global first, then local.
00111         const QString fullPath = mimeFilesIter.previous();
00112         QFile qfile(fullPath);
00113         if (!qfile.open(QFile::ReadOnly))
00114             continue;
00115         QDomDocument doc;
00116         if (!doc.setContent(&qfile)) {
00117             kWarning() << "Parse error in " << fullPath;
00118             continue;
00119         }
00120         const QDomElement mimeTypeElement = doc.documentElement();
00121         if (mimeTypeElement.tagName() != "mime-type")
00122             continue;
00123         name = mimeTypeElement.attribute("type");
00124         if (name.isEmpty())
00125             continue;
00126 
00127         for ( QDomElement e = mimeTypeElement.firstChildElement();
00128               !e.isNull();
00129               e = e.nextSiblingElement() ) {
00130             const QString tag = e.tagName();
00131             if (tag == "comment") {
00132                 QString lang = e.attribute("xml:lang");
00133                 if (lang.isEmpty()) {
00134                     comment = e.text();
00135                     lang = "en";
00136                 }
00137                 commentsByLanguage.insert(lang, e.text());
00138             } else if (tag == "icon") { // as written out by shared-mime-info >= 0.40
00139                 userIcon = e.attribute("name");
00140             } else if (tag == "glob-deleteall") { // as written out by shared-mime-info > 0.60
00141                 mainPattern.clear();
00142                 m_parsedMimeTypes[name] = QString();
00143             } else if (tag == "glob" && mainPattern.isEmpty()) { // as written out by shared-mime-info > 0.60
00144                 const QString pattern = e.attribute("pattern");
00145                 if (pattern.startsWith('*')) {
00146                     mainPattern = pattern;
00147                 }
00148             }
00149         }
00150     }
00151     if (name.isEmpty()) {
00152         return 0;
00153     }
00154     Q_FOREACH(const QString& lang, KGlobal::locale()->languageList()) {
00155         const QString comm = commentsByLanguage.value(lang);
00156         if (!comm.isEmpty()) {
00157             comment = comm;
00158             break;
00159         }
00160         const int pos = lang.indexOf('_');
00161         if (pos != -1) {
00162             // "en_US" not found? try just "en"
00163             const QString shortLang = lang.left(pos);
00164             const QString comm = commentsByLanguage.value(shortLang);
00165             if (!comm.isEmpty()) {
00166                 comment = comm;
00167                 break;
00168             }
00169         }
00170     }
00171     if (comment.isEmpty()) {
00172         kWarning() << "Missing <comment> field in" << file;
00173     }
00174 
00175     //kDebug() << "Creating mimetype" << name << "from file" << file << mimeFiles;
00176 
00177     KMimeType* e;
00178     if ( name == "inode/directory" )
00179         e = new KFolderMimeType( file, name, comment );
00180     else
00181         e = new KMimeType( file, name, comment );
00182 
00183     if (e->isDeleted())
00184     {
00185         delete e;
00186         return 0;
00187     }
00188 
00189     if ( !(e->isValid()) )
00190     {
00191         kWarning(7012) << "Invalid MimeType : " << file;
00192         delete e;
00193         return 0;
00194     }
00195 
00196     if (!userIcon.isEmpty()) {
00197         e->setUserSpecifiedIcon(userIcon);
00198     }
00199     // mainPattern could be empty, but by doing this unconditionally
00200     // we also remember that we parsed this mimetype.
00201     m_parsedMimeTypes[name] = mainPattern;
00202 
00203     return e;
00204 }
00205 
00206 void KBuildMimeTypeFactory::saveHeader(QDataStream &str)
00207 {
00208     KSycocaFactory::saveHeader(str);
00209     // This header is read by KMimeTypeFactory's constructor
00210     str << (qint32) m_fastPatternOffset;
00211     str << (qint32) m_oldOtherPatternOffset;
00212     const AliasesMap& aliasMap = aliases();
00213     str << (qint32) aliasMap.count();
00214     for (AliasesMap::const_iterator it = aliasMap.begin(); it != aliasMap.end(); ++it) {
00215         str << it.key() << it.value();
00216     }
00217     str << (qint32) m_highWeightPatternOffset;
00218     str << (qint32) m_lowWeightPatternOffset;
00219     str << (qint32) m_parentsMapOffset;
00220 }
00221 
00222 void KBuildMimeTypeFactory::parseSubclassFile(const QString& fileName)
00223 {
00224     ParentsMap& parentsMap = this->parentsMap();
00225     QFile qfile( fileName );
00226     //kDebug(7021) << "Now parsing" << fileName;
00227     if (qfile.open(QIODevice::ReadOnly)) {
00228         QTextStream stream(&qfile);
00229         stream.setCodec("ISO 8859-1");
00230         while (!stream.atEnd()) {
00231             const QString line = stream.readLine();
00232             if (line.isEmpty() || line[0] == '#')
00233                 continue;
00234             const int pos = line.indexOf(' ');
00235             if (pos == -1) // syntax error
00236                 continue;
00237             const QString derivedTypeName = line.left(pos);
00238             KMimeType::Ptr derivedType = findMimeTypeByName(derivedTypeName, KMimeType::ResolveAliases);
00239             if (!derivedType)
00240                 kWarning(7012) << fileName << " refers to unknown mimetype " << derivedTypeName;
00241             else {
00242                 const QString parentTypeName = line.mid(pos+1);
00243                 Q_ASSERT(!parentTypeName.isEmpty());
00244                 //derivedType->setParentMimeType(parentTypeName);
00245                 parentsMap[derivedTypeName].append(parentTypeName);
00246             }
00247         }
00248     }
00249 }
00250 
00251 void KBuildMimeTypeFactory::parseAliasFile(const QString& fileName)
00252 {
00253     AliasesMap& aliasMap = aliases();
00254     QFile qfile( fileName );
00255     //kDebug(7021) << "Now parsing" << fileName;
00256     if (qfile.open(QIODevice::ReadOnly)) {
00257         QTextStream stream(&qfile);
00258         stream.setCodec("ISO 8859-1");
00259         while (!stream.atEnd()) {
00260             const QString line = stream.readLine();
00261             if (line.isEmpty() || line[0] == '#')
00262                 continue;
00263             const int pos = line.indexOf(' ');
00264             if (pos == -1) // syntax error
00265                 continue;
00266             const QString aliasTypeName = line.left(pos);
00267             const QString parentTypeName = line.mid(pos+1);
00268             Q_ASSERT(!aliasTypeName.isEmpty());
00269             Q_ASSERT(!parentTypeName.isEmpty());
00270             aliasMap.insert(aliasTypeName, parentTypeName);
00271         }
00272     }
00273 }
00274 
00275 // Called by kbuildsycoca since it needs the subclasses and aliases for the trader index
00276 void KBuildMimeTypeFactory::parseSubclasses()
00277 {
00278     // First clear up any old data (loaded by the incremental mode) that we are going to reload anyway
00279     aliases().clear();
00280 
00281 #if 0
00282     KSycocaEntryDict::Iterator itmime = m_entryDict->begin();
00283     const KSycocaEntryDict::Iterator endmime = m_entryDict->end();
00284     for( ; itmime != endmime ; ++itmime ) {
00285         const KSycocaEntry::Ptr& entry = (*itmime);
00286         Q_ASSERT( entry->isType( KST_KMimeType ) );
00287         KMimeType::Ptr mimeType = KMimeType::Ptr::staticCast( entry );
00288         mimeType->internalClearData();
00289     }
00290 #endif
00291 
00292     const QStringList subclassFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", "subclasses");
00293     //kDebug() << subclassFiles;
00294     Q_FOREACH(const QString& file, subclassFiles) {
00295         parseSubclassFile(file);
00296     }
00297 
00298     const QStringList aliasFiles = KGlobal::dirs()->findAllResources("xdgdata-mime", "aliases");
00299     //kDebug() << aliasFiles;
00300     Q_FOREACH(const QString& file, aliasFiles) {
00301         parseAliasFile(file);
00302     }
00303 }
00304 
00305 void KBuildMimeTypeFactory::save(QDataStream &str)
00306 {
00307     m_parser.setParsedPatternMap(m_parsedMimeTypes);
00308     m_parser.parseGlobs();
00309 
00310     KSycocaFactory::save(str);
00311 
00312     savePatternLists(str);
00313 
00314     m_parentsMapOffset = str.device()->pos();
00315     ParentsMap& parentsMap = this->parentsMap();
00316     str << (qint32) parentsMap.count();
00317     for (ParentsMap::const_iterator it = parentsMap.constBegin(); it != parentsMap.constEnd(); ++it) {
00318         str << it.key() << it.value().join("|");
00319     }
00320 
00321     int endOfFactoryData = str.device()->pos();
00322 
00323     // Update header (pass #3)
00324     saveHeader(str);
00325 
00326     // Seek to end.
00327     str.device()->seek(endOfFactoryData);
00328 }
00329 
00330 static bool isFastPattern(const QString& pattern)
00331 {
00332    // starts with "*.", has no other '*' and no other '.'
00333    return pattern.lastIndexOf('*') == 0
00334       && pattern.lastIndexOf('.') == 1
00335       // and contains no other special character
00336       && !pattern.contains('?')
00337       && !pattern.contains('[')
00338       ;
00339 }
00340 
00341 
00342 
00343 void KBuildMimeTypeFactory::savePatternLists(QDataStream &str)
00344 {
00345     // Store each patterns into either m_fastPatternDict (*.txt, *.html etc. with default weight 50)
00346     // or for the rest, like core.*, *.tar.bz2, *~, into highWeightPatternOffset (>50)
00347     // or lowWeightPatternOffset (<=50)
00348 
00349     OtherPatternList highWeightPatternOffset, lowWeightPatternOffset;
00350 
00351     // For each entry in the globs list
00352     const KMimeFileParser::AllGlobs& allGlobs = m_parser.mimeTypeGlobs();
00353     Q_FOREACH(const QString& mimeTypeName, m_parser.allMimeTypes()) {
00354         const KMimeType::Ptr mimeType = findMimeTypeByName(mimeTypeName, KMimeType::DontResolveAlias);
00355         if (!mimeType) {
00356             kDebug() << "globs file refers to unknown mimetype" << mimeTypeName;
00357             continue;
00358         }
00359         const KMimeFileParser::GlobList globs = allGlobs.value(mimeTypeName);
00360         Q_FOREACH(const KMimeFileParser::Glob& glob, globs) {
00361             const QString &pattern = glob.pattern;
00362             Q_ASSERT(!pattern.isEmpty());
00363             if (glob.weight == 50 && isFastPattern(pattern)) {
00364                 // The bulk of the patterns is *.foo with weight 50 --> those go into the fast
00365                 // pattern dict.
00366                 m_fastPatternDict->add(pattern.mid(2) /* extension only*/, KSycocaEntry::Ptr::staticCast(mimeType));
00367             } else if (glob.weight > 50) {
00368                 highWeightPatternOffset.append(OtherPattern(pattern, mimeType->offset(), glob.weight));
00369             } else {
00370                 lowWeightPatternOffset.append(OtherPattern(pattern, mimeType->offset(), glob.weight));
00371             }
00372         }
00373     }
00374 
00375     m_fastPatternOffset = str.device()->pos();
00376     m_fastPatternDict->save(str);
00377 
00378     // The high and low weight pattern lists are already sorted by decreasing
00379     // weight, this is done by update-mime-database.
00380 
00381     m_highWeightPatternOffset = str.device()->pos();
00382     Q_FOREACH(const OtherPattern& op, highWeightPatternOffset) {
00383         str << op.pattern;
00384         str << (qint32)op.offset;
00385         str << (qint32)op.weight;
00386     }
00387     str << QString(""); // end of list marker (has to be a string !)
00388 
00389     m_lowWeightPatternOffset = str.device()->pos();
00390     Q_FOREACH(const OtherPattern& op, lowWeightPatternOffset) {
00391         str << op.pattern;
00392         str << (qint32)op.offset;
00393         str << (qint32)op.weight;
00394     }
00395     str << QString(""); // end of list marker (has to be a string !)
00396 
00397     // For compat with kde-4.1 kdecore: write the old "other patterns" thing, but empty
00398     m_oldOtherPatternOffset = str.device()->pos();
00399     str << QString(""); // end of list marker (has to be a string !)
00400 }

KDED

Skip menu "KDED"
  • Main Page
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal