Skip to content

Commit

Permalink
Add a simple FileDescriptor class that wraps a file stream
Browse files Browse the repository at this point in the history
The stream is open for as long as the object is alive.
Refs #7263

FileDescriptor uses streams rather than c file handles. Refs #7263
  • Loading branch information
martyngigg committed Jun 29, 2013
1 parent b846d13 commit 8681dff
Show file tree
Hide file tree
Showing 4 changed files with 261 additions and 3 deletions.
9 changes: 6 additions & 3 deletions Code/Mantid/Framework/Kernel/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ set ( SRC_FILES
src/EnvironmentHistory.cpp
src/Exception.cpp
src/FacilityInfo.cpp
src/FileDescriptor.cpp
src/FileValidator.cpp
src/FilterChannel.cpp
src/FilteredTimeSeriesProperty.cpp
Expand All @@ -40,8 +41,8 @@ set ( SRC_FILES
src/MagneticIon.cpp
src/MandatoryValidator.cpp
src/MantidVersion.cpp
src/Material.cpp
src/MaskedProperty.cpp
src/Material.cpp
src/Math/Distributions/BoseEinsteinDistribution.cpp
src/Math/Distributions/HermitePolynomials.cpp
src/Matrix.cpp
Expand Down Expand Up @@ -135,6 +136,7 @@ set ( INC_FILES
inc/MantidKernel/Exception.h
inc/MantidKernel/FacilityInfo.h
inc/MantidKernel/Fast_Exponential.h
inc/MantidKernel/FileDescriptor.h
inc/MantidKernel/FileValidator.h
inc/MantidKernel/FilterChannel.h
inc/MantidKernel/FilteredTimeSeriesProperty.h
Expand All @@ -161,8 +163,8 @@ set ( INC_FILES
inc/MantidKernel/MagneticIon.h
inc/MantidKernel/MandatoryValidator.h
inc/MantidKernel/MantidVersion.h
inc/MantidKernel/Material.h
inc/MantidKernel/MaskedProperty.h
inc/MantidKernel/Material.h
inc/MantidKernel/Math/Distributions/BoseEinsteinDistribution.h
inc/MantidKernel/Math/Distributions/HermitePolynomials.h
inc/MantidKernel/Matrix.h
Expand Down Expand Up @@ -251,6 +253,7 @@ set ( TEST_FILES
EnabledWhenPropertyTest.h
EnvironmentHistoryTest.h
FacilitiesTest.h
FileDescriptorTest.h
FileValidatorTest.h
FilterChannelTest.h
FilteredTimeSeriesPropertyTest.h
Expand All @@ -272,8 +275,8 @@ set ( TEST_FILES
MagneticFormFactorTableTest.h
MagneticIonTest.h
MandatoryValidatorTest.h
MaterialTest.h
MaskedPropertyTest.h
MaterialTest.h
MatrixPropertyTest.h
MatrixTest.h
MemoryTest.h
Expand Down
87 changes: 87 additions & 0 deletions Code/Mantid/Framework/Kernel/inc/MantidKernel/FileDescriptor.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
#ifndef MANTID_KERNEL_FILEDESCRIPTOR_H_
#define MANTID_KERNEL_FILEDESCRIPTOR_H_

#include "MantidKernel/ClassMacros.h"
#include "MantidKernel/DllConfig.h"

#include <fstream>
#include <string>

namespace Mantid
{
namespace Kernel
{

/**
Defines a wrapper around an open file. Details of the file such as the filename & extension can be queried.
The file is closed when the object is destroyed.
The object stores an opened fstream object that can be accessed using the data() method.
Copyright &copy; 2013 ISIS Rutherford Appleton Laboratory & NScD Oak Ridge National Laboratory
This file is part of Mantid.
Mantid is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
Mantid 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 General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
File change history is stored at: <https://github.com/mantidproject/mantid>
Code Documentation is available at: <http://doxygen.mantidproject.org>
*/
class MANTID_KERNEL_DLL FileDescriptor
{
public:
/// Constructor accepting a filename
FileDescriptor(const std::string & filename);
/// Destructor
~FileDescriptor();

/**
* Access the filename
* @returns A reference to a const string containing the filename
*/
inline const std::string & filename() const { return m_filename; }
/**
* Access the file extension. Defined as the string after and including the last period character
* @returns A reference to a const string containing the file extension
*/
inline const std::string & extension() const { return m_extension; }
/**
* Access the open file stream. DO NOT CLOSE IT
* @returns The current stream
*/
inline std::ifstream & data() { return m_file; }

/// Reset the file stream to the start of the file
void resetStreamToStart();

private:
DISABLE_DEFAULT_CONSTRUCT(FileDescriptor);
DISABLE_COPY_AND_ASSIGN(FileDescriptor);

/// Open the file and cache description elements
void initialize(const std::string & filename);

/// Full filename
std::string m_filename;
/// Extension
std::string m_extension;
/// Open file stream
std::ifstream m_file;
};


} // namespace Kernel
} // namespace Mantid

#endif /* MANTID_KERNEL_FILEDESCRIPTOR_H_ */
69 changes: 69 additions & 0 deletions Code/Mantid/Framework/Kernel/src/FileDescriptor.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
#include "MantidKernel/FileDescriptor.h"
#include "MantidKernel/Exception.h"

#include <Poco/File.h>
#include <Poco/Path.h>

namespace Mantid
{
namespace Kernel
{
//----------------------------------------------------------------------------------------------
// Public methods
//----------------------------------------------------------------------------------------------

/**
* @param filename A string containing a filename. The file must exist
* @throws std::invalid_argument if the filename is empty or the file does not exist
*/
FileDescriptor::FileDescriptor(const std::string & filename) :
m_filename(), m_extension(), m_file(NULL)
{
if(filename.empty())
{
throw std::invalid_argument("FileDescriptor() - Empty filename '" + filename + "'");
}
if(!Poco::File(filename).exists())
{
throw std::invalid_argument("FileDescriptor() - File '" + filename + "' does not exist");
}
initialize(filename);
}

/**
* Closes the file handle
*/
FileDescriptor::~FileDescriptor()
{
m_file.close();
}

/**
* Moves the stream pointer back to the start of the file, without
* reopening the file. Note that this will affect the stream that
* has been accessed using the stream() method
*/
void FileDescriptor::resetStreamToStart()
{
m_file.seekg(0);
}

//----------------------------------------------------------------------------------------------
// Private methods
//----------------------------------------------------------------------------------------------

/**
* Set the description fields and opens the file
* @param filename A string pointing to an existing file
*/
void FileDescriptor::initialize(const std::string& filename)
{
m_filename = filename;
m_extension = "." + Poco::Path(filename).getExtension();

m_file.open(m_filename.c_str(), std::ios::in | std::ios::binary);
if(!m_file) throw std::runtime_error("FileDescriptor::initialize - Cannot open file '" + filename + "' for reading");
}

} // namespace Kernel
} // namespace Mantid
99 changes: 99 additions & 0 deletions Code/Mantid/Framework/Kernel/test/FileDescriptorTest.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
#ifndef MANTID_KERNEL_FILEDESCRIPTORTEST_H_
#define MANTID_KERNEL_FILEDESCRIPTORTEST_H_

#include <cxxtest/TestSuite.h>

#include "MantidKernel/FileDescriptor.h"
#include "MantidKernel/ConfigService.h"

#include <Poco/Path.h>
#include <Poco/File.h>

using Mantid::Kernel::FileDescriptor;

class FileDescriptorTest : public CxxTest::TestSuite
{
public:
// This pair of boilerplate methods prevent the suite being created statically
// This means the constructor isn't called when running other tests
static FileDescriptorTest *createSuite() { return new FileDescriptorTest(); }
static void destroySuite( FileDescriptorTest *suite ) { delete suite; }

FileDescriptorTest()
{
using Mantid::Kernel::ConfigService;
auto dataPaths = ConfigService::Instance().getDataSearchDirs();
for(auto it = dataPaths.begin(); it != dataPaths.end(); ++it)
{
Poco::Path nxsPath(*it, "CNCS_7860_event.nxs");
if(Poco::File(nxsPath).exists()) m_testNexusPath = nxsPath.toString();
Poco::Path nonNxsPath(*it, "CSP79590.raw");
if(Poco::File(nonNxsPath).exists()) m_testNonNexusPath = nonNxsPath.toString();

if(!m_testNexusPath.empty() && !m_testNonNexusPath.empty()) break;
}
if(m_testNexusPath.empty() || m_testNonNexusPath.empty())
{
throw std::runtime_error("Unable to find test files for FileDescriptorTest. "
"The AutoTestData directory needs to be in the search path");
}
}

//===================== Success cases ============================================
void test_Constructor_With_Existing_File_Initializes_Description_Fields()
{
const std::string filename = m_testNexusPath;
FileDescriptor descr(filename);

TS_ASSERT_EQUALS(filename, descr.filename());
TS_ASSERT_EQUALS(".nxs", descr.extension());
}

void test_File_Is_Opened_After_Object_Is_Constructed()
{
FileDescriptor descr(m_testNexusPath);
TS_ASSERT(descr.data().is_open());
}

void test_Intial_Stream_Is_Positioned_At_Start_Of_File()
{
const std::string filename = m_testNexusPath;
FileDescriptor descr(filename);

auto & stream = descr.data();
long int streamPos = stream.tellg();

TS_ASSERT_EQUALS(0, streamPos);
}

void test_ResetStreamToStart_Repositions_Stream_Start_Of_File()
{
const std::string filename = m_testNexusPath;
FileDescriptor descr(filename);
auto & stream = descr.data();
char byte('0');
stream >> byte; // read byte from stream

TS_ASSERT_EQUALS(1, stream.tellg());
descr.resetStreamToStart();
TS_ASSERT_EQUALS(0, stream.tellg());
}

//===================== Failure cases ============================================
void test_Constructor_Throws_With_Empty_filename()
{
TS_ASSERT_THROWS(FileDescriptor(""), std::invalid_argument);
}

void test_Constructor_Throws_With_NonExistant_filename()
{
TS_ASSERT_THROWS(FileDescriptor("__ThisShouldBeANonExistantFile.txt"), std::invalid_argument);
}

private:
std::string m_testNexusPath;
std::string m_testNonNexusPath;
};


#endif /* MANTID_KERNEL_FILEDESCRIPTORTEST_H_ */

0 comments on commit 8681dff

Please sign in to comment.