# HG changeset patch # User Tom Fredrik Blenning Klaussen # Date 1345470588 -7200 # Node ID a3834af3657988078a160b2e339a459d842a5573 Working with memory backend. diff -r 000000000000 -r a3834af36579 CMakeLists.txt --- /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) diff -r 000000000000 -r a3834af36579 DataController.cpp --- /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 +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "EditDistance.hpp" + +#include +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()<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 > 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 line, elems) { + QTreeWidgetItem* item = 0; + bool anyAdded = false; + + if (showNameDups) { + QTreeWidgetItem* topLevelItem = 0; + foreach(QSharedPointer 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 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 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 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 > oList; + + int absoluteCutoff = line->name().length() * editDistanceCutoff; + foreach(QSharedPointer 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 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; +} diff -r 000000000000 -r a3834af36579 DataController.hpp --- /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 + +#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 diff -r 000000000000 -r a3834af36579 EditDistance.cpp --- /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 + +#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 lup(a, b); + + boost::optional res = cache.value(lup); + if (res) + return *res; + + + // Allocate distance matrix + QList > d; + QList temp; + for (int i=0;i +#include +#include + +#include "ThreadSafeLookup.hpp" + +class EditDistance { +protected: + typedef ThreadSafeLookup, int> cacheType; + //typedef QMap, int> cacheType; + //typedef QHash, int> cacheType; +public: + static int Compute(QString a, QString b, bool removeDiacritics = false); + static QString removeDiacritics(QString in); + + static cacheType cache; +}; + +#endif //EDITDISTANCE_HPP diff -r 000000000000 -r a3834af36579 Exception.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 + +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 diff -r 000000000000 -r a3834af36579 FileDbLink.cpp --- /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 +#include + +#include "PermissionException.hpp" +#include "EditDistance.hpp" + +#include + +#include +#include +#include + +#include + +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()< > FileDbLink::sortOn(SORTORDER order, bool extended) +{ + QList > list = (extended) ? computedValues() : computedValues(); + + switch (order) { + case PATH: + { + QList > oList; + foreach(QSharedPointer info, list) { + oList.push_back(info); + } + return oList; + } + case SIZE: + { + QMultiMap > oList; + foreach(QSharedPointer info, list) { + oList.insert(info->size(), info); + } + return oList.values(); + } + case MTIME: + { + QMultiMap > oList; + foreach(QSharedPointer info, list) { + oList.insert(info->mtime(), info); + } + return oList.values(); + } + case CHECKSUM: + { + QMultiMap > oList; + foreach(QSharedPointer info, list) { + oList.insert(info->checksum(), info); + } + return oList.values(); + } + case EDIT: + { + assert(extended); + QMultiMap > oList; + foreach(QSharedPointer info, list) { + QSharedPointer ptr; + ptr = info.dynamicCast(); + oList.insert(ptr->editDistance(), info); + } + return oList.values(); + + } + } + abort(); +} + +QSharedPointer FileDbLink::computedValue(const QSharedPointer& info, + const QList >& entries) +{ + QString p1 = info->name(); + int minDist = 100000; + QString other; + for (QList >::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(new ExtendedDBInfo(*info, other, minDist)); +} + +const QList > FileDbLink::computedValues() const +{ + QList > list; + QList > entries = values(); + +#if 1 + list = QtConcurrent::blockingMapped(entries, boost::bind( &FileDbLink::computedValue, _1, entries)); +#else + for (QList >::const_iterator it1 = entries.begin(); + it1 != entries.end(); ++it1) { + QSharedPointer ext = computedValue(*it1, entries); + list.push_back(ext); + } +#endif + return list; +} diff -r 000000000000 -r a3834af36579 FileDbLink.hpp --- /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 +#include +#include + +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 computedValue(const QSharedPointer& info, const QList >&); + + +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 > values() const = 0; + virtual const QList > computedValues() const; + + enum SORTORDER { PATH, SIZE, MTIME, CHECKSUM, EDIT }; + + virtual const QList > sortOn(SORTORDER order, bool extended = false); +}; + +#endif //FILEDBLINK diff -r 000000000000 -r a3834af36579 IOException.cpp --- /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) +{ +} diff -r 000000000000 -r a3834af36579 IOException.hpp --- /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 diff -r 000000000000 -r a3834af36579 MemoryDbLink.cpp --- /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 + +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 >::iterator pos; + pos = entries.find(dbinfo.path()); + if (pos == entries.end()) { + entries.insert(dbinfo.path(), QSharedPointer(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 info, entries) { + list << info->serialize(); + } + return list; +} + +const QList > MemoryDbLink::values() const +{ + return entries.values(); +} diff -r 000000000000 -r a3834af36579 MemoryDbLink.hpp --- /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 +#include + +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 > values() const; + +private: + void addFile(const DBInfo& info); + bool tryAddFile(const DBInfo& info); + QMap > entries; +}; + +#endif //MEMORYDBLINK_HPP diff -r 000000000000 -r a3834af36579 OrderedPair.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 + +template +class OrderedPair : public QPair +{ +public: + OrderedPair(const S& a, const S&b) : QPair(a + +template +class ProtectedDictionary { + + QMap map; +}; + +#endif //PROTECTEDCONTAINER_HPP diff -r 000000000000 -r a3834af36579 ProtectedDictionary.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 + +template +class ProtectedDictionary { + + QMap map; +}; + +#endif //PROTECTEDCONTAINER_HPP diff -r 000000000000 -r a3834af36579 TestEditDistance.cpp --- /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"); +} diff -r 000000000000 -r a3834af36579 TestFramework.hpp --- /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 +#else +#include +#endif + +//Here comes our helperfunctions +#include +inline std::ostream& operator<<(std::ostream& out, const QString& rhs) +{ + return out << rhs.toStdString(); +} + +#endif //TESTFRAMEWORK_HPP diff -r 000000000000 -r a3834af36579 ThreadSafeLookup.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 + +#include + +#include + +struct Nothing { + Nothing() {} + Nothing(const Nothing&) {} + Nothing(const Nothing*) {} +}; + +template +struct Locking { + typedef QMutex Lock_t; + typedef QMutexLocker Locker_t; +}; + +template <> +struct Locking { + typedef Nothing Lock_t; + typedef Nothing Locker_t; +}; + +template +class ThreadSafeLookup { + +private: + typedef QHash map_t; + map_t map; + + typename Locking::Lock_t masterLock; +public: + + boost::optional value(const Key_t& key) + { + boost::optional retVal; + typename Locking::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::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 diff -r 000000000000 -r a3834af36579 main.cpp --- /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 +#include + +#include +#include +#include + +#include +#include +#include + +#include + + + +int main(int argc, char *argv[]) { + + QApplication app(argc, argv); + + + DataController dc; + + + return app.exec(); +} + +