Skip to content

File InputStreamReader.h

File List > detail > tools > InputStreamReader.h

Go to the documentation of this file

// Copyright (c) 2012-2013, IGN France.
// Copyright (c) 2012-2022, Oslandia.
// SPDX-License-Identifier: LGPL-2.0-or-later

#ifndef _SFCGAL_TOOLS_INPUTSTREAMREADER_H_
#define _SFCGAL_TOOLS_INPUTSTREAMREADER_H_

#include "SFCGAL/config.h"

#include <cctype>
#include <sstream>
#include <stack>
#include <string>

namespace SFCGAL {
namespace tools {

template <typename CharType>
class BasicInputStreamReader;

typedef BasicInputStreamReader<char> InputStreamReader;
typedef BasicInputStreamReader<wchar_t> WInputStreamReader;

template <typename CharType>
class BasicInputStreamReader {
public:
  typedef CharType                                         char_type;
  typedef typename std::basic_string<char_type>            string_type;
  typedef typename std::basic_istream<char_type>           istream_type;
  typedef typename std::basic_istream<char_type>::pos_type pos_type;

  BasicInputStreamReader(istream_type &s, bool skipWhiteSpaces = true)
      : _s(s), _states(), _skipWhiteSpaces(skipWhiteSpaces)
  {
    _s >> std::noskipws;
  }

  bool
  match(char_type const &c)
  {
    begin();

    if (_skipWhiteSpaces) {
      skipWhiteSpaces();
    }

    if (!_s.eof() && _s.get() == c) {
      commit();
      return true;
    } else {
      rollback();
      return false;
    }
  }

  bool
  imatch(char_type const &c)
  {
    begin();

    if (_skipWhiteSpaces) {
      skipWhiteSpaces();
    }

    if (!_s.eof() && ::tolower(_s.get()) == ::tolower(c)) {
      commit();
      return true;
    } else {
      rollback();
      return false;
    }
  }

  bool
  match(string_type const &str)
  {
    begin();

    if (_skipWhiteSpaces) {
      skipWhiteSpaces();
    }

    for (typename string_type::const_iterator it = str.begin(); it != str.end();
         ++it) {
      if (!_s.eof() && _s.get() == *it) {
        continue;
      }

      rollback();
      return false;
    }

    commit();
    return true;
  }

  bool
  imatch(string_type const &str)
  {
    begin();

    if (_skipWhiteSpaces) {
      skipWhiteSpaces();
    }

    for (typename string_type::const_iterator it = str.begin(); it != str.end();
         ++it) {
      if (!_s.eof() && ::tolower(_s.get()) == ::tolower(*it)) {
        continue;
      }

      rollback();
      return false;
    }

    commit();
    return true;
  }

  template <typename T>
  bool
  read(T &value)
  {
    begin();

    if (_skipWhiteSpaces) {
      skipWhiteSpaces();
    }

    if (_s >> CGAL::iformat(value)) {
      commit();
      return true;
    } else {
      rollback();
      return false;
    }
  }

  auto
  readBytes(std::string &buffer, size_t bytesToRead) -> void
  {
    begin();

    _s.read(&buffer[0], bytesToRead);

    commit();
  }
  void
  begin()
  {
    _states.push(_s.tellg());
  }
  void
  commit()
  {
    _states.pop();
  }
  void
  rollback()
  {
    _s.seekg(_states.top());
    _s.clear();
    _states.pop();
  }

  bool
  eof() const
  {
    return _s.eof() || (_s.peek() == std::char_traits<char_type>::eof());
  }

  inline istream_type &
  s()
  {
    return _s;
  }
  inline istream_type const &
  s() const
  {
    return _s;
  }

  string_type
  context(size_t nMax = 20)
  {
    begin();
    std::basic_ostringstream<char_type> oss;

    for (size_t i = 0; i < nMax; i++) {
      if (eof()) {
        break;
      }

      oss << (char_type)s().get();
    }

    if (!eof()) {
      oss << "...";
    }

    rollback();
    return oss.str();
  }

private:
  istream_type &_s;
  std::stack<pos_type> _states;
  bool _skipWhiteSpaces;

  void
  skipWhiteSpaces()
  {
    while (!_s.eof() && std::isspace(_s.peek())) {
      _s.get();
      continue;
    }
  }

  BasicInputStreamReader(BasicInputStreamReader const &other);
};

} // namespace tools
} // namespace SFCGAL

#endif