Skip to content

Commit

Permalink
Refactor|FS|libcore: Added FileIndex as a separate class
Browse files Browse the repository at this point in the history
The index of files was previously an internal part of FileSystem,
however now it is a class of its own to facilitate observable and
custom indices.
  • Loading branch information
skyjake committed Jul 9, 2014
1 parent 13b67c8 commit 97b1034
Show file tree
Hide file tree
Showing 7 changed files with 315 additions and 96 deletions.
7 changes: 5 additions & 2 deletions doomsday/libcore/filesys.pri
Expand Up @@ -6,6 +6,7 @@ publicHeaders(root, \
include/de/DirectoryFeed \
include/de/Feed \
include/de/File \
include/de/FileIndex \
include/de/Folder \
include/de/FS \
include/de/FileSystem \
Expand All @@ -27,8 +28,9 @@ publicHeaders(filesys, \
include/de/filesys/directoryfeed.h \
include/de/filesys/feed.h \
include/de/filesys/file.h \
include/de/filesys/folder.h \
include/de/filesys/fileindex.h \
include/de/filesys/filesystem.h \
include/de/filesys/folder.h \
include/de/filesys/libraryfile.h \
include/de/filesys/linkfile.h \
include/de/filesys/nativefile.h \
Expand All @@ -47,8 +49,9 @@ SOURCES += \
src/filesys/directoryfeed.cpp \
src/filesys/feed.cpp \
src/filesys/file.cpp \
src/filesys/folder.cpp \
src/filesys/fileindex.cpp \
src/filesys/filesystem.cpp \
src/filesys/folder.cpp \
src/filesys/libraryfile.cpp \
src/filesys/linkfile.cpp \
src/filesys/nativefile.cpp \
Expand Down
1 change: 1 addition & 0 deletions doomsday/libcore/include/de/FileIndex
@@ -0,0 +1 @@
#include "filesys/fileindex.h"
107 changes: 107 additions & 0 deletions doomsday/libcore/include/de/filesys/fileindex.h
@@ -0,0 +1,107 @@
/** @file fileindex.h Index for looking up files of specific type.
*
* @authors Copyright (c) 2014 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program 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 Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#ifndef LIBDENG2_FILEINDEX_H
#define LIBDENG2_FILEINDEX_H

#include "../File"

#include <map>
#include <list>
#include <utility>

namespace de {

class File;

/**
* Indexes files for quick access.
*
* @ingroup fs
*/
class FileIndex
{
public:
typedef std::multimap<String, File *> Index;
typedef std::pair<Index::iterator, Index::iterator> IndexRange;
typedef std::pair<Index::const_iterator, Index::const_iterator> ConstIndexRange;
typedef std::list<File *> FoundFiles;

class IPredicate
{
public:
virtual ~IPredicate() {}

/**
* Determines if a file should be included in the index.
* @param file File.
*
* @return @c true to index file, @c false to ignore.
*/
virtual bool shouldIncludeInIndex(File const &file) const = 0;
};

DENG2_DEFINE_AUDIENCE2(Addition, void fileAdded (File const &, FileIndex &))
DENG2_DEFINE_AUDIENCE2(Removal, void fileRemoved(File const &, FileIndex &))

public:
FileIndex();

/**
* Sets the predicate that determines whether a file should be included in the
* index.
*
* @param predicate Predicate for inclusion. Must exist while the index is being
* used.
*/
void setPredicate(IPredicate const &predicate);

/**
* Adds a file to the index if the predicate permits.
*
* @param file File.
*
* @return @c true, if the file was added to the index.
*/
bool maybeAdd(File const &file);

/**
* Removes a file from the index, if it has been indexed. If not, nothing is done.
*
* @param file File.
*/
void remove(File const &file);

int size() const;

void findPartialPath(String const &path, FoundFiles &found) const;

void print() const;

// C++ iterator:
typedef Index::const_iterator const_iterator;
Index::const_iterator begin() const;
Index::const_iterator end() const;

private:
DENG2_PRIVATE(d)
};

} // namespace de

#endif // LIBDENG2_FILEINDEX_H
13 changes: 5 additions & 8 deletions doomsday/libcore/include/de/filesys/filesystem.h
Expand Up @@ -22,10 +22,10 @@

#include "../libcore.h"
#include "../Folder"
#include "../FileIndex"
#include "../System"

#include <QFlags>
#include <map>

/**
* @defgroup fs File System
Expand Down Expand Up @@ -105,11 +105,8 @@ class DENG2_PUBLIC FileSystem : public System
/// between them. @ingroup errors
DENG2_ERROR(AmbiguousError);

typedef std::multimap<String, File *> Index;
typedef std::pair<Index::iterator, Index::iterator> IndexRange;
typedef std::pair<Index::const_iterator, Index::const_iterator> ConstIndexRange;
typedef std::pair<String, File *> IndexEntry;
typedef std::list<File *> FoundFiles;
typedef FileIndex Index;
typedef FileIndex::FoundFiles FoundFiles;

public:
/**
Expand Down Expand Up @@ -251,7 +248,7 @@ class DENG2_PUBLIC FileSystem : public System
*
* @note The file names are indexed in lower case.
*/
Index const &nameIndex() const;
FileIndex const &nameIndex() const;

/**
* Retrieves the index of files of a particular type.
Expand All @@ -266,7 +263,7 @@ class DENG2_PUBLIC FileSystem : public System
* FS::Index const &nativeFileIndex = App::fileSystem().indexFor(DENG2_TYPE_NAME(NativeFile));
* @endcode
*/
Index const &indexFor(String const &typeIdentifier) const;
FileIndex const &indexFor(String const &typeIdentifier) const;

/**
* Adds a file to the main index.
Expand Down
3 changes: 3 additions & 0 deletions doomsday/libcore/include/de/libcore.h
Expand Up @@ -363,6 +363,9 @@ class PrivateAutoPtr
InstType *get() const {
return ptr;
}
InstType const *getConst() const {
return ptr;
}
operator InstType *() const {
return ptr;
}
Expand Down
165 changes: 165 additions & 0 deletions doomsday/libcore/src/filesys/fileindex.cpp
@@ -0,0 +1,165 @@
/** @file fileindex.cpp Index for looking up files of specific type.
*
* @authors Copyright (c) 2014 Jaakko Keränen <jaakko.keranen@iki.fi>
*
* @par License
* LGPL: http://www.gnu.org/licenses/lgpl.html
*
* <small>This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation; either version 3 of the License, or (at your
* option) any later version. This program 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 Lesser
* General Public License for more details. You should have received a copy of
* the GNU Lesser General Public License along with this program; if not, see:
* http://www.gnu.org/licenses</small>
*/

#include "de/FileIndex"
#include "de/ReadWriteLockable"

namespace de {

DENG2_PIMPL(FileIndex), public ReadWriteLockable
{
IPredicate const *predicate;
Index index;

Instance(Public *i)
: Base(i)
, predicate(0)
{}

static String indexedName(File const &file)
{
return file.name().lower();
}

void add(File const &file)
{
DENG2_GUARD_WRITE(this);

index.insert(std::pair<String, File *>(indexedName(file), const_cast<File *>(&file)));
}

void remove(File const &file)
{
DENG2_GUARD_WRITE(this);

if(index.empty())
{
return;
}

// Look up the ones that might be this file.
IndexRange range = index.equal_range(indexedName(file));

for(Index::iterator i = range.first; i != range.second; ++i)
{
if(i->second == &file)
{
// This is the one to deindex.
index.erase(i);
break;
}
}
}

void findPartialPath(String const &path, FoundFiles &found) const
{
String baseName = path.fileName().lower();
String dir = path.fileNamePath().lower();

if(!dir.empty() && !dir.beginsWith("/"))
{
// Always begin with a slash. We don't want to match partial folder names.
dir = "/" + dir;
}

DENG2_GUARD_READ(this);

ConstIndexRange range = index.equal_range(baseName);
for(Index::const_iterator i = range.first; i != range.second; ++i)
{
File *file = i->second;
if(file->path().fileNamePath().endsWith(dir, Qt::CaseInsensitive))
{
found.push_back(file);
}
}
}

DENG2_PIMPL_AUDIENCE(Addition)
DENG2_PIMPL_AUDIENCE(Removal)
};

DENG2_AUDIENCE_METHOD(FileIndex, Addition)
DENG2_AUDIENCE_METHOD(FileIndex, Removal)

FileIndex::FileIndex() : d(new Instance(this))
{}

void FileIndex::setPredicate(IPredicate const &predicate)
{
d->predicate = &predicate;
}

bool FileIndex::maybeAdd(File const &file)
{
if(d->predicate && !d->predicate->shouldIncludeInIndex(file))
{
return false;
}

d->add(file);

// Notify audience.
DENG2_FOR_AUDIENCE2(Addition, i)
{
i->fileAdded(file, *this);
}

return true;
}

void FileIndex::remove(File const &file)
{
d->remove(file);

// Notify audience.
DENG2_FOR_AUDIENCE2(Removal, i)
{
i->fileRemoved(file, *this);
}
}

int FileIndex::size() const
{
return d->index.size();
}

void FileIndex::findPartialPath(String const &path, FoundFiles &found) const
{
d->findPartialPath(path, found);
}

FileIndex::Index::const_iterator FileIndex::begin() const
{
return d.getConst()->index.begin();
}

FileIndex::Index::const_iterator FileIndex::end() const
{
return d.getConst()->index.end();
}

void FileIndex::print() const
{
for(Index::const_iterator i = d->index.begin(); i != d->index.end(); ++i)
{
LOG_TRACE("\"%s\": ") << i->first << i->second->description();
}
}

} // namespace de

0 comments on commit 97b1034

Please sign in to comment.