<commit_before>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkVoxBoCUBImageIO.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "itkVoxBoCUBImageIO.h"
#include "itkIOCommon.h"
#include "itkExceptionObject.h"
#include "itkMetaDataObject.h"
#include "itkByteSwapper.h"
#include <itksys/SystemTools.hxx>
#include <iostream>
#include <list>
#include <string>
#include <math.h>
#include <time.h>


#include "itk_zlib.h"

namespace itk {


/**
 *
 * \class GenericCUBFileAdaptor
 *
 * \brief Reader and Writer for the VoxBo file format.
 *
 * A generic reader and writer object for VoxBo files. Basically it
 * provides uniform access to gzip and normal files
 *
 * \author Burstein, <NAME>.; Yushkevich, Paul; Gee, <NAME>. 
 *
 * This implementation was contributed as a paper to the Insight Journal
 * http://insight-journal.org/midas/handle.php?handle=1926/303
 *
 */
class GenericCUBFileAdaptor
{
public:
  GenericCUBFileAdaptor() {}
  virtual ~GenericCUBFileAdaptor() {}

  virtual unsigned char ReadByte() = 0;
  virtual void ReadData(void *data, unsigned long bytes) = 0;
  virtual void WriteData(const void *data, unsigned long bytes) = 0;

  std::string ReadHeader()
    {
    // Read everything up to the \f symbol
    itksys_ios::ostringstream oss;
    unsigned char byte = ReadByte();
    while(byte != '\f')
      {
      oss << byte;
      byte = ReadByte();
      }

    // Read the next byte
    unsigned char term = ReadByte();
    if(term == '\r')
      {
      term = ReadByte();
      }
    // Throw exception if term is not there
    if(term != '\n')
      {
      ExceptionObject exception;
      exception.SetDescription("Header is not terminated by newline.");
      throw exception;
      }

    // Return the header string
    return oss.str();
    }
};

/**
 * A reader for gzip files
 */

class CompressedCUBFileAdaptor : public GenericCUBFileAdaptor
{
public:
  CompressedCUBFileAdaptor(const char *file, const char *mode)
    {
    m_GzFile = ::gzopen(file, mode);
    if(m_GzFile == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be accessed");
      throw exception;
      }
    }

  virtual ~CompressedCUBFileAdaptor()
    {
    if(m_GzFile)
      {
      ::gzflush(m_GzFile,Z_FINISH);
            ::gzclose(m_GzFile);
      }
    }

  unsigned char ReadByte()
    {
    int byte = ::gzgetc(m_GzFile);
    if(byte < 0)
      {
      itksys_ios::ostringstream oss;
      oss << "Error reading byte from file at position: " << ::gztell(m_GzFile);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    return static_cast<unsigned char>(byte);
    }

  void ReadData(void *data, unsigned long bytes)
    {
    if(m_GzFile == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
      }

    size_t bread = ::gzread(m_GzFile, data, bytes);
    if( bread != bytes )
      {
      itksys_ios::ostringstream oss;
      oss << "File size does not match header: "
        << bytes << " bytes requested but only "
        << bread << " bytes available!" << std::endl
        << "At file position " << ::gztell(m_GzFile);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    }

  void WriteData(const void *data, unsigned long bytes)
    {
    if(m_GzFile == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be written");
      throw exception;
      }

    size_t bwritten = ::gzwrite(m_GzFile, (void *) data, bytes);
    if( bwritten != bytes )
      {
        ExceptionObject exception;
        exception.SetDescription("Could not write all bytes to file");
        std::cout<<"Could not write all bytes to file"<<std::endl;
        throw exception;
      }
     ::gzflush(m_GzFile,Z_SYNC_FLUSH);
    }

private:
  gzFile m_GzFile;
};


/**
 * A reader for non-gzip files
 */
class DirectCUBFileAdaptor : public GenericCUBFileAdaptor
{
public:
  DirectCUBFileAdaptor(const char *file, const char *mode)
    {
    m_File = fopen(file, mode);
    if(!m_File)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
      }
    }

  ~DirectCUBFileAdaptor()
    {
    if(m_File)
      {
      fclose(m_File);
      }
    }

  unsigned char ReadByte()
    {
    int byte = fgetc(m_File);
    if(byte == EOF)
      {
      itksys_ios::ostringstream oss;
      oss << "Error reading byte from file at position: " << ::ftell(m_File);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    return static_cast<unsigned char>(byte);
    }

  void ReadData(void *data, unsigned long bytes)
    {
    if(m_File == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
      }

    size_t bread = fread(data, 1, bytes, m_File);
    if( bread != bytes )
      {
      itksys_ios::ostringstream oss;
      oss << "File size does not match header: "
        << bytes << " bytes requested but only "
        << bread << " bytes available!" << std::endl
        << "At file position " << ftell(m_File);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    }

  void WriteData(const void *data, unsigned long bytes)
    {
    if(m_File == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be written");
      throw exception;
      }

    size_t bwritten = fwrite(data, 1, bytes, m_File);
    if( bwritten != bytes )
      {
      ExceptionObject exception;
      exception.SetDescription("Could not write all bytes to file");
      throw exception;
      }
    }

private:
  FILE *m_File;
};


/**
 * \class VoxBoCUBImageIOSwapHelper
 *
 * \brief A swap helper class, used to perform swapping for any input
 * data type.
 *
 */
template<typename TPixel> 
class VoxBoCUBImageIOSwapHelper
{
public:
  typedef ImageIOBase::ByteOrder ByteOrder;
  static void SwapIfNecessary(
    void *buffer, unsigned long numberOfBytes, ByteOrder dataByteOrder)
    {
    if ( dataByteOrder == ImageIOBase::LittleEndian )
      {
      ByteSwapper<TPixel>::SwapRangeFromSystemToLittleEndian(
        (TPixel*)buffer, numberOfBytes / sizeof(TPixel) );
      }
    else if ( dataByteOrder == ImageIOBase::BigEndian )
      {
      ByteSwapper<TPixel>::SwapRangeFromSystemToBigEndian(
        (TPixel *)buffer, numberOfBytes / sizeof(TPixel) );
      }
    }
};


// Strings
const char *VoxBoCUBImageIO::m_VB_IDENTIFIER_SYSTEM = "VB98";
const char *VoxBoCUBImageIO::m_VB_IDENTIFIER_FILETYPE = "CUB1";
const char *VoxBoCUBImageIO::m_VB_DIMENSIONS = "VoxDims(XYZ)";
const char *VoxBoCUBImageIO::m_VB_SPACING = "VoxSizes(XYZ)";
const char *VoxBoCUBImageIO::m_VB_ORIGIN = "Origin(XYZ)";
const char *VoxBoCUBImageIO::m_VB_DATATYPE = "DataType";
const char *VoxBoCUBImageIO::m_VB_BYTEORDER = "Byteorder";
const char *VoxBoCUBImageIO::m_VB_ORIENTATION = "Orientation";
const char *VoxBoCUBImageIO::m_VB_BYTEORDER_MSB = "msbfirst";
const char *VoxBoCUBImageIO::m_VB_BYTEORDER_LSB = "lsbfirst";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_BYTE = "Byte";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_INT = "Integer";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_FLOAT = "Float";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_DOUBLE = "Double";

/** Constructor */
VoxBoCUBImageIO::VoxBoCUBImageIO()
{
  InitializeOrientationMap();
  m_ByteOrder = BigEndian;
  m_Reader = NULL;
  m_Writer = NULL;
}


/** Destructor */
VoxBoCUBImageIO::~VoxBoCUBImageIO()
{
  if(m_Reader)
    {
    delete m_Reader;
    }

  if(m_Writer)
    {
    delete m_Writer;
    }
}

GenericCUBFileAdaptor *
VoxBoCUBImageIO::CreateReader(const char *filename)
{
  try
    {
    bool compressed;
    if(CheckExtension(filename, compressed))
      {
      if(compressed)
        {
        return new CompressedCUBFileAdaptor(filename, "rb");
        }
      else
        {
        return new DirectCUBFileAdaptor(filename, "rb");
        }
      }
    else
      {
      return NULL;
      }
    }
  catch(...)
    {
    return NULL;
    }
}

GenericCUBFileAdaptor *
VoxBoCUBImageIO::CreateWriter(const char *filename)
{
  try
    {
    bool compressed;
    if(CheckExtension(filename, compressed))
      {
      if(compressed)
        {
        return new CompressedCUBFileAdaptor(filename, "wb");
        }
      else
        {
        return new DirectCUBFileAdaptor(filename, "wb");
        }
      }
    else
      {
      return NULL;
      }
    }
  catch(...)
    {
    return NULL;
    }
}

bool VoxBoCUBImageIO::CanReadFile( const char* filename )
{
  // First check if the file can be read
  GenericCUBFileAdaptor *reader = CreateReader(filename);
  if(reader == NULL)
    {
    itkDebugMacro(<<"The file is not a valid CUB file");
    return false;
    }

  // Now check the content
  bool iscub = true;
  try
    {
    // Get the header
    std::istringstream iss(reader->ReadHeader());

    // Read the first two words
    std::string word;

    // Read the first line from the file
    iss >> word;
    if(word != m_VB_IDENTIFIER_SYSTEM)
      {
      iscub = false;
      }

    // Read the second line
    iss >> word;
    if(word != m_VB_IDENTIFIER_FILETYPE)
      {
      iscub = false;
      }
    }
  catch(...)
    {
    iscub = false;
    }

  delete reader;
  return iscub;
}

bool VoxBoCUBImageIO::CanWriteFile( const char * name )
{
  bool compressed;
  return CheckExtension(name, compressed);
}

void VoxBoCUBImageIO::Read(void* buffer)
{
  if(m_Reader == NULL)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
    }

  m_Reader->ReadData(buffer, GetImageSizeInBytes());
  this->SwapBytesIfNecessary(buffer, GetImageSizeInBytes());
}

/**
 *  Read Information about the VoxBoCUB file
 *  and put the cursor of the stream just before the first data pixel
 */
void VoxBoCUBImageIO::ReadImageInformation()
{
  // Make sure there is no other reader
  if(m_Reader)
    {
    delete m_Reader;
    }

  // Create a reader
  m_Reader = CreateReader(m_FileName.c_str());
  if(m_Reader == NULL)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
    }

  // Set the number of dimensions to three
  SetNumberOfDimensions(3);

  // Read the file header
  std::istringstream issHeader(m_Reader->ReadHeader());

  // Read every string in the header. Parse the strings that are special
  while(issHeader.good())
    {
    // Read a line from the stream
    char linebuffer[512];
    issHeader.getline(linebuffer, 512);

    // Get the key string
    std::istringstream iss(linebuffer);
    std::string key;

    // Read the key and strip the colon from it
    iss >> key;
    if(key[key.size() - 1] == ':')
      {
      // Strip the colon off the key
      key = key.substr(0, key.size() - 1);

      // Check if this is a relevant key
      if(key == m_VB_DIMENSIONS)
        {
        iss >> m_Dimensions[0];
        iss >> m_Dimensions[1];
        iss >> m_Dimensions[2];
        }

      else if(key == m_VB_SPACING)
        {
        iss >> m_Spacing[0];
        iss >> m_Spacing[1];
        iss >> m_Spacing[2];
        }

      else if(key == m_VB_ORIGIN)
        {
        double ox, oy, oz;
        iss >> ox; iss >> oy; iss >> oz;
        m_Origin[0] = -ox * m_Spacing[0];
        m_Origin[1] = -oy * m_Spacing[1];
        m_Origin[2] = -oz * m_Spacing[2];
        }

      else if(key == m_VB_DATATYPE)
        {
        std::string type;
        iss >> type;
        m_PixelType = SCALAR;
        if(type == m_VB_DATATYPE_BYTE)
          {
          m_ComponentType = UCHAR;
          }
        else if(type == m_VB_DATATYPE_INT)
          {
          m_ComponentType = USHORT;
          }
        else if(type == m_VB_DATATYPE_FLOAT)
          {
          m_ComponentType = FLOAT;
          }
        else if(type == m_VB_DATATYPE_DOUBLE)
          {
          m_ComponentType = DOUBLE;
          }
        }

      else if(key == m_VB_BYTEORDER)
        {
        std::string type;
        iss >> type;
        if(type == m_VB_BYTEORDER_MSB)
          {
          SetByteOrderToBigEndian();
          }
        else if(type == m_VB_BYTEORDER_LSB)
          {
          SetByteOrderToLittleEndian();
          }
        else
          {
          ExceptionObject exception(__FILE__, __LINE__);
          exception.SetDescription("Unknown byte order constant");
          throw exception;
          }
        }

      else if(key == m_VB_ORIENTATION)
        {
        std::string code;
        iss >> code;

        // Set the orientation code in the data dictionary
        OrientationMap::const_iterator it = m_OrientationMap.find(code);
        if(it != m_OrientationMap.end())
          {
          itk::MetaDataDictionary &dic =this->GetMetaDataDictionary();
          EncapsulateMetaData<OrientationFlags>(
            dic, ITK_CoordinateOrientation, it->second);
          }
        }

      else
        {
        // Encode the right hand side of the string in the meta-data dic
        std::string word;
        itksys_ios::ostringstream oss;
        while(iss >> word)
          {
          if(oss.str().size())
            {
            oss << " ";
            }
          oss << word;
          }
        itk::MetaDataDictionary &dic =this->GetMetaDataDictionary();
        EncapsulateMetaData<std::string>(dic, key, oss.str());
       }
      }
    }
}

void
VoxBoCUBImageIO
::WriteImageInformation(void)
{
  if(m_Writer == NULL)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
    }

  // Check that the number of dimensions is correct
  if(GetNumberOfDimensions() != 3)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("Unsupported number of dimensions");
    throw exception;
    }

  // Put together a header
  itksys_ios::ostringstream header;

  // Write the identifiers
  header << m_VB_IDENTIFIER_SYSTEM << std::endl;
  header << m_VB_IDENTIFIER_FILETYPE << std::endl;

    // Write the data type
  switch(m_ComponentType)
    {
    case CHAR:
    case UCHAR:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_BYTE << std::endl;
      break;
    case SHORT:
    case USHORT:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_INT << std::endl;
      break;
    case FLOAT:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_FLOAT << std::endl;
      break;
    case DOUBLE:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_DOUBLE << std::endl;
      break;
    default:
      ExceptionObject exception(__FILE__, __LINE__);
      exception.SetDescription("Unsupported pixel component type");
      throw exception;
    }

  // Write the image dimensions
  header << m_VB_DIMENSIONS << ":\t"
    << m_Dimensions[0] << "\t"
    << m_Dimensions[1] << "\t"
    << m_Dimensions[2] << std::endl;

  // Write the spacing
  header << m_VB_SPACING << ":\t"
    << m_Spacing[0] << "\t"
    << m_Spacing[1] << "\t"
    << m_Spacing[2] << std::endl;

  // Write the origin (have to convert to bytes)

  double x= -m_Origin[0] / m_Spacing[0];
  double y= -m_Origin[1] / m_Spacing[1];
  double z= -m_Origin[2] / m_Spacing[2];
  header << m_VB_ORIGIN << ":\t"
    << ((x>=0)?(int) (x+.5):(int) (x-.5)) << "\t"
    << ((y>=0)?(int) (y+.5):(int) (y-.5)) << "\t"
    << ((z>=0)?(int) (z+.5):(int) (z-.5)) << std::endl;


  // Write the byte order
  header << m_VB_BYTEORDER << ":\t" << ((ByteSwapper<short>::SystemIsBigEndian()) ? m_VB_BYTEORDER_MSB : m_VB_BYTEORDER_LSB) << std::endl;

  // Write the orientation code
  MetaDataDictionary &dic = GetMetaDataDictionary();
  OrientationFlags oflag;
  if(ExposeMetaData<OrientationFlags>(dic, ITK_CoordinateOrientation, oflag))
    {
    InverseOrientationMap::const_iterator it =
      m_InverseOrientationMap.find(oflag);
    if(it != m_InverseOrientationMap.end())
      {
      header << m_VB_ORIENTATION << ":\t" << it->second << std::endl;
      }
  }

  //Add CUB specific parameters to header from MetaDictionary

  std::vector<std::string> keys= dic.GetKeys();
  std::string word;
  for(size_t i=0; i<keys.size(); i++)<commit_msg>COMP: Fixing warning about uninitialized variable.<commit_after>/*=========================================================================

  Program:   Insight Segmentation & Registration Toolkit
  Module:    itkVoxBoCUBImageIO.cxx
  Language:  C++
  Date:      $Date$
  Version:   $Revision$

  Copyright (c) Insight Software Consortium. All rights reserved.
  See ITKCopyright.txt or http://www.itk.org/HTML/Copyright.htm for details.

     This software is distributed WITHOUT ANY WARRANTY; without even
     the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR
     PURPOSE.  See the above copyright notices for more information.

=========================================================================*/
#include "itkVoxBoCUBImageIO.h"
#include "itkIOCommon.h"
#include "itkExceptionObject.h"
#include "itkMetaDataObject.h"
#include "itkByteSwapper.h"
#include <itksys/SystemTools.hxx>
#include <iostream>
#include <list>
#include <string>
#include <math.h>
#include <time.h>


#include "itk_zlib.h"

namespace itk {


/**
 *
 * \class GenericCUBFileAdaptor
 *
 * \brief Reader and Writer for the VoxBo file format.
 *
 * A generic reader and writer object for VoxBo files. Basically it
 * provides uniform access to gzip and normal files
 *
 * \author <NAME>.; <NAME>; Gee, <NAME>. 
 *
 * This implementation was contributed as a paper to the Insight Journal
 * http://insight-journal.org/midas/handle.php?handle=1926/303
 *
 */
class GenericCUBFileAdaptor
{
public:
  GenericCUBFileAdaptor() {}
  virtual ~GenericCUBFileAdaptor() {}

  virtual unsigned char ReadByte() = 0;
  virtual void ReadData(void *data, unsigned long bytes) = 0;
  virtual void WriteData(const void *data, unsigned long bytes) = 0;

  std::string ReadHeader()
    {
    // Read everything up to the \f symbol
    itksys_ios::ostringstream oss;
    unsigned char byte = ReadByte();
    while(byte != '\f')
      {
      oss << byte;
      byte = ReadByte();
      }

    // Read the next byte
    unsigned char term = ReadByte();
    if(term == '\r')
      {
      term = ReadByte();
      }
    // Throw exception if term is not there
    if(term != '\n')
      {
      ExceptionObject exception;
      exception.SetDescription("Header is not terminated by newline.");
      throw exception;
      }

    // Return the header string
    return oss.str();
    }
};

/**
 * A reader for gzip files
 */

class CompressedCUBFileAdaptor : public GenericCUBFileAdaptor
{
public:
  CompressedCUBFileAdaptor(const char *file, const char *mode)
    {
    m_GzFile = ::gzopen(file, mode);
    if(m_GzFile == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be accessed");
      throw exception;
      }
    }

  virtual ~CompressedCUBFileAdaptor()
    {
    if(m_GzFile)
      {
      ::gzflush(m_GzFile,Z_FINISH);
            ::gzclose(m_GzFile);
      }
    }

  unsigned char ReadByte()
    {
    int byte = ::gzgetc(m_GzFile);
    if(byte < 0)
      {
      itksys_ios::ostringstream oss;
      oss << "Error reading byte from file at position: " << ::gztell(m_GzFile);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    return static_cast<unsigned char>(byte);
    }

  void ReadData(void *data, unsigned long bytes)
    {
    if(m_GzFile == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
      }

    size_t bread = ::gzread(m_GzFile, data, bytes);
    if( bread != bytes )
      {
      itksys_ios::ostringstream oss;
      oss << "File size does not match header: "
        << bytes << " bytes requested but only "
        << bread << " bytes available!" << std::endl
        << "At file position " << ::gztell(m_GzFile);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    }

  void WriteData(const void *data, unsigned long bytes)
    {
    if(m_GzFile == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be written");
      throw exception;
      }

    size_t bwritten = ::gzwrite(m_GzFile, (void *) data, bytes);
    if( bwritten != bytes )
      {
        ExceptionObject exception;
        exception.SetDescription("Could not write all bytes to file");
        std::cout<<"Could not write all bytes to file"<<std::endl;
        throw exception;
      }
     ::gzflush(m_GzFile,Z_SYNC_FLUSH);
    }

private:
  gzFile m_GzFile;
};


/**
 * A reader for non-gzip files
 */
class DirectCUBFileAdaptor : public GenericCUBFileAdaptor
{
public:
  DirectCUBFileAdaptor(const char *file, const char *mode)
    {
    m_File = fopen(file, mode);
    if(!m_File)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
      }
    }

  ~DirectCUBFileAdaptor()
    {
    if(m_File)
      {
      fclose(m_File);
      }
    }

  unsigned char ReadByte()
    {
    int byte = fgetc(m_File);
    if(byte == EOF)
      {
      itksys_ios::ostringstream oss;
      oss << "Error reading byte from file at position: " << ::ftell(m_File);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    return static_cast<unsigned char>(byte);
    }

  void ReadData(void *data, unsigned long bytes)
    {
    if(m_File == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be read");
      throw exception;
      }

    size_t bread = fread(data, 1, bytes, m_File);
    if( bread != bytes )
      {
      itksys_ios::ostringstream oss;
      oss << "File size does not match header: "
        << bytes << " bytes requested but only "
        << bread << " bytes available!" << std::endl
        << "At file position " << ftell(m_File);
      ExceptionObject exception;
      exception.SetDescription(oss.str().c_str());
      throw exception;
      }
    }

  void WriteData(const void *data, unsigned long bytes)
    {
    if(m_File == NULL)
      {
      ExceptionObject exception;
      exception.SetDescription("File cannot be written");
      throw exception;
      }

    size_t bwritten = fwrite(data, 1, bytes, m_File);
    if( bwritten != bytes )
      {
      ExceptionObject exception;
      exception.SetDescription("Could not write all bytes to file");
      throw exception;
      }
    }

private:
  FILE *m_File;
};


/**
 * \class VoxBoCUBImageIOSwapHelper
 *
 * \brief A swap helper class, used to perform swapping for any input
 * data type.
 *
 */
template<typename TPixel> 
class VoxBoCUBImageIOSwapHelper
{
public:
  typedef ImageIOBase::ByteOrder ByteOrder;
  static void SwapIfNecessary(
    void *buffer, unsigned long numberOfBytes, ByteOrder dataByteOrder)
    {
    if ( dataByteOrder == ImageIOBase::LittleEndian )
      {
      ByteSwapper<TPixel>::SwapRangeFromSystemToLittleEndian(
        (TPixel*)buffer, numberOfBytes / sizeof(TPixel) );
      }
    else if ( dataByteOrder == ImageIOBase::BigEndian )
      {
      ByteSwapper<TPixel>::SwapRangeFromSystemToBigEndian(
        (TPixel *)buffer, numberOfBytes / sizeof(TPixel) );
      }
    }
};


// Strings
const char *VoxBoCUBImageIO::m_VB_IDENTIFIER_SYSTEM = "VB98";
const char *VoxBoCUBImageIO::m_VB_IDENTIFIER_FILETYPE = "CUB1";
const char *VoxBoCUBImageIO::m_VB_DIMENSIONS = "VoxDims(XYZ)";
const char *VoxBoCUBImageIO::m_VB_SPACING = "VoxSizes(XYZ)";
const char *VoxBoCUBImageIO::m_VB_ORIGIN = "Origin(XYZ)";
const char *VoxBoCUBImageIO::m_VB_DATATYPE = "DataType";
const char *VoxBoCUBImageIO::m_VB_BYTEORDER = "Byteorder";
const char *VoxBoCUBImageIO::m_VB_ORIENTATION = "Orientation";
const char *VoxBoCUBImageIO::m_VB_BYTEORDER_MSB = "msbfirst";
const char *VoxBoCUBImageIO::m_VB_BYTEORDER_LSB = "lsbfirst";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_BYTE = "Byte";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_INT = "Integer";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_FLOAT = "Float";
const char *VoxBoCUBImageIO::m_VB_DATATYPE_DOUBLE = "Double";

/** Constructor */
VoxBoCUBImageIO::VoxBoCUBImageIO()
{
  InitializeOrientationMap();
  m_ByteOrder = BigEndian;
  m_Reader = NULL;
  m_Writer = NULL;
}


/** Destructor */
VoxBoCUBImageIO::~VoxBoCUBImageIO()
{
  if(m_Reader)
    {
    delete m_Reader;
    }

  if(m_Writer)
    {
    delete m_Writer;
    }
}

GenericCUBFileAdaptor *
VoxBoCUBImageIO::CreateReader(const char *filename)
{
  try
    {
    bool compressed;
    if(CheckExtension(filename, compressed))
      {
      if(compressed)
        {
        return new CompressedCUBFileAdaptor(filename, "rb");
        }
      else
        {
        return new DirectCUBFileAdaptor(filename, "rb");
        }
      }
    else
      {
      return NULL;
      }
    }
  catch(...)
    {
    return NULL;
    }
}

GenericCUBFileAdaptor *
VoxBoCUBImageIO::CreateWriter(const char *filename)
{
  try
    {
    bool compressed;
    if(CheckExtension(filename, compressed))
      {
      if(compressed)
        {
        return new CompressedCUBFileAdaptor(filename, "wb");
        }
      else
        {
        return new DirectCUBFileAdaptor(filename, "wb");
        }
      }
    else
      {
      return NULL;
      }
    }
  catch(...)
    {
    return NULL;
    }
}

bool VoxBoCUBImageIO::CanReadFile( const char* filename )
{
  // First check if the file can be read
  GenericCUBFileAdaptor *reader = CreateReader(filename);
  if(reader == NULL)
    {
    itkDebugMacro(<<"The file is not a valid CUB file");
    return false;
    }

  // Now check the content
  bool iscub = true;
  try
    {
    // Get the header
    std::istringstream iss(reader->ReadHeader());

    // Read the first two words
    std::string word;

    // Read the first line from the file
    iss >> word;
    if(word != m_VB_IDENTIFIER_SYSTEM)
      {
      iscub = false;
      }

    // Read the second line
    iss >> word;
    if(word != m_VB_IDENTIFIER_FILETYPE)
      {
      iscub = false;
      }
    }
  catch(...)
    {
    iscub = false;
    }

  delete reader;
  return iscub;
}

bool VoxBoCUBImageIO::CanWriteFile( const char * name )
{
  bool compressed;
  return CheckExtension(name, compressed);
}

void VoxBoCUBImageIO::Read(void* buffer)
{
  if(m_Reader == NULL)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
    }

  m_Reader->ReadData(buffer, GetImageSizeInBytes());
  this->SwapBytesIfNecessary(buffer, GetImageSizeInBytes());
}

/**
 *  Read Information about the VoxBoCUB file
 *  and put the cursor of the stream just before the first data pixel
 */
void VoxBoCUBImageIO::ReadImageInformation()
{
  // Make sure there is no other reader
  if(m_Reader)
    {
    delete m_Reader;
    }

  // Create a reader
  m_Reader = CreateReader(m_FileName.c_str());
  if(m_Reader == NULL)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
    }

  // Set the number of dimensions to three
  SetNumberOfDimensions(3);

  // Read the file header
  std::istringstream issHeader(m_Reader->ReadHeader());

  // Read every string in the header. Parse the strings that are special
  while(issHeader.good())
    {
    // Read a line from the stream
    char linebuffer[512];
    issHeader.getline(linebuffer, 512);

    // Get the key string
    std::istringstream iss(linebuffer);
    std::string key;

    // Read the key and strip the colon from it
    iss >> key;
    if(key[key.size() - 1] == ':')
      {
      // Strip the colon off the key
      key = key.substr(0, key.size() - 1);

      // Check if this is a relevant key
      if(key == m_VB_DIMENSIONS)
        {
        iss >> m_Dimensions[0];
        iss >> m_Dimensions[1];
        iss >> m_Dimensions[2];
        }

      else if(key == m_VB_SPACING)
        {
        iss >> m_Spacing[0];
        iss >> m_Spacing[1];
        iss >> m_Spacing[2];
        }

      else if(key == m_VB_ORIGIN)
        {
        double ox, oy, oz;
        iss >> ox; iss >> oy; iss >> oz;
        m_Origin[0] = -ox * m_Spacing[0];
        m_Origin[1] = -oy * m_Spacing[1];
        m_Origin[2] = -oz * m_Spacing[2];
        }

      else if(key == m_VB_DATATYPE)
        {
        std::string type;
        iss >> type;
        m_PixelType = SCALAR;
        if(type == m_VB_DATATYPE_BYTE)
          {
          m_ComponentType = UCHAR;
          }
        else if(type == m_VB_DATATYPE_INT)
          {
          m_ComponentType = USHORT;
          }
        else if(type == m_VB_DATATYPE_FLOAT)
          {
          m_ComponentType = FLOAT;
          }
        else if(type == m_VB_DATATYPE_DOUBLE)
          {
          m_ComponentType = DOUBLE;
          }
        }

      else if(key == m_VB_BYTEORDER)
        {
        std::string type;
        iss >> type;
        if(type == m_VB_BYTEORDER_MSB)
          {
          SetByteOrderToBigEndian();
          }
        else if(type == m_VB_BYTEORDER_LSB)
          {
          SetByteOrderToLittleEndian();
          }
        else
          {
          ExceptionObject exception(__FILE__, __LINE__);
          exception.SetDescription("Unknown byte order constant");
          throw exception;
          }
        }

      else if(key == m_VB_ORIENTATION)
        {
        std::string code;
        iss >> code;

        // Set the orientation code in the data dictionary
        OrientationMap::const_iterator it = m_OrientationMap.find(code);
        if(it != m_OrientationMap.end())
          {
          itk::MetaDataDictionary &dic =this->GetMetaDataDictionary();
          EncapsulateMetaData<OrientationFlags>(
            dic, ITK_CoordinateOrientation, it->second);
          }
        }

      else
        {
        // Encode the right hand side of the string in the meta-data dic
        std::string word;
        itksys_ios::ostringstream oss;
        while(iss >> word)
          {
          if(oss.str().size())
            {
            oss << " ";
            }
          oss << word;
          }
        itk::MetaDataDictionary &dic =this->GetMetaDataDictionary();
        EncapsulateMetaData<std::string>(dic, key, oss.str());
       }
      }
    }
}

void
VoxBoCUBImageIO
::WriteImageInformation(void)
{
  if(m_Writer == NULL)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("File cannot be read");
    throw exception;
    }

  // Check that the number of dimensions is correct
  if(GetNumberOfDimensions() != 3)
    {
    ExceptionObject exception(__FILE__, __LINE__);
    exception.SetDescription("Unsupported number of dimensions");
    throw exception;
    }

  // Put together a header
  itksys_ios::ostringstream header;

  // Write the identifiers
  header << m_VB_IDENTIFIER_SYSTEM << std::endl;
  header << m_VB_IDENTIFIER_FILETYPE << std::endl;

    // Write the data type
  switch(m_ComponentType)
    {
    case CHAR:
    case UCHAR:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_BYTE << std::endl;
      break;
    case SHORT:
    case USHORT:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_INT << std::endl;
      break;
    case FLOAT:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_FLOAT << std::endl;
      break;
    case DOUBLE:
      header << m_VB_DATATYPE << ":\t" << m_VB_DATATYPE_DOUBLE << std::endl;
      break;
    default:
      ExceptionObject exception(__FILE__, __LINE__);
      exception.SetDescription("Unsupported pixel component type");
      throw exception;
    }

  // Write the image dimensions
  header << m_VB_DIMENSIONS << ":\t"
    << m_Dimensions[0] << "\t"
    << m_Dimensions[1] << "\t"
    << m_Dimensions[2] << std::endl;

  // Write the spacing
  header << m_VB_SPACING << ":\t"
    << m_Spacing[0] << "\t"
    << m_Spacing[1] << "\t"
    << m_Spacing[2] << std::endl;

  // Write the origin (have to convert to bytes)

  double x= -m_Origin[0] / m_Spacing[0];
  double y= -m_Origin[1] / m_Spacing[1];
  double z= -m_Origin[2] / m_Spacing[2];
  header << m_VB_ORIGIN << ":\t"
    << ((x>=0)?(int) (x+.5):(int) (x-.5)) << "\t"
    << ((y>=0)?(int) (y+.5):(int) (y-.5)) << "\t"
    << ((z>=0)?(int) (z+.5):(int) (z-.5)) << std::endl;


  // Write the byte order
  header << m_VB_BYTEORDER << ":\t" << ((ByteSwapper<short>::SystemIsBigEndian()) ? m_VB_BYTEORDER_MSB : m_VB_BYTEORDER_LSB) << std::endl;

  // Write the orientation code
  MetaDataDictionary &dic = GetMetaDataDictionary();
  OrientationFlags oflag = SpatialOrientation::ITK_COORDINATE_ORIENTATION_INVALID;
  if(ExposeMetaData<OrientationFlags>(dic, ITK_CoordinateOrientation, oflag))
    {
    InverseOrientationMap::const_iterator it =
      m_InverseOrientationMap.find(oflag);
    if(it != m_InverseOrientationMap.end())
      {
      header << m_VB_ORIENTATION << ":\t" << it->second << std::endl;
      }
  }

  //Add CUB specific parameters to header from MetaDictionary

  std::vector<std::string> keys= dic.GetKeys();
  std::string word;
  for(size_t i=0; i<keys.size(); i++)
{'fraction_non_alphanumeric': 0.12683370496420607, 'fraction_numerical': 0.0035793920901302663, 'mean_word_length': 2.6141448414802246, 'pattern_counts': {'":': 18, '<': 356, '<?xml version=': 0, '>': 126, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5811467', 'n_tokens_mistral': 11329, 'n_tokens_neox': 11071, 'n_words': 2591}