Mercurial > dedupe
changeset 0:a3834af36579
Working with memory backend.
| author | Tom Fredrik Blenning Klaussen <bfg@blenning.no> |
|---|---|
| date | Mon, 20 Aug 2012 15:49:48 +0200 |
| parents | |
| children | aae83c0a771d |
| files | CMakeLists.txt DataController.cpp DataController.hpp EditDistance.cpp EditDistance.hpp Exception.hpp FileDbLink.cpp FileDbLink.hpp IOException.cpp IOException.hpp MemoryDbLink.cpp MemoryDbLink.hpp OrderedPair.hpp PermissionException.hpp PrecompiledHeader.cmake ProtectedContainer.hpp ProtectedDictionary.hpp TestEditDistance.cpp TestFramework.hpp ThreadSafeLookup.hpp main.cpp |
| diffstat | 21 files changed, 1165 insertions(+), 0 deletions(-) [+] |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/CMakeLists.txt Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,47 @@ +PROJECT(DeDupe) +CMAKE_MINIMUM_REQUIRED(VERSION 2.6.4) + +INCLUDE(PrecompiledHeader.cmake) + +FIND_PACKAGE(Boost) +IF (Boost_FOUND) + INCLUDE_DIRECTORIES(${Boost_INCLUDE_DIR}) + ADD_DEFINITIONS( "-DHAS_BOOST" ) +ENDIF() + + +FIND_PACKAGE(Qt4 COMPONENTS QtOpenGL QtXml REQUIRED) + +INCLUDE(${QT_USE_FILE}) +ADD_DEFINITIONS(${QT_DEFINITIONS}) + + +SET(SOURCES + DataController.cpp + EditDistance.cpp + IOException.cpp + FileDbLink.cpp + MemoryDbLink.cpp + main.cpp +) + +SET(MOC_HEADERS + DataController.hpp +) + +# Returns the moc_xxx.cpp files in the foo_MOC_SRCS variable +QT4_WRAP_CPP(MOC_SOURCES ${MOC_HEADERS}) + +MESSAGE(WARNING ${MOC_SOURCES}) + +SET(CMAKE_CXX_FLAGS "-g2 -Wall -Werror -fno-inline") +ADD_EXECUTABLE(DeDupe ${SOURCES} ${MOC_SOURCES}) +TARGET_LINK_LIBRARIES(DeDupe ${QT_LIBRARIES}) + +ENABLE_TESTING() + +ADD_EXECUTABLE(TestEditDistance TestEditDistance.cpp EditDistance.cpp) +ADD_TEST(TestEditDistance TestEditDistance) + +TARGET_LINK_LIBRARIES(TestEditDistance ${QT_LIBRARIES}) +#ADD_PRECOMPILED_HEADER(TestEditDistance TestFramework.hpp)
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataController.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,326 @@ +#include "DataController.hpp" + +#include "MemoryDbLink.hpp" + +#include "PermissionException.hpp" +#include "DataController.hpp" + +#include <QtGui/QApplication> +#include <QtCore/QDir> + +#include <QtCore/QDebug> +#include <QtCore/QTimer> +#include <QtCore/QCryptographicHash> +#include <QtCore/QDateTime> + +#include <QtGui/QMainWindow> +#include <QtGui/QTreeWidget> +#include <QtGui/QHeaderView> +#include <QtGui/QMenuBar> +#include <QtGui/QToolBar> +#include <QtGui/QDoubleSpinBox> +#include <QtGui/QLabel> +#include <QtGui/QHBoxLayout> +#include <QtGui/QProgressBar> + +#include "EditDistance.hpp" + +#include <cassert> +void findFiles(const QDir& dir, FileDbLink& dblink) +{ + /* + QProgressDialog progressDialog(this); + progressDialog.setCancelButtonText(tr("&Cancel")); + progressDialog.setRange(0, files.size()); + progressDialog.setWindowTitle(tr("Find Files")); + */ + + foreach(QString filename, dir.entryList(QDir::NoDotAndDotDot | QDir::Dirs)) { + filename = dir.absoluteFilePath(filename); + findFiles(QDir(filename), dblink); + } + + foreach(QString filename, dir.entryList(QDir::Files)) { + filename = dir.absoluteFilePath(filename); + try { + dblink.updateIfModified(filename); + } + catch (const PermissionException& e) { + qDebug()<<e.toString(); + } + catch (Exception& e) { + qDebug()<<e.toString(); + exit(1); + } + } +} + +QTreeWidgetItem* DataController::createItem(const FileDbLink::DBInfo& info) +{ + QTreeWidgetItem* item = new QTreeWidgetItem(); + item->setData(0, Qt::DisplayRole, info.name()); + item->setData(0, 32, info.path()); + item->setData(0, 33, info.name()); + + item->setData(1, Qt::DisplayRole, info.size()); + item->setData(2, Qt::DisplayRole, info.mtime()); + item->setData(3, Qt::DisplayRole, info.checksum().toHex()); + return item; +} + +void DataController::delayPopulate() +{ + populateDelay->start(); +} + + +void DataController::populate() +{ + populate(nameFilter->isChecked(), sizeFilter->isChecked(), mtimeFilter->isChecked(), + checksumFilter->isChecked(), (100 - editCutoffSpin->value()) * .01); +} + +void DataController::populate(bool showNameDups, bool showSizeDups, + bool showMTimeDups, bool showCheckSumDups, + float editDistanceCutoff) +{ + tw->clear(); + + const QList<QSharedPointer<FileDbLink::DBInfo> > elems = dblink->sortOn(FileDbLink::EDIT, true); + + QProgressBar bar; + + bar.resize(200,25); + bar.setValue(0); + bar.setMinimum(0); + bar.setMaximum(elems.size()); + bar.show(); + + connect(this, SIGNAL(populateProgress(int)), &bar, SLOT(setValue(int))); + + int n = 0; + + tw->setUpdatesEnabled(false); + + foreach(QSharedPointer<FileDbLink::DBInfo> line, elems) { + QTreeWidgetItem* item = 0; + bool anyAdded = false; + + if (showNameDups) { + QTreeWidgetItem* topLevelItem = 0; + foreach(QSharedPointer<FileDbLink::DBInfo> dup, elems) { + if(dup != line && line->name() == dup->name() ) { + if (!topLevelItem) { + topLevelItem = new QTreeWidgetItem(); + topLevelItem->setData(0, Qt::DisplayRole, "Name"); + if(!item) + item = createItem(*line); + item->addChild(topLevelItem); + anyAdded = true; + } + topLevelItem->addChild(createItem(*dup)); + } + } + } + + if (showSizeDups) { + QTreeWidgetItem* topLevelItem = 0; + foreach(QSharedPointer<FileDbLink::DBInfo> dup, elems) { + if(dup != line && line->size() == dup->size() ) { + if (!topLevelItem) { + topLevelItem = new QTreeWidgetItem(); + topLevelItem->setData(0, Qt::DisplayRole, "Size"); + if(!item) + item = createItem(*line); + item->addChild(topLevelItem); + anyAdded = true; + } + topLevelItem->addChild(createItem(*dup)); + } + } + } + + if (showMTimeDups) { + QTreeWidgetItem* topLevelItem = 0; + foreach(QSharedPointer<FileDbLink::DBInfo> dup, elems) { + if(dup != line && line->mtime() == dup->mtime() ) { + if (!topLevelItem) { + topLevelItem = new QTreeWidgetItem(); + topLevelItem->setData(0, Qt::DisplayRole, "MTime"); + if(!item) + item = createItem(*line); + item->addChild(topLevelItem); + anyAdded = true; + } + topLevelItem->addChild(createItem(*dup)); + } + } + } + + if (showCheckSumDups) { + QTreeWidgetItem* topLevelItem = 0; + foreach(QSharedPointer<FileDbLink::DBInfo> dup, elems) { + if(dup != line && line->checksum() == dup->checksum() ) { + if (!topLevelItem) { + topLevelItem = new QTreeWidgetItem(); + topLevelItem->setData(0, Qt::DisplayRole, "Checksum"); + if(!item) + item = createItem(*line); + item->addChild(topLevelItem); + anyAdded = true; + } + topLevelItem->addChild(createItem(*dup)); + } + } + } + + if (editDistanceCutoff < 1.0) { + QTreeWidgetItem* topLevelItem = 0; + QMultiMap<int, QSharedPointer<FileDbLink::DBInfo> > oList; + + int absoluteCutoff = line->name().length() * editDistanceCutoff; + foreach(QSharedPointer<FileDbLink::DBInfo> dup, elems) { + if(dup != line) { + int distance = EditDistance::Compute(line->name(), dup->name()); + + if (distance <= absoluteCutoff) { + oList.insert(distance, dup); + } + } + } + + if (oList.size() > 0 ) { + topLevelItem = new QTreeWidgetItem(); + topLevelItem->setData(0, Qt::DisplayRole, "Editdistance"); + if(!item) + item = createItem(*line); + item->addChild(topLevelItem); + anyAdded = true; + } + + foreach(QSharedPointer<FileDbLink::DBInfo> dup, oList.values()) { + topLevelItem->addChild(createItem(*dup)); + } + } + + + + if (item) + tw->addTopLevelItem(item); + emit populateProgress(++n); + if (n % 64 == 0) { + QCoreApplication::processEvents(); + } + } + tw->setUpdatesEnabled(true); + +} + + +DataController::DataController() : showFullPath(false) +{ + populateDelay = new QTimer(this); + populateDelay->setSingleShot(true); + populateDelay->setInterval(500); + connect(populateDelay, SIGNAL(timeout()), this, SLOT(populate())); + + dblink = new MemoryDbLink(); + + findFiles(QDir("."), *dblink); + + mw = new QMainWindow(); + QMenuBar* mb = new QMenuBar(); + + QMenu* menu = mb->addMenu("&View"); + QAction* action = menu->addAction("Show full path"); + action->setCheckable(true); + connect(action, SIGNAL(toggled(bool)), this, SLOT(setShowFullPath(bool))); + + mw->setMenuBar(mb); + + QToolBar* filterBar = new QToolBar("Filters"); + + nameFilter = filterBar->addAction("Name"); + nameFilter->setCheckable(true); + connect(nameFilter, SIGNAL(toggled(bool)), this, SLOT(delayPopulate())); + + sizeFilter = filterBar->addAction("Size"); + sizeFilter->setCheckable(true); + connect(sizeFilter, SIGNAL(toggled(bool)), this, SLOT(delayPopulate())); + + mtimeFilter = filterBar->addAction("MTime"); + mtimeFilter->setCheckable(true); + connect(mtimeFilter, SIGNAL(toggled(bool)), this, SLOT(delayPopulate())); + + checksumFilter = filterBar->addAction("Checksum"); + checksumFilter->setCheckable(true); + connect(checksumFilter, SIGNAL(toggled(bool)), this, SLOT(delayPopulate())); + + QWidget* widget = new QWidget(); + QLayout* layout = new QHBoxLayout(); + layout->setContentsMargins(0,0,0,0); + widget->setLayout(layout); + layout->addWidget(new QLabel("Edit distance", widget)); + editCutoffSpin = new QSpinBox(widget); + layout->addWidget(editCutoffSpin); + editCutoffSpin->setMinimum(0); + editCutoffSpin->setMaximum(100); + editCutoffSpin->setValue(70); + editCutoffSpin->setSingleStep(10); + editCutoffSpin->setSuffix("%"); + connect(editCutoffSpin, SIGNAL(valueChanged(int)), this, SLOT(delayPopulate())); + filterBar->addWidget(widget); + + mw->addToolBar(filterBar); + + tw = new QTreeWidget(mw); + mw->setCentralWidget(tw); + tw->setEditTriggers(QAbstractItemView::NoEditTriggers); + + tw->setHeaderLabels(QString("Path;Size;Date;Checksum").split(";")); + + populate(); + + tw->setSortingEnabled(true); + tw->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + tw->setSelectionBehavior(QAbstractItemView::SelectRows); + //tw->resizeColumnsToContents(); + mw->resize(800,800); + mw->show(); + + +} + +DataController::~DataController() +{ +} + + +void DataController::cellDoubleClicked(int row, int column) +{ + if(column == 0) { + toggleShowFullPath(); + } +} + + +void DataController::setShowFullPath(bool showFullPath) +{ + this->showFullPath = showFullPath; + + tw->setSortingEnabled(false); + + int role = (showFullPath)?32:33; + for (int row = 0; row < tw->topLevelItemCount(); ++row) { + QTreeWidgetItem* pathItem = tw->topLevelItem(row); + pathItem->setData(0, Qt::DisplayRole, pathItem->data(0,role)); + } + tw->setSortingEnabled(true); +} + +bool DataController::toggleShowFullPath() +{ + bool showFullPath = ! this->showFullPath; + setShowFullPath(showFullPath); + return showFullPath; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/DataController.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,57 @@ +#ifndef DATACONTROLLER_HPP +#define DATACONTROLLER_HPP + +#include <QtCore/QObject> + +#include "FileDbLink.hpp" + +class QMainWindow; +class QTreeWidget; +class QTreeWidgetItem; +class QAction; +class QSpinBox; +class QTimer; + +class DataController : QObject { +private: + Q_OBJECT + + public: + DataController(); + ~DataController(); + +public slots: + //void cellClicked(int row, int column); + void cellDoubleClicked(int row, int column); + bool toggleShowFullPath(); + void setShowFullPath(bool); + void populate(); + void delayPopulate(); + + +signals: + void populateProgress(int); + +private: + void populate(bool showNameDups, bool showSizeDups, + bool showMTimeDups, bool showCheckSumDups, + float editDistanceCutoff); + + QTreeWidgetItem* createItem(const FileDbLink::DBInfo& info); + + bool showFullPath; + QMainWindow* mw; + QTreeWidget* tw; + + FileDbLink* dblink; + + QAction* nameFilter; + QAction* sizeFilter; + QAction* mtimeFilter; + QAction* checksumFilter; + QSpinBox* editCutoffSpin; + + QTimer* populateDelay; +}; + +#endif //DATACONTROLLER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EditDistance.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,80 @@ +#include "EditDistance.hpp" + +#include <QtCore/QList> + +#define CharComparer(A, B) (QChar(A) == QChar(B)) + +EditDistance::cacheType EditDistance::cache; + +QString EditDistance::removeDiacritics(QString in) +{ + QString out; + foreach(QChar c, in) { + if (c.decompositionTag() == QChar::NoDecomposition) { + out.append(c); + } + else { + QString tmp = c.decomposition(); + out.append(tmp[0]); + } + } + return out; +} + +int EditDistance::Compute(QString a, QString b, bool remove) { + if (remove) { + a=removeDiacritics(a); + b=removeDiacritics(b); + } + + OrderedPair<QString> lup(a, b); + + boost::optional<int> res = cache.value(lup); + if (res) + return *res; + + + // Allocate distance matrix + QList<QList<int> > d; + QList<int> temp; + for (int i=0;i<b.size()+1;i++) { + temp.append(0); + } + for (int i=0;i<a.size()+1;i++) { + d.append(temp); + } +#if 0 + // Get character comparer + CharComparer isEqual = (ignoreCase) ? + (CharComparer)CharCompareIgnoreCase : CharCompare; +#endif + // Compute distance + for (int i = 0; i <= a.size(); i++) + d[i][0] = i; + for (int j = 0; j <= b.size(); j++) + d[0][j] = j; + for (int i = 1; i <= a.size(); i++) + { + for (int j = 1; j <= b.size(); j++) + { + if (CharComparer(a.at(i-1), b.at(j - 1))) + { + // No change required + d[i][j] = d[i - 1][j - 1]; + } + else + { + d[i][ j] = + std::min(d[i - 1][ j] + 1, // Deletion + std::min(d[i][ j - 1] + 1, // Insertion + d[i - 1][ j - 1] + 1)); // Substitution + } + } + } + + // Return final value + int retVal = d[a.size()][ b.size()]; + cache.insert(lup, retVal); + + return retVal; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/EditDistance.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,24 @@ +#ifndef EDITDISTANCE_HPP +#define EDITDISTANCE_HPP + +#include "OrderedPair.hpp" + +#include <QtCore/QString> +#include <QtCore/QMap> +#include <QtCore/QHash> + +#include "ThreadSafeLookup.hpp" + +class EditDistance { +protected: + typedef ThreadSafeLookup<OrderedPair<QString>, int> cacheType; + //typedef QMap<OrderedPair<QString>, int> cacheType; + //typedef QHash<OrderedPair<QString>, int> cacheType; +public: + static int Compute(QString a, QString b, bool removeDiacritics = false); + static QString removeDiacritics(QString in); + + static cacheType cache; +}; + +#endif //EDITDISTANCE_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Exception.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,26 @@ +#ifndef EXCEPTION_HPP +#define EXCEPTION_HPP + +#include <QtCore/QString> + +class Exception { + +public: + virtual QString toString() const + { + return errorMsg_; + } + + +protected: + Exception(const QString& errorMsg) : errorMsg_(errorMsg) {} + virtual ~Exception() {} + + void setErrorMsg(QString& errorMsg); + const QString& errorMsg(QString& errorMsg); + +private: + QString errorMsg_; +}; + +#endif //EXCEPTION_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FileDbLink.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,140 @@ +#include "FileDbLink.hpp" + +#include <QtCore/QDebug> +#include <QtCore/QtConcurrentMap> + +#include "PermissionException.hpp" +#include "EditDistance.hpp" + +#include <cassert> + +#include <functional> +#include <algorithm> +#include <iostream> + +#include <boost/bind.hpp> + +void FileDbLink::updateIfModified(const QString& path) +{ + QFileInfo fileinfo(path); + if (!exists(path)) { + addFile(fileinfo); + } +} + +void FileDbLink::addFile(const QFileInfo& fileinfo) +{ + addFile(fileinfo.absoluteFilePath(), fileinfo.size(), fileinfo.lastModified()); +} + +void FileDbLink::addFile(const QString& path, qint64 size, const QDateTime& lastModified) +{ + QCryptographicHash hash( QCryptographicHash::Sha1 ); + QFile file(path); + if ( file.open( QIODevice::ReadOnly ) ) { + hash.addData( file.readAll() ); + } + else { + QString errorMsg = path + ": " + file.errorString(); + qDebug()<<file.error(); + switch (file.error()) { + case QFile::PermissionsError: + throw PermissionException(errorMsg); + default: + throw IOException(errorMsg); + } + } + + addFile(path, size, lastModified, hash); + +} + +const QList<QSharedPointer<FileDbLink::DBInfo> > FileDbLink::sortOn(SORTORDER order, bool extended) +{ + QList<QSharedPointer<DBInfo> > list = (extended) ? computedValues() : computedValues(); + + switch (order) { + case PATH: + { + QList<QSharedPointer<FileDbLink::DBInfo> > oList; + foreach(QSharedPointer<DBInfo> info, list) { + oList.push_back(info); + } + return oList; + } + case SIZE: + { + QMultiMap<quint64, QSharedPointer<DBInfo> > oList; + foreach(QSharedPointer<DBInfo> info, list) { + oList.insert(info->size(), info); + } + return oList.values(); + } + case MTIME: + { + QMultiMap<QDateTime, QSharedPointer<DBInfo> > oList; + foreach(QSharedPointer<DBInfo> info, list) { + oList.insert(info->mtime(), info); + } + return oList.values(); + } + case CHECKSUM: + { + QMultiMap<QByteArray, QSharedPointer<DBInfo> > oList; + foreach(QSharedPointer<DBInfo> info, list) { + oList.insert(info->checksum(), info); + } + return oList.values(); + } + case EDIT: + { + assert(extended); + QMultiMap<int, QSharedPointer<DBInfo> > oList; + foreach(QSharedPointer<DBInfo> info, list) { + QSharedPointer<ExtendedDBInfo> ptr; + ptr = info.dynamicCast<ExtendedDBInfo>(); + oList.insert(ptr->editDistance(), info); + } + return oList.values(); + + } + } + abort(); +} + +QSharedPointer<FileDbLink::DBInfo> FileDbLink::computedValue(const QSharedPointer<DBInfo>& info, + const QList<QSharedPointer<DBInfo> >& entries) +{ + QString p1 = info->name(); + int minDist = 100000; + QString other; + for (QList<QSharedPointer<DBInfo> >::const_iterator it2 = entries.begin(); + it2 != entries.end(); ++it2) { + if (info == *it2) + continue; + QString p2 = (*it2)->name(); + int dist = EditDistance::Compute(p1, p2, false); + if (dist < minDist) { + minDist = dist; + other = (*it2)->path(); + } + } + return QSharedPointer<DBInfo>(new ExtendedDBInfo(*info, other, minDist)); +} + +const QList<QSharedPointer<FileDbLink::DBInfo> > FileDbLink::computedValues() const +{ + QList<QSharedPointer<DBInfo> > list; + QList<QSharedPointer<DBInfo> > entries = values(); + +#if 1 + list = QtConcurrent::blockingMapped(entries, boost::bind( &FileDbLink::computedValue, _1, entries)); +#else + for (QList<QSharedPointer<DBInfo> >::const_iterator it1 = entries.begin(); + it1 != entries.end(); ++it1) { + QSharedPointer<DBInfo> ext = computedValue(*it1, entries); + list.push_back(ext); + } +#endif + return list; +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/FileDbLink.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,109 @@ +#ifndef FILEDBLINK +#define FILEDBLINK + +#include <QtCore/QDateTime> +#include <QtCore/QCryptographicHash> +#include <QtCore/QFileInfo> + +class FileDbLink { +public: + class DBInfo { + public: + DBInfo(const QString& path, qint64 size, const QDateTime& mtime, const QByteArray& hash) : path_(path), + size_(size), + mtime_(mtime), + hash_(hash) + {} + DBInfo() {} + + virtual ~DBInfo() {} + + const QString& path() const + { + return path_; + } + + QString name() const + { + QFileInfo finf(path()); + return finf.fileName(); + } + + quint64 size() const + { + return size_; + } + + const QDateTime& mtime() const + { + return mtime_; + } + + const QByteArray checksum() const + { + return hash_; + } + + virtual QString serialize() const + { + QString size = QString::number(size_); + QString str = path_; + str += ", " + size; + str += ", " + mtime_.toString(); + str += ", " + hash_.toHex(); + return str; + } + + private: + QString path_; + qint64 size_; + QDateTime mtime_; + QByteArray hash_; + }; + + class ExtendedDBInfo : public DBInfo { + public: + ExtendedDBInfo(const DBInfo& dbinfo, const QString& closestEditPath, int editDistance) : DBInfo(dbinfo), closestEditPath_(closestEditPath), editDistance_(editDistance) {} + ExtendedDBInfo() {} + + virtual ~ExtendedDBInfo() {} + + virtual QString serialize() const + { + QString dist = QString::number(editDistance_); + QString str = DBInfo::serialize(); + str += ", " + closestEditPath_; + str += ", " + dist; + return str; + } + + int editDistance() const + { + return editDistance_; + } + + private: + QString closestEditPath_; + int editDistance_; + }; + + static QSharedPointer<DBInfo> computedValue(const QSharedPointer<DBInfo>& info, const QList<QSharedPointer<DBInfo> >&); + + +public: + virtual ~FileDbLink() {} + + void updateIfModified(const QString& path); + virtual void addFile(const QString& path, qint64 size, const QDateTime& dtime, const QCryptographicHash& hash) = 0; + void addFile(const QString& path, qint64 size, const QDateTime& dtime); + void addFile(const QFileInfo& fileinfo); + virtual bool exists(const QString& path) = 0; + virtual const QList<QSharedPointer<DBInfo> > values() const = 0; + virtual const QList<QSharedPointer<DBInfo> > computedValues() const; + + enum SORTORDER { PATH, SIZE, MTIME, CHECKSUM, EDIT }; + + virtual const QList<QSharedPointer<DBInfo> > sortOn(SORTORDER order, bool extended = false); +}; + +#endif //FILEDBLINK
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IOException.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,5 @@ +#include "IOException.hpp" + +IOException::IOException(const QString& errorMsg) : Exception(errorMsg) +{ +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/IOException.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,11 @@ +#ifndef IOEXCEPTION_HPP +#define IOEXCEPTION_HPP + +#include "Exception.hpp" + +class IOException : public Exception { +public: + IOException(const QString& errMsg); +}; + +#endif //IOEXCEPTION_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MemoryDbLink.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,41 @@ +#include "MemoryDbLink.hpp" + +#include <QtCore/QStringList> + +void MemoryDbLink::addFile(const QString& path, qint64 size, const QDateTime& dtime, const QCryptographicHash& hash) +{ + addFile(DBInfo(path, size, dtime, hash.result())); +} + +bool MemoryDbLink::tryAddFile(const DBInfo& dbinfo) +{ + QMap<QString, QSharedPointer<DBInfo> >::iterator pos; + pos = entries.find(dbinfo.path()); + if (pos == entries.end()) { + entries.insert(dbinfo.path(), QSharedPointer<DBInfo>(new DBInfo(dbinfo))); + return true; + } + return false; +} + +void MemoryDbLink::addFile(const DBInfo& dbinfo) +{ + if (!tryAddFile(dbinfo)) { + abort(); //Should throw exception + } +} + + +QStringList MemoryDbLink::toStringList() +{ + QStringList list; + foreach(QSharedPointer<DBInfo> info, entries) { + list << info->serialize(); + } + return list; +} + +const QList<QSharedPointer<FileDbLink::DBInfo> > MemoryDbLink::values() const +{ + return entries.values(); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/MemoryDbLink.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,25 @@ +#ifndef MEMORYDBLINK_HPP +#define MEMORYDBLINK_HPP +#include "FileDbLink.hpp" + +#include <QtCore/QMap> +#include <QtCore/QSharedPointer> + +class MemoryDbLink : public FileDbLink { +public: + virtual void addFile(const QString& path, qint64 size, const QDateTime& dtime, const QCryptographicHash& hash); + bool exists(const QString& path) + { + return (entries.contains(path)); + } + + QStringList toStringList(); + const QList<QSharedPointer<DBInfo> > values() const; + +private: + void addFile(const DBInfo& info); + bool tryAddFile(const DBInfo& info); + QMap<QString, QSharedPointer<DBInfo> > entries; +}; + +#endif //MEMORYDBLINK_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/OrderedPair.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,15 @@ +#ifndef ORDEREDPAIR_HPP +#define ORDEREDPAIR_HPP + +#include <QtCore/QPair> + +template <typename S> +class OrderedPair : public QPair<S, S> +{ +public: + OrderedPair(const S& a, const S&b) : QPair<S, S>(a<b?a:b, a<b?b:a) + { + } +}; + +#endif //ORDEREDPAIR_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PermissionException.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,12 @@ +#ifndef PERMISSIONEXCEPTION_HPP +#define PERMISSIONEXCEPTION_HPP + +#include "IOException.hpp" + +class PermissionException : public IOException { +public: + PermissionException(const QString& errorMsg) : IOException(errorMsg) {} + +}; + +#endif //PERMISSIONEXCEPTION_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/PrecompiledHeader.cmake Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,76 @@ +# Macro for setting up precompiled headers. Usage: +# +# add_precompiled_header(target header.h [FORCEINCLUDE]) +# +# MSVC: A source file with the same name as the header must exist and +# be included in the target (E.g. header.cpp). +# +# MSVC: Add FORCEINCLUDE to automatically include the precompiled +# header file from every source file. +# +# GCC: The precompiled header is always automatically included from +# every header file. +MACRO(ADD_PRECOMPILED_HEADER _targetName _input) + GET_FILENAME_COMPONENT(_inputWe ${_input} NAME_WE) + SET(pch_source ${_inputWe}.cpp) + FOREACH(arg ${ARGN}) + IF(arg STREQUAL FORCEINCLUDE) + SET(FORCEINCLUDE ON) + ELSE(arg STREQUAL FORCEINCLUDE) + SET(FORCEINCLUDE OFF) + ENDIF(arg STREQUAL FORCEINCLUDE) + ENDFOREACH(arg) + + IF(MSVC) + GET_TARGET_PROPERTY(sources ${_targetName} SOURCES) + SET(_sourceFound FALSE) + FOREACH(_source ${sources}) + SET(PCH_COMPILE_FLAGS "") + IF(_source MATCHES \\.\(cc|cxx|cpp\)$) +GET_FILENAME_COMPONENT(_sourceWe ${_source} NAME_WE) +IF(_sourceWe STREQUAL ${_inputWe}) +SET(PCH_COMPILE_FLAGS "${PCH_COMPILE_FLAGS} /Yc${_input}") +SET(_sourceFound TRUE) +ELSE(_sourceWe STREQUAL ${_inputWe}) +SET(PCH_COMPILE_FLAGS "${PCH_COMPILE_FLAGS} /Yu${_input}") +IF(FORCEINCLUDE) +SET(PCH_COMPILE_FLAGS "${PCH_COMPILE_FLAGS} /FI${_input}") +ENDIF(FORCEINCLUDE) +ENDIF(_sourceWe STREQUAL ${_inputWe}) +SET_SOURCE_FILES_PROPERTIES(${_source} PROPERTIES COMPILE_FLAGS "${PCH_COMPILE_FLAGS}") + ENDIF(_source MATCHES \\.\(cc|cxx|cpp\)$) + ENDFOREACH() + IF(NOT _sourceFound) + MESSAGE(FATAL_ERROR "A source file for ${_input} was not found. Required for MSVC builds.") + ENDIF(NOT _sourceFound) + ENDIF(MSVC) + + IF(CMAKE_COMPILER_IS_GNUCXX) + GET_FILENAME_COMPONENT(_name ${_input} NAME) + SET(_source "${CMAKE_CURRENT_SOURCE_DIR}/${_input}") + SET(_outdir "${CMAKE_CURRENT_BINARY_DIR}/${_name}.gch") + MAKE_DIRECTORY(${_outdir}) + SET(_output "${_outdir}/.c++") + + STRING(TOUPPER "CMAKE_CXX_FLAGS_${CMAKE_BUILD_TYPE}" _flags_var_name) + SET(_compiler_FLAGS ${${_flags_var_name}}) + + GET_DIRECTORY_PROPERTY(_directory_flags INCLUDE_DIRECTORIES) + FOREACH(item ${_directory_flags}) + LIST(APPEND _compiler_FLAGS "-I${item}") + ENDFOREACH(item) + + GET_DIRECTORY_PROPERTY(_directory_flags DEFINITIONS) + LIST(APPEND _compiler_FLAGS ${_directory_flags}) + + SEPARATE_ARGUMENTS(_compiler_FLAGS) + MESSAGE("${CMAKE_CXX_COMPILER} -DPCHCOMPILE ${_compiler_FLAGS} -x c++-header -o {_output} ${_source}") + ADD_CUSTOM_COMMAND( + OUTPUT ${_output} + COMMAND ${CMAKE_CXX_COMPILER} ${_compiler_FLAGS} -x c++-header -o ${_output} ${_source} + DEPENDS ${_source} ) + ADD_CUSTOM_TARGET(${_targetName}_gch DEPENDS ${_output}) + ADD_DEPENDENCIES(${_targetName} ${_targetName}_gch) + SET_TARGET_PROPERTIES(${_targetName} PROPERTIES COMPILE_FLAGS "-include ${_name} -Winvalid-pch") + ENDIF(CMAKE_COMPILER_IS_GNUCXX) +ENDMACRO()
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProtectedContainer.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,12 @@ +#ifndef PROTECTEDCONTAINER_HPP +#define PROTECTEDCONTAINER_HPP + +#include <QtCore/QMap> + +template <typename Key_t, typename Value_t> +class ProtectedDictionary { + + QMap<Key_t, Value_t> map; +}; + +#endif //PROTECTEDCONTAINER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ProtectedDictionary.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,12 @@ +#ifndef PROTECTEDCONTAINER_HPP +#define PROTECTEDCONTAINER_HPP + +#include <QtCore/QMap> + +template <typename Key_t, typename Value_t> +class ProtectedDictionary { + + QMap<Key_t, Value_t> map; +}; + +#endif //PROTECTEDCONTAINER_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TestEditDistance.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,23 @@ +#include "EditDistance.hpp" +#include "TestFramework.hpp" + +BOOST_AUTO_TEST_CASE( TestEditDistance ) +{ + BOOST_REQUIRE_EQUAL(EditDistance::Compute("kitten", "sitting", false), 3); + BOOST_REQUIRE_EQUAL(EditDistance::Compute("Saturday", "Sunday", false), 3); + BOOST_REQUIRE_EQUAL(EditDistance::Compute("kitten", "kitten.cpp.o", false), 6); + BOOST_REQUIRE_EQUAL(EditDistance::Compute(QString::fromUtf8("kítten"), "sitting", false), 4); +} + +BOOST_AUTO_TEST_CASE( TestEditDistanceRemoveDiacritics ) +{ + BOOST_REQUIRE_EQUAL(EditDistance::Compute(QString::fromUtf8("kítten"), "sitting", true), 3); +} + +BOOST_AUTO_TEST_CASE( TestNormalization ) +{ + BOOST_REQUIRE_EQUAL(EditDistance::removeDiacritics("kitten"), "kitten"); + BOOST_REQUIRE_EQUAL(EditDistance::removeDiacritics(QString::fromUtf8("Händel")), "Handel"); + BOOST_REQUIRE_EQUAL(EditDistance::removeDiacritics(QString::fromUtf8("Hånda")), "Handa"); + BOOST_REQUIRE_EQUAL(EditDistance::removeDiacritics(QString::fromUtf8("Líll")), "Lill"); +}
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/TestFramework.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,27 @@ +#ifndef TESTFRAMEWORK_HPP +#define TESTFRAMEWORK_HPP + +/* + This header sets up everything for the testframework e.g. switches + between headerincludes and dynamic linking + */ + +//This should really be defined from the build framework +#define BOOSTTEST_HEADER_INCLUDE + +#define BOOST_TEST_MAIN + +#ifdef BOOSTTEST_HEADER_INCLUDE +#include <boost/test/included/unit_test.hpp> +#else +#include <boost/test/unit_test.hpp> +#endif + +//Here comes our helperfunctions +#include <QtCore/QString> +inline std::ostream& operator<<(std::ostream& out, const QString& rhs) +{ + return out << rhs.toStdString(); +} + +#endif //TESTFRAMEWORK_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/ThreadSafeLookup.hpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,65 @@ +#ifndef THREADSAFELOOKUP_HPP +#define THREADSAFELOOKUP_HPP + +#include <QtCore/QMap> + +#include <QtCore/QMutex> + +#include <boost/optional.hpp> + +struct Nothing { + Nothing() {} + Nothing(const Nothing&) {} + Nothing(const Nothing*) {} +}; + +template <bool Locks> +struct Locking { + typedef QMutex Lock_t; + typedef QMutexLocker Locker_t; +}; + +template <> +struct Locking<false> { + typedef Nothing Lock_t; + typedef Nothing Locker_t; +}; + +template <typename Key_t, typename Value_t, bool isLocking = true> +class ThreadSafeLookup { + +private: + typedef QHash<Key_t, Value_t> map_t; + map_t map; + + typename Locking<isLocking>::Lock_t masterLock; +public: + + boost::optional<Value_t> value(const Key_t& key) + { + boost::optional<Value_t> retVal; + typename Locking<isLocking>::Locker_t lock(&masterLock); + typename map_t::const_iterator c = map.find(key); + if (c != map.end()) { + retVal = c.value(); + } + return retVal; + } + + bool insert(const Key_t& key, const Value_t& value, bool forceInsert = false) + { + typename Locking<isLocking>::Locker_t lock(&masterLock); + typename map_t::iterator c = map.find(key); + bool exists = (c != map.end()); + + if (exists) { + c.value() = value; + } + else { + map.insert(key, value); + } + return exists; + } +}; + +#endif //THREADSAFELOOKUP_HPP
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/main.cpp Mon Aug 20 15:49:48 2012 +0200 @@ -0,0 +1,32 @@ +#include "MemoryDbLink.hpp" + +#include "PermissionException.hpp" +#include "DataController.hpp" + +#include <QtGui/QApplication> +#include <QtCore/QDir> + +#include <QtCore/QDebug> +#include <QtCore/QCryptographicHash> +#include <QtCore/QDateTime> + +#include <QtGui/QMainWindow> +#include <QtGui/QTableWidget> +#include <QtGui/QHeaderView> + +#include <cassert> + + + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + + DataController dc; + + + return app.exec(); +} + +
