kdelibs/kdecore/sycoca/ksycocafactory.cpp
2014-11-13 01:04:59 +02:00

248 lines
6.7 KiB
C++

/* This file is part of the KDE libraries
* Copyright (C) 1999 David Faure <faure@kde.org>
*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public
* License version 2 as published by the Free Software Foundation;
*
* This library is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* Library General Public License for more details.
*
* You should have received a copy of the GNU Library General Public License
* along with this library; see the file COPYING.LIB. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301, USA.
*/
#include "ksycocafactory.h"
#include "ksycoca.h"
#include "ksycocatype.h"
#include "ksycocaentry.h"
#include "ksycocadict_p.h"
#include <config.h>
#include <kdebug.h>
#include <QThread>
#include <QtCore/QHash>
class KSycocaFactory::Private
{
public:
Private() : mOffset(0),
m_sycocaDictOffset(0),
m_beginEntryOffset(0),
m_endEntryOffset(0) {}
~Private()
{
delete m_sycocaDict;
}
int mOffset;
int m_sycocaDictOffset;
int m_beginEntryOffset;
int m_endEntryOffset;
KSycocaDict *m_sycocaDict;
};
KSycocaFactory::KSycocaFactory(KSycocaFactoryId factory_id)
: m_resourceList(0), m_entryDict(0), m_str(0), d(new Private)
{
if (!KSycoca::self()->isBuilding() && (m_str = KSycoca::self()->findFactory(factory_id))) {
// Read position of index tables....
qint32 i;
(*m_str) >> i;
d->m_sycocaDictOffset = i;
(*m_str) >> i;
d->m_beginEntryOffset = i;
(*m_str) >> i;
d->m_endEntryOffset = i;
QDataStream* str = stream();
int saveOffset = str->device()->pos();
// Init index tables
d->m_sycocaDict = new KSycocaDict(str, d->m_sycocaDictOffset);
saveOffset = str->device()->seek(saveOffset);
} else {
// We are in kbuildsycoca4 -- build new database!
m_entryDict = new KSycocaEntryDict;
d->m_sycocaDict = new KSycocaDict;
d->m_beginEntryOffset = 0;
d->m_endEntryOffset = 0;
// m_resourceList will be filled in by inherited constructors
}
KSycoca::self()->addFactory(this);
}
KSycocaFactory::~KSycocaFactory()
{
delete m_entryDict;
delete d;
}
void
KSycocaFactory::saveHeader(QDataStream &str)
{
// Write header
str.device()->seek(d->mOffset);
str << (qint32) d->m_sycocaDictOffset;
str << (qint32) d->m_beginEntryOffset;
str << (qint32) d->m_endEntryOffset;
}
void
KSycocaFactory::save(QDataStream &str)
{
if (!m_entryDict) return; // Error! Function should only be called when
// building database
if (!d->m_sycocaDict) return; // Error!
d->mOffset = str.device()->pos(); // store position in member variable
d->m_sycocaDictOffset = 0;
// Write header (pass #1)
saveHeader(str);
d->m_beginEntryOffset = str.device()->pos();
// Write all entries.
int entryCount = 0;
for(KSycocaEntryDict::Iterator it = m_entryDict->begin();
it != m_entryDict->end(); ++it)
{
KSycocaEntry::Ptr entry = *it;
entry->save(str);
entryCount++;
}
d->m_endEntryOffset = str.device()->pos();
// Write indices...
// Linear index
str << (qint32) entryCount;
for(KSycocaEntryDict::Iterator it = m_entryDict->begin();
it != m_entryDict->end(); ++it)
{
str << qint32(it->data()->offset());
}
// Dictionary index
d->m_sycocaDictOffset = str.device()->pos();
d->m_sycocaDict->save(str);
int endOfFactoryData = str.device()->pos();
// Update header (pass #2)
saveHeader(str);
// Seek to end.
str.device()->seek(endOfFactoryData);
}
void
KSycocaFactory::addEntry(const KSycocaEntry::Ptr& newEntry)
{
if (!m_entryDict) return; // Error! Function should only be called when
// building database
if (!d->m_sycocaDict) return; // Error!
KSycocaEntry::Ptr oldEntry = m_entryDict->value(newEntry->storageId());
if (oldEntry) {
// Already exists -> replace
// We found a more-local override, e.g. ~/.local/share/applications/kde4/foo.desktop
// So forget about the more global file.
//
// This can also happen with two .protocol files using the same protocol= entry.
// If we didn't remove one here, we would end up asserting because save()
// wasn't called on one of the entries.
//kDebug(7021) << "removing" << oldEntry.data() << oldEntry->entryPath() << "because of" << newEntry->entryPath() << "they have the same storageId" << newEntry->storageId();
removeEntry(newEntry->storageId());
}
const QString name = newEntry->storageId();
m_entryDict->insert( name, newEntry );
d->m_sycocaDict->add( name, newEntry );
}
void
KSycocaFactory::removeEntry(const QString& entryName)
{
if (!m_entryDict) return; // Error! Function should only be called when
// building database
if (!d->m_sycocaDict) return; // Error!
m_entryDict->remove( entryName );
d->m_sycocaDict->remove( entryName ); // O(N)
}
KSycocaEntry::List KSycocaFactory::allEntries() const
{
KSycocaEntry::List list;
// Assume we're NOT building a database
QDataStream* str = stream();
if (!str) return list;
str->device()->seek(d->m_endEntryOffset);
qint32 entryCount;
(*str) >> entryCount;
if (entryCount > 8192)
{
kDebug(7021) << QThread::currentThread() << "error detected in factory" << this;
KSycoca::flagError();
return list;
}
// offsetList is needed because createEntry() modifies the stream position
qint32 *offsetList = new qint32[entryCount];
for(int i = 0; i < entryCount; i++)
{
(*str) >> offsetList[i];
}
for(int i = 0; i < entryCount; i++)
{
KSycocaEntry *newEntry = createEntry(offsetList[i]);
if (newEntry)
{
list.append( KSycocaEntry::Ptr( newEntry ) );
}
}
delete [] offsetList;
return list;
}
int KSycocaFactory::offset() const
{
return d->mOffset;
}
const KSycocaResourceList * KSycocaFactory::resourceList() const
{
return m_resourceList;
}
const KSycocaDict * KSycocaFactory::sycocaDict() const
{
return d->m_sycocaDict;
}
bool KSycocaFactory::isEmpty() const
{
return d->m_beginEntryOffset == d->m_endEntryOffset;
}
QDataStream* KSycocaFactory::stream() const
{
return m_str;
}
void KSycocaFactory::virtual_hook( int /*id*/, void* /*data*/)
{ /*BASE::virtual_hook( id, data );*/ }