diff options
Diffstat (limited to '3rdparty/clucene/src/CLucene/store/FSDirectory.cpp')
-rw-r--r-- | 3rdparty/clucene/src/CLucene/store/FSDirectory.cpp | 662 |
1 files changed, 662 insertions, 0 deletions
diff --git a/3rdparty/clucene/src/CLucene/store/FSDirectory.cpp b/3rdparty/clucene/src/CLucene/store/FSDirectory.cpp new file mode 100644 index 000000000..5f96e91cd --- /dev/null +++ b/3rdparty/clucene/src/CLucene/store/FSDirectory.cpp @@ -0,0 +1,662 @@ +/* + * Copyright (C) 2003-2006 Ben van Klinken and the CLucene Team + * + * Distributable under the terms of either the Apache License (Version 2.0) or + * the GNU Lesser General Public License, as specified in the COPYING file. + * + * Changes are Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +*/ +#include <QtCore/QDir> +#include <QtCore/QDateTime> +#include <QtCore/QFileInfo> +#include <QtCore/QByteArray> +#include <QtCore/QCryptographicHash> + +#include "CLucene/StdHeader.h" +#include "FSDirectory.h" +#include "CLucene/index/IndexReader.h" +#include "CLucene/util/Misc.h" +#include "CLucene/debug/condition.h" + +CL_NS_DEF(store) +CL_NS_USE(util) + +bool FSDirectory::disableLocks = false; + +// This cache of directories ensures that there is a unique Directory instance +// per path, so that synchronization on the Directory can be used to synchronize +// access between readers and writers. +static CL_NS(util)::CLHashMap<QString, FSDirectory*, + CL_NS(util)::Compare::Qstring, CL_NS(util)::Equals::Qstring, + CL_NS(util)::Deletor::DummyQString> DIRECTORIES(false, false); + +// # pragma mark -- FSDirectory::FSLock + +FSDirectory::FSLock::FSLock(const QString& _lockDir, const QString& name) + : lockDir(_lockDir) + , lockFile(_lockDir + QDir::separator() + name) +{ +} + +FSDirectory::FSLock::~FSLock() +{ +} + +bool FSDirectory::FSLock::obtain() +{ + if (disableLocks) + return true; + + if (QFile::exists(lockFile)) + return false; + + QDir dir(lockDir); + if (!dir.exists()) { + if (!dir.mkpath(lockDir)) { + // 34: len of "Couldn't create lock directory: " + char* err = _CL_NEWARRAY( + char, 34 + strlen(lockDir.toLocal8Bit().constData()) + 1); + strcpy(err, "Couldn't create lock directory: "); + strcat(err, lockDir.toLocal8Bit().constData()); + _CLTHROWA_DEL(CL_ERR_IO, err); + } + } + + QFile file(lockFile); + return file.open(QIODevice::ReadWrite); +} + +void FSDirectory::FSLock::release() +{ + if (disableLocks) + return; + + QFile file(lockFile); + file.remove(); +} + +bool FSDirectory::FSLock::isLocked() +{ + if (disableLocks) + return false; + return QFile::exists(lockFile); +} + +QString FSDirectory::FSLock::toString() const +{ + QString ret(QLatin1String("Lock@")); + return ret.append(lockFile); +} + +// # pragma mark -- FSDirectory::FSIndexInput + +FSDirectory::FSIndexInput::FSIndexInput(const QString& path, int32_t bufferSize) + : BufferedIndexInput(bufferSize) +{ + CND_PRECONDITION(!path.isEmpty(), "path is NULL"); + + handle = _CLNEW SharedHandle(); + handle->fhandle.setFileName(path); + handle->fhandle.open(QIODevice::ReadOnly); + + if (handle->fhandle.error() != QFile::NoError) { + switch(handle->fhandle.error()) { + case 1: + _CLTHROWA(CL_ERR_IO, "An error occurred when reading from the file"); + break; + case 2: + _CLTHROWA(CL_ERR_IO, "An error occurred when writing to the file."); + break; + case 5: + _CLTHROWA(CL_ERR_IO, "The file could not be opened."); + break; + case 6: + _CLTHROWA(CL_ERR_IO, "The operation was aborted."); + break; + case 7: + _CLTHROWA(CL_ERR_IO, "A timeout occurred."); + break; + case 8: + _CLTHROWA(CL_ERR_IO, "An unspecified error occurred."); + break; + case 9: + _CLTHROWA(CL_ERR_IO, "The file could not be removed."); + break; + case 10: + _CLTHROWA(CL_ERR_IO, "The file could not be renamed."); + break; + case 11: + _CLTHROWA(CL_ERR_IO, "The position in the file could not be changed."); + break; + case 12: + _CLTHROWA(CL_ERR_IO, "The file could not be resized.e"); + break; + case 13: + _CLTHROWA(CL_ERR_IO, "The file could not be accessed."); + break; + case 14: + _CLTHROWA(CL_ERR_IO, "The file could not be copied."); + break; + case 4: + default: + _CLTHROWA(CL_ERR_IO, "A fatal error occurred."); + } + } + + //Store the file length + handle->_length = handle->fhandle.size(); + handle->_fpos = 0; + this->_pos = 0; +} + +FSDirectory::FSIndexInput::FSIndexInput(const FSIndexInput& other) + : BufferedIndexInput(other) +{ + if (other.handle == NULL) + _CLTHROWA(CL_ERR_NullPointer, "other handle is null"); + + SCOPED_LOCK_MUTEX(*other.handle->THIS_LOCK) + + _pos = other.handle->_fpos; + handle = _CL_POINTER(other.handle); +} + +FSDirectory::FSIndexInput::~FSIndexInput() +{ + FSIndexInput::close(); +} + +void FSDirectory::FSIndexInput::close() +{ + BufferedIndexInput::close(); +#ifdef _LUCENE_THREADMUTEX + if (handle != NULL) { + // Here we have a bit of a problem... We need to lock the handle to + // ensure that we can safely delete the handle... But if we delete the + // handle, then the scoped unlock, won't be able to unlock the mutex... + + // take a reference of the lock object... + _LUCENE_THREADMUTEX* mutex = handle->THIS_LOCK; + //lock the mutex + mutex->lock(); + + // determine if we are about to delete the handle... + bool doUnlock = (handle->__cl_refcount > 1); + // decdelete (deletes if refcount is down to 0) + _CLDECDELETE(handle); + + if (doUnlock) + mutex->unlock(); + else + delete mutex; + } +#else + _CLDECDELETE(handle); +#endif +} + +IndexInput* FSDirectory::FSIndexInput::clone() const +{ + return _CLNEW FSDirectory::FSIndexInput(*this); +} + +void FSDirectory::FSIndexInput::seekInternal(const int64_t position) +{ + CND_PRECONDITION(position >= 0 && position < handle->_length, + "Seeking out of range") + _pos = position; +} + +void FSDirectory::FSIndexInput::readInternal(uint8_t* b, const int32_t len) +{ + SCOPED_LOCK_MUTEX(*handle->THIS_LOCK) + + CND_PRECONDITION(handle != NULL, "shared file handle has closed"); + CND_PRECONDITION(handle->fhandle.isOpen(), "file is not open"); + + if (handle->_fpos != _pos) { + handle->fhandle.seek(_pos); + if (handle->fhandle.pos() != _pos) + _CLTHROWA( CL_ERR_IO, "File IO Seek error"); + handle->_fpos = _pos; + } + + bufferLength = (int32_t)handle->fhandle.read((char*)b, len); + if (bufferLength == 0) + _CLTHROWA(CL_ERR_IO, "read past EOF"); + + if (bufferLength == -1) + _CLTHROWA(CL_ERR_IO, "read error"); + + _pos += bufferLength; + handle->_fpos =_pos; +} + +// # pragma mark -- FSDirectory::FSIndexInput::SharedHandle + +FSDirectory::FSIndexInput::SharedHandle::SharedHandle() + : _fpos(0) + , _length(0) +{ +#ifdef _LUCENE_THREADMUTEX + THIS_LOCK = new _LUCENE_THREADMUTEX; +#endif +} + +FSDirectory::FSIndexInput::SharedHandle::~SharedHandle() +{ + if (fhandle.isOpen()) + fhandle.close(); +} + +// # pragma mark -- FSDirectory::FSIndexOutput + +FSDirectory::FSIndexOutput::FSIndexOutput(const QString& path) +{ + //O_BINARY - Opens file in binary (untranslated) mode + //O_CREAT - Creates and opens new file for writing. Has no effect if file specified by filename exists + //O_RANDOM - Specifies that caching is optimized for, but not restricted to, random access from disk. + //O_WRONLY - Opens file for writing only; + fhandle.setFileName(path); + fhandle.open(QIODevice::ReadWrite | QIODevice::Truncate); + + if (fhandle.error() != QFile::NoError) { + switch(fhandle.error()) { + case 1: + _CLTHROWA(CL_ERR_IO, "An error occurred when reading from the file"); + break; + case 2: + _CLTHROWA(CL_ERR_IO, "An error occurred when writing to the file."); + break; + case 5: + _CLTHROWA(CL_ERR_IO, "The file could not be opened."); + break; + case 6: + _CLTHROWA(CL_ERR_IO, "The operation was aborted."); + break; + case 7: + _CLTHROWA(CL_ERR_IO, "A timeout occurred."); + break; + case 8: + _CLTHROWA(CL_ERR_IO, "An unspecified error occurred."); + break; + case 9: + _CLTHROWA(CL_ERR_IO, "The file could not be removed."); + break; + case 10: + _CLTHROWA(CL_ERR_IO, "The file could not be renamed."); + break; + case 11: + _CLTHROWA(CL_ERR_IO, "The position in the file could not be changed."); + break; + case 12: + _CLTHROWA(CL_ERR_IO, "The file could not be resized.e"); + break; + case 13: + _CLTHROWA(CL_ERR_IO, "The file could not be accessed."); + break; + case 14: + _CLTHROWA(CL_ERR_IO, "The file could not be copied."); + break; + case 4: + default: + _CLTHROWA(CL_ERR_IO, "A fatal error occurred."); + } + } +} + +FSDirectory::FSIndexOutput::~FSIndexOutput() +{ + if (fhandle.isOpen()) { + try { + FSIndexOutput::close(); + } catch (CLuceneError& err) { + //ignore IO errors... + if (err.number() != CL_ERR_IO) + throw; + } + } +} + +void FSDirectory::FSIndexOutput::close() +{ + try { + BufferedIndexOutput::close(); + } catch (CLuceneError& err) { + //ignore IO errors... + if (err.number() != CL_ERR_IO) + throw; + } + fhandle.close(); +} + +int64_t FSDirectory::FSIndexOutput::length() +{ + CND_PRECONDITION(fhandle.isOpen(), "file is not open"); + return fhandle.size(); +} + +void FSDirectory::FSIndexOutput::seek(const int64_t pos) +{ + CND_PRECONDITION(fhandle.isOpen(), "file is not open"); + + BufferedIndexOutput::seek(pos); + fhandle.seek(pos); + if (fhandle.pos() != pos) + _CLTHROWA(CL_ERR_IO, "File IO Seek error"); +} + +void FSDirectory::FSIndexOutput::flushBuffer(const uint8_t* b, const int32_t size) +{ + CND_PRECONDITION(fhandle.isOpen(), "file is not open"); + + if (size > 0 && fhandle.write((const char*)b, size) != size) + _CLTHROWA(CL_ERR_IO, "File IO Write error"); +} + +// # pragma mark -- FSDirectory + +FSDirectory::FSDirectory(const QString& path, const bool createDir) + : Directory() + , refCount(0) + , useMMap(false) +{ + //set a realpath so that if we change directory, we can still function + directory = QFileInfo(path).absoluteFilePath(); + lockDir = directory; + + QDir dir(lockDir); + if (!dir.exists()) { + if (!dir.mkpath(lockDir)) + _CLTHROWA_DEL(CL_ERR_IO, "Cannot create temp directory"); + } + + QFileInfo info(lockDir); + if (info.isFile() || info.isSymLink()) + _CLTHROWA(CL_ERR_IO, "Found regular file where directory expected"); + + if (createDir) + create(); + + dir.setPath(directory); + if (!dir.exists()) { + //19: len of " is not a directory" + char* err = + _CL_NEWARRAY(char, 19 + strlen(path.toLocal8Bit().constData()) + 1); + strcpy(err, path.toLocal8Bit().constData()); + strcat(err, " is not a directory"); + _CLTHROWA_DEL(CL_ERR_IO, err); + } +} + +void FSDirectory::create() +{ + SCOPED_LOCK_MUTEX(THIS_LOCK) + + bool clear = false; + QDir dir(directory); + if (!dir.exists()) { + if (!dir.mkpath(directory)) { + char* err = _CL_NEWARRAY( // 27 len of "Couldn't create directory:" + char, 27 + strlen(directory.toLocal8Bit().constData()) + 1); + strcpy(err, "Couldn't create directory: "); + strcat(err, directory.toLocal8Bit().constData()); + _CLTHROWA_DEL(CL_ERR_IO, err); + } + } else { + clear = true; + } + + QFileInfo info(directory); + if (info.isFile() || info.isSymLink()) { + char tmp[1024]; + _snprintf(tmp, 1024, "%s not a directory", + directory.toLocal8Bit().constData()); + _CLTHROWA(CL_ERR_IO, tmp); + } + + if (clear) { + dir.setPath(directory); + // clear probably existing lucene index files + QStringList fileList = dir.entryList(QDir::Files | QDir::Hidden + | QDir::NoSymLinks); + foreach(const QString file, fileList) { + if (CL_NS(index)::IndexReader::isLuceneFile(file)) { + if (!dir.remove(file)) + _CLTHROWA(CL_ERR_IO, "Couldn't delete file "); + } + } + + // clear probably existing file locks + QFileInfo dirInfo(lockDir); + if (dirInfo.exists() && dirInfo.isReadable() && dirInfo.isWritable() + && !dirInfo.isFile() && !dirInfo.isSymLink()) { + QDir lockDirectory(lockDir); + fileList = dir.entryList(QStringList() << getLockPrefix() + + QLatin1Char('*'), QDir::Files | QDir::Hidden | QDir::NoSymLinks); + + foreach(const QString file, fileList) { + if (!lockDirectory.remove(file)) + _CLTHROWA(CL_ERR_IO, "Couldn't delete file "); + } + } + else { + //todo: richer error: + lockDir.getAbsolutePath()); + _CLTHROWA(CL_ERR_IO, "Cannot read lock directory"); + } + } +} + +void FSDirectory::priv_getFN(QString& buffer, const QString& name) const +{ + buffer.clear(); + buffer.append(directory); + buffer.append(QDir::separator()); + buffer.append(name); +} + +FSDirectory::~FSDirectory() +{ +} + +QStringList FSDirectory::list() const +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QDir dir(directory); + return dir.entryList(QDir::Files | QDir::Hidden); +} + +bool FSDirectory::fileExists(const QString& name) const +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QDir dir(directory); + return dir.entryList().contains(name); +} + +QString FSDirectory::getDirName() const +{ + return directory; +} + +//static +FSDirectory* FSDirectory::getDirectory(const QString& file, const bool _create) +{ + FSDirectory* dir = NULL; + { + if (file.isEmpty()) + _CLTHROWA(CL_ERR_IO, "Invalid directory"); + + SCOPED_LOCK_MUTEX(DIRECTORIES.THIS_LOCK) + dir = DIRECTORIES.get(file); + if ( dir == NULL ){ + dir = _CLNEW FSDirectory(file, _create); + DIRECTORIES.put(dir->directory, dir); + } else if (_create) { + dir->create(); + } + + { + SCOPED_LOCK_MUTEX(dir->THIS_LOCK) + dir->refCount++; + } + } + + return _CL_POINTER(dir); +} + +int64_t FSDirectory::fileModified(const QString& name) const +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QFileInfo fInfo(directory + QDir::separator() + name); + return fInfo.lastModified().toTime_t(); +} + +//static +int64_t FSDirectory::fileModified(const QString& dir, const QString& name) +{ + QFileInfo fInfo(dir + QDir::separator() + name); + return fInfo.lastModified().toTime_t(); +} + +void FSDirectory::touchFile(const QString& name) +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QFile file(directory + QDir::separator() + name); + if (!file.open(QIODevice::ReadWrite)) + _CLTHROWA(CL_ERR_IO, "IO Error while touching file"); +} + +int64_t FSDirectory::fileLength(const QString& name) const +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QFileInfo fInfo(directory + QDir::separator() + name); + return fInfo.size(); +} + +IndexInput* FSDirectory::openInput(const QString& name) +{ + return openInput(name, CL_NS(store)::BufferedIndexOutput::BUFFER_SIZE); +} + +IndexInput* FSDirectory::openInput(const QString& name, int32_t bufferSize ) +{ + CND_PRECONDITION(directory[0]!=0,"directory is not open") + + return _CLNEW FSIndexInput(directory + QDir::separator() + name, bufferSize); +} + +void FSDirectory::close() +{ + SCOPED_LOCK_MUTEX(DIRECTORIES.THIS_LOCK) + { + SCOPED_LOCK_MUTEX(THIS_LOCK) + + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + //refcount starts at 1 + if (--refCount <= 0) { + Directory* dir = DIRECTORIES.get(getDirName()); + if (dir) { + //this will be removed in ~FSDirectory + DIRECTORIES.remove(getDirName()); + _CLDECDELETE(dir); + } + } + } +} + +QString FSDirectory::getLockPrefix() const +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QString dirName(QFileInfo(directory).absoluteFilePath()); + if (dirName.isEmpty()) + _CLTHROWA(CL_ERR_Runtime, "Invalid directory path"); + + // to be compatible with jlucene, + // we need to make some changes ... + if (dirName.at(1) == QLatin1Char(':')) + dirName[0] = dirName.at(0).toUpper(); + + TCHAR tBuffer[2048] = { 0 }; + dirName.toWCharArray(tBuffer); + + char aBuffer[4096] = { 0 }; + STRCPY_TtoA(aBuffer, tBuffer, 4096); + + QString string(QLatin1String("lucene-")); + QByteArray hash(QCryptographicHash::hash(aBuffer, QCryptographicHash::Md5)); + + // TODO: verify this !!! + return string.append(QLatin1String(hash.toHex().constData())); +} + +bool FSDirectory::doDeleteFile(const QString& name) +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QDir dir(directory); + return dir.remove(name); +} + +void FSDirectory::renameFile(const QString& from, const QString& to) +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + SCOPED_LOCK_MUTEX(THIS_LOCK) + + if (fileExists(to)) + deleteFile(to, false); + + QFile file(directory + QDir::separator() + from); + QString newFile(directory + QDir::separator() + to); + if (!file.rename(newFile)) { + // try a second time if we fail + if (fileExists(to)) + deleteFile(to, false); + + if (!file.rename(newFile)) { + QString error(QLatin1String("Could not rename: %1 to %2!!!!")); + error.arg(from).arg(newFile); + QByteArray bArray(error.toLocal8Bit()); + _CLTHROWA(CL_ERR_IO, bArray.constData()); + } + } +} + +IndexOutput* FSDirectory::createOutput(const QString& name) +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + QString file = directory + QDir::separator() + name; + if (QFileInfo(file).exists()) { + if (!QFile::remove(file)) { + QByteArray bArray("Cannot overwrite: "); + bArray.append(name.toLocal8Bit()); + _CLTHROWA(CL_ERR_IO, bArray.constData()); + } + } + return _CLNEW FSIndexOutput(file); +} + +LuceneLock* FSDirectory::makeLock(const QString& name) +{ + CND_PRECONDITION(!directory.isEmpty(), "directory is not open"); + + + QString lockFile(getLockPrefix()); + lockFile.append(QLatin1Char('-')).append(name); + + return _CLNEW FSLock(lockDir, lockFile); +} + +QString FSDirectory::toString() const +{ + return QString::fromLatin1("FSDirectory@").append(directory); +} + +CL_NS_END |