Files
rdkit/Code/RDGeneral/RDProps.h
Greg Landrum 3aab2653cd Fixes #9068: raise a ValueError when trying to set properties with empty names (#9085)
* Fixes #9068

* fix a problem with empty labels in s-group parsing

* fix empty column names in smiles suppliers

* add the check to setPODVal()

---------

Co-authored-by: = <=>
2026-02-09 05:58:25 +01:00

179 lines
5.3 KiB
C++

// Copyright (C) 2016-2026 Brian Kelley and other RDKit contributors
// @@ All Rights Reserved @@
//
// This file is part of the RDKit.
// The contents are covered by the terms of the BSD license
// which is included in the file license.txt, found at the root
// of the RDKit source tree.
//
#include <RDGeneral/export.h>
#ifndef RDKIT_RDPROPS_H
#define RDKIT_RDPROPS_H
#include "Dict.h"
#include "types.h"
namespace RDKit {
class RDProps {
protected:
mutable Dict d_props;
// It is a quirk of history that this is mutable
// as the RDKit allows properties to be set
// on const objects.
public:
RDProps() : d_props() {}
RDProps(const RDProps &rhs) : d_props(rhs.d_props) {}
RDProps &operator=(const RDProps &rhs) {
if (this == &rhs) {
return *this;
}
d_props = rhs.d_props;
return *this;
}
RDProps(RDProps &&o) noexcept = default;
RDProps &operator=(RDProps &&rhs) noexcept = default;
void clear() { d_props.reset(); }
//! gets the underlying Dictionary
const Dict &getDict() const { return d_props; }
Dict &getDict() { return d_props; }
// ------------------------------------
// Local Property Dict functionality
// all setProp functions are const because they
// are not meant to change the atom chemically
// ------------------------------------
//! returns a list with the names of our \c properties
STR_VECT getPropList(bool includePrivate = true,
bool includeComputed = true) const {
const STR_VECT &tmp = d_props.keys();
STR_VECT res, computed;
if (!includeComputed &&
getPropIfPresent(RDKit::detail::computedPropName, computed)) {
computed.emplace_back(RDKit::detail::computedPropName);
}
auto pos = tmp.begin();
while (pos != tmp.end()) {
if ((includePrivate || (*pos)[0] != '_') &&
std::find(computed.begin(), computed.end(), *pos) == computed.end()) {
res.push_back(*pos);
}
++pos;
}
return res;
}
//! sets a \c property value
/*!
\param key the name under which the \c property should be stored.
If a \c property is already stored under this name, it will be
replaced.
\param val the value to be stored
\param computed (optional) allows the \c property to be flagged
\c computed.
*/
//! \overload
template <typename T>
void setProp(const std::string_view key, T val, bool computed = false) const {
if(key.empty()) {
throw ValueErrorException("Cannot set property with empty key");
}
if (computed) {
STR_VECT compLst;
getPropIfPresent(RDKit::detail::computedPropName, compLst);
if (std::find(compLst.begin(), compLst.end(), key) == compLst.end()) {
compLst.emplace_back(key);
d_props.setVal(RDKit::detail::computedPropName, compLst);
}
}
d_props.setVal(key, val);
}
//! allows retrieval of a particular property value
/*!
\param key the name under which the \c property should be stored.
If a \c property is already stored under this name, it will be
replaced.
\param res a reference to the storage location for the value.
<b>Notes:</b>
- if no \c property with name \c key exists, a KeyErrorException will be
thrown.
- the \c boost::lexical_cast machinery is used to attempt type
conversions.
If this fails, a \c boost::bad_lexical_cast exception will be thrown.
*/
//! \overload
template <typename T>
void getProp(const std::string_view key, T &res) const {
d_props.getVal(key, res);
}
//! \overload
template <typename T>
T getProp(const std::string_view key) const {
return d_props.getVal<T>(key);
}
//! returns whether or not we have a \c property with name \c key
//! and assigns the value if we do
//! \overload
template <typename T>
bool getPropIfPresent(const std::string_view key, T &res) const {
return d_props.getValIfPresent(key, res);
}
//! \overload
bool hasProp(const std::string_view key) const { return d_props.hasVal(key); }
//! clears the value of a \c property
/*!
<b>Notes:</b>
- if no \c property with name \c key exists, this will be a no-op.
- if the \c property is marked as \c computed, it will also be removed
from our list of \c computedProperties
*/
//! \overload
void clearProp(const std::string_view key) const {
STR_VECT compLst;
if (getPropIfPresent(RDKit::detail::computedPropName, compLst)) {
auto svi = std::find(compLst.begin(), compLst.end(), key);
if (svi != compLst.end()) {
compLst.erase(svi);
d_props.setVal(RDKit::detail::computedPropName, compLst);
}
}
d_props.clearVal(key);
}
//! clears all of our \c computed \c properties
void clearComputedProps() const {
STR_VECT compLst;
if (getPropIfPresent(RDKit::detail::computedPropName, compLst) &&
!compLst.empty()) {
for (const auto &sv : compLst) {
d_props.clearVal(sv);
}
compLst.clear();
d_props.setVal(RDKit::detail::computedPropName, compLst);
}
}
//! update the properties from another
/*
\param source Source to update the properties from
\param preserve Existing If true keep existing data, else override from
the source
*/
void updateProps(const RDProps &source, bool preserveExisting = false) {
d_props.update(source.getDict(), preserveExisting);
}
};
} // namespace RDKit
#endif