mapping::Tree: introduce TreeChildrenOperator

This commit is contained in:
Leonid Stryzhevskyi 2024-05-05 09:42:24 +03:00
parent 0ee2e3325d
commit a29c664581
5 changed files with 254 additions and 109 deletions

View File

@ -28,71 +28,6 @@
namespace oatpp { namespace data { namespace mapping {
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TreeMap
TreeMap::TreeMap(const TreeMap& other) {
operator=(other);
}
TreeMap::TreeMap(TreeMap&& other) noexcept {
operator=(std::forward<TreeMap>(other));
}
TreeMap& TreeMap::operator = (const TreeMap& other) {
m_map = other.m_map;
m_order.resize(other.m_order.size());
for(v_uint64 i = 0; i < other.m_order.size(); i ++) {
auto& po = other.m_order[i];
auto& pt = m_order[i];
pt.first = po.first;
pt.second = &m_map.at(po.first.lock());
}
return *this;
}
TreeMap& TreeMap::operator = (TreeMap&& other) noexcept {
m_map = std::move(other.m_map);
m_order = std::move(other.m_order);
for(auto& p : m_order) {
p.second = &m_map.at(p.first.lock());
}
return *this;
}
Tree& TreeMap::operator [] (const type::String& key) {
auto it = m_map.find(key);
if(it == m_map.end()) {
auto& result = m_map[key];
m_order.emplace_back(key.getPtr(), &result);
return result;
}
return it->second;
}
const Tree& TreeMap::operator [] (const type::String& key) const {
auto it = m_map.find(key);
if(it == m_map.end()) {
throw std::runtime_error("[oatpp::data::mapping::Tree::TreeMap::operator[]]: const operator[] can't add items.");
}
return it->second;
}
std::pair<type::String, std::reference_wrapper<Tree>> TreeMap::operator [] (v_uint64 index) {
auto& item = m_order.at(index);
return {item.first.lock(), *item.second};
}
std::pair<type::String, std::reference_wrapper<const Tree>> TreeMap::operator [] (v_uint64 index) const {
auto& item = m_order.at(index);
return {item.first.lock(), *item.second};
}
v_uint64 TreeMap::size() const {
return m_map.size();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// Tree::Attributes
@ -635,8 +570,8 @@ std::vector<std::pair<type::String, Tree>>& Tree::getPairs() {
if(m_type == Type::UNDEFINED) {
setPairs({});
}
if(m_type != Type::MAP) {
throw std::runtime_error("[oatpp::data::mapping::Tree::getMap()]: NOT a MAP.");
if(m_type != Type::PAIRS) {
throw std::runtime_error("[oatpp::data::mapping::Tree::getMap()]: NOT a PAIRS.");
}
auto data = reinterpret_cast<std::vector<std::pair<type::String, Tree>>*>(m_data);
return *data;
@ -777,4 +712,181 @@ type::String Tree::debugPrint(v_uint32 indent0, v_uint32 indentDelta, bool first
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TreeMap
TreeMap::TreeMap(const TreeMap& other) {
operator=(other);
}
TreeMap::TreeMap(TreeMap&& other) noexcept {
operator=(std::forward<TreeMap>(other));
}
TreeMap& TreeMap::operator = (const TreeMap& other) {
m_map = other.m_map;
m_order.resize(other.m_order.size());
for(v_uint64 i = 0; i < other.m_order.size(); i ++) {
auto& po = other.m_order[i];
auto& pt = m_order[i];
pt.first = po.first;
pt.second = &m_map.at(po.first.lock());
}
return *this;
}
TreeMap& TreeMap::operator = (TreeMap&& other) noexcept {
m_map = std::move(other.m_map);
m_order = std::move(other.m_order);
for(auto& p : m_order) {
p.second = &m_map.at(p.first.lock());
}
return *this;
}
Tree& TreeMap::operator [] (const type::String& key) {
auto it = m_map.find(key);
if(it == m_map.end()) {
auto& result = m_map[key];
m_order.emplace_back(key.getPtr(), &result);
return result;
}
return it->second;
}
const Tree& TreeMap::operator [] (const type::String& key) const {
auto it = m_map.find(key);
if(it == m_map.end()) {
throw std::runtime_error("[oatpp::data::mapping::Tree::TreeMap::operator[]]: const operator[] can't add items.");
}
return it->second;
}
std::pair<type::String, std::reference_wrapper<Tree>> TreeMap::operator [] (v_uint64 index) {
auto& item = m_order.at(index);
return {item.first.lock(), *item.second};
}
std::pair<type::String, std::reference_wrapper<const Tree>> TreeMap::operator [] (v_uint64 index) const {
auto& item = m_order.at(index);
return {item.first.lock(), *item.second};
}
v_uint64 TreeMap::size() const {
return m_map.size();
}
////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
// TreeChildrenOperator
TreeChildrenOperator::TreeChildrenOperator(Tree& tree)
: TreeChildrenOperator(const_cast<const Tree&>(tree))
{
m_const = false;
if(tree.getType() == Tree::Type::VECTOR) {
m_vector = std::addressof(tree.getVector());
} else if(tree.getType() == Tree::Type::MAP) {
m_map = std::addressof(tree.getMap());
} else if(tree.getType() == Tree::Type::PAIRS) {
m_pairs = std::addressof(tree.getPairs());
}
}
TreeChildrenOperator::TreeChildrenOperator(const Tree& tree)
: m_vector(nullptr)
, m_map(nullptr)
, m_pairs(nullptr)
, c_vector(nullptr)
, c_map(nullptr)
, c_pairs(nullptr)
{
m_const = true;
if(tree.getType() == Tree::Type::VECTOR) {
m_type = VECTOR;
c_vector = std::addressof(tree.getVector());
} else if(tree.getType() == Tree::Type::MAP) {
m_type = MAP;
c_map = std::addressof(tree.getMap());
} else if(tree.getType() == Tree::Type::PAIRS) {
m_type = PAIRS;
c_pairs = std::addressof(tree.getPairs());
} else {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::TreeChildrenOperator()]: Node type is NOT suppoerted");
}
}
std::pair<type::String, Tree*> TreeChildrenOperator::getPair(v_uint64 index) {
if(m_const) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getPair()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: break;
case MAP: {
const auto& p = (*m_map)[index];
return {p.first, std::addressof(p.second.get())};
}
case PAIRS: {
auto& p = m_pairs->at(index);
return {p.first, &p.second};
}
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getPair()]: Node type doesn't support pairs");
}
std::pair<type::String, const Tree*> TreeChildrenOperator::getPair(v_uint64 index) const {
switch (m_type) {
case VECTOR: break;
case MAP: {
const auto& p = (*c_map)[index];
return {p.first, std::addressof(p.second.get())};
}
case PAIRS: {
auto& p = (*c_pairs)[index];
return {p.first, &p.second};
}
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getPair()]: Node type doesn't support pairs");
}
Tree* TreeChildrenOperator::getItem(v_uint64 index) {
if(m_const) {
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getItem()]: Can't operate on CONST tree node");
}
switch (m_type) {
case VECTOR: return std::addressof(m_vector->at(index));
case MAP: return std::addressof((*m_map)[index].second.get());
case PAIRS: return &m_pairs->at(index).second;
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getItem()]: Invalid iterator type");
}
const Tree* TreeChildrenOperator::getItem(v_uint64 index) const {
switch (m_type) {
case VECTOR: return std::addressof(c_vector->at(index));
case MAP: return std::addressof((*c_map)[index].second.get());
case PAIRS: return &c_pairs->at(index).second;
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::getItem()]: Invalid operator type");
}
v_uint64 TreeChildrenOperator::size() const {
switch (m_type) {
case VECTOR: return c_vector->size();
case MAP: return c_map->size();
case PAIRS: return c_pairs->size();
default:
break;
}
throw std::runtime_error("[oatpp::data::mapping::TreeChildrenOperator::size()]: Invalid operator type");
}
}}}

View File

@ -260,6 +260,39 @@ public:
};
class TreeChildrenOperator {
private:
enum IteratorType {
VECTOR,
MAP,
PAIRS
};
private:
std::vector<Tree>* m_vector;
TreeMap* m_map;
std::vector<std::pair<type::String, Tree>>* m_pairs;
private:
const std::vector<Tree>* c_vector;
const TreeMap* c_map;
const std::vector<std::pair<type::String, Tree>>* c_pairs;
private:
bool m_const;
IteratorType m_type;
public:
TreeChildrenOperator(Tree& tree);
TreeChildrenOperator(const Tree& tree);
std::pair<type::String, Tree*> getPair(v_uint64 index);
std::pair<type::String, const Tree*> getPair(v_uint64 index) const;
Tree* getItem(v_uint64 index);
const Tree* getItem(v_uint64 index) const;
v_uint64 size() const;
};
//////////////////////////////////////////////////////
// Tree::NodePrimitiveType

View File

@ -201,12 +201,8 @@ oatpp::Void TreeToObjectMapper::mapEnum(const TreeToObjectMapper* mapper, State&
oatpp::Void TreeToObjectMapper::mapCollection(const TreeToObjectMapper* mapper, State& state, const Type* type) {
if(state.tree->getType() != Tree::Type::VECTOR) {
if(state.tree->isNull()){
return oatpp::Void(type);
}
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapCollection()]: Node is NOT a VECTOR.");
return nullptr;
if(state.tree->isNull()){
return oatpp::Void(type);
}
auto dispatcher = static_cast<const data::type::__class::Collection::PolymorphicDispatcher*>(type->polymorphicDispatcher);
@ -214,28 +210,25 @@ oatpp::Void TreeToObjectMapper::mapCollection(const TreeToObjectMapper* mapper,
auto itemType = dispatcher->getItemType();
const auto& vector = state.tree->getVector();
v_int64 index = 0;
const TreeChildrenOperator childrenOperator(*state.tree);
v_uint64 childrenCount = childrenOperator.size();
State nestedState;
nestedState.config = state.config;
for(const auto& node : vector) {
for(v_uint64 index = 0; index < childrenCount; index ++) {
nestedState.tree = &node;
nestedState.tree = childrenOperator.getItem(index);
auto item = mapper->map(nestedState, itemType);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(nestedState.errorStack);
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapCollection()]: index=" + utils::Conversion::int64ToStr(index));
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapCollection()]: index=" + utils::Conversion::uint64ToStr(index));
return nullptr;
}
dispatcher->addItem(collection, item);
index ++;
}
return collection;
@ -244,12 +237,8 @@ oatpp::Void TreeToObjectMapper::mapCollection(const TreeToObjectMapper* mapper,
oatpp::Void TreeToObjectMapper::mapMap(const TreeToObjectMapper* mapper, State& state, const Type* type) {
if(state.tree->getType() != Tree::Type::MAP) {
if(state.tree->isNull()){
return oatpp::Void(type);
}
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapMap()]: Node is NOT a MAP.");
return nullptr;
if(state.tree->isNull()){
return oatpp::Void(type);
}
auto dispatcher = static_cast<const data::type::__class::Map::PolymorphicDispatcher*>(type->polymorphicDispatcher);
@ -262,27 +251,27 @@ oatpp::Void TreeToObjectMapper::mapMap(const TreeToObjectMapper* mapper, State&
}
auto valueType = dispatcher->getValueType();
const auto& treeMap = state.tree->getMap();
auto treeMapSize = treeMap.size();
const TreeChildrenOperator childrenOperator(*state.tree);
v_uint64 childrenCount = childrenOperator.size();
State nestedState;
nestedState.config = state.config;
for(v_uint64 i = 0; i < treeMapSize; i ++) {
for(v_uint64 i = 0; i < childrenCount; i ++) {
const auto& node = treeMap[i];
const auto& pair = childrenOperator.getPair(i);
nestedState.tree = &node.second.get();
nestedState.tree = pair.second;
auto item = mapper->map(nestedState, valueType);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(nestedState.errorStack);
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapMap()]: key='" + node.first + "'");
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapMap()]: key='" + pair.first + "'");
return nullptr;
}
dispatcher->addItem(map, node.first, item);
dispatcher->addItem(map, pair.first, item);
}
@ -292,12 +281,8 @@ oatpp::Void TreeToObjectMapper::mapMap(const TreeToObjectMapper* mapper, State&
oatpp::Void TreeToObjectMapper::mapObject(const TreeToObjectMapper* mapper, State& state, const Type* type) {
if(state.tree->getType() != Tree::Type::MAP) {
if(state.tree->isNull()){
return oatpp::Void(type);
}
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapObject()]: Node is NOT a MAP.");
return nullptr;
if(state.tree->isNull()){
return oatpp::Void(type);
}
auto dispatcher = static_cast<const oatpp::data::type::__class::AbstractObject::PolymorphicDispatcher*>(type->polymorphicDispatcher);
@ -306,31 +291,31 @@ oatpp::Void TreeToObjectMapper::mapObject(const TreeToObjectMapper* mapper, Stat
std::vector<std::pair<oatpp::BaseObject::Property*, const Tree*>> polymorphs;
const auto& treeMap = state.tree->getMap();
auto treeMapSize = treeMap.size();
const TreeChildrenOperator childrenOperator(*state.tree);
v_uint64 childrenCount = childrenOperator.size();
for(v_uint64 i = 0; i < treeMapSize; i ++) {
for(v_uint64 i = 0; i < childrenCount; i ++) {
const auto& node = treeMap[i];
const auto& pair = childrenOperator.getPair(i);
auto fieldIterator = fieldsMap.find(node.first);
auto fieldIterator = fieldsMap.find(pair.first);
if(fieldIterator != fieldsMap.end()){
auto field = fieldIterator->second;
if(field->info.typeSelector && field->type == oatpp::Any::Class::getType()) {
polymorphs.emplace_back(field, &node.second.get()); // store polymorphs for later processing.
polymorphs.emplace_back(field, pair.second); // store polymorphs for later processing.
} else {
State nestedState;
nestedState.tree = &node.second.get();
nestedState.tree = pair.second;
nestedState.config = state.config;
auto value = mapper->map(nestedState, field->type);
if(!nestedState.errorStack.empty()) {
state.errorStack.splice(nestedState.errorStack);
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapObject()]: field='" + node.first + "'");
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapObject()]: field='" + pair.first + "'");
return nullptr;
}
@ -344,7 +329,7 @@ oatpp::Void TreeToObjectMapper::mapObject(const TreeToObjectMapper* mapper, Stat
}
} else if (!state.config->allowUnknownFields) {
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapObject()]: Error. Unknown field '" + node.first + "'");
state.errorStack.push("[oatpp::data::mapping::TreeToObjectMapper::mapObject()]: Error. Unknown field '" + pair.first + "'");
return nullptr;
}

View File

@ -76,14 +76,17 @@ Tree::Tree(const std::shared_ptr<mapping::Tree>& node, const Type* const type)
Tree& Tree::operator = (std::nullptr_t) {
m_ptr.reset();
return *this;
}
Tree& Tree::operator = (const Tree& other) {
m_ptr = other.m_ptr;
return *this;
}
Tree& Tree::operator = (Tree&& other) {
m_ptr = std::move(other.m_ptr);
return *this;
}
Tree& Tree::operator = (const mapping::Tree& other) {
@ -92,6 +95,7 @@ Tree& Tree::operator = (const mapping::Tree& other) {
} else {
m_ptr = std::make_shared<mapping::Tree>(other);
}
return *this;
}
Tree& Tree::operator = (mapping::Tree&& other) {
@ -100,6 +104,7 @@ Tree& Tree::operator = (mapping::Tree&& other) {
} else {
m_ptr = std::make_shared<mapping::Tree>(std::forward<mapping::Tree>(other));
}
return *this;
}
bool Tree::operator == (std::nullptr_t) const {

View File

@ -59,6 +59,7 @@ class TestDto1 : public oatpp::DTO {
DTO_FIELD(Vector<oatpp::Object<TestDto1>>, vector);
DTO_FIELD(Fields<oatpp::Object<TestDto1>>, map);
DTO_FIELD(Fields<String>, pairs);
};
@ -100,6 +101,15 @@ void TreeToObjectMapperTest::onRun() {
tree["map"]["nested_2"]["i32"] = 2;
tree["map"]["nested_3"]["i32"] = 3;
auto& pairs = tree["pairs"].getPairs();
pairs.push_back({"same-key", {}});
pairs.push_back({"same-key", {}});
pairs.push_back({"same-key", {}});
pairs[0].second = "value1";
pairs[1].second = "value2";
pairs[2].second = "value3";
TreeToObjectMapper::State state;
state.config = &config;
state.tree = &tree;