diff --git a/backend/hdf5/BaseTagHDF5.cpp b/backend/hdf5/BaseTagHDF5.cpp index 8d86ac0aa..16bc30460 100644 --- a/backend/hdf5/BaseTagHDF5.cpp +++ b/backend/hdf5/BaseTagHDF5.cpp @@ -132,21 +132,33 @@ ndsize_t BaseTagHDF5::featureCount() const { std::shared_ptr BaseTagHDF5::getFeature(const std::string &name_or_id) const { std::shared_ptr feature; boost::optional g = feature_group(false); - if (g) { boost::optional group = g->findGroupByNameOrAttribute("name", name_or_id); if (group) feature = std::make_shared(file(), block(), group.get()); else { - for (ndsize_t i = 0; i < g->objectCount(); i++) { - H5Group gr = g->openGroup(g->objectName(i), false); - std::shared_ptr feat = std::make_shared(file(), block(), gr); - std::shared_ptr da = feat->data(); - if (da->name() == name_or_id || da->id() == name_or_id) { - feature = std::make_shared(file(), block(), gr); - break; + bool found = false; + ndsize_t index; + for (index = 0; index < featureCount(); ++index) { + Feature feat = getFeature(index); + if (feat.targetType() == TargetType::DataArray) { + DataArray da = feat.dataArray(); + found = da && (da.name() == name_or_id || da.id() == name_or_id); + if (found) { + break; + } + } else { + DataFrame df = feat.dataFrame(); + found = df && (df.name() == name_or_id || df.id() == name_or_id); + if (found) { + break; + } } } + if (found) { + H5Group gr = g->openGroup(g->objectName(index), false); + feature = std::make_shared(file(), block(), gr); + } } } return feature; @@ -160,16 +172,38 @@ std::shared_ptr BaseTagHDF5::getFeature(ndsize_t index) const { } -std::shared_ptr BaseTagHDF5::createFeature(const std::string &name_or_id, LinkType link_type) { - if(!block()->hasEntity({name_or_id, ObjectType::DataArray})) { - throw std::runtime_error("DataArray not found in Block!"); +std::shared_ptr BaseTagHDF5::createFeature(const std::string &name_or_id, LinkType link_type, TargetType target_type) { + bool entity_valid = true; + std::shared_ptr ptr; + switch(target_type) { + case TargetType::DataArray: + entity_valid = block()->hasEntity({name_or_id, ObjectType::DataArray}); + break; + case TargetType::DataFrame: + entity_valid = block()->hasEntity({name_or_id, ObjectType::DataFrame}); + break; + } + if (!entity_valid) { + throw std::runtime_error("DataArray/DataFrame not found in Block!"); } std::string rep_id = util::createId(); boost::optional g = feature_group(true); - H5Group group = g->openGroup(rep_id, true); - DataArray data = std::dynamic_pointer_cast(block()->getEntity({name_or_id, ObjectType::DataArray})); - return std::make_shared(file(), block(), group, rep_id, data, link_type); + + switch(target_type) { + case TargetType::DataArray: { + DataArray data = std::dynamic_pointer_cast(block()->getEntity({name_or_id, ObjectType::DataArray})); + ptr = std::make_shared(file(), block(), group, rep_id, data, link_type); + break; + } + case TargetType::DataFrame: { + entity_valid = block()->hasEntity({name_or_id, ObjectType::DataFrame}); + DataFrame data = std::dynamic_pointer_cast(block()->getEntity({name_or_id, ObjectType::DataFrame})); + ptr = std::make_shared(file(), block(), group, rep_id, data, link_type); + break; + } + } + return ptr; } diff --git a/backend/hdf5/BaseTagHDF5.hpp b/backend/hdf5/BaseTagHDF5.hpp index cacf52a21..89289a865 100644 --- a/backend/hdf5/BaseTagHDF5.hpp +++ b/backend/hdf5/BaseTagHDF5.hpp @@ -86,7 +86,7 @@ class BaseTagHDF5 : public EntityWithSourcesHDF5, virtual public base::IBaseTag virtual std::shared_ptr getFeature(ndsize_t index) const; - virtual std::shared_ptr createFeature(const std::string &name_or_id, LinkType link_type); + virtual std::shared_ptr createFeature(const std::string &name_or_id, LinkType link_type, TargetType target_type); virtual bool deleteFeature(const std::string &name_or_id); diff --git a/backend/hdf5/FeatureHDF5.cpp b/backend/hdf5/FeatureHDF5.cpp index e1fc86bed..79b0924b3 100644 --- a/backend/hdf5/FeatureHDF5.cpp +++ b/backend/hdf5/FeatureHDF5.cpp @@ -11,6 +11,7 @@ #include #include #include "DataArrayHDF5.hpp" +#include "DataFrameHDF5.hpp" using namespace std; @@ -38,6 +39,22 @@ LinkType linkTypeFromString(const string &str) { } +string targetTypeToString(TargetType target_type) { + static vector type_names = {"DataArray", "DataFrame"}; + return type_names[static_cast(target_type)]; +} + + +TargetType targetTypeFromString(const string &str) { + if (str == "DataArray") + return TargetType::DataArray; + else if (str == "DataFrame") + return TargetType::DataFrame; + else + throw runtime_error("Unable to create a TargetType from the string: " + str); +} + + FeatureHDF5::FeatureHDF5(const shared_ptr &file, const shared_ptr &block, const H5Group &group) : EntityHDF5(file, group), block(block) { @@ -57,7 +74,25 @@ FeatureHDF5::FeatureHDF5(const shared_ptr &file, const shared_ptr linkType(link_type); // TODO: the line below currently throws an exception if the DataArray // is not in block - to consider if we prefer copying it to the block - this->data(data.id()); + this->data(data); +} + + +FeatureHDF5::FeatureHDF5(const shared_ptr &file, const shared_ptr &block, const H5Group &group, + const string &id, DataFrame data, LinkType link_type) + : FeatureHDF5(file, block, group, id, data, link_type, util::getTime()) +{ +} + + +FeatureHDF5::FeatureHDF5(const shared_ptr &file, const shared_ptr &block, const H5Group &group, + const string &id, DataFrame data, LinkType link_type, time_t time) + : EntityHDF5(file, group, id, time), block(block) +{ + linkType(link_type); + // TODO: the line below currently throws an exception if the DataArray + // is not in block - to consider if we prefer copying it to the block + this->data(data); } @@ -68,8 +103,14 @@ void FeatureHDF5::linkType(LinkType link_type) { } -void FeatureHDF5::data(const std::string &name_or_id) { - std::shared_ptr ida = block->getEntity(name_or_id); +void FeatureHDF5::targetType(TargetType ttype) { + group().setAttr("target_type", targetTypeToString(ttype)); + forceUpdatedAt(); +} + + +void FeatureHDF5::data(const DataArray &data) { + std::shared_ptr ida = block->getEntity(data.name()); if (!ida) { throw std::runtime_error("FeatureHDF5::data: DataArray not found in block!"); } @@ -80,13 +121,57 @@ void FeatureHDF5::data(const std::string &name_or_id) { auto target = dynamic_pointer_cast(ida); group().createLink(target->group(), "data"); + targetType(TargetType::DataArray); + forceUpdatedAt(); } -shared_ptr FeatureHDF5::data() const { - shared_ptr da; +void FeatureHDF5::data(const DataFrame &data) { + std::shared_ptr idf = block->getEntity(data.name()); + if (!idf) { + throw std::runtime_error("FeatureHDF5::data: DataFrame not found in block!"); + } + if (group().hasGroup("data")) { + group().removeGroup("data"); + } + auto target = dynamic_pointer_cast(idf); + + group().createLink(target->group(), "data"); + targetType(TargetType::DataFrame); + forceUpdatedAt(); +} + + +void FeatureHDF5::data(const std::string &name_or_id) { + TargetType tt = TargetType::DataArray; + if (group().hasGroup("data")) { + group().removeGroup("data"); + } + std::shared_ptr ida = block->getEntity(name_or_id); + if (!ida) { + std::shared_ptr idf = block->getEntity(name_or_id); + if (!idf) { + throw std::runtime_error("FeatureHDF5::data: entity is not found in block, neither DataArray nor DataFrame!"); + } + tt = TargetType::DataFrame; + auto target = dynamic_pointer_cast(idf); + group().createLink(target->group(), "data"); + } else { + auto target = dynamic_pointer_cast(ida); + group().createLink(target->group(), "data"); + } + targetType(tt); + forceUpdatedAt(); +} + + +shared_ptr FeatureHDF5::dataArray() const { + if (targetType() != TargetType::DataArray) { + throw std::runtime_error("Cannot convert Feature data to DataArray! Feature target is of type DataFrame!"); + } + shared_ptr da; if (group().hasGroup("data")) { H5Group other_group = group().openGroup("data", false); da = make_shared(file(), block, other_group); @@ -98,16 +183,42 @@ shared_ptr FeatureHDF5::data() const { } +shared_ptr FeatureHDF5::dataFrame() const { + if (targetType() != TargetType::DataFrame) { + throw std::runtime_error("Cannot convert Feature data to DataFrame! Feature target is of type DataArray!"); + } + shared_ptr df; + if (group().hasGroup("data")) { + H5Group other_group = group().openGroup("data", false); + df = make_shared(file(), block, other_group); + + if (!block->hasEntity(df)) { + throw std::runtime_error("FeatureHDF5::data: DataFrame not found!"); + } + } + return df; +} + + LinkType FeatureHDF5::linkType() const { if (group().hasAttr("link_type")) { string link_type; group().getAttr("link_type", link_type); return linkTypeFromString(link_type); } else { - throw MissingAttr("data"); + throw MissingAttr("link_type"); } } +TargetType FeatureHDF5::targetType() const { + if (group().hasAttr("target_type")) { + string target_type; + group().getAttr("target_type", target_type); + return targetTypeFromString(target_type); + } else { + return TargetType::DataArray; + } +} FeatureHDF5::~FeatureHDF5() {} diff --git a/backend/hdf5/FeatureHDF5.hpp b/backend/hdf5/FeatureHDF5.hpp index 8d3379e12..802a7e754 100644 --- a/backend/hdf5/FeatureHDF5.hpp +++ b/backend/hdf5/FeatureHDF5.hpp @@ -35,6 +35,21 @@ std::string linkTypeToString(LinkType link_type); */ LinkType linkTypeFromString(const std::string &str); +/** + * Converts a TargetType into a string. + * + * @param target_type The type to convert. + */ +std::string targetTypeToString(TargetType target_type); + +/** + * Create a target type from a string. If no matching type + * exists, an exception will be thrown. + * + * @return The target type that matches the string. + */ +TargetType targetTypeFromString(const std::string &str); + /** * Class that represents a NIX feature entity @@ -44,6 +59,7 @@ class FeatureHDF5 : virtual public base::IFeature, public EntityHDF5 { private: std::shared_ptr block; + void targetType(TargetType type); public: @@ -53,29 +69,57 @@ class FeatureHDF5 : virtual public base::IFeature, public EntityHDF5 { FeatureHDF5(const std::shared_ptr &file, const std::shared_ptr &block, const H5Group &group); /** - * Standard constructor for new Feature + * Standard constructor for new Feature linking to a DataArray */ FeatureHDF5(const std::shared_ptr &file, const std::shared_ptr &block, const H5Group &group, const std::string &id, DataArray data, LinkType link_type); /** - * Standard constructor for new Feature with time + * Standard constructor for new Feature linking to a DataFrame + */ + FeatureHDF5(const std::shared_ptr &file, const std::shared_ptr &block, const H5Group &group, + const std::string &id, DataFrame data, LinkType link_type); + + /** + * Standard constructor for new Feature linking a DataArray with time */ FeatureHDF5(const std::shared_ptr &file, const std::shared_ptr &block, const H5Group &group, const std::string &id, DataArray data, LinkType link_type, time_t time); + /** + * Standard constructor for new Feature linking a DataFrame with time + */ + FeatureHDF5(const std::shared_ptr &file, const std::shared_ptr &block, const H5Group &group, + const std::string &id, DataFrame data, LinkType link_type, time_t time); + void linkType(LinkType type); LinkType linkType() const; + + TargetType targetType() const; + + /** + * links to the given data (DataArray or DataFrame) method tries to find the given name_or_id + * first among the DataArrays and then the DataFrames. When passing a name there might be + * an ambiguity that cannot be resolved. Rather, use the id or use the data(DataArray) or + * data(DataFrame) overloads instead. + */ + DEPRECATED void data(const std::string &name_or_id); + + + void data(const DataArray &data); + + + void data(const DataFrame &data); - void data(const std::string &name_or_id); + std::shared_ptr dataArray() const; - std::shared_ptr data() const; + std::shared_ptr dataFrame() const; virtual ~FeatureHDF5(); diff --git a/backend/hdf5/h5x/H5Object.cpp b/backend/hdf5/h5x/H5Object.cpp index 499a3b183..ea8b704ba 100644 --- a/backend/hdf5/h5x/H5Object.cpp +++ b/backend/hdf5/h5x/H5Object.cpp @@ -110,7 +110,7 @@ void H5Object::close() { H5Object::~H5Object() { - close(); + H5Object::close(); } diff --git a/include/nix/Exception.hpp b/include/nix/Exception.hpp index 476837e94..d80b03f1c 100644 --- a/include/nix/Exception.hpp +++ b/include/nix/Exception.hpp @@ -116,7 +116,6 @@ class IncompatibleDimensions: public std::invalid_argument { class InvalidDimension : public std::invalid_argument { - public: InvalidDimension(const std::string &what, const std::string &where): std::invalid_argument("InvalidDimension: " + what + " evoked at: " + where) { } @@ -127,12 +126,19 @@ class ConsistencyError: public std::runtime_error { ConsistencyError(const std::string &what) : runtime_error("ConsistencyError: " + what){ } }; - class MissingAttr: public std::runtime_error { +class MissingAttr: public std::runtime_error { public: MissingAttr(const std::string &name): std::runtime_error("MissingAttribute: Obligatory attribute " + name + " is not set!") { } }; +class InvalidLinkType: public std::invalid_argument { +public: + InvalidLinkType(const std::string &what, const std::string &where): + std::invalid_argument("InvalidLinkType: " + what + " evoked at: " + where) { } +}; + + namespace check { template diff --git a/include/nix/Feature.hpp b/include/nix/Feature.hpp index fca1232d9..52a97a290 100644 --- a/include/nix/Feature.hpp +++ b/include/nix/Feature.hpp @@ -122,6 +122,15 @@ class NIXAPI Feature : public base::Entity { return backend()->linkType(); } + /** + * @brief Getter for the traget type. + * + * @return The current target type of the feature. + */ + TargetType targetType() const { + return backend()->targetType(); + } + /** * @brief Sets the data array associated with this feature. * @@ -136,13 +145,39 @@ class NIXAPI Feature : public base::Entity { */ void data(const DataArray &data); + /** + * @brief Sets the data frame associated with this feature. + * + * @param data The DataFrame to set. + */ + void data(const DataFrame &data); + + /** + * @brief Gets the data array associated with this feature. + * + * @return The associated data array. + * @deprecated This function has been deprecated and will be removed + */ + DEPRECATED DataArray data() const { + return dataArray(); + } + /** * @brief Gets the data array associated with this feature. * * @return The associated data array. */ - DataArray data() const { - return backend()->data(); + DataArray dataArray() const { + return backend()->dataArray(); + } + + /** + * @brief Gets the DataFrame associated with this feature. + * + * @return The associated DataFrame. + */ + DataFrame dataFrame() const { + return backend()->dataFrame(); } /** @@ -169,6 +204,7 @@ class NIXAPI Feature : public base::Entity { ImplContainer::operator=(other); return *this; } + }; /** @@ -193,6 +229,29 @@ NIXAPI std::string link_type_to_string(LinkType ltype); */ NIXAPI std::ostream& operator<<(std::ostream &out, const LinkType ltype); +/** + * @brief Convert a target type into string representation. + * + * @param ttype The target type. + * + * @return A human readable name for the given type. + */ +NIXAPI std::string target_type_to_string(TargetType ttype); + + +/** + * @brief Output operator for link type. + * + * Prints a human readable string representation of the + * link type to an output stream. + * + * @param out The output stream. + * @param ltype The link type to print. + * + * @return The output stream. + */ +NIXAPI std::ostream& operator<<(std::ostream &out, const TargetType ttype); + template<> struct objectToType { diff --git a/include/nix/MultiTag.hpp b/include/nix/MultiTag.hpp index 51095af09..3bdc516c0 100644 --- a/include/nix/MultiTag.hpp +++ b/include/nix/MultiTag.hpp @@ -529,21 +529,28 @@ class NIXAPI MultiTag : public base::EntityWithSources { * * @return The created feature object. */ - Feature createFeature(const DataArray &data, LinkType link_type) { - return backend()->createFeature(data.id(), link_type); - } + Feature createFeature(const DataArray &data, LinkType link_type); + + /** + * @brief Create a new feature. + * + * @param data The data frame of this feature. + * @param link_type The link type of this feature. + * + * @return The created feature object. + */ + Feature createFeature(const DataFrame &data, LinkType link_type); /** * @brief Create a new feature. * - * @param name_or_id Name or id of the data array that is part of the new feature. + * @param name_or_id Name or id of the DataArray/DataFrame that is part of the new feature. * @param link_type The link type of this feature. + * @param target_type The target type, i.e. TargetType::DataArray or TargetType::DataFrame * * @return The created feature object. */ - Feature createFeature(const std::string &name_or_id, LinkType link_type) { - return backend()->createFeature(name_or_id, link_type); - } + Feature createFeature(const std::string &name_or_id, LinkType link_type, TargetType target_type = TargetType::DataArray); /** * @brief Delete a feature from the tag. diff --git a/include/nix/Tag.hpp b/include/nix/Tag.hpp index 0d8de4f0e..2143a33e1 100644 --- a/include/nix/Tag.hpp +++ b/include/nix/Tag.hpp @@ -399,14 +399,23 @@ class NIXAPI Tag : public base::EntityWithSources { /** * @brief Create a new feature. * - * @param data_array_id The id of the data array of this feature. + * @param data The data frame of this feature. + * @param link_type The link type of this feature. + * + * @return The created feature object. + */ + Feature createFeature(const DataFrame &data, LinkType link_type); + + /** + * @brief Create a new feature. + * + * @param name_or_id The id of the DataArray/DataFrame of this feature. * @param link_type The link type of this feature. + * @param target_type The target type, i.e. TargetType::DataArray or TargetType::DataFrame * * @return The created feature object. */ - Feature createFeature(const std::string &data_array_id, LinkType link_type) { - return backend()->createFeature(data_array_id, link_type); - } + Feature createFeature(const std::string &name_or_id, LinkType link_type, TargetType target_type = TargetType::DataArray); /** * @brief Deletes a feature from the tag. diff --git a/include/nix/base/IBaseTag.hpp b/include/nix/base/IBaseTag.hpp index 4efbdd0b9..0090acc4a 100644 --- a/include/nix/base/IBaseTag.hpp +++ b/include/nix/base/IBaseTag.hpp @@ -68,7 +68,7 @@ class NIXAPI IBaseTag : virtual public base::IEntityWithSources { virtual std::shared_ptr getFeature(ndsize_t index) const = 0; - virtual std::shared_ptr createFeature(const std::string &data_array_id, LinkType link_type) = 0; + virtual std::shared_ptr createFeature(const std::string &data_array_id, LinkType link_type, TargetType traget_type) = 0; virtual bool deleteFeature(const std::string &id) = 0; diff --git a/include/nix/base/IFeature.hpp b/include/nix/base/IFeature.hpp index 12721ba43..ebe19f276 100644 --- a/include/nix/base/IFeature.hpp +++ b/include/nix/base/IFeature.hpp @@ -21,13 +21,20 @@ namespace nix { /** * @brief Enumeration for link types. * - * TODO remove "untagged" from link type */ enum class LinkType : int { Tagged, Untagged, Indexed }; +/** + * @brief Enumeration for feature target types. + * + */ +enum class TargetType : int { + DataArray, DataFrame +}; + namespace base { @@ -45,11 +52,17 @@ class NIXAPI IFeature: virtual public base::IEntity { virtual LinkType linkType() const = 0; + + virtual TargetType targetType() const = 0; + virtual void data(const std::string &name_or_id) = 0; + + virtual std::shared_ptr dataArray() const = 0; - virtual std::shared_ptr data() const = 0; + + virtual std::shared_ptr dataFrame() const = 0; virtual ~IFeature() {} diff --git a/include/nix/util/dataAccess.hpp b/include/nix/util/dataAccess.hpp index 935b6b22c..2ddb7e25c 100644 --- a/include/nix/util/dataAccess.hpp +++ b/include/nix/util/dataAccess.hpp @@ -416,7 +416,7 @@ NIXAPI bool positionInData(const DataArray &data, const NDSize &position); NIXAPI bool positionAndExtentInData(const DataArray &data, const NDSize &position, const NDSize &count); /** - * @brief Retruns the feature data associated with a Tag. + * @brief Returns the feature data associated with a Tag. * * @param tag The Tag whos feature data is requested * @param feature_index The index of the desired feature. Default is 0. @@ -430,7 +430,7 @@ NIXAPI bool positionAndExtentInData(const DataArray &data, const NDSize &positio NIXAPI DEPRECATED DataView retrieveFeatureData(const Tag &tag, ndsize_t feature_index=0, RangeMatch range_match = RangeMatch::Inclusive); /** - * @brief Retruns the feature data associated with a Tag. + * @brief Returns the feature data associated with a Tag. * * @param tag The Tag whos feature data is requested * @param feature_index The index of the desired feature. Default is 0. @@ -441,7 +441,7 @@ NIXAPI DEPRECATED DataView retrieveFeatureData(const Tag &tag, ndsize_t feature_ NIXAPI DataView featureData(const Tag &tag, ndsize_t feature_index=0, RangeMatch range_match = RangeMatch::Exclusive); /** - * @brief Retruns the feature data associated with a Tag. + * @brief Returns the feature data associated with a Tag. * * @param tag The Tag whos feature data is requested. * @param feature The Feature of which the tagged data is requested. @@ -453,7 +453,7 @@ NIXAPI DataView featureData(const Tag &tag, const Feature &feature, RangeMatch r /** - * @brief Retruns the feature data associated with a Tag. + * @brief Returns the feature data associated with a Tag. * * @param tag The Tag whos feature data is requested. * @param feature The Feature of which the tagged data is requested. diff --git a/src/Feature.cpp b/src/Feature.cpp index be0fcd08d..2d3673112 100644 --- a/src/Feature.cpp +++ b/src/Feature.cpp @@ -19,6 +19,19 @@ void Feature::data(const DataArray &data) { backend()->data(data.id()); } +void Feature::data(const DataFrame &data) { + util::checkEntityInput(data); + if (!data.isValidEntity()) { + throw UninitializedEntity(); + } + backend()->data(data.id()); +} + +void Feature::data(const std::string &name_or_id) { + util::checkNameOrId(name_or_id); + backend()->data(name_or_id); +} + std::string link_type_to_string(LinkType ltype) { std::string str; @@ -39,9 +52,19 @@ std::ostream &operator<<(std::ostream &out, const LinkType ltype) { return out; } -void Feature::data(const std::string &name_or_id) { - util::checkNameOrId(name_or_id); - backend()->data(name_or_id); +std::string target_type_to_string(TargetType ttype) { + std::string str; + + switch(ttype) { + case TargetType::DataArray: str = "DataArray"; break; + case TargetType::DataFrame: str = "DataFrame"; break; + } + return str; +} + +std::ostream &operator<<(std::ostream &out, const TargetType ttype) { + out << "TargetType::" << target_type_to_string(ttype); + return out; } } diff --git a/src/MultiTag.cpp b/src/MultiTag.cpp index c98ad9cc5..ad1072499 100644 --- a/src/MultiTag.cpp +++ b/src/MultiTag.cpp @@ -130,6 +130,30 @@ std::vector MultiTag::taggedData(std::vector &position_indic } +Feature MultiTag::createFeature(const DataArray &data, LinkType link_type) { + if (!util::checkEntityInput(data)) { + throw UninitializedEntity(); + } + return createFeature(data.id(), link_type, TargetType::DataArray); +} + + +Feature MultiTag::createFeature(const DataFrame &data, LinkType link_type) { + if (!util::checkEntityInput(data)) { + throw UninitializedEntity(); + } + return createFeature(data.id(), link_type, TargetType::DataFrame); +} + + +Feature MultiTag::createFeature(const std::string &name_or_id, LinkType link_type, TargetType target_type) { + if (target_type == TargetType::DataFrame && link_type == LinkType::Tagged) { + throw InvalidLinkType("LinkType::Tagged is not allowed when using DataFrames!", "MultiTag::createFeature"); + } + return backend()->createFeature(name_or_id, link_type, target_type); +} + + bool MultiTag::hasFeature(const Feature &feature) const { if (!util::checkEntityInput(feature, false)) { return false; diff --git a/src/Tag.cpp b/src/Tag.cpp index 0797846de..a05460fee 100644 --- a/src/Tag.cpp +++ b/src/Tag.cpp @@ -106,7 +106,23 @@ Feature Tag::createFeature(const DataArray &data, LinkType link_type) { if (!util::checkEntityInput(data)) { throw UninitializedEntity(); } - return backend()->createFeature(data.id(), link_type); + return createFeature(data.id(), link_type, TargetType::DataArray); +} + + +Feature Tag::createFeature(const DataFrame &data, LinkType link_type) { + if (!util::checkEntityInput(data)) { + throw UninitializedEntity(); + } + return createFeature(data.id(), link_type, TargetType::DataFrame); +} + + +Feature Tag::createFeature(const std::string &name_or_id, LinkType link_type, TargetType target_type) { + if (target_type == TargetType::DataFrame && link_type == LinkType::Tagged) { + throw InvalidLinkType("LinkType::Tagged is not allowed when using DataFrames!", "Tag::createFeature"); + } + return backend()->createFeature(name_or_id, link_type, target_type); } diff --git a/src/util/dataAccess.cpp b/src/util/dataAccess.cpp index f5c71a758..083e45123 100644 --- a/src/util/dataAccess.cpp +++ b/src/util/dataAccess.cpp @@ -734,7 +734,11 @@ DataView taggedData(const Tag &tag, const DataArray &array, RangeMatch match) { DataView featureData(const Tag &tag, const Feature &feature, RangeMatch match) { - DataArray data = feature.data(); + if (feature.targetType() != TargetType::DataArray) { + throw std::runtime_error("Implement Tag::featureData for DataFrame!"); + } + //FIXME + DataArray data = feature.dataArray(); if (data == none) { throw UninitializedEntity(); } @@ -820,7 +824,11 @@ std::vector retrieveFeatureData(const MultiTag &tag, std::vector featureData(const MultiTag &tag, std::vector position_indices, const Feature &feature, RangeMatch match) { std::vector views; - DataArray data = feature.data(); + // FIXME!!! + if (feature.targetType() != TargetType::DataArray) { + throw std::runtime_error("Implement mtag featureData for DataFrames!"); + } + DataArray data = feature.dataArray(); if (data == nix::none) { throw UninitializedEntity(); } diff --git a/src/valid/validate.cpp b/src/valid/validate.cpp index 91a0101fb..6526c9f9c 100644 --- a/src/valid/validate.cpp +++ b/src/valid/validate.cpp @@ -196,7 +196,7 @@ Result validate(const SetDimension &set_dim) { Result validate(const Feature &feature) { Result result_base = validate_entity(feature); Result result = validator({ - must(feature, &Feature::data, notFalse(), "data is not set!"), + must(feature, &Feature::dataArray, notFalse(), "data is not set!"), must(feature, &Feature::linkType, notSmaller(0), "linkType is not set!") }); diff --git a/test/BaseTestFeature.cpp b/test/BaseTestFeature.cpp index 5df7ab0d6..2b5fa62df 100644 --- a/test/BaseTestFeature.cpp +++ b/test/BaseTestFeature.cpp @@ -63,7 +63,21 @@ void BaseTestFeature::testLinkType(){ } -void BaseTestFeature::testData() { +void BaseTestFeature::testTargetType(){ + CPPUNIT_ASSERT(target_type_to_string(nix::TargetType::DataArray) == "DataArray"); + CPPUNIT_ASSERT(target_type_to_string(nix::TargetType::DataFrame) == "DataFrame"); + + Feature rp = tag.createFeature(data_array, nix::LinkType::Tagged); + CPPUNIT_ASSERT(rp.targetType() == nix::TargetType::DataArray); + tag.deleteFeature(rp.id()); + + Feature ft = tag.createFeature(data_frame, nix::LinkType::Untagged); + CPPUNIT_ASSERT(ft.targetType() == nix::TargetType::DataFrame); + tag.deleteFeature(ft.id()); +} + + +void BaseTestFeature::testDataArrayFeature() { DataArray a; Feature f; CPPUNIT_ASSERT_THROW(tag.createFeature(a, nix::LinkType::Tagged), UninitializedEntity); @@ -75,20 +89,64 @@ void BaseTestFeature::testData() { CPPUNIT_ASSERT_THROW(f.data(a), UninitializedEntity); Feature rp = tag.createFeature(data_array, nix::LinkType::Tagged); + tag.hasFeature(rp.id()); + tag.getFeature(rp.id()); + Feature ft2 = tag.getFeature(data_array.name()); + CPPUNIT_ASSERT(ft2 && ft2.dataArray().name() == data_array.name()); + CPPUNIT_ASSERT(ft2 && ft2.id() == rp.id()); + DataArray da_2 = block.createDataArray("array2", "Test", DataType::Double, nix::NDSize({ 0 })); + CPPUNIT_ASSERT(rp.dataArray().id() == data_array.id()); CPPUNIT_ASSERT(rp.data().id() == data_array.id()); rp.data(da_2); - CPPUNIT_ASSERT(rp.data().id() == da_2.id()); + CPPUNIT_ASSERT(rp.dataArray().id() == da_2.id()); + CPPUNIT_ASSERT_THROW(rp.dataFrame(), std::runtime_error); block.deleteDataArray(da_2.id()); // make sure link is gone with deleted data array - CPPUNIT_ASSERT(rp.data() == nix::none); + CPPUNIT_ASSERT(rp.dataArray() == nix::none); CPPUNIT_ASSERT_THROW(rp.data(""), EmptyString); CPPUNIT_ASSERT_THROW(rp.data("worng_id"), std::runtime_error); tag.deleteFeature(rp.id()); } +void BaseTestFeature::testDataFrameFeature() { + DataFrame df; + Feature ft; + CPPUNIT_ASSERT_THROW(tag.createFeature(df, nix::LinkType::Tagged), UninitializedEntity); + CPPUNIT_ASSERT_THROW(ft.data(df), UninitializedEntity); + std::vector cols = { + {"bool", "", nix::DataType::Bool}, + {"int32", "V", nix::DataType::Int32}}; + df = block.createDataFrame("df", "test_df", cols); + ft = tag.createFeature(df, nix::LinkType::Untagged); + ft.data(df.name()); + block.deleteDataFrame(df); + CPPUNIT_ASSERT_THROW(ft.data(df), UninitializedEntity); + CPPUNIT_ASSERT(ft.dataFrame() == nix::none); + CPPUNIT_ASSERT_THROW(ft.data(""), EmptyString); + CPPUNIT_ASSERT_THROW(ft.data("worng_id"), std::runtime_error); + CPPUNIT_ASSERT_THROW(ft.dataArray(), std::runtime_error); + CPPUNIT_ASSERT_THROW(ft.data(), std::runtime_error); + + ft = tag.createFeature(data_frame, nix::LinkType::Indexed); + + DataFrame df2 = ft.dataFrame(); + Feature ft2 = tag.getFeature(data_frame.name()); + CPPUNIT_ASSERT(ft2 && ft2.dataFrame().name() == data_frame.name()); + CPPUNIT_ASSERT(ft2 && ft2.id() == ft.id()); + CPPUNIT_ASSERT(ft.targetType() == nix::TargetType::DataFrame && ft.linkType() == nix::LinkType::Indexed); + tag.deleteFeature(ft.id()); + + ft = tag.createFeature(data_frame, nix::LinkType::Untagged); + CPPUNIT_ASSERT(ft.targetType() == nix::TargetType::DataFrame && ft.linkType() == nix::LinkType::Untagged); + tag.deleteFeature(ft.id()); + + CPPUNIT_ASSERT_THROW(tag.createFeature(data_frame, nix::LinkType::Tagged), InvalidLinkType); +} + + void BaseTestFeature::testLinkType2Str() { CPPUNIT_ASSERT(link_type_to_string(nix::LinkType::Tagged) == "Tagged"); CPPUNIT_ASSERT(link_type_to_string(nix::LinkType::Untagged) == "Untagged"); diff --git a/test/BaseTestFeature.hpp b/test/BaseTestFeature.hpp index 14f6d2111..822bcad5f 100644 --- a/test/BaseTestFeature.hpp +++ b/test/BaseTestFeature.hpp @@ -18,6 +18,7 @@ class BaseTestFeature : public CPPUNIT_NS::TestFixture { protected: nix::File file; nix::DataArray data_array; + nix::DataFrame data_frame; nix::Block block; nix::Tag tag; @@ -26,7 +27,9 @@ class BaseTestFeature : public CPPUNIT_NS::TestFixture { void testId(); void testLinkType(); - void testData(); + void testTargetType(); + void testDataArrayFeature(); + void testDataFrameFeature(); void testLinkType2Str(); void testStreamOperator(); void testOperator(); diff --git a/test/TestOptionalObligatory.cpp b/test/TestOptionalObligatory.cpp index 525f8c805..f3d21a247 100644 --- a/test/TestOptionalObligatory.cpp +++ b/test/TestOptionalObligatory.cpp @@ -415,11 +415,11 @@ void TestOptionalObligatory::testPropertyValues() { void TestOptionalObligatory::testFeatureData() { static const bool accepts_none = test::accepts_noneT::value; - is_opt = std::conditional::value, + is_opt = std::conditional::value, std::integral_constant, - std::integral_constant::value> + std::integral_constant::value> >::type::value; - is_set = test::TtoBool(util::deRef(feature.data())); + is_set = test::TtoBool(util::deRef(feature.dataArray())); summarize("Feature::data", is_opt, is_set, accepts_none); CPPUNIT_ASSERT(test::isValidOptional(is_opt, is_set, accepts_none) || test::isValidObligatory(is_opt, is_set, accepts_none)); diff --git a/test/hdf5/TestFeatureHDF5.hpp b/test/hdf5/TestFeatureHDF5.hpp index d523d4406..ed17e8a8e 100644 --- a/test/hdf5/TestFeatureHDF5.hpp +++ b/test/hdf5/TestFeatureHDF5.hpp @@ -14,7 +14,9 @@ class TestFeatureHDF5 : public BaseTestFeature { CPPUNIT_TEST(testValidate); CPPUNIT_TEST(testId); CPPUNIT_TEST(testLinkType); - CPPUNIT_TEST(testData); + CPPUNIT_TEST(testTargetType); + CPPUNIT_TEST(testDataArrayFeature); + CPPUNIT_TEST(testDataFrameFeature); CPPUNIT_TEST(testLinkType2Str); CPPUNIT_TEST(testStreamOperator); CPPUNIT_TEST(testOperator); @@ -28,6 +30,16 @@ class TestFeatureHDF5 : public BaseTestFeature { data_array = block.createDataArray("featureTest", "Test", nix::DataType::Double, nix::NDSize({ 0 })); + std::vector cols = { + {"bool", "", nix::DataType::Bool}, + {"int32", "V", nix::DataType::Int32}, + {"uint32", "V", nix::DataType::UInt32}, + {"int64", "A", nix::DataType::Int64}, + {"uint64", "A", nix::DataType::UInt64}, + {"string", "", nix::DataType::String}, + {"double", "mV", nix::DataType::Double}}; + data_frame = block.createDataFrame("test dataframe", "nix:test", cols); + tag = block.createTag("featureTest", "Test", {0.0, 2.0, 3.4}); }