<commit_before>#pragma once
#ifndef WTL_FILESYSTEM_HPP_
#define WTL_FILESYSTEM_HPP_

#ifdef _WIN32
  #include <direct.h>
  #include <io.h>
  #define mkdir(path, mode) _mkdir(path)
  #define chdir _chdir
  #define getcwd _getcwd
  #define access _access
  #define F_OK 0
  #include <algorithm> // replace()
#else
  #include <sys/stat.h>
  #include <unistd.h>
#endif
#include <cerrno>
#include <stdexcept>
#include <string>
#include <ostream>

namespace wtl {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
namespace filesystem {

class path {
  public:
    using value_type = char;
#ifdef _WIN32
    static constexpr value_type preferred_separator = '\\';
#else
    static constexpr value_type preferred_separator = '/';
#endif
    using string_type = std::basic_string<value_type>;

    path() = default;
    path(const path&) = default;
    path(path&&) = default;
    ~path() = default;

    template <class T>
    path(const T& x): native_(x) {to_native(native_);}
    path(string_type&& x): native_(std::move(x)) {to_native(native_);}

    path parent_path() const {
        auto pos = native_.find_last_of(preferred_separator);
        if (pos == string_type::npos) return path();
        if (pos == 0u) return path("/");
        return path(native_.substr(0u, pos));
    }
    path filename() const {
        auto pos = native_.find_last_of(preferred_separator);
        return path(native_.substr(++pos));
    }
    path stem() const {
        auto name = filename().native();
        auto pos = name.find_last_of('.');
        if (pos == string_type::npos || pos == 0u) return path(name);
        if (pos == name.size() - 1u && name == "..") return path(name);
        return path(name.substr(0u, pos));
    }
    path extension() const {
        auto name = filename().native();
        auto pos = name.find_last_of('.');
        if (pos == string_type::npos || pos == 0u) return path();
        if (pos == name.size() - 1u && name == "..") return path();
        return path(name.substr(pos));
    }
    path& append(const path& p) {
        if (p.is_absolute()) {
            native_ = p.native_;
        } else {
            if (native_.back() != preferred_separator) {
                native_ += preferred_separator;
            }
            native_ += p.native();
        }
        return *this;
    }
    path& operator/=(const path& p) {
        return append(p);
    }
    path& concat(const path& p) {
        native_ += p.native();
        return *this;
    }
    path& operator+=(const path& p) {
        return concat(p);
    }
    bool is_absolute() const {return native_.front() == preferred_separator;}
    bool is_relative() const {return !is_absolute();}
    const string_type& native() const noexcept {return native_;}
    const value_type* c_str() const noexcept {return native_.c_str();}
    operator string_type() const noexcept {return native_;}
    std::string string() const noexcept {return native_;}
    std::string generic_string() const noexcept {
        std::string copy(native_);
        to_generic(copy);
        return copy;
    }
  private:
#ifdef _WIN32
    void to_native(string_type& data) const {
        std::replace(data.begin(), data.end(), '/', preferred_separator);
    }
    void to_generic(string_type& data) const {
        std::replace(data.begin(), data.end(), preferred_separator, '/');
    }
#else
    void to_native(string_type&) const noexcept {}
    void to_generic(string_type&) const noexcept {}
#endif
    string_type native_;
};

inline path operator/(const path& lhs, const path& rhs) {
    return path(lhs) /= rhs;
}
inline bool operator==(const path& lhs, const path& rhs) noexcept {
    return lhs.native() == rhs.native();
}
inline bool operator!=(const path& lhs, const path& rhs) noexcept {
    return lhs.native() != rhs.native();
}
inline std::ostream& operator<<(std::ostream& ost, const path& p) {
    return ost << '"' << p.string() << '"';
}

inline bool create_directory(const path& p) {
    const int status = ::mkdir(p.c_str(), 0755);
    if (status && errno != EEXIST) {
        throw std::runtime_error(p.native());
    }
    return status == 0;
}

inline void current_path(const path& p) {
    if (::chdir(p.c_str())) {
        throw std::runtime_error(p.native());
    }
}

inline path current_path() {
    char buffer[1024];
    if (!::getcwd(buffer, sizeof(buffer))) {
        throw std::runtime_error(buffer);
    }
    return path(buffer);
}

inline bool exists(const path& p) {
    return !::access(p.c_str(), F_OK);
}

} // namespace filesystem
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

// RAII
class ChDir {
  public:
    ChDir(const std::string& dst, bool mkdir=false) {
        if (dst.empty() || dst == ".") return;
        if (mkdir) {
            filesystem::create_directory(dst);
        }
        filesystem::current_path(dst);
    }
    ~ChDir() {
        filesystem::current_path(origin_);
    }
  private:
    const filesystem::path origin_ = filesystem::current_path();
};

} // namespace wtl

#endif /* WTL_FILESYSTEM_HPP_ */
<commit_msg>:sparkles: Add operator= and empty() to fs::path<commit_after>#pragma once
#ifndef WTL_FILESYSTEM_HPP_
#define WTL_FILESYSTEM_HPP_

#ifdef _WIN32
  #include <direct.h>
  #include <io.h>
  #define mkdir(path, mode) _mkdir(path)
  #define chdir _chdir
  #define getcwd _getcwd
  #define access _access
  #define F_OK 0
  #include <algorithm> // replace()
#else
  #include <sys/stat.h>
  #include <unistd.h>
#endif
#include <cerrno>
#include <stdexcept>
#include <string>
#include <ostream>

namespace wtl {
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////
namespace filesystem {

class path {
  public:
    using value_type = char;
#ifdef _WIN32
    static constexpr value_type preferred_separator = '\\';
#else
    static constexpr value_type preferred_separator = '/';
#endif
    using string_type = std::basic_string<value_type>;

    path() = default;
    path(const path&) = default;
    path(path&&) = default;
    path(string_type&& x): native_(std::move(x)) {to_native(native_);}
    template <class T>
    path(const T& x): native_(x) {to_native(native_);}

    ~path() = default;

    path& operator=(const path&) = default;
    path& operator=(path&&) = default;
    path& operator=(string_type&& s) {return *this = path(std::move(s));}
    template <class T>
    path& operator=(const T& s) {return *this = path(s);}

    path& append(const path& p) {
        if (p.is_absolute()) {
            native_ = p.native_;
        } else {
            if (native_.back() != preferred_separator) {
                native_ += preferred_separator;
            }
            native_ += p.native();
        }
        return *this;
    }
    path& operator/=(const path& p) {
        return append(p);
    }
    path& concat(const path& p) {
        native_ += p.native();
        return *this;
    }
    path& operator+=(const path& p) {
        return concat(p);
    }

    path parent_path() const {
        auto pos = native_.find_last_of(preferred_separator);
        if (pos == string_type::npos) return path();
        if (pos == 0u) return path("/");
        return path(native_.substr(0u, pos));
    }
    path filename() const {
        auto pos = native_.find_last_of(preferred_separator);
        return path(native_.substr(++pos));
    }
    path stem() const {
        auto name = filename().native();
        auto pos = name.find_last_of('.');
        if (pos == string_type::npos || pos == 0u) return path(name);
        if (pos == name.size() - 1u && name == "..") return path(name);
        return path(name.substr(0u, pos));
    }
    path extension() const {
        auto name = filename().native();
        auto pos = name.find_last_of('.');
        if (pos == string_type::npos || pos == 0u) return path();
        if (pos == name.size() - 1u && name == "..") return path();
        return path(name.substr(pos));
    }

    bool empty() const noexcept {return native_.empty();}
    bool is_absolute() const {return native_.front() == preferred_separator;}
    bool is_relative() const {return !is_absolute();}

    const string_type& native() const noexcept {return native_;}
    const value_type* c_str() const noexcept {return native_.c_str();}
    operator string_type() const noexcept {return native_;}
    std::string string() const noexcept {return native_;}
    std::string generic_string() const noexcept {
        std::string copy(native_);
        to_generic(copy);
        return copy;
    }

  private:
#ifdef _WIN32
    void to_native(string_type& data) const {
        std::replace(data.begin(), data.end(), '/', preferred_separator);
    }
    void to_generic(string_type& data) const {
        std::replace(data.begin(), data.end(), preferred_separator, '/');
    }
#else
    void to_native(string_type&) const noexcept {}
    void to_generic(string_type&) const noexcept {}
#endif
    string_type native_;
};

inline path operator/(const path& lhs, const path& rhs) {
    return path(lhs) /= rhs;
}
inline bool operator==(const path& lhs, const path& rhs) noexcept {
    return lhs.native() == rhs.native();
}
inline bool operator!=(const path& lhs, const path& rhs) noexcept {
    return lhs.native() != rhs.native();
}
inline std::ostream& operator<<(std::ostream& ost, const path& p) {
    return ost << '"' << p.string() << '"';
}

inline bool create_directory(const path& p) {
    const int status = ::mkdir(p.c_str(), 0755);
    if (status && errno != EEXIST) {
        throw std::runtime_error(p.native());
    }
    return status == 0;
}

inline void current_path(const path& p) {
    if (::chdir(p.c_str())) {
        throw std::runtime_error(p.native());
    }
}

inline path current_path() {
    char buffer[1024];
    if (!::getcwd(buffer, sizeof(buffer))) {
        throw std::runtime_error(buffer);
    }
    return path(buffer);
}

inline bool exists(const path& p) {
    return !::access(p.c_str(), F_OK);
}

} // namespace filesystem
/////////1/////////2/////////3/////////4/////////5/////////6/////////7/////////

// RAII
class ChDir {
  public:
    ChDir(const std::string& dst, bool mkdir=false) {
        if (dst.empty() || dst == ".") return;
        if (mkdir) {
            filesystem::create_directory(dst);
        }
        filesystem::current_path(dst);
    }
    ~ChDir() {
        filesystem::current_path(origin_);
    }
  private:
    const filesystem::path origin_ = filesystem::current_path();
};

} // namespace wtl

#endif /* WTL_FILESYSTEM_HPP_ */
<|endoftext|>


<commit_before>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "GenericHDF5Format.h"

#include <DataExchangeFormat.h>
#include <DataSource.h>
#include <Hdf5SubsampleWidget.h>
#include <Utilities.h>

#include <h5cpp/h5readwrite.h>
#include <h5cpp/h5vtktypemaps.h>

#include <QDialog>
#include <QDialogButtonBox>
#include <QInputDialog>
#include <QStringList>
#include <QVBoxLayout>

#include <vtkDataArray.h>
#include <vtkImageData.h>
#include <vtkPointData.h>

#include <string>
#include <vector>

#include <iostream>

using std::cerr;
using std::cout;
using std::endl;

namespace tomviz {

template <typename T>
void ReorderArrayC(T* in, T* out, int dim[3])
{
  for (int i = 0; i < dim[0]; ++i) {
    for (int j = 0; j < dim[1]; ++j) {
      for (int k = 0; k < dim[2]; ++k) {
        out[static_cast<size_t>(i * dim[1] + j) * dim[2] + k] =
          in[static_cast<size_t>(k * dim[1] + j) * dim[0] + i];
      }
    }
  }
}

template <typename T>
void ReorderArrayF(T* in, T* out, int dim[3])
{
  for (int i = 0; i < dim[0]; ++i) {
    for (int j = 0; j < dim[1]; ++j) {
      for (int k = 0; k < dim[2]; ++k) {
        out[static_cast<size_t>(k * dim[1] + j) * dim[0] + i] =
          in[static_cast<size_t>(i * dim[1] + j) * dim[2] + k];
      }
    }
  }
}

bool GenericHDF5Format::addScalarArray(h5::H5ReadWrite& reader,
                                       const std::string& path,
                                       vtkImageData* image,
                                       const std::string& name)
{
  // Get the type of the data
  h5::H5ReadWrite::DataType type = reader.dataType(path);
  int vtkDataType = h5::H5VtkTypeMaps::dataTypeToVtk(type);

  // Get the dimensions
  std::vector<int> dims = reader.getDimensions(path);

  // Set up the strides and counts
  int strides[3] = { 1, 1, 1 };
  size_t start[3], counts[3];
  if (DataSource::wasSubsampled(image)) {
    // If the main image was subsampled, we need to use the same
    // subsampling for the scalars
    DataSource::subsampleStrides(image, strides);
    int bs[6];
    DataSource::subsampleVolumeBounds(image, bs);

    for (int i = 0; i < 3; ++i) {
      start[i] = static_cast<size_t>(bs[i * 2]);
      counts[i] = (bs[i * 2 + 1] - start[i]) / strides[i];
    }
  } else {
    for (int i = 0; i < 3; ++i) {
      start[i] = 0;
      counts[i] = dims[i];
    }
  }

  // vtk requires the counts to be an int array
  int vtkCounts[3];
  for (int i = 0; i < 3; ++i)
    vtkCounts[i] = counts[i];

  vtkNew<vtkImageData> tmp;
  tmp->SetDimensions(&vtkCounts[0]);
  tmp->AllocateScalars(vtkDataType, 1);

  if (!reader.readData(path, type, tmp->GetScalarPointer(), strides, start,
                       counts)) {
    std::cerr << "Failed to read the data\n";
    return false;
  }

  auto* array = vtkAbstractArray::CreateArray(vtkDataType);
  array->SetNumberOfTuples(counts[0] * counts[1] * counts[2]);
  array->SetName(name.c_str());
  image->GetPointData()->AddArray(array);

  // HDF5 typically stores data as row major order.
  // VTK expects column major order.
  auto inPtr = tmp->GetPointData()->GetScalars()->GetVoidPointer(0);
  auto outPtr = array->GetVoidPointer(0);
  switch (vtkDataType) {
    vtkTemplateMacro(tomviz::ReorderArrayF(reinterpret_cast<VTK_TT*>(inPtr),
                                           reinterpret_cast<VTK_TT*>(outPtr),
                                           &vtkCounts[0]));
    default:
      cout << "Generic HDF5 Format: Unknown data type" << endl;
  }
  image->Modified();

  return true;
}

bool GenericHDF5Format::isDataExchange(const std::string& fileName)
{
  using h5::H5ReadWrite;
  H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::ReadOnly;
  H5ReadWrite reader(fileName.c_str(), mode);

  // If /exchange/data is a dataset, assume this is a DataExchangeFormat
  return reader.isDataSet("/exchange/data");
}

bool GenericHDF5Format::isFxi(const std::string& fileName)
{
  using h5::H5ReadWrite;
  H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::ReadOnly;
  H5ReadWrite reader(fileName.c_str(), mode);

  // Look for a few keys, loosely defined BNL FXI data format.
  return reader.isDataSet("/img_tomo") && reader.isDataSet("/img_bkg");
}

bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader,
                                   const std::string& path, vtkImageData* image,
                                   const QVariantMap& options)
{
  // Get the type of the data
  h5::H5ReadWrite::DataType type = reader.dataType(path);
  int vtkDataType = h5::H5VtkTypeMaps::dataTypeToVtk(type);

  // This is the easiest way I could find to get the size of the type
  int size = vtkDataArray::GetDataTypeSize(vtkDataType);

  // Get the dimensions
  std::vector<int> dims = reader.getDimensions(path);

  int bs[6] = { -1, -1, -1, -1, -1, -1 };
  int strides[3] = { 1, 1, 1 };
  if (options.contains("subsampleVolumeBounds")) {
    // Get the subsample volume bounds if the caller specified them
    QVariantList list = options["subsampleVolumeBounds"].toList();
    for (int i = 0; i < list.size() && i < 6; ++i)
      bs[i] = list[i].toInt();
    DataSource::setWasSubsampled(image, true);
    DataSource::setSubsampleVolumeBounds(image, bs);
  } else {
    // Set it to the defaults
    for (int i = 0; i < 3; ++i) {
      bs[i * 2] = 0;
      bs[i * 2 + 1] = dims[i];
    }
  }

  if (options.contains("subsampleStrides")) {
    // Get the strides if the caller specified them
    QVariantList list = options["subsampleStrides"].toList();
    for (int i = 0; i < list.size() && i < 3; ++i) {
      strides[i] = list[i].toInt();
      if (strides[i] < 1)
        strides[i] = 1;
    }

    DataSource::setWasSubsampled(image, true);
    DataSource::setSubsampleStrides(image, strides);
  }

  bool askForSubsample = false;
  if (options.contains("askForSubsample")) {
    // If the options specify whether to ask for a subsample, use that
    askForSubsample = options["askForSubsample"].toBool();
  } else {
    // Otherwise, only ask for a subsample if the data looks large
    int subsampleDimOverride = 1200;
    if (options.contains("subsampleDimOverride"))
      subsampleDimOverride = options["subsampleDimOverride"].toInt();

    askForSubsample =
      std::any_of(dims.cbegin(), dims.cend(), [subsampleDimOverride](int i) {
        return i >= subsampleDimOverride;
      });
  }

  if (askForSubsample) {
    int dimensions[3] = { dims[0], dims[1], dims[2] };
    QDialog dialog;
    dialog.setWindowTitle("Pick Subsample");
    QVBoxLayout layout;
    dialog.setLayout(&layout);

    Hdf5SubsampleWidget widget(dimensions, size);
    layout.addWidget(&widget);

    if (DataSource::wasSubsampled(image)) {
      // If it was previously subsampled, start with the previous values
      DataSource::subsampleStrides(image, strides);
      widget.setStrides(strides);

      DataSource::subsampleVolumeBounds(image, bs);
      widget.setBounds(bs);
    }

    QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |
                             QDialogButtonBox::Help);
    layout.addWidget(&buttons);
    QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog,
                     &QDialog::accept);
    QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog,
                     &QDialog::reject);
    QObject::connect(&buttons, &QDialogButtonBox::helpRequested,
                     []() { openHelpUrl("data/#hdf5-subsampling"); });

    // Check if the user cancels
    if (!dialog.exec())
      return false;

    widget.bounds(bs);
    widget.strides(strides);

    DataSource::setWasSubsampled(image, true);
    DataSource::setSubsampleStrides(image, strides);
    DataSource::setSubsampleVolumeBounds(image, bs);
  }

  // Do one final check to make sure none of the bounds are less than 0
  if (std::any_of(std::begin(bs), std::end(bs), [](int i) { return i < 0; })) {
    // Set them to their defaults so we don't seg fault
    for (int i = 0; i < 3; ++i) {
      bs[i * 2] = 0;
      bs[i * 2 + 1] = dims[i];
    }
  }

  // Set up the strides and counts
  size_t start[3] = { static_cast<size_t>(bs[0]), static_cast<size_t>(bs[2]),
                      static_cast<size_t>(bs[4]) };
  size_t counts[3];
  for (size_t i = 0; i < 3; ++i)
    counts[i] = (bs[i * 2 + 1] - start[i]) / strides[i];

  // vtk requires the counts to be an int array
  int vtkCounts[3];
  for (int i = 0; i < 3; ++i)
    vtkCounts[i] = counts[i];

  vtkNew<vtkImageData> tmp;
  tmp->SetDimensions(&vtkCounts[0]);
  tmp->AllocateScalars(vtkDataType, 1);
  image->SetDimensions(&vtkCounts[0]);
  image->AllocateScalars(vtkDataType, 1);

  if (!reader.readData(path, type, tmp->GetScalarPointer(), strides, start,
                       counts)) {
    std::cerr << "Failed to read the data\n";
    return false;
  }

  // HDF5 typically stores data as row major order.
  // VTK expects column major order.
  auto inPtr = tmp->GetPointData()->GetScalars()->GetVoidPointer(0);
  auto outPtr = image->GetPointData()->GetScalars()->GetVoidPointer(0);
  switch (image->GetPointData()->GetScalars()->GetDataType()) {
    vtkTemplateMacro(tomviz::ReorderArrayF(reinterpret_cast<VTK_TT*>(inPtr),
                                           reinterpret_cast<VTK_TT*>(outPtr),
                                           &vtkCounts[0]));
    default:
      cout << "Generic HDF5 Format: Unknown data type" << endl;
  }
  image->Modified();

  return true;
}

bool GenericHDF5Format::read(const std::string& fileName, vtkImageData* image,
                             const QVariantMap& options)
{
  Q_UNUSED(options)

  using h5::H5ReadWrite;
  H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::ReadOnly;
  H5ReadWrite reader(fileName.c_str(), mode);

  // Find all 3D datasets. If there is more than one, have the user choose.
  std::vector<std::string> datasets = reader.allDataSets();
  for (auto it = datasets.begin(); it != datasets.end();) {
    // Remove all non-3D datasets
    std::vector<int> dims = reader.getDimensions(*it);
    if (dims.size() != 3)
      datasets.erase(it);
    else
      ++it;
  }

  if (datasets.empty()) {
    std::cerr << "No 3D datasets found in " << fileName.c_str() << "\n";
    return false;
  }

  std::string dataNode = datasets[0];

  if (datasets.size() != 1) {
    // If there is more than one dataset, have the user choose one
    QStringList items;
    for (auto& d : datasets)
      items.append(QString::fromStdString(d));

    bool ok;
    QString res = QInputDialog::getItem(
      nullptr, "Choose volume", "Choose volume to load:", items, 0, false, &ok);

    // Check if user canceled
    if (!ok)
      return false;

    dataNode = datasets[items.indexOf(res)];
  }

  return readVolume(reader, dataNode, image);
}

bool GenericHDF5Format::writeVolume(h5::H5ReadWrite& writer,
                                    const std::string& path,
                                    const std::string& name,
                                    vtkImageData* image)
{
  int dim[3];
  image->GetDimensions(dim);
  std::vector<int> dims({ dim[0], dim[1], dim[2] });

  // We must allocate a new array, and copy the reordered array into it.
  auto arrayPtr = image->GetPointData()->GetScalars();
  auto dataPtr = arrayPtr->GetVoidPointer(0);
  vtkNew<vtkImageData> reorderedImageData;
  reorderedImageData->SetDimensions(dim);
  reorderedImageData->AllocateScalars(arrayPtr->GetDataType(), 1);
  auto outPtr =
    reorderedImageData->GetPointData()->GetScalars()->GetVoidPointer(0);

  switch (arrayPtr->GetDataType()) {
    vtkTemplateMacro(tomviz::ReorderArrayC(reinterpret_cast<VTK_TT*>(dataPtr),
                                           reinterpret_cast<VTK_TT*>(outPtr),
                                           dim));
    default:
      cout << "Generic HDF5 Format: Unknown data type" << endl;
  }

  h5::H5ReadWrite::DataType type =
    h5::H5VtkTypeMaps::VtkToDataType(arrayPtr->GetDataType());

  return writer.writeData(path, name, dims, type, outPtr);
}

} // namespace tomviz
<commit_msg>Add ability to load multiple volumes from HDF5<commit_after>/* This source file is part of the Tomviz project, https://tomviz.org/.
   It is released under the 3-Clause BSD License, see "LICENSE". */

#include "GenericHDF5Format.h"

#include <DataExchangeFormat.h>
#include <DataSource.h>
#include <Hdf5SubsampleWidget.h>
#include <Utilities.h>

#include <h5cpp/h5readwrite.h>
#include <h5cpp/h5vtktypemaps.h>

#include <QCheckBox>
#include <QDialog>
#include <QDialogButtonBox>
#include <QMessageBox>
#include <QScrollArea>
#include <QStringList>
#include <QVBoxLayout>

#include <vtkDataArray.h>
#include <vtkImageData.h>
#include <vtkPointData.h>

#include <string>
#include <vector>

#include <iostream>
#include <sstream>

using std::cerr;
using std::cout;
using std::endl;

namespace tomviz {

template <typename T>
void ReorderArrayC(T* in, T* out, int dim[3])
{
  for (int i = 0; i < dim[0]; ++i) {
    for (int j = 0; j < dim[1]; ++j) {
      for (int k = 0; k < dim[2]; ++k) {
        out[static_cast<size_t>(i * dim[1] + j) * dim[2] + k] =
          in[static_cast<size_t>(k * dim[1] + j) * dim[0] + i];
      }
    }
  }
}

template <typename T>
void ReorderArrayF(T* in, T* out, int dim[3])
{
  for (int i = 0; i < dim[0]; ++i) {
    for (int j = 0; j < dim[1]; ++j) {
      for (int k = 0; k < dim[2]; ++k) {
        out[static_cast<size_t>(k * dim[1] + j) * dim[0] + i] =
          in[static_cast<size_t>(i * dim[1] + j) * dim[2] + k];
      }
    }
  }
}

bool GenericHDF5Format::addScalarArray(h5::H5ReadWrite& reader,
                                       const std::string& path,
                                       vtkImageData* image,
                                       const std::string& name)
{
  // Get the type of the data
  h5::H5ReadWrite::DataType type = reader.dataType(path);
  int vtkDataType = h5::H5VtkTypeMaps::dataTypeToVtk(type);

  // Get the dimensions
  std::vector<int> dims = reader.getDimensions(path);

  // Set up the strides and counts
  int strides[3] = { 1, 1, 1 };
  size_t start[3], counts[3];
  if (DataSource::wasSubsampled(image)) {
    // If the main image was subsampled, we need to use the same
    // subsampling for the scalars
    DataSource::subsampleStrides(image, strides);
    int bs[6];
    DataSource::subsampleVolumeBounds(image, bs);

    for (int i = 0; i < 3; ++i) {
      start[i] = static_cast<size_t>(bs[i * 2]);
      counts[i] = (bs[i * 2 + 1] - start[i]) / strides[i];
    }
  } else {
    for (int i = 0; i < 3; ++i) {
      start[i] = 0;
      counts[i] = dims[i];
    }
  }

  // vtk requires the counts to be an int array
  int vtkCounts[3];
  for (int i = 0; i < 3; ++i)
    vtkCounts[i] = counts[i];

  // Make sure the dimensions match those of the image, or else
  // we will probably experience a crash later...
  int imageDims[3];
  image->GetDimensions(imageDims);
  for (int i = 0; i < 3; ++i) {
    if (vtkCounts[i] != imageDims[i]) {
      std::stringstream ss;
      if (DataSource::wasSubsampled(image)) {
        ss << "Subsampled dimensions of ";
      } else {
        ss << "Dimensions of ";
      }
      ss << path << " (" << vtkCounts[0] << ", " << vtkCounts[1] << ", "
         << vtkCounts[2] << ") do not match the dimensions of the image ("
         << imageDims[0] << ", " << imageDims[1] << ", " << imageDims[2]
         << ")\nThe array cannot be added.";

      std::cerr << "Error in GenericHDF5Format::addScalarArray():\n"
                << ss.str() << std::endl;
      QMessageBox::critical(nullptr, "Dimensions do not match",
                            ss.str().c_str());
      return false;
    }
  }

  vtkNew<vtkImageData> tmp;
  tmp->SetDimensions(&vtkCounts[0]);
  tmp->AllocateScalars(vtkDataType, 1);

  if (!reader.readData(path, type, tmp->GetScalarPointer(), strides, start,
                       counts)) {
    std::cerr << "Failed to read the data\n";
    return false;
  }

  auto* array = vtkAbstractArray::CreateArray(vtkDataType);
  array->SetNumberOfTuples(counts[0] * counts[1] * counts[2]);
  array->SetName(name.c_str());
  image->GetPointData()->AddArray(array);

  // HDF5 typically stores data as row major order.
  // VTK expects column major order.
  auto inPtr = tmp->GetPointData()->GetScalars()->GetVoidPointer(0);
  auto outPtr = array->GetVoidPointer(0);
  switch (vtkDataType) {
    vtkTemplateMacro(tomviz::ReorderArrayF(reinterpret_cast<VTK_TT*>(inPtr),
                                           reinterpret_cast<VTK_TT*>(outPtr),
                                           &vtkCounts[0]));
    default:
      cout << "Generic HDF5 Format: Unknown data type" << endl;
  }
  image->Modified();

  return true;
}

bool GenericHDF5Format::isDataExchange(const std::string& fileName)
{
  using h5::H5ReadWrite;
  H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::ReadOnly;
  H5ReadWrite reader(fileName.c_str(), mode);

  // If /exchange/data is a dataset, assume this is a DataExchangeFormat
  return reader.isDataSet("/exchange/data");
}

bool GenericHDF5Format::isFxi(const std::string& fileName)
{
  using h5::H5ReadWrite;
  H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::ReadOnly;
  H5ReadWrite reader(fileName.c_str(), mode);

  // Look for a few keys, loosely defined BNL FXI data format.
  return reader.isDataSet("/img_tomo") && reader.isDataSet("/img_bkg");
}

bool GenericHDF5Format::readVolume(h5::H5ReadWrite& reader,
                                   const std::string& path, vtkImageData* image,
                                   const QVariantMap& options)
{
  // Get the type of the data
  h5::H5ReadWrite::DataType type = reader.dataType(path);
  int vtkDataType = h5::H5VtkTypeMaps::dataTypeToVtk(type);

  // This is the easiest way I could find to get the size of the type
  int size = vtkDataArray::GetDataTypeSize(vtkDataType);

  // Get the dimensions
  std::vector<int> dims = reader.getDimensions(path);

  int bs[6] = { -1, -1, -1, -1, -1, -1 };
  int strides[3] = { 1, 1, 1 };
  if (options.contains("subsampleVolumeBounds")) {
    // Get the subsample volume bounds if the caller specified them
    QVariantList list = options["subsampleVolumeBounds"].toList();
    for (int i = 0; i < list.size() && i < 6; ++i)
      bs[i] = list[i].toInt();
    DataSource::setWasSubsampled(image, true);
    DataSource::setSubsampleVolumeBounds(image, bs);
  } else {
    // Set it to the defaults
    for (int i = 0; i < 3; ++i) {
      bs[i * 2] = 0;
      bs[i * 2 + 1] = dims[i];
    }
  }

  if (options.contains("subsampleStrides")) {
    // Get the strides if the caller specified them
    QVariantList list = options["subsampleStrides"].toList();
    for (int i = 0; i < list.size() && i < 3; ++i) {
      strides[i] = list[i].toInt();
      if (strides[i] < 1)
        strides[i] = 1;
    }

    DataSource::setWasSubsampled(image, true);
    DataSource::setSubsampleStrides(image, strides);
  }

  bool askForSubsample = false;
  if (options.contains("askForSubsample")) {
    // If the options specify whether to ask for a subsample, use that
    askForSubsample = options["askForSubsample"].toBool();
  } else {
    // Otherwise, only ask for a subsample if the data looks large
    int subsampleDimOverride = 1200;
    if (options.contains("subsampleDimOverride"))
      subsampleDimOverride = options["subsampleDimOverride"].toInt();

    askForSubsample =
      std::any_of(dims.cbegin(), dims.cend(), [subsampleDimOverride](int i) {
        return i >= subsampleDimOverride;
      });
  }

  if (askForSubsample) {
    int dimensions[3] = { dims[0], dims[1], dims[2] };
    QDialog dialog;
    dialog.setWindowTitle("Pick Subsample");
    QVBoxLayout layout;
    dialog.setLayout(&layout);

    Hdf5SubsampleWidget widget(dimensions, size);
    layout.addWidget(&widget);

    if (DataSource::wasSubsampled(image)) {
      // If it was previously subsampled, start with the previous values
      DataSource::subsampleStrides(image, strides);
      widget.setStrides(strides);

      DataSource::subsampleVolumeBounds(image, bs);
      widget.setBounds(bs);
    }

    QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel |
                             QDialogButtonBox::Help);
    layout.addWidget(&buttons);
    QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog,
                     &QDialog::accept);
    QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog,
                     &QDialog::reject);
    QObject::connect(&buttons, &QDialogButtonBox::helpRequested,
                     []() { openHelpUrl("data/#hdf5-subsampling"); });

    // Check if the user cancels
    if (!dialog.exec())
      return false;

    widget.bounds(bs);
    widget.strides(strides);

    DataSource::setWasSubsampled(image, true);
    DataSource::setSubsampleStrides(image, strides);
    DataSource::setSubsampleVolumeBounds(image, bs);
  }

  // Do one final check to make sure none of the bounds are less than 0
  if (std::any_of(std::begin(bs), std::end(bs), [](int i) { return i < 0; })) {
    // Set them to their defaults so we don't seg fault
    for (int i = 0; i < 3; ++i) {
      bs[i * 2] = 0;
      bs[i * 2 + 1] = dims[i];
    }
  }

  // Set up the strides and counts
  size_t start[3] = { static_cast<size_t>(bs[0]), static_cast<size_t>(bs[2]),
                      static_cast<size_t>(bs[4]) };
  size_t counts[3];
  for (size_t i = 0; i < 3; ++i)
    counts[i] = (bs[i * 2 + 1] - start[i]) / strides[i];

  // vtk requires the counts to be an int array
  int vtkCounts[3];
  for (int i = 0; i < 3; ++i)
    vtkCounts[i] = counts[i];

  vtkNew<vtkImageData> tmp;
  tmp->SetDimensions(&vtkCounts[0]);
  tmp->AllocateScalars(vtkDataType, 1);
  image->SetDimensions(&vtkCounts[0]);
  image->AllocateScalars(vtkDataType, 1);

  if (!reader.readData(path, type, tmp->GetScalarPointer(), strides, start,
                       counts)) {
    std::cerr << "Failed to read the data\n";
    return false;
  }

  // HDF5 typically stores data as row major order.
  // VTK expects column major order.
  auto inPtr = tmp->GetPointData()->GetScalars()->GetVoidPointer(0);
  auto outPtr = image->GetPointData()->GetScalars()->GetVoidPointer(0);
  switch (image->GetPointData()->GetScalars()->GetDataType()) {
    vtkTemplateMacro(tomviz::ReorderArrayF(reinterpret_cast<VTK_TT*>(inPtr),
                                           reinterpret_cast<VTK_TT*>(outPtr),
                                           &vtkCounts[0]));
    default:
      cout << "Generic HDF5 Format: Unknown data type" << endl;
  }
  image->Modified();

  return true;
}

bool GenericHDF5Format::read(const std::string& fileName, vtkImageData* image,
                             const QVariantMap& options)
{
  Q_UNUSED(options)

  using h5::H5ReadWrite;
  H5ReadWrite::OpenMode mode = H5ReadWrite::OpenMode::ReadOnly;
  H5ReadWrite reader(fileName.c_str(), mode);

  // Find all 3D datasets. If there is more than one, have the user choose.
  std::vector<std::string> datasets = reader.allDataSets();
  for (auto it = datasets.begin(); it != datasets.end();) {
    // Remove all non-3D datasets
    std::vector<int> dims = reader.getDimensions(*it);
    if (dims.size() != 3)
      datasets.erase(it);
    else
      ++it;
  }

  if (datasets.empty()) {
    std::cerr << "No 3D datasets found in " << fileName.c_str() << "\n";
    return false;
  }

  std::string dataNode = datasets[0];

  if (datasets.size() == 1) {
    // Only one volume. Load and return.
    return readVolume(reader, dataNode, image);
  }

  // If there is more than one volume, have the user choose.
  QStringList items;
  for (auto& d : datasets)
    items.append(QString::fromStdString(d));

  // Choose the volumes to load
  QDialog dialog;
  dialog.setWindowTitle("Choose volumes");
  QVBoxLayout layout;
  dialog.setLayout(&layout);

  // Use a scroll area in case there are a lot of options
  QScrollArea scrollArea;
  scrollArea.setWidgetResizable(true); // Necessary for some reason
  layout.addWidget(&scrollArea);

  QWidget scrollAreaWidget;
  QVBoxLayout scrollAreaLayout;
  scrollAreaWidget.setLayout(&scrollAreaLayout);
  scrollArea.setWidget(&scrollAreaWidget);

  // Add the checkboxes
  QList<QCheckBox*> checkboxes;
  for (const auto& item : items) {
    checkboxes.append(new QCheckBox(item, &scrollAreaWidget));
    scrollAreaLayout.addWidget(checkboxes.back());
  }

  // Setup Ok and Cancel buttons
  QDialogButtonBox buttons(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
  layout.addWidget(&buttons);
  QObject::connect(&buttons, &QDialogButtonBox::accepted, &dialog,
                   &QDialog::accept);
  QObject::connect(&buttons, &QDialogButtonBox::rejected, &dialog,
                   &QDialog::reject);

  if (!dialog.exec()) {
    // User canceled, just return
    return false;
  }

  // Find the checked checkboxes
  std::vector<std::string> selectedDatasets;
  for (const auto& cb : checkboxes) {
    if (cb->isChecked()) {
      // Take advantage of the fact that the text is the name exactly
      selectedDatasets.push_back(cb->text().toStdString());
    }
  }

  if (selectedDatasets.empty()) {
    QString msg = "At least one volume must be selected";
    std::cerr << msg.toStdString() << std::endl;
    QMessageBox::critical(nullptr, "Invalid Selection", msg);
    return false;
  }

  // Read the first dataset with readVolume(). This might ask for
  // subsampling options, which will be applied to the rest of the
  // datasets.
  if (!readVolume(reader, selectedDatasets[0], image)) {
    auto msg =
      QString("Failed to read the data at: ") + selectedDatasets[0].c_str();
    std::cerr << msg.toStdString() << std::endl;
    QMessageBox::critical(nullptr, "Read Failed", msg);
    return false;
  }

  // Set the name of the first array
  image->GetPointData()->GetScalars()->SetName(selectedDatasets[0].c_str());

  // Add any more datasets with addScalarArray()
  for (size_t i = 1; i < selectedDatasets.size(); ++i) {
    const auto& path = selectedDatasets[i];
    if (!addScalarArray(reader, path, image, path)) {
      auto msg = QString("Failed to read or add the data of: ") + path.c_str();
      std::cerr << msg.toStdString() << std::endl;
      QMessageBox::critical(nullptr, "Failure", msg);
      return false;
    }
  }

  // Made it to the end...
  return true;
}

bool GenericHDF5Format::writeVolume(h5::H5ReadWrite& writer,
                                    const std::string& path,
                                    const std::string& name,
                                    vtkImageData* image)
{
  int dim[3];
  image->GetDimensions(dim);
  std::vector<int> dims({ dim[0], dim[1], dim[2] });

  // We must allocate a new array, and copy the reordered array into it.
  auto arrayPtr = image->GetPointData()->GetScalars();
  auto dataPtr = arrayPtr->GetVoidPointer(0);
  vtkNew<vtkImageData> reorderedImageData;
  reorderedImageData->SetDimensions(dim);
  reorderedImageData->AllocateScalars(arrayPtr->GetDataType(), 1);
  auto outPtr =
    reorderedImageData->GetPointData()->GetScalars()->GetVoidPointer(0);

  switch (arrayPtr->GetDataType()) {
    vtkTemplateMacro(tomviz::ReorderArrayC(reinterpret_cast<VTK_TT*>(dataPtr),
                                           reinterpret_cast<VTK_TT*>(outPtr),
                                           dim));
    default:
      cout << "Generic HDF5 Format: Unknown data type" << endl;
  }

  h5::H5ReadWrite::DataType type =
    h5::H5VtkTypeMaps::VtkToDataType(arrayPtr->GetDataType());

  return writer.writeData(path, name, dims, type, outPtr);
}

} // namespace tomviz
<|endoftext|>


<commit_before>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/util/device_name_utils.h"

#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/logging.h"

namespace tensorflow {

static bool IsAlpha(char c) {
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

static bool IsAlphaNum(char c) { return IsAlpha(c) || (c >= '0' && c <= '9'); }

// Returns true iff "in" is a valid job name.
static bool IsJobName(StringPiece in) {
  if (in.empty()) return false;
  if (!IsAlpha(in[0])) return false;
  for (size_t i = 1; i < in.size(); ++i) {
    if (!(IsAlphaNum(in[i]) || in[i] == '_')) return false;
  }
  return true;
}

// Returns true and fills in "*job" iff "*in" starts with a job name.
static bool ConsumeJobName(StringPiece* in, string* job) {
  if (in->empty()) return false;
  if (!IsAlpha((*in)[0])) return false;
  size_t i = 1;
  for (; i < in->size(); ++i) {
    const char c = (*in)[i];
    if (c == '/') break;
    if (!(IsAlphaNum(c) || c == '_')) {
      return false;
    }
  }
  job->assign(in->data(), i);
  in->remove_prefix(i);
  return true;
}

// Returns true and fills in "*device_type" iff "*in" starts with a device type
// name.
static bool ConsumeDeviceType(StringPiece* in, string* device_type) {
  if (in->empty()) return false;
  if (!IsAlpha((*in)[0])) return false;
  size_t i = 1;
  for (; i < in->size(); ++i) {
    const char c = (*in)[i];
    if (c == '/' || c == ':') break;
    if (!(IsAlphaNum(c) || c == '_')) {
      return false;
    }
  }
  device_type->assign(in->data(), i);
  in->remove_prefix(i);
  return true;
}

// Returns true and fills in "*val" iff "*in" starts with a decimal
// number.
static bool ConsumeNumber(StringPiece* in, int* val) {
  uint64 tmp;
  if (str_util::ConsumeLeadingDigits(in, &tmp)) {
    *val = tmp;
    return true;
  } else {
    return false;
  }
}

/* static */
string DeviceNameUtils::FullName(const string& job, int replica, int task,
                                 const string& type, int id) {
  CHECK(IsJobName(job)) << job;
  CHECK_LE(0, replica);
  CHECK_LE(0, task);
  CHECK(!type.empty());
  CHECK_LE(0, id);
  return strings::StrCat("/job:", job, "/replica:", replica, "/task:", task,
                         "/device:", type, ":", id);
}

/* static */
string DeviceNameUtils::LegacyName(const string& job, int replica, int task,
                                   const string& type, int id) {
  CHECK(IsJobName(job)) << job;
  CHECK_LE(0, replica);
  CHECK_LE(0, task);
  CHECK(!type.empty());
  CHECK_LE(0, id);
  return strings::StrCat("/job:", job, "/replica:", replica, "/task:", task,
                         "/", str_util::Lowercase(type), ":", id);
}

bool DeviceNameUtils::ParseFullName(StringPiece fullname, ParsedName* p) {
  p->Clear();
  if (fullname == "/") {
    return true;
  }
  StringPiece tmp;
  while (!fullname.empty()) {
    bool progress = false;
    if (str_util::ConsumePrefix(&fullname, "/job:")) {
      p->has_job = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_job && !ConsumeJobName(&fullname, &p->job)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/replica:")) {
      p->has_replica = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_replica && !ConsumeNumber(&fullname, &p->replica)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/task:")) {
      p->has_task = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_task && !ConsumeNumber(&fullname, &p->task)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/device:")) {
      p->has_type = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_type && !ConsumeDeviceType(&fullname, &p->type)) {
        return false;
      }
      if (!str_util::ConsumePrefix(&fullname, ":")) {
        p->has_id = false;
      } else {
        p->has_id = !str_util::ConsumePrefix(&fullname, "*");
        if (p->has_id && !ConsumeNumber(&fullname, &p->id)) {
          return false;
        }
      }
      progress = true;
    }

    // Handle legacy naming convention for cpu and gpu.
    if (str_util::ConsumePrefix(&fullname, "/cpu:") ||
        str_util::ConsumePrefix(&fullname, "/CPU:")) {
      p->has_type = true;
      p->type = "CPU";  // Treat '/cpu:..' as uppercase '/device:CPU:...'
      p->has_id = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_id && !ConsumeNumber(&fullname, &p->id)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/gpu:") ||
        str_util::ConsumePrefix(&fullname, "/GPU:")) {
      p->has_type = true;
      p->type = "GPU";  // Treat '/gpu:..' as uppercase '/device:GPU:...'
      p->has_id = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_id && !ConsumeNumber(&fullname, &p->id)) {
        return false;
      }
      progress = true;
    }

    if (!progress) {
      return false;
    }
  }
  return true;
}

/* static */
string DeviceNameUtils::ParsedNameToString(const ParsedName& pn) {
  string buf;
  if (pn.has_job) strings::StrAppend(&buf, "/job:", pn.job);
  if (pn.has_replica) strings::StrAppend(&buf, "/replica:", pn.replica);
  if (pn.has_task) strings::StrAppend(&buf, "/task:", pn.task);
  if (pn.has_type) {
    strings::StrAppend(&buf, "/device:", pn.type, ":");
    if (pn.has_id) {
      strings::StrAppend(&buf, pn.id);
    } else {
      strings::StrAppend(&buf, "*");
    }
  }
  return buf;
}

/* static */
bool DeviceNameUtils::IsSpecification(const ParsedName& less_specific,
                                      const ParsedName& more_specific) {
  if (less_specific.has_job &&
      (!more_specific.has_job || (less_specific.job != more_specific.job))) {
    return false;
  }
  if (less_specific.has_replica &&
      (!more_specific.has_replica ||
       (less_specific.replica != more_specific.replica))) {
    return false;
  }
  if (less_specific.has_task &&
      (!more_specific.has_task || (less_specific.task != more_specific.task))) {
    return false;
  }
  if (less_specific.has_type &&
      (!more_specific.has_type || (less_specific.type != more_specific.type))) {
    return false;
  }
  if (less_specific.has_id &&
      (!more_specific.has_id || (less_specific.id != more_specific.id))) {
    return false;
  }
  return true;
}

/* static */
bool DeviceNameUtils::IsCompleteSpecification(const ParsedName& pattern,
                                              const ParsedName& name) {
  CHECK(name.has_job && name.has_replica && name.has_task && name.has_type &&
        name.has_id);

  if (pattern.has_job && (pattern.job != name.job)) return false;
  if (pattern.has_replica && (pattern.replica != name.replica)) return false;
  if (pattern.has_task && (pattern.task != name.task)) return false;
  if (pattern.has_type && (pattern.type != name.type)) return false;
  if (pattern.has_id && (pattern.id != name.id)) return false;
  return true;
}

/* static */
Status DeviceNameUtils::MergeDevNames(ParsedName* target,
                                      const ParsedName& other,
                                      bool allow_soft_placement) {
  if (other.has_job) {
    if (target->has_job && target->job != other.job) {
      return errors::InvalidArgument(
          "Cannot merge devices with incompatible jobs: '",
          ParsedNameToString(*target), "' and '", ParsedNameToString(other),
          "'");
    } else {
      target->has_job = other.has_job;
      target->job = other.job;
    }
  }

  if (other.has_replica) {
    if (target->has_replica && target->replica != other.replica) {
      return errors::InvalidArgument(
          "Cannot merge devices with incompatible replicas: '",
          ParsedNameToString(*target), "' and '", ParsedNameToString(other),
          "'");
    } else {
      target->has_replica = other.has_replica;
      target->replica = other.replica;
    }
  }

  if (other.has_task) {
    if (target->has_task && target->task != other.task) {
      return errors::InvalidArgument(
          "Cannot merge devices with incompatible tasks: '",
          ParsedNameToString(*target), "' and '", ParsedNameToString(other),
          "'");
    } else {
      target->has_task = other.has_task;
      target->task = other.task;
    }
  }

  if (other.has_type) {
    if (target->has_type && target->type != other.type) {
      if (!allow_soft_placement) {
        return errors::InvalidArgument(
            "Cannot merge devices with incompatible types: '",
            ParsedNameToString(*target), "' and '", ParsedNameToString(other),
            "'");
      } else {
        target->has_id = false;
        target->has_type = false;
        return Status::OK();
      }
    } else {
      target->has_type = other.has_type;
      target->type = other.type;
    }
  }

  if (other.has_id) {
    if (target->has_id && target->id != other.id) {
      if (!allow_soft_placement) {
        return errors::InvalidArgument(
            "Cannot merge devices with incompatible ids: '",
            ParsedNameToString(*target), "' and '", ParsedNameToString(other),
            "'");
      } else {
        target->has_id = false;
        return Status::OK();
      }
    } else {
      target->has_id = other.has_id;
      target->id = other.id;
    }
  }

  return Status::OK();
}

/* static */
bool DeviceNameUtils::IsSameAddressSpace(const ParsedName& a,
                                         const ParsedName& b) {
  return (a.has_job && b.has_job && (a.job == b.job)) &&
         (a.has_replica && b.has_replica && (a.replica == b.replica)) &&
         (a.has_task && b.has_task && (a.task == b.task));
}

/* static */
bool DeviceNameUtils::IsSameAddressSpace(StringPiece src, StringPiece dst) {
  ParsedName x;
  ParsedName y;
  return ParseFullName(src, &x) && ParseFullName(dst, &y) &&
         IsSameAddressSpace(x, y);
}

/* static */
string DeviceNameUtils::LocalName(StringPiece type, int id) {
  return strings::StrCat(type, ":", id);
}

/* static */
string DeviceNameUtils::LocalName(StringPiece fullname) {
  ParsedName x;
  CHECK(ParseFullName(fullname, &x)) << fullname;
  return LocalName(x.type, x.id);
}

/* static */
bool DeviceNameUtils::ParseLocalName(StringPiece name, ParsedName* p) {
  if (!ConsumeDeviceType(&name, &p->type)) {
    return false;
  }
  if (!str_util::ConsumePrefix(&name, ":")) {
    return false;
  }
  if (!ConsumeNumber(&name, &p->id)) {
    return false;
  }
  return name.empty();
}

/* static */
bool DeviceNameUtils::SplitDeviceName(StringPiece name, string* task,
                                      string* device) {
  ParsedName pn;
  if (ParseFullName(name, &pn) && pn.has_type && pn.has_id) {
    task->clear();
    task->reserve(
        (pn.has_job ? (5 + pn.job.size()) : 0) +
        (pn.has_replica ? (9 + 4 /*estimated UB for # replica digits*/) : 0) +
        (pn.has_task ? (6 + 4 /*estimated UB for # task digits*/) : 0));
    if (pn.has_job) {
      strings::StrAppend(task, "/job:", pn.job);
    }
    if (pn.has_replica) {
      strings::StrAppend(task, "/replica:", pn.replica);
    }
    if (pn.has_task) {
      strings::StrAppend(task, "/task:", pn.task);
    }
    device->clear();
    strings::StrAppend(device, pn.type, ":", pn.id);
    return true;
  }
  return false;
}

}  // namespace tensorflow
<commit_msg>Refactoring device name utils (#11797)<commit_after>/* Copyright 2015 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
==============================================================================*/

#include "tensorflow/core/util/device_name_utils.h"

#include "tensorflow/core/lib/core/errors.h"
#include "tensorflow/core/lib/strings/str_util.h"
#include "tensorflow/core/lib/strings/strcat.h"
#include "tensorflow/core/platform/logging.h"

namespace tensorflow {

static bool IsAlpha(char c) {
  return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
}

static bool IsAlphaNum(char c) { return IsAlpha(c) || (c >= '0' && c <= '9'); }

// Returns true iff "in" is a valid job name.
static bool IsJobName(StringPiece in) {
  if (in.empty()) return false;
  if (!IsAlpha(in[0])) return false;
  for (size_t i = 1; i < in.size(); ++i) {
    if (!(IsAlphaNum(in[i]) || in[i] == '_')) return false;
  }
  return true;
}

// Returns true and fills in "*job" iff "*in" starts with a job name.
static bool ConsumeJobName(StringPiece* in, string* job) {
  if (in->empty()) return false;
  if (!IsAlpha((*in)[0])) return false;
  size_t i = 1;
  for (; i < in->size(); ++i) {
    const char c = (*in)[i];
    if (c == '/') break;
    if (!(IsAlphaNum(c) || c == '_')) {
      return false;
    }
  }
  job->assign(in->data(), i);
  in->remove_prefix(i);
  return true;
}

// Returns true and fills in "*device_type" iff "*in" starts with a device type
// name.
static bool ConsumeDeviceType(StringPiece* in, string* device_type) {
  if (in->empty()) return false;
  if (!IsAlpha((*in)[0])) return false;
  size_t i = 1;
  for (; i < in->size(); ++i) {
    const char c = (*in)[i];
    if (c == '/' || c == ':') break;
    if (!(IsAlphaNum(c) || c == '_')) {
      return false;
    }
  }
  device_type->assign(in->data(), i);
  in->remove_prefix(i);
  return true;
}

// Returns true and fills in "*val" iff "*in" starts with a decimal
// number.
static bool ConsumeNumber(StringPiece* in, int* val) {
  uint64 tmp;
  if (str_util::ConsumeLeadingDigits(in, &tmp)) {
    *val = tmp;
    return true;
  } else {
    return false;
  }
}

// Returns a fully qualified device name given the parameters.
static string DeviceName(const string& job, int replica, int task,
                         const string& device_prefix, const string& device_type,
                         int id) {
  CHECK(IsJobName(job)) << job;
  CHECK_LE(0, replica);
  CHECK_LE(0, task);
  CHECK(!device_type.empty());
  CHECK_LE(0, id);
  return strings::StrCat("/job:", job, "/replica:", replica, "/task:", task,
                         device_prefix, device_type, ":", id);
}

/* static */
string DeviceNameUtils::FullName(const string& job, int replica, int task,
                                 const string& type, int id) {
  return DeviceName(job, replica, task, "/device:", type, id);
}

/* static */
string DeviceNameUtils::LegacyName(const string& job, int replica, int task,
                                   const string& type, int id) {
  return DeviceName(job, replica, task, "/", str_util::Lowercase(type), id);
}

bool DeviceNameUtils::ParseFullName(StringPiece fullname, ParsedName* p) {
  p->Clear();
  if (fullname == "/") {
    return true;
  }
  StringPiece tmp;
  while (!fullname.empty()) {
    bool progress = false;
    if (str_util::ConsumePrefix(&fullname, "/job:")) {
      p->has_job = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_job && !ConsumeJobName(&fullname, &p->job)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/replica:")) {
      p->has_replica = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_replica && !ConsumeNumber(&fullname, &p->replica)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/task:")) {
      p->has_task = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_task && !ConsumeNumber(&fullname, &p->task)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/device:")) {
      p->has_type = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_type && !ConsumeDeviceType(&fullname, &p->type)) {
        return false;
      }
      if (!str_util::ConsumePrefix(&fullname, ":")) {
        p->has_id = false;
      } else {
        p->has_id = !str_util::ConsumePrefix(&fullname, "*");
        if (p->has_id && !ConsumeNumber(&fullname, &p->id)) {
          return false;
        }
      }
      progress = true;
    }

    // Handle legacy naming convention for cpu and gpu.
    if (str_util::ConsumePrefix(&fullname, "/cpu:") ||
        str_util::ConsumePrefix(&fullname, "/CPU:")) {
      p->has_type = true;
      p->type = "CPU";  // Treat '/cpu:..' as uppercase '/device:CPU:...'
      p->has_id = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_id && !ConsumeNumber(&fullname, &p->id)) {
        return false;
      }
      progress = true;
    }
    if (str_util::ConsumePrefix(&fullname, "/gpu:") ||
        str_util::ConsumePrefix(&fullname, "/GPU:")) {
      p->has_type = true;
      p->type = "GPU";  // Treat '/gpu:..' as uppercase '/device:GPU:...'
      p->has_id = !str_util::ConsumePrefix(&fullname, "*");
      if (p->has_id && !ConsumeNumber(&fullname, &p->id)) {
        return false;
      }
      progress = true;
    }

    if (!progress) {
      return false;
    }
  }
  return true;
}

/* static */
string DeviceNameUtils::ParsedNameToString(const ParsedName& pn) {
  string buf;
  if (pn.has_job) strings::StrAppend(&buf, "/job:", pn.job);
  if (pn.has_replica) strings::StrAppend(&buf, "/replica:", pn.replica);
  if (pn.has_task) strings::StrAppend(&buf, "/task:", pn.task);
  if (pn.has_type) {
    strings::StrAppend(&buf, "/device:", pn.type, ":");
    if (pn.has_id) {
      strings::StrAppend(&buf, pn.id);
    } else {
      strings::StrAppend(&buf, "*");
    }
  }
  return buf;
}

/* static */
bool DeviceNameUtils::IsSpecification(const ParsedName& less_specific,
                                      const ParsedName& more_specific) {
  if (less_specific.has_job &&
      (!more_specific.has_job || (less_specific.job != more_specific.job))) {
    return false;
  }
  if (less_specific.has_replica &&
      (!more_specific.has_replica ||
       (less_specific.replica != more_specific.replica))) {
    return false;
  }
  if (less_specific.has_task &&
      (!more_specific.has_task || (less_specific.task != more_specific.task))) {
    return false;
  }
  if (less_specific.has_type &&
      (!more_specific.has_type || (less_specific.type != more_specific.type))) {
    return false;
  }
  if (less_specific.has_id &&
      (!more_specific.has_id || (less_specific.id != more_specific.id))) {
    return false;
  }
  return true;
}

/* static */
bool DeviceNameUtils::IsCompleteSpecification(const ParsedName& pattern,
                                              const ParsedName& name) {
  CHECK(name.has_job && name.has_replica && name.has_task && name.has_type &&
        name.has_id);

  if (pattern.has_job && (pattern.job != name.job)) return false;
  if (pattern.has_replica && (pattern.replica != name.replica)) return false;
  if (pattern.has_task && (pattern.task != name.task)) return false;
  if (pattern.has_type && (pattern.type != name.type)) return false;
  if (pattern.has_id && (pattern.id != name.id)) return false;
  return true;
}

/* static */
Status DeviceNameUtils::MergeDevNames(ParsedName* target,
                                      const ParsedName& other,
                                      bool allow_soft_placement) {
  if (other.has_job) {
    if (target->has_job && target->job != other.job) {
      return errors::InvalidArgument(
          "Cannot merge devices with incompatible jobs: '",
          ParsedNameToString(*target), "' and '", ParsedNameToString(other),
          "'");
    } else {
      target->has_job = other.has_job;
      target->job = other.job;
    }
  }

  if (other.has_replica) {
    if (target->has_replica && target->replica != other.replica) {
      return errors::InvalidArgument(
          "Cannot merge devices with incompatible replicas: '",
          ParsedNameToString(*target), "' and '", ParsedNameToString(other),
          "'");
    } else {
      target->has_replica = other.has_replica;
      target->replica = other.replica;
    }
  }

  if (other.has_task) {
    if (target->has_task && target->task != other.task) {
      return errors::InvalidArgument(
          "Cannot merge devices with incompatible tasks: '",
          ParsedNameToString(*target), "' and '", ParsedNameToString(other),
          "'");
    } else {
      target->has_task = other.has_task;
      target->task = other.task;
    }
  }

  if (other.has_type) {
    if (target->has_type && target->type != other.type) {
      if (!allow_soft_placement) {
        return errors::InvalidArgument(
            "Cannot merge devices with incompatible types: '",
            ParsedNameToString(*target), "' and '", ParsedNameToString(other),
            "'");
      } else {
        target->has_id = false;
        target->has_type = false;
        return Status::OK();
      }
    } else {
      target->has_type = other.has_type;
      target->type = other.type;
    }
  }

  if (other.has_id) {
    if (target->has_id && target->id != other.id) {
      if (!allow_soft_placement) {
        return errors::InvalidArgument(
            "Cannot merge devices with incompatible ids: '",
            ParsedNameToString(*target), "' and '", ParsedNameToString(other),
            "'");
      } else {
        target->has_id = false;
        return Status::OK();
      }
    } else {
      target->has_id = other.has_id;
      target->id = other.id;
    }
  }

  return Status::OK();
}

/* static */
bool DeviceNameUtils::IsSameAddressSpace(const ParsedName& a,
                                         const ParsedName& b) {
  return (a.has_job && b.has_job && (a.job == b.job)) &&
         (a.has_replica && b.has_replica && (a.replica == b.replica)) &&
         (a.has_task && b.has_task && (a.task == b.task));
}

/* static */
bool DeviceNameUtils::IsSameAddressSpace(StringPiece src, StringPiece dst) {
  ParsedName x;
  ParsedName y;
  return ParseFullName(src, &x) && ParseFullName(dst, &y) &&
         IsSameAddressSpace(x, y);
}

/* static */
string DeviceNameUtils::LocalName(StringPiece type, int id) {
  return strings::StrCat(type, ":", id);
}

/* static */
string DeviceNameUtils::LocalName(StringPiece fullname) {
  ParsedName x;
  CHECK(ParseFullName(fullname, &x)) << fullname;
  return LocalName(x.type, x.id);
}

/* static */
bool DeviceNameUtils::ParseLocalName(StringPiece name, ParsedName* p) {
  if (!ConsumeDeviceType(&name, &p->type)) {
    return false;
  }
  if (!str_util::ConsumePrefix(&name, ":")) {
    return false;
  }
  if (!ConsumeNumber(&name, &p->id)) {
    return false;
  }
  return name.empty();
}

/* static */
bool DeviceNameUtils::SplitDeviceName(StringPiece name, string* task,
                                      string* device) {
  ParsedName pn;
  if (ParseFullName(name, &pn) && pn.has_type && pn.has_id) {
    task->clear();
    task->reserve(
        (pn.has_job ? (5 + pn.job.size()) : 0) +
        (pn.has_replica ? (9 + 4 /*estimated UB for # replica digits*/) : 0) +
        (pn.has_task ? (6 + 4 /*estimated UB for # task digits*/) : 0));
    if (pn.has_job) {
      strings::StrAppend(task, "/job:", pn.job);
    }
    if (pn.has_replica) {
      strings::StrAppend(task, "/replica:", pn.replica);
    }
    if (pn.has_task) {
      strings::StrAppend(task, "/task:", pn.task);
    }
    device->clear();
    strings::StrAppend(device, pn.type, ":", pn.id);
    return true;
  }
  return false;
}

}  // namespace tensorflow
<|endoftext|>


<commit_before>#include "../common/indep-test-dep.hpp"

using namespace wrd::indep;

TEST(BuildFeature, dateGetter) {
	BuildFeature::Date date;
    ASSERT_GT(date.getYear(), 0);
    ASSERT_GT(date.getMonth(), 0);
    ASSERT_GT(date.getDay(), 0);
    ASSERT_STRNE(date.get().c_str(), "");
}

TEST(BuildFeature, timeGetter) {
    ASSERT_GT(BuildFeature::Time::getHour(), 0);
    ASSERT_GT(BuildFeature::Time::getMin(), 0);
    ASSERT_GT(BuildFeature::Time::getSec(), 0);
    ASSERT_STRNE(BuildFeature::Time::get().c_str(), "");
}

TEST(BuildFeature, versionGetter) {
    ASSERT_GE(BuildFeature::Version::getMajor(), 0);
    ASSERT_GE(BuildFeature::Version::getMinor(), 0);
    ASSERT_GE(BuildFeature::Version::getFix(), 0);
    ASSERT_STRNE(BuildFeature::Version::get().c_str(), "");
    ASSERT_STRNE(BuildFeature::Version::getValue().c_str(), "");
    ASSERT_STRNE(BuildFeature::Version::getName().c_str(), "");
}

TEST(BuildFeature, platformGetter) {
    BuildFeature::PlatformType type = BuildFeature::Platform::get();
    ASSERT_GT(type, BuildFeature::PLATFORM_TYPE_START);
    ASSERT_LE(type, BuildFeature::PLATFORM_TYPE_END);
    ASSERT_STRNE(BuildFeature::Platform::getVersion().c_str(), "");
    ASSERT_STRNE(BuildFeature::Platform::getName().c_str(), "");
}
<commit_msg>- [indep] fix wrong tc: build time can be 0.<commit_after>#include "../common/indep-test-dep.hpp"

using namespace wrd::indep;

TEST(BuildFeature, dateGetter) {
	BuildFeature::Date date;
    ASSERT_GT(date.getYear(), 0);
    ASSERT_GT(date.getMonth(), 0);
    ASSERT_GT(date.getDay(), 0);
    ASSERT_STRNE(date.get().c_str(), "");
}

TEST(BuildFeature, timeGetter) {
    ASSERT_GT(BuildFeature::Time::getHour(), 0);
    ASSERT_GE(BuildFeature::Time::getMin(), 0);
    ASSERT_GE(BuildFeature::Time::getSec(), 0);
    ASSERT_STRNE(BuildFeature::Time::get().c_str(), "");
}

TEST(BuildFeature, versionGetter) {
    ASSERT_GE(BuildFeature::Version::getMajor(), 0);
    ASSERT_GE(BuildFeature::Version::getMinor(), 0);
    ASSERT_GE(BuildFeature::Version::getFix(), 0);
    ASSERT_STRNE(BuildFeature::Version::get().c_str(), "");
    ASSERT_STRNE(BuildFeature::Version::getValue().c_str(), "");
    ASSERT_STRNE(BuildFeature::Version::getName().c_str(), "");
}

TEST(BuildFeature, platformGetter) {
    BuildFeature::PlatformType type = BuildFeature::Platform::get();
    ASSERT_GT(type, BuildFeature::PLATFORM_TYPE_START);
    ASSERT_LE(type, BuildFeature::PLATFORM_TYPE_END);
    ASSERT_STRNE(BuildFeature::Platform::getVersion().c_str(), "");
    ASSERT_STRNE(BuildFeature::Platform::getName().c_str(), "");
}
<|endoftext|>


<commit_before>/**
 * @file   LawOfCosines.hpp
 * @brief  LawOfCosines class prototype.
 * @author zer0
 * @date   2019-05-02
 *
 * @see <https://en.wikipedia.org/wiki/Law_of_cosines>
 * @see <https://ko.wikipedia.org/wiki/%EC%BD%94%EC%82%AC%EC%9D%B8_%EB%B2%95%EC%B9%99>
 *
 * @translate{ko, 코사인 법칙은 삼각형의 두 변과 그 사잇각을 알 때 남은 한 변을 구하거나, 세 변을 알 때 세 각을 구하는 데 사용될 수 있다.}
 *
 * @remarks
 *  @code
 *          C
 *          *
 *         /|
 *     b  / |
 *       /  | a
 *      /   |
 *   A *----* B
 *       c
 *  @endcode
 *  - A is internal angle of between b and c.
 *  - B is internal angle of between a and c.
 *  - C is internal angle of between a and b.
 *  - a is the distance between B and C.
 *  - b is the distance between A and C.
 *  - c is the distance between A and B.
 *  The law of cosines states:
 *  \f[
 *   c^{2} = a^{2} + b^{2} - 2ab\cos{C}
 *  \f]
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_MATH_LAWOFCOSINES_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_MATH_LAWOFCOSINES_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace math {

} // namespace math

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_MATH_LAWOFCOSINES_HPP__

<commit_msg>Fixed unknown command warning in doxygen.<commit_after>/**
 * @file   LawOfCosines.hpp
 * @brief  LawOfCosines class prototype.
 * @author zer0
 * @date   2019-05-02
 *
 * @translate{ko, 코사인 법칙은 삼각형의 두 변과 그 사잇각을 알 때 남은 한 변을 구하거나 세 변을 알 때 세 각을 구하는 데 사용될 수 있다.}
 *
 * @see <https://en.wikipedia.org/wiki/Law_of_cosines>
 * @see <https://ko.wikipedia.org/wiki/%EC%BD%94%EC%82%AC%EC%9D%B8_%EB%B2%95%EC%B9%99>
 *
 * @remarks
 *  @code
 *          C
 *          *
 *         /|
 *     b  / |
 *       /  | a
 *      /   |
 *   A *----* B
 *       c
 *  @endcode
 *  - A is internal angle of between b and c.
 *  - B is internal angle of between a and c.
 *  - C is internal angle of between a and b.
 *  - a is the distance between B and C.
 *  - b is the distance between A and C.
 *  - c is the distance between A and B.
 *  The law of cosines states:
 *  \f[
 *   c^{2} = a^{2} + b^{2} - 2ab\cos{C}
 *  \f]
 */

#ifndef __INCLUDE_LIBTBAG__LIBTBAG_MATH_LAWOFCOSINES_HPP__
#define __INCLUDE_LIBTBAG__LIBTBAG_MATH_LAWOFCOSINES_HPP__

// MS compatible compilers support #pragma once
#if defined(_MSC_VER) && (_MSC_VER >= 1020)
#pragma once
#endif

#include <libtbag/config.h>
#include <libtbag/predef.hpp>

// -------------------
NAMESPACE_LIBTBAG_OPEN
// -------------------

namespace math {

} // namespace math

// --------------------
NAMESPACE_LIBTBAG_CLOSE
// --------------------

#endif // __INCLUDE_LIBTBAG__LIBTBAG_MATH_LAWOFCOSINES_HPP__

<|endoftext|>


<commit_before>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#include "vast/system/index.hpp"

#define SUITE index
#include "test.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"
#include "vast/concept/printable/std/chrono.hpp"
#include "vast/concept/printable/vast/event.hpp"
#include "vast/concept/printable/to_string.hpp"
#include "vast/detail/spawn_container_source.hpp"
#include "vast/detail/spawn_generator_source.hpp"
#include "vast/event.hpp"
#include "vast/ids.hpp"
#include "vast/query_options.hpp"

#include "fixtures/actor_system_and_events.hpp"

using caf::after;
using std::chrono_literals::operator""s;

using namespace vast;
using namespace std::chrono;

namespace {

static constexpr size_t partition_size = 100;

static constexpr size_t in_mem_partitions = 8;

static constexpr size_t taste_count = 4;

static constexpr size_t num_collectors = 1;

const timestamp epoch;

using interval = system::meta_index::interval;

auto int_generator(int mod = std::numeric_limits<int>::max()) {
  int i = 0;
  return [i, mod]() mutable {
    auto result = event::make(i % mod, integer_type{}, i);
    result.timestamp(epoch + std::chrono::seconds(i));
    ++i;
    return result;
  };
}

struct fixture : fixtures::deterministic_actor_system_and_events {
  fixture() {
    directory /= "index";
    index = self->spawn(system::index, directory / "index", partition_size,
                        in_mem_partitions, taste_count, num_collectors);
  }

  ~fixture() {
    anon_send_exit(index, caf::exit_reason::user_shutdown);
  }

  // Returns the state of the `index`.
  system::index_state& state() {
    return deref<caf::stateful_actor<system::index_state>>(index).state;
  }

  auto partition_intervals() {
    std::vector<interval> result;
    for (auto& kvp : state().part_index.partitions())
      result.emplace_back(kvp.second.range);
    std::sort(result.begin(), result.end(),
              [](auto& x, auto& y) { return x.from < y.from; });
    return result;
  }

  auto query(std::string_view expr) {
    self->send(index, unbox(to<expression>(expr)));
    run_exhaustively();
    std::tuple<uuid, size_t, size_t> result;
    self->receive(
      [&](uuid& query_id, size_t hits, size_t scheduled) {
        result = std::tie(query_id, hits, scheduled);
      },
      after(0s) >> [&] { FAIL("INDEX did not respond to query"); });
    return result;
  }

  ids receive_result(const uuid& query_id, size_t hits, size_t scheduled) {
    if (hits == scheduled)
      CHECK_EQUAL(query_id, uuid::nil());
    else
      CHECK_NOT_EQUAL(query_id, uuid::nil());
    ids result;
    size_t collected = 0;
    auto fetch = [&](size_t chunk) {
      for (size_t i = 0; i < chunk; ++i)
        self->receive(
          [&](ids& sub_result) {
            ++collected;
            result |= sub_result;
          },
          after(0s) >> [&] {
            FAIL("missing sub result #" << (i + 1) << " in partition #"
                                        << (collected + 1));
          }
        );
    };
    fetch(scheduled);
    while (collected < hits) {
      auto chunk = std::min(hits - collected, taste_count);
      self->send(index, query_id, chunk);
      run_exhaustively();
      fetch(chunk);
    }
    return result;
  }

  // Handle to the INDEX actor.
  caf::actor index;
};

} // namespace <anonymous>

FIXTURE_SCOPE(index_tests, fixture)

TEST(ingestion) {
  MESSAGE("ingest 1000 integers");
  auto src = detail::spawn_generator_source(sys, 1000, int_generator(), index);
  run_exhaustively();
  MESSAGE("verify partition index");
  REQUIRE_EQUAL(state().part_index.size(), 10u);
  auto intervals = partition_intervals();
  CHECK_EQUAL(intervals[0], interval(epoch, epoch + 99s));
  CHECK_EQUAL(intervals[1], interval(epoch + 100s, epoch + 199s));
  CHECK_EQUAL(intervals[2], interval(epoch + 200s, epoch + 299s));
  CHECK_EQUAL(intervals[3], interval(epoch + 300s, epoch + 399s));
  CHECK_EQUAL(intervals[4], interval(epoch + 400s, epoch + 499s));
  CHECK_EQUAL(intervals[5], interval(epoch + 500s, epoch + 599s));
  CHECK_EQUAL(intervals[6], interval(epoch + 600s, epoch + 699s));
  CHECK_EQUAL(intervals[7], interval(epoch + 700s, epoch + 799s));
  CHECK_EQUAL(intervals[8], interval(epoch + 800s, epoch + 899s));
  CHECK_EQUAL(intervals[9], interval(epoch + 900s, epoch + 999s));
}

TEST(one-shot integer query result) {
  MESSAGE("fill first " << taste_count << " partitions");
  auto src = detail::spawn_generator_source(sys,
                                            partition_size * taste_count,
                                            int_generator(2), index);
  run_exhaustively();
  MESSAGE("query half of the values");
  auto [query_id, hits, scheduled] = query(":int == 1");
  CHECK_EQUAL(query_id, uuid::nil());
  CHECK_EQUAL(hits, taste_count);
  CHECK_EQUAL(scheduled, taste_count);
  ids expected_result;
  for (size_t i = 0; i < (partition_size * taste_count) / 2; ++i) {
    expected_result.append_bit(false);
    expected_result.append_bit(true);
  }
  auto result = receive_result(query_id, hits, scheduled);
  CHECK_EQUAL(result, expected_result);
}

TEST(iterable integer query result) {
  MESSAGE("fill first " << (taste_count * 3) << " partitions");
  auto src = detail::spawn_generator_source(sys,
                                            partition_size * taste_count * 3,
                                            int_generator(2), index);
  run_exhaustively();
  MESSAGE("query half of the values");
  auto [query_id, hits, scheduled] = query(":int == 1");
  CHECK_NOT_EQUAL(query_id, uuid::nil());
  CHECK_EQUAL(hits, taste_count * 3);
  CHECK_EQUAL(scheduled, taste_count);
  ids expected_result;
  for (size_t i = 0; i < (partition_size * taste_count * 3) / 2; ++i) {
    expected_result.append_bit(false);
    expected_result.append_bit(true);
  }
  MESSAGE("collect results");
  auto result = receive_result(query_id, hits, scheduled);
  CHECK_EQUAL(result, expected_result);
}

TEST(iterable bro conn log query result) {
  REQUIRE_EQUAL(bro_conn_log.size(), 8462u);
  MESSAGE("ingest conn.log");
  detail::spawn_container_source(sys, bro_conn_log, index);
  run_exhaustively();
  MESSAGE("issue field type query");
  {
    auto expected_result = make_ids({680, 682, 719, 720}, bro_conn_log.size());
    auto [query_id, hits, scheduled] = query(":addr == 169.254.225.22");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(result), rank(expected_result));
    CHECK_EQUAL(result, expected_result);
  }
  MESSAGE("issue field name query");
  {
    auto expected_result = make_ids({680, 682, 719, 720}, bro_conn_log.size());
    auto [query_id, hits, scheduled] = query("id.orig_h == 169.254.225.22");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(result), rank(expected_result));
    CHECK_EQUAL(result, expected_result);
  }
  MESSAGE("issue historical point query with conjunction");
  {
    auto expected_result
      = make_ids({105,  150,  246,  257,  322,  419,  440,  480,  579,  595,
                 642,  766,  1224, 1251, 1751, 1762, 2670, 3661, 3682, 3820,
                 6345, 6777, 7677, 7843, 8002, 8286, 8325, 8354},
                 bro_conn_log.size());
    auto [query_id, hits, scheduled] = query("service == \"http\" "
                                             "&& :addr == 212.227.96.110");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(expected_result), 28u);
    CHECK_EQUAL(rank(result), 28u);
    CHECK_EQUAL(result, expected_result);
  }
}

FIXTURE_SCOPE_END()
<commit_msg>Add another (simpler) failing unit test<commit_after>/******************************************************************************
 *                    _   _____   __________                                  *
 *                   | | / / _ | / __/_  __/     Visibility                   *
 *                   | |/ / __ |_\ \  / /          Across                     *
 *                   |___/_/ |_/___/ /_/       Space and Time                 *
 *                                                                            *
 * This file is part of VAST. It is subject to the license terms in the       *
 * LICENSE file found in the top-level directory of this distribution and at  *
 * http://vast.io/license. No part of VAST, including this file, may be       *
 * copied, modified, propagated, or distributed except according to the terms *
 * contained in the LICENSE file.                                             *
 ******************************************************************************/

#include "vast/system/index.hpp"

#define SUITE index
#include "test.hpp"

#include "vast/concept/parseable/to.hpp"
#include "vast/concept/parseable/vast/expression.hpp"
#include "vast/concept/printable/std/chrono.hpp"
#include "vast/concept/printable/vast/event.hpp"
#include "vast/concept/printable/to_string.hpp"
#include "vast/detail/spawn_container_source.hpp"
#include "vast/detail/spawn_generator_source.hpp"
#include "vast/event.hpp"
#include "vast/ids.hpp"
#include "vast/query_options.hpp"

#include "fixtures/actor_system_and_events.hpp"

using caf::after;
using std::chrono_literals::operator""s;

using namespace vast;
using namespace std::chrono;

namespace {

static constexpr size_t partition_size = 100;

static constexpr size_t in_mem_partitions = 8;

static constexpr size_t taste_count = 4;

static constexpr size_t num_collectors = 1;

const timestamp epoch;

using interval = system::meta_index::interval;

auto int_generator(int mod = std::numeric_limits<int>::max()) {
  int i = 0;
  return [i, mod]() mutable {
    auto result = event::make(i % mod, integer_type{}, i);
    result.timestamp(epoch + std::chrono::seconds(i));
    ++i;
    return result;
  };
}

struct fixture : fixtures::deterministic_actor_system_and_events {
  fixture() {
    directory /= "index";
    index = self->spawn(system::index, directory / "index", partition_size,
                        in_mem_partitions, taste_count, num_collectors);
  }

  ~fixture() {
    anon_send_exit(index, caf::exit_reason::user_shutdown);
  }

  // Returns the state of the `index`.
  system::index_state& state() {
    return deref<caf::stateful_actor<system::index_state>>(index).state;
  }

  auto partition_intervals() {
    std::vector<interval> result;
    for (auto& kvp : state().part_index.partitions())
      result.emplace_back(kvp.second.range);
    std::sort(result.begin(), result.end(),
              [](auto& x, auto& y) { return x.from < y.from; });
    return result;
  }

  auto query(std::string_view expr) {
    self->send(index, unbox(to<expression>(expr)));
    run_exhaustively();
    std::tuple<uuid, size_t, size_t> result;
    self->receive(
      [&](uuid& query_id, size_t hits, size_t scheduled) {
        result = std::tie(query_id, hits, scheduled);
      },
      after(0s) >> [&] { FAIL("INDEX did not respond to query"); });
    return result;
  }

  ids receive_result(const uuid& query_id, size_t hits, size_t scheduled) {
    if (hits == scheduled)
      CHECK_EQUAL(query_id, uuid::nil());
    else
      CHECK_NOT_EQUAL(query_id, uuid::nil());
    ids result;
    size_t collected = 0;
    auto fetch = [&](size_t chunk) {
      for (size_t i = 0; i < chunk; ++i)
        self->receive(
          [&](ids& sub_result) {
            ++collected;
            result |= sub_result;
          },
          after(0s) >> [&] {
            FAIL("missing sub result #" << (i + 1) << " in partition #"
                                        << (collected + 1));
          }
        );
    };
    fetch(scheduled);
    while (collected < hits) {
      auto chunk = std::min(hits - collected, taste_count);
      self->send(index, query_id, chunk);
      run_exhaustively();
      fetch(chunk);
    }
    return result;
  }

  // Handle to the INDEX actor.
  caf::actor index;
};

} // namespace <anonymous>

FIXTURE_SCOPE(index_tests, fixture)

TEST(ingestion) {
  MESSAGE("ingest 1000 integers");
  auto src = detail::spawn_generator_source(sys, 1000, int_generator(), index);
  run_exhaustively();
  MESSAGE("verify partition index");
  REQUIRE_EQUAL(state().part_index.size(), 10u);
  auto intervals = partition_intervals();
  CHECK_EQUAL(intervals[0], interval(epoch, epoch + 99s));
  CHECK_EQUAL(intervals[1], interval(epoch + 100s, epoch + 199s));
  CHECK_EQUAL(intervals[2], interval(epoch + 200s, epoch + 299s));
  CHECK_EQUAL(intervals[3], interval(epoch + 300s, epoch + 399s));
  CHECK_EQUAL(intervals[4], interval(epoch + 400s, epoch + 499s));
  CHECK_EQUAL(intervals[5], interval(epoch + 500s, epoch + 599s));
  CHECK_EQUAL(intervals[6], interval(epoch + 600s, epoch + 699s));
  CHECK_EQUAL(intervals[7], interval(epoch + 700s, epoch + 799s));
  CHECK_EQUAL(intervals[8], interval(epoch + 800s, epoch + 899s));
  CHECK_EQUAL(intervals[9], interval(epoch + 900s, epoch + 999s));
}

TEST(one-shot integer query result) {
  MESSAGE("fill first " << taste_count << " partitions");
  auto src = detail::spawn_generator_source(sys,
                                            partition_size * taste_count,
                                            int_generator(2), index);
  run_exhaustively();
  MESSAGE("query half of the values");
  auto [query_id, hits, scheduled] = query(":int == 1");
  CHECK_EQUAL(query_id, uuid::nil());
  CHECK_EQUAL(hits, taste_count);
  CHECK_EQUAL(scheduled, taste_count);
  ids expected_result;
  for (size_t i = 0; i < (partition_size * taste_count) / 2; ++i) {
    expected_result.append_bit(false);
    expected_result.append_bit(true);
  }
  auto result = receive_result(query_id, hits, scheduled);
  CHECK_EQUAL(result, expected_result);
}

TEST(iterable integer query result) {
  MESSAGE("fill first " << (taste_count * 3) << " partitions");
  auto src = detail::spawn_generator_source(sys,
                                            partition_size * taste_count * 3,
                                            int_generator(2), index);
  run_exhaustively();
  MESSAGE("query half of the values");
  auto [query_id, hits, scheduled] = query(":int == 1");
  CHECK_NOT_EQUAL(query_id, uuid::nil());
  CHECK_EQUAL(hits, taste_count * 3);
  CHECK_EQUAL(scheduled, taste_count);
  ids expected_result;
  for (size_t i = 0; i < (partition_size * taste_count * 3) / 2; ++i) {
    expected_result.append_bit(false);
    expected_result.append_bit(true);
  }
  MESSAGE("collect results");
  auto result = receive_result(query_id, hits, scheduled);
  CHECK_EQUAL(result, expected_result);
}

TEST(iterable bro conn log query result) {
  REQUIRE_EQUAL(bro_conn_log.size(), 8462u);
  MESSAGE("ingest conn.log");
  detail::spawn_container_source(sys, bro_conn_log, index);
  run_exhaustively();
  MESSAGE("issue field type query");
  {
    auto expected_result = make_ids({680, 682, 719, 720}, bro_conn_log.size());
    auto [query_id, hits, scheduled] = query(":addr == 169.254.225.22");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(result), rank(expected_result));
    CHECK_EQUAL(result, expected_result);
  }
  MESSAGE("issue field name queries");
  {
    auto expected_result = make_ids({680, 682, 719, 720}, bro_conn_log.size());
    auto [query_id, hits, scheduled] = query("id.orig_h == 169.254.225.22");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(result), rank(expected_result));
    CHECK_EQUAL(result, expected_result);
  }
  {
    auto [query_id, hits, scheduled] = query("service == \"http\"");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(result), 2386u);
  }
  MESSAGE("issue historical point query with conjunction");
  {
    auto expected_result
      = make_ids({105,  150,  246,  257,  322,  419,  440,  480,  579,  595,
                 642,  766,  1224, 1251, 1751, 1762, 2670, 3661, 3682, 3820,
                 6345, 6777, 7677, 7843, 8002, 8286, 8325, 8354},
                 bro_conn_log.size());
    auto [query_id, hits, scheduled] = query("service == \"http\" "
                                             "&& :addr == 212.227.96.110");
    auto result = receive_result(query_id, hits, scheduled);
    CHECK_EQUAL(rank(expected_result), 28u);
    CHECK_EQUAL(rank(result), 28u);
    CHECK_EQUAL(result, expected_result);
  }
}

FIXTURE_SCOPE_END()
<|endoftext|>


<commit_before>/*************************************************************************
 *
 *  OpenOffice.org - a multi-platform office productivity suite
 *
 *  $RCSfile: thesdsp.hxx,v $
 *
 *  $Revision: 1.4 $
 *
 *  last change: $Author: vg $ $Date: 2007-05-25 12:25:59 $
 *
 *  The Contents of this file are made available subject to
 *  the terms of GNU Lesser General Public License Version 2.1.
 *
 *
 *    GNU Lesser General Public License Version 2.1
 *    =============================================
 *    Copyright 2005 by Sun Microsystems, Inc.
 *    901 San Antonio Road, Palo Alto, CA 94303, USA
 *
 *    This library is free software; you can redistribute it and/or
 *    modify it under the terms of the GNU Lesser General Public
 *    License version 2.1, as published by the Free Software Foundation.
 *
 *    This library 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 library; if not, write to the Free Software
 *    Foundation, Inc., 59 Temple Place, Suite 330, Boston,
 *    MA  02111-1307  USA
 *
 ************************************************************************/

#ifndef _LINGUISTIC_THESDSP_HXX_
#define _LINGUISTIC_THESDSP_HXX_

#ifndef _COM_SUN_STAR_UNO_REFERENCE_H_
#include <com/sun/star/uno/Reference.h>
#endif
#ifndef _COM_SUN_STAR_UNO_SEQUENCE_H_
#include <com/sun/star/uno/Sequence.h>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYACCESS_HPP_
#include <com/sun/star/beans/XPropertyAccess.hpp>
#endif
#ifndef _COM_SUN_STAR_BEANS_XPROPERTYCHANGELISTENER_HPP_
#include <com/sun/star/beans/XPropertyChangeListener.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XCOMPONENT_HPP_
#include <com/sun/star/lang/XComponent.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XINITIALIZATION_HPP_
#include <com/sun/star/lang/XInitialization.hpp>
#endif
#ifndef _COM_SUN_STAR_LANG_XSERVICEDISPLAYNAME_HPP_
#include <com/sun/star/lang/XServiceDisplayName.hpp>
#endif

#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/linguistic2/XThesaurus.hpp>

#include <uno/lbnames.h>            // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type
#include <cppuhelper/implbase1.hxx> // helper for implementations
#include <cppuhelper/implbase5.hxx> // helper for implementations

#ifndef _CPPUHELPER_INTERFACECONTAINER_H_
#include <cppuhelper/interfacecontainer.h>
#endif

#include <vos/mutex.hxx>

#ifndef _TOOLS_TABLE_HXX
#include <tools/table.hxx>
#endif

#include "lngopt.hxx"

class LinguOptions;

namespace com { namespace sun { namespace star { namespace beans {
    class XPropertySet;
}}}}

///////////////////////////////////////////////////////////////////////////

class SeqLangSvcEntry_Thes
{
    friend class ThesaurusDispatcher;

    ::com::sun::star::uno::Sequence< ::rtl::OUString >      aSvcImplNames;
    ::com::sun::star::uno::Sequence<
        ::com::sun::star::uno::Reference<
            ::com::sun::star::linguistic2::XThesaurus > >   aSvcRefs;
//  INT16           nLang;     //used as key in the table
    SvcFlags        aFlags;

public:
    SeqLangSvcEntry_Thes() {}
    SeqLangSvcEntry_Thes( const ::com::sun::star::uno::Sequence<
            ::rtl::OUString > &rSvcImplNames );
    ~SeqLangSvcEntry_Thes();

    BOOL    IsAlreadyWarned() const     { return aFlags.bAlreadyWarned != 0; }
    void    SetAlreadyWarned(BOOL bVal) { aFlags.bAlreadyWarned = 0 != bVal; }
    BOOL    IsDoWarnAgain() const       { return aFlags.bDoWarnAgain != 0; }
    void    SetDoWarnAgain(BOOL bVal)   { aFlags.bDoWarnAgain = 0 != bVal; }
};

DECLARE_TABLE( ThesSvcList, SeqLangSvcEntry_Thes * )

class ThesaurusDispatcher :
    public cppu::WeakImplHelper1
    <
        ::com::sun::star::linguistic2::XThesaurus
    >,
    public LinguDispatcher
{
    ThesSvcList     aSvcList;

    ::com::sun::star::uno::Reference<
        ::com::sun::star::beans::XPropertySet >     xPropSet;

    // disallow copy-constructor and assignment-operator for now
    ThesaurusDispatcher(const ThesaurusDispatcher &);
    ThesaurusDispatcher & operator = (const ThesaurusDispatcher &);

    inline ::com::sun::star::uno::Reference<
        ::com::sun::star::beans::XPropertySet >
            GetPropSet();

    void    ClearSvcList();

public:
    ThesaurusDispatcher();
    virtual ~ThesaurusDispatcher();

    // XSupportedLocales
    virtual ::com::sun::star::uno::Sequence<
            ::com::sun::star::lang::Locale > SAL_CALL
        getLocales()
            throw(::com::sun::star::uno::RuntimeException);
    virtual sal_Bool SAL_CALL
        hasLocale( const ::com::sun::star::lang::Locale& aLocale )
            throw(::com::sun::star::uno::RuntimeException);

    // XThesaurus
    virtual ::com::sun::star::uno::Sequence<
            ::com::sun::star::uno::Reference<
                ::com::sun::star::linguistic2::XMeaning > > SAL_CALL
        queryMeanings( const ::rtl::OUString& aTerm,
                const ::com::sun::star::lang::Locale& aLocale,
                const ::com::sun::star::beans::PropertyValues& aProperties )
            throw(::com::sun::star::lang::IllegalArgumentException,
                  ::com::sun::star::uno::RuntimeException);

    // LinguDispatcher
    virtual void
        SetServiceList( const ::com::sun::star::lang::Locale &rLocale,
                const ::com::sun::star::uno::Sequence<
                    rtl::OUString > &rSvcImplNames );
    virtual ::com::sun::star::uno::Sequence< rtl::OUString >
        GetServiceList( const ::com::sun::star::lang::Locale &rLocale ) const;
    virtual DspType
        GetDspType() const;
};

inline ::com::sun::star::uno::Reference<
    ::com::sun::star::beans::XPropertySet >
        ThesaurusDispatcher::GetPropSet()
{
    return xPropSet.is() ?
        xPropSet : xPropSet = linguistic::GetLinguProperties();
}

///////////////////////////////////////////////////////////////////////////

#endif

<commit_msg>INTEGRATION: CWS changefileheader (1.4.40); FILE MERGED 2008/04/01 15:21:28 thb 1.4.40.3: #i85898# Stripping all external header guards 2008/04/01 12:31:46 thb 1.4.40.2: #i85898# Stripping all external header guards 2008/03/31 16:25:38 rt 1.4.40.1: #i87441# Change license header to LPGL v3.<commit_after>/*************************************************************************
 *
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
 *
 * Copyright 2008 by Sun Microsystems, Inc.
 *
 * OpenOffice.org - a multi-platform office productivity suite
 *
 * $RCSfile: thesdsp.hxx,v $
 * $Revision: 1.5 $
 *
 * This file is part of OpenOffice.org.
 *
 * OpenOffice.org is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License version 3
 * only, as published by the Free Software Foundation.
 *
 * OpenOffice.org 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 version 3 for more details
 * (a copy is included in the LICENSE file that accompanied this code).
 *
 * You should have received a copy of the GNU Lesser General Public License
 * version 3 along with OpenOffice.org.  If not, see
 * <http://www.openoffice.org/license.html>
 * for a copy of the LGPLv3 License.
 *
 ************************************************************************/

#ifndef _LINGUISTIC_THESDSP_HXX_
#define _LINGUISTIC_THESDSP_HXX_

#include <com/sun/star/uno/Reference.h>
#include <com/sun/star/uno/Sequence.h>
#include <com/sun/star/beans/XPropertyAccess.hpp>
#include <com/sun/star/beans/XPropertyChangeListener.hpp>
#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/lang/XInitialization.hpp>
#include <com/sun/star/lang/XServiceDisplayName.hpp>

#include <com/sun/star/lang/XServiceInfo.hpp>
#include <com/sun/star/linguistic2/XThesaurus.hpp>

#include <uno/lbnames.h>            // CPPU_CURRENT_LANGUAGE_BINDING_NAME macro, which specify the environment type
#include <cppuhelper/implbase1.hxx> // helper for implementations
#include <cppuhelper/implbase5.hxx> // helper for implementations
#include <cppuhelper/interfacecontainer.h>

#include <vos/mutex.hxx>
#include <tools/table.hxx>

#include "lngopt.hxx"

class LinguOptions;

namespace com { namespace sun { namespace star { namespace beans {
    class XPropertySet;
}}}}

///////////////////////////////////////////////////////////////////////////

class SeqLangSvcEntry_Thes
{
    friend class ThesaurusDispatcher;

    ::com::sun::star::uno::Sequence< ::rtl::OUString >      aSvcImplNames;
    ::com::sun::star::uno::Sequence<
        ::com::sun::star::uno::Reference<
            ::com::sun::star::linguistic2::XThesaurus > >   aSvcRefs;
//  INT16           nLang;     //used as key in the table
    SvcFlags        aFlags;

public:
    SeqLangSvcEntry_Thes() {}
    SeqLangSvcEntry_Thes( const ::com::sun::star::uno::Sequence<
            ::rtl::OUString > &rSvcImplNames );
    ~SeqLangSvcEntry_Thes();

    BOOL    IsAlreadyWarned() const     { return aFlags.bAlreadyWarned != 0; }
    void    SetAlreadyWarned(BOOL bVal) { aFlags.bAlreadyWarned = 0 != bVal; }
    BOOL    IsDoWarnAgain() const       { return aFlags.bDoWarnAgain != 0; }
    void    SetDoWarnAgain(BOOL bVal)   { aFlags.bDoWarnAgain = 0 != bVal; }
};

DECLARE_TABLE( ThesSvcList, SeqLangSvcEntry_Thes * )

class ThesaurusDispatcher :
    public cppu::WeakImplHelper1
    <
        ::com::sun::star::linguistic2::XThesaurus
    >,
    public LinguDispatcher
{
    ThesSvcList     aSvcList;

    ::com::sun::star::uno::Reference<
        ::com::sun::star::beans::XPropertySet >     xPropSet;

    // disallow copy-constructor and assignment-operator for now
    ThesaurusDispatcher(const ThesaurusDispatcher &);
    ThesaurusDispatcher & operator = (const ThesaurusDispatcher &);

    inline ::com::sun::star::uno::Reference<
        ::com::sun::star::beans::XPropertySet >
            GetPropSet();

    void    ClearSvcList();

public:
    ThesaurusDispatcher();
    virtual ~ThesaurusDispatcher();

    // XSupportedLocales
    virtual ::com::sun::star::uno::Sequence<
            ::com::sun::star::lang::Locale > SAL_CALL
        getLocales()
            throw(::com::sun::star::uno::RuntimeException);
    virtual sal_Bool SAL_CALL
        hasLocale( const ::com::sun::star::lang::Locale& aLocale )
            throw(::com::sun::star::uno::RuntimeException);

    // XThesaurus
    virtual ::com::sun::star::uno::Sequence<
            ::com::sun::star::uno::Reference<
                ::com::sun::star::linguistic2::XMeaning > > SAL_CALL
        queryMeanings( const ::rtl::OUString& aTerm,
                const ::com::sun::star::lang::Locale& aLocale,
                const ::com::sun::star::beans::PropertyValues& aProperties )
            throw(::com::sun::star::lang::IllegalArgumentException,
                  ::com::sun::star::uno::RuntimeException);

    // LinguDispatcher
    virtual void
        SetServiceList( const ::com::sun::star::lang::Locale &rLocale,
                const ::com::sun::star::uno::Sequence<
                    rtl::OUString > &rSvcImplNames );
    virtual ::com::sun::star::uno::Sequence< rtl::OUString >
        GetServiceList( const ::com::sun::star::lang::Locale &rLocale ) const;
    virtual DspType
        GetDspType() const;
};

inline ::com::sun::star::uno::Reference<
    ::com::sun::star::beans::XPropertySet >
        ThesaurusDispatcher::GetPropSet()
{
    return xPropSet.is() ?
        xPropSet : xPropSet = linguistic::GetLinguProperties();
}

///////////////////////////////////////////////////////////////////////////

#endif

<|endoftext|>