From 8db5ff97ffe34aa42f71b5c8aebfa0878e5fde89 Mon Sep 17 00:00:00 2001 From: Bent Bisballe Nyeng Date: Sat, 4 Jul 2020 16:58:08 +0200 Subject: Add error message support and reporting on all commands. --- src/Makefile.am | 2 + src/errorcodes.h | 44 ++++++++ src/exceptions.h | 106 ++++++++++++++++++ src/message.h | 9 ++ src/messagehandler.cc | 79 ++++++++++---- src/messageparser.cc | 119 ++++++++++++++++---- src/messageparser.h | 20 +++- src/munia_proto.cc | 60 +++++++--- src/nodemanager.cc | 297 ++++++++------------------------------------------ src/nodemanager.h | 172 +++-------------------------- src/nodetree.cc | 266 ++++++++++++++++++++++---------------------- src/nodetree.h | 49 ++++++--- 12 files changed, 600 insertions(+), 623 deletions(-) create mode 100644 src/errorcodes.h create mode 100644 src/exceptions.h diff --git a/src/Makefile.am b/src/Makefile.am index d70301c..c8a6560 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -31,6 +31,8 @@ muniacli_SOURCES = \ EXTRA_DIST = \ connectionhandler.h \ + errorcodes.h \ + exceptions.h \ http.h \ message.h \ messageparser.h \ diff --git a/src/errorcodes.h b/src/errorcodes.h new file mode 100644 index 0000000..e305441 --- /dev/null +++ b/src/errorcodes.h @@ -0,0 +1,44 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * errorcodes.h + * + * Fri Jul 3 13:19:18 CEST 2020 + * Copyright 2020 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Munia. + * + * Munia is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Munia 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Munia; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +enum class ErrorCode +{ + Ok = 0, + LoginFailed = 1, + Unauthorized = 2, + MissingParent = 4, // create + NoSuchId = 6, // create remove + IdAlreadyExists = 7, // create + ProtectedNode = 8, // remove + NewParentIsSelf = 9, // move + NewParentIsChildOfSelf = 10, // move + WrongNumberOfArguments = 11, + System = 41, + Unknown = 42, +}; diff --git a/src/exceptions.h b/src/exceptions.h new file mode 100644 index 0000000..79f1113 --- /dev/null +++ b/src/exceptions.h @@ -0,0 +1,106 @@ +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */ +/* vim: set et sw=2 ts=2: */ +/*************************************************************************** + * exceptions.h + * + * Fri Jul 3 13:36:49 CEST 2020 + * Copyright 2020 Bent Bisballe Nyeng + * deva@aasimon.org + ****************************************************************************/ + +/* + * This file is part of Munia. + * + * Munia is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * Munia 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with Munia; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. + */ +#pragma once + +#include "message.h" +#include "errorcodes.h" + +namespace Error +{ + +struct MissingParent + : public ErrorMessage +{ + MissingParent() + { + code = ErrorCode::MissingParent; + msg = "Parent node does not exist."; + } +}; + +struct NoSuchId + : public ErrorMessage +{ + NoSuchId() + { + code = ErrorCode::NoSuchId; + msg = "Node does not exist."; + } +}; + +struct IdAlreadyExists + : public ErrorMessage +{ + IdAlreadyExists() + { + code = ErrorCode::IdAlreadyExists; + msg = "Id already exists."; + } +}; + +struct ProtectedNode + : public ErrorMessage +{ + ProtectedNode() + { + code = ErrorCode::ProtectedNode; + msg = "Node is protected."; + } +}; + +struct NewParentIsSelf + : public ErrorMessage +{ + NewParentIsSelf() + { + code = ErrorCode::NewParentIsSelf; + msg = "New parent is the node itself."; + } +}; +struct NewParentIsChildOfSelf + : public ErrorMessage +{ + NewParentIsChildOfSelf() + { + code = ErrorCode::NewParentIsChildOfSelf; + msg = "New parent is a child of node itself."; + } +}; + +struct System + : public ErrorMessage +{ + System(const char* message) + { + code = ErrorCode::System; + msg = "Unknown error occurred: "; + msg += message; + } +}; + +} // ::Error diff --git a/src/message.h b/src/message.h index 3a6afdc..8d2d12f 100644 --- a/src/message.h +++ b/src/message.h @@ -32,6 +32,7 @@ #include #include "node.h" +#include "errorcodes.h" enum class cmd { @@ -99,6 +100,13 @@ struct update_t std::string value; }; +struct ErrorMessage +{ + ErrorCode code; + std::string msg; + struct lws* wsi; +}; + struct message_t { cmd_t cmd; @@ -111,6 +119,7 @@ struct message_t remove_t remove; move_t move; update_t update; + ErrorMessage error; NodeIdList nodes; diff --git a/src/messagehandler.cc b/src/messagehandler.cc index 9a00e46..d20c963 100644 --- a/src/messagehandler.cc +++ b/src/messagehandler.cc @@ -34,6 +34,8 @@ #include "messageparser.h" +#include "errorcodes.h" + MessageList handle_msg(MessageList msgList, clientid_t wsi) { @@ -47,11 +49,19 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) if(m.cmd == cmd::login) { connection_handler.login(wsi, m.login.user, m.login.password); + if(!connection_handler.authenticated(wsi)) + { + outmsgs.push_back(create_msg_error(ErrorCode::LoginFailed, + "Bad username or password.", wsi)); + continue; + } } // If client is not authenticated; do not continue beyond this point... if(!connection_handler.authenticated(wsi)) { + outmsgs.push_back(create_msg_error(ErrorCode::Unauthorized, + "Not authorized.", wsi)); continue; } @@ -59,12 +69,19 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) { case cmd::login: // Already handled, before this switch ... case + INFO(messagehandler, "Handling login command\n"); break; case cmd::logout: + INFO(messagehandler, "Handling logout command\n"); connection_handler.logout(wsi); break; + case cmd::create_with_attributes: + // Never sent to the server + INFO(messagehandler, "Handling create_with_attributes command\n"); + break; + case cmd::create: { INFO(messagehandler, "Handling create command\n"); @@ -74,9 +91,15 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) m.create.insertbeforeid); outmsgs.push_back(m); } - catch (std::exception& e) + catch (const ErrorMessage& e) { - DEBUG(messagehandler, "Error creating node\n"); + // Set client id (wsi) and forward upstream + outmsgs.push_back(create_msg_error(e, wsi)); + } + catch (...) + { + outmsgs.push_back(create_msg_error(ErrorCode::Unknown, + "Unknown create error", wsi)); } } break; @@ -99,9 +122,15 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) id++; } } - catch (std::exception& e) + catch (const ErrorMessage& e) + { + // Set client id (wsi) and forward upstream + outmsgs.push_back(create_msg_error(e, wsi)); + } + catch (...) { - DEBUG(messagehandler, "Error remove node\n"); + outmsgs.push_back(create_msg_error(ErrorCode::Unknown, + "Unknown remove error", wsi)); } } break; @@ -113,7 +142,8 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) { node_t removenode = node_manager.node(m.move.id); NodeIdListPair tilpair = - node_manager.moveNode(m.move.id, m.move.parentid, m.move.insertbeforeid); + node_manager.moveNode(m.move.id, m.move.parentid, + m.move.insertbeforeid); node_t createnode = node_manager.node(m.move.id); message_t removemsg = create_msg_remove(removenode); @@ -143,24 +173,27 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) outmsgs.push_back(removemsg); outmsgs.push_back(createmsg); } - catch (std::exception& e) + catch (const ErrorMessage& e) { - DEBUG(messagehandler, "Error moving node\n"); + // Set client id (wsi) and forward upstream + outmsgs.push_back(create_msg_error(e, wsi)); + } + catch (...) + { + outmsgs.push_back(create_msg_error(ErrorCode::Unknown, + "Unknown move error", wsi)); } } break; case cmd::subscribe: - { - //connection_handler.subscribe(wsi, m.subscribe.id); - outmsgs.push_back(m); - } + INFO(messagehandler, "Handling subscribe command\n"); + outmsgs.push_back(m); break; case cmd::unsubscribe: - //connection_handler.unsubscribe(wsi, m.subscribe.id); + INFO(messagehandler, "Handling unsubscribe command\n"); outmsgs.push_back(m); - DEBUG(handler, "unsubscribe: %d\n", (int)m.unsubscribe.id); break; case cmd::update: @@ -173,24 +206,24 @@ MessageList handle_msg(MessageList msgList, clientid_t wsi) m.update.value); outmsgs.push_back(m); } - catch (std::exception& e) + catch (const ErrorMessage& e) { - DEBUG(messagehandler, "Error updating node\n"); + // Set client id (wsi) and forward upstream + outmsgs.push_back(create_msg_error(e, wsi)); + } + catch (...) + { + outmsgs.push_back(create_msg_error(ErrorCode::Unknown, + "Unknown update error", wsi)); } } break; case cmd::error: - ERR(messagehandler, "An error occurred.\n"); - break; - - default: - WARN(messagehandler, "!!! Unknown command %d\n", m.cmd); + INFO(messagehandler, "Handling error command\n"); + outmsgs.push_back(m); // Just forward the error message break; } - node_manager.tree.toStdOut(); - DEBUG(messagehandler, "%d affected nodes registered\n", - (int)m.nodes.size()); } return outmsgs; diff --git a/src/messageparser.cc b/src/messageparser.cc index 7393c5c..b0a2a6f 100644 --- a/src/messageparser.cc +++ b/src/messageparser.cc @@ -30,6 +30,7 @@ #include #include #include +#include #include @@ -156,7 +157,8 @@ static nodeid_t getId(std::map &sym, std::string token) inline static void create_msg_list(MsgTokensList& msgTokensList, MessageList& msgList, - bool clientmode) + bool clientmode, + struct lws* wsi) { size_t origin = 0; if(clientmode) @@ -174,6 +176,8 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, //malformed msg if(t.size() < origin+1) { + msgList.push_back(create_msg_error(ErrorCode::Unknown, + "malformed message", wsi)); continue; } @@ -192,7 +196,13 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, else if(t[origin] == "remove") m.cmd = cmd::remove; else if(t[origin] == "move") m.cmd = cmd::move; else if(t[origin] == "update") m.cmd = cmd::update; - else m.cmd = cmd::error; + else if(t[origin] == "error") m.cmd = cmd::error; + else + { + msgList.push_back(create_msg_error(ErrorCode::Unknown, + "unknown command", wsi)); + continue; + } switch(m.cmd) { @@ -200,7 +210,9 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, { if(t.size() != origin+1+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } m.subscribe.id = getId(sym, t[origin+1]); @@ -211,7 +223,9 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, { if(t.size() != origin+1+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } m.unsubscribe.id = getId(sym, t[origin+1]); @@ -223,7 +237,9 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, { if(t.size() != origin+1+2) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } m.login.user = t[origin+1]; @@ -235,23 +251,27 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, case cmd::logout: if(t.size() != origin+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } break; + case cmd::create_with_attributes: + // Never sent to the server + break; + case cmd::create: { - if(!clientmode && t.size() != origin+1+2+1) + if(t.size() != origin+1+2+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); - continue; - } - if(clientmode && t.size() != origin+1+2+1) - { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } + if(clientmode) { m.create.id = getId(sym, t[origin+1]); @@ -264,23 +284,29 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, m.create.parentid = getId(sym, t[origin+2]); m.create.insertbeforeid = getId(sym, t[origin+3]); } - break; } + break; + case cmd::remove: { if(t.size() != origin+1+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } m.remove.id = getId(sym, t[origin+1]); } break; + case cmd::move: { if(t.size() != origin+2+1+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } m.move.id = getId(sym, t[origin+1]); @@ -288,11 +314,14 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, m.move.insertbeforeid = getId(sym, t[origin+3]); } break; + case cmd::update: { if(t.size() != origin+3+1) { - DEBUG(msgparser, "Wrong number of parameters\n"); + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); continue; } m.update.id = getId(sym, t[origin+1]); @@ -300,14 +329,27 @@ inline static void create_msg_list(MsgTokensList& msgTokensList, m.update.value = t[origin+3]; } break; - default: + + case cmd::error: + { + if(t.size() != origin+2+1) + { + msgList.push_back( + create_msg_error(ErrorCode::WrongNumberOfArguments, + "Wrong number of arguments.", wsi)); + continue; + } + m.error.code = static_cast(atoi(t[origin+1].data())); + m.error.msg = t[origin+2]; + } break; }; + msgList.push_back(m); } } -MessageList parse_msg(std::string data) +MessageList parse_msg(std::string data, struct lws* wsi) { DEBUG(msgparser, "Parsing: '%s'\n", data.c_str()); @@ -315,19 +357,19 @@ MessageList parse_msg(std::string data) parse_into_msg_tokens(data, msgTokensList); MessageList msgList; - create_msg_list(msgTokensList, msgList, false); + create_msg_list(msgTokensList, msgList, false, wsi); return msgList; } -MessageList parse_msg_client(std::string data) +MessageList parse_msg_client(std::string data, struct lws* wsi) { DEBUG(msgparser, "Parsing: '%s'\n", data.c_str()); MsgTokensList msgTokensList; parse_into_msg_tokens(data, msgTokensList); MessageList msgList; - create_msg_list(msgTokensList, msgList, true); + create_msg_list(msgTokensList, msgList, true, wsi); return msgList; } @@ -392,7 +434,18 @@ std::vector msg_tostring(message_t m) m.update.attribute + "\" \"" + m.update.value + "\";"; msgs.push_back(msg); break; - default: + case cmd::error: + msg = "error " + + std::to_string(static_cast(m.error.code)) + " \"" + + m.error.msg + "\";"; + msgs.push_back(msg); + break; + case cmd::login: + case cmd::logout: + case cmd::subscribe: + case cmd::unsubscribe: + assert(false); + // The server never sends these messages so no need to serialise them. break; } @@ -444,3 +497,23 @@ message_t create_msg_move(nodeid_t id, nodeid_t to, nodeid_t insertbeforeid) m.move.insertbeforeid = insertbeforeid; return m; } + +message_t create_msg_error(ErrorCode code, const std::string &msg, + struct lws* wsi) +{ + message_t m; + m.cmd = cmd::error; + m.error.wsi = wsi; + m.error.code = code; + m.error.msg = msg; + return m; +} + +message_t create_msg_error(const ErrorMessage &error, struct lws* wsi) +{ + message_t m; + m.cmd = cmd::error; + m.error = error; + m.error.wsi = wsi; + return m; +} diff --git a/src/messageparser.h b/src/messageparser.h index 218f3a8..030c77b 100644 --- a/src/messageparser.h +++ b/src/messageparser.h @@ -27,13 +27,13 @@ */ #pragma once -#include "message.h" - #include #include -MessageList parse_msg(std::string msg); -MessageList parse_msg_client(std::string msg); +#include "message.h" + +MessageList parse_msg(std::string msg, struct lws* wsi); +MessageList parse_msg_client(std::string msg, struct lws* wsi); //message_t create_msg(cmd::cmd_t type, node_t node); @@ -52,3 +52,15 @@ message_t create_msg_create_with_attributes(node_t node, message_t create_msg_update(node_t node, const std::string &attr); message_t create_msg_remove(node_t node); message_t create_msg_move(nodeid_t id, nodeid_t to, nodeid_t insertbeforeid); + +//! Create new error message. +//! \param code is the ErrorCode for this error message. +//! \param msg is the error in textural form for easy presentation on the +//! clients. +//! \param wsi is the client id that the error relates to (usually the one +//! that sent the command resulting in the error) +message_t create_msg_error(ErrorCode code, const std::string &msg, + struct lws* wsi); + +//! Convenience function. +message_t create_msg_error(const ErrorMessage &error, struct lws* wsi); diff --git a/src/munia_proto.cc b/src/munia_proto.cc index 0508f9f..f0ec5be 100644 --- a/src/munia_proto.cc +++ b/src/munia_proto.cc @@ -98,8 +98,6 @@ int callback_lws_node(struct lws *wsi, enum lws_callback_reasons reason, void *user, void *in, size_t len) { - node_manager.tree.toStdOut(); - DEBUG(proto, "Callback on %p\n", wsi); switch (reason) @@ -119,7 +117,8 @@ int callback_lws_node(struct lws *wsi, // the position of the remove msg. // msgqueue_t::iterator i = msgqueue[wsi].begin(); - while(i != msgqueue[wsi].end()) { + while(i != msgqueue[wsi].end()) + { message_t &msg = *i; if(msg.cmd == cmd::remove) { @@ -168,10 +167,21 @@ int callback_lws_node(struct lws *wsi, { msgstr += " "; } - auto msgs = msg_tostring(msg); - for(const auto& msg_string : msgs) + if(msg.cmd == cmd::error) + { + auto msgs = msg_tostring(msg); + for(const auto& msg_string : msgs) + { + msgstr += msg_string; + } + } + else { - msgstr += std::to_string(msg.tid) + " " + msg_string; + auto msgs = msg_tostring(msg); + for(const auto& msg_string : msgs) + { + msgstr += std::to_string(msg.tid) + " " + msg_string; + } } } @@ -216,7 +226,7 @@ int callback_lws_node(struct lws *wsi, std::string data; data.append((char*)in, len); - MessageList mlst = parse_msg(data); + MessageList mlst = parse_msg(data, wsi); DEBUG(proto, "Handling %d incoming message\n", (int)mlst.size()); MessageList omsgs = handle_msg(mlst, wsi); DEBUG(proto, "Handling %d outgoing messages\n", (int)omsgs.size()); @@ -224,21 +234,31 @@ int callback_lws_node(struct lws *wsi, MessageList::iterator omi = omsgs.begin(); while(omi != omsgs.end()) { - DEBUG(proto, "Message\n"); + DEBUG(proto, "Message %d\n", (int)omi->cmd); if(omi->cmd == cmd::subscribe) { - connection_handler.subscribe(wsi, omi->subscribe.id); NodeIdList ids; try { ids = node_manager.subNodes(omi->subscribe.id); + connection_handler.subscribe(wsi, omi->subscribe.id); + } + catch (const ErrorMessage& e) + { + // Set client id (wsi) and forward upstream + msgqueue[wsi].push_back(create_msg_error(e, wsi)); + omi++; + continue; } - catch(...) + catch (...) { - DEBUG(proto, "No such node %d\n", (int)omi->subscribe.id); + msgqueue[wsi].push_back( + create_msg_error(ErrorCode::Unknown, + "Unknown subscribe error", wsi)); omi++; continue; } + NodeIdList::iterator id = ids.begin(); while(id != ids.end()) { @@ -282,12 +302,22 @@ int callback_lws_node(struct lws *wsi, { ids = node_manager.subNodes(omi->unsubscribe.id); } - catch(...) + catch (const ErrorMessage& e) + { + // Set client id (wsi) and forward upstream + msgqueue[wsi].push_back(create_msg_error(e, wsi)); + omi++; + continue; + } + catch (...) { - DEBUG(proto, "No such node %d\n", (int)omi->unsubscribe.id); + msgqueue[wsi].push_back( + create_msg_error(ErrorCode::Unknown, + "Unknown subscribe error", wsi)); omi++; continue; } + NodeIdList::reverse_iterator id = ids.rbegin(); while(id != ids.rend()) { @@ -302,6 +332,10 @@ int callback_lws_node(struct lws *wsi, } } + else if(omi->cmd == cmd::error) + { + msgqueue[omi->error.wsi].push_back(*omi); + } else { DEBUG(proto, "%d nodes affected by command\n", diff --git a/src/nodemanager.cc b/src/nodemanager.cc index e94c798..e10b64d 100644 --- a/src/nodemanager.cc +++ b/src/nodemanager.cc @@ -27,10 +27,10 @@ */ #include "nodemanager.h" -#include - #include "hugin.hpp" +#include "exceptions.h" + // Global NodeManager object. NodeManager node_manager; @@ -41,21 +41,15 @@ NodeManager node_manager; #define PROJECTS_ID 4 #define FIRST_NODE_ID 10 -static bool isProtected(nodeid_t id) -{ - return id < FIRST_NODE_ID; -} - NodeManager::NodeManager() { - idCount = FIRST_NODE_ID; } NodeManager::~NodeManager() { } -void NodeManager::init(std::string filename) +void NodeManager::init(const std::string& filename) { DEBUG(nodemgr, "Reading nodes from file: %s\n", filename.c_str()); file = filename; @@ -103,182 +97,93 @@ void NodeManager::init(std::string filename) tree.toStdOut(); } -node_t NodeManager::node(nodeid_t t) -{ - return tree.data(t); -} - -nodeid_t NodeManager::createId() -{ - return tree.createId(); -} - -bool NodeManager::hasId(nodeid_t id) +NodeIdList NodeManager::createNode(nodeid_t parentid, nodeid_t id, + nodeid_t insertbeforeid) { - return tree.hasId(id); -} - -NodeIdListPair NodeManager::moveNode(nodeid_t id, nodeid_t to, nodeid_t beforeId) - throw (std::exception) -{ - if(isProtected(id)) - { - return NodeIdListPair(); - } - - if(id == to) - { - throw std::exception(); // Node and new parent are the same node. - } - - //node_t t = tree.data(id); - - // Make sure the new parent exists. This will throw an exception if it doesn't - //node_t t_ = tree.data(to); - - //t.parentid = to; - - //NodeIdList tilremove = tree.remove(id); - NodeIdList tilremove; - tilremove.push_back(id); - NodeIdList ancestors = tree.ancestorList(id); - tilremove.insert(tilremove.end(), ancestors.begin(), ancestors.end()); - - // NodeIdList tilcreate = tree.insertAsChild(to, id, t); - NodeIdList tilcreate; - tilcreate.push_back(to); - ancestors = tree.ancestorList(to); - tilcreate.insert(tilcreate.end(), ancestors.begin(), ancestors.end()); - - tree.move(id, to, beforeId); + NodeIdList affectedNodes; - NodeIdListPair tilpair; - tilpair.first = tilremove; - tilpair.second = tilcreate; + node_t t; + t.attributes["title"] = ""; + t.id = id; + affectedNodes = tree.insertAsChild(parentid, id, t, insertbeforeid); flushNodes(); - return tilpair; + return affectedNodes; } -NodeIdList NodeManager::removeNode(nodeid_t id) - throw (std::exception) +NodeIdList NodeManager::updateNode(nodeid_t id, const std::string &name, + const std::string &value) { if(isProtected(id)) { - return NodeIdList(); + throw Error::ProtectedNode(); } NodeIdList affectedNodes; - if(tree.bfs(id).size() > 1) - { - throw std::exception(); - } - - try - { - affectedNodes = tree.remove(id); - } - catch(std::exception& e) - { - throw e; - } + affectedNodes = tree.updateData(id, name, value); flushNodes(); return affectedNodes; } -NodeIdList NodeManager::updateNode(nodeid_t id, const std::string &name, - const std::string &value) - throw (std::exception) +NodeIdList NodeManager::removeNode(nodeid_t id) { if(isProtected(id)) { - return NodeIdList(); + throw Error::ProtectedNode(); } NodeIdList affectedNodes; - - try - { - affectedNodes = tree.updateData(id, name, value); - } - catch(std::exception& e) - { - throw e; - } - + affectedNodes = tree.remove(id); flushNodes(); return affectedNodes; } -NodeIdList NodeManager::createNode(nodeid_t parentid, nodeid_t id, nodeid_t insertbeforeid) - throw (std::exception) +NodeIdListPair NodeManager::moveNode(nodeid_t id, nodeid_t to, + nodeid_t beforeId) { - if(hasId(id)) + if(isProtected(id)) { - // Id alerady in tree, ignore - return {}; + throw Error::ProtectedNode(); } - NodeIdList affectedNodes; + NodeIdList tilremove; + tilremove.push_back(id); + NodeIdList ancestors = tree.ancestorList(id); + tilremove.insert(tilremove.end(), ancestors.begin(), ancestors.end()); - node_t t; - t.attributes["title"] = ""; - t.id = id; + NodeIdList tilcreate; + tilcreate.push_back(to); + ancestors = tree.ancestorList(to); + tilcreate.insert(tilcreate.end(), ancestors.begin(), ancestors.end()); - try - { - affectedNodes = tree.insertAsChild(parentid, id, t, insertbeforeid); - } - catch(std::exception& e) - { - throw e; - } + tree.move(id, to, beforeId); // TODO: Use node list pair from return value flushNodes(); - return affectedNodes; + return {tilremove, tilcreate}; } NodeIdList NodeManager::subNodes(nodeid_t t) - throw (std::exception) { NodeIdList affectedNodes; - - try - { - affectedNodes = tree.bfs(t); - } - catch(std::exception& e) - { - throw e; - } - + affectedNodes = tree.bfs(t); return affectedNodes; } -//NodeIdList NodeManager::ancestorList(nodeid_t id) -// throw (std::exception) -//{ -// NodeIdList ancestors; -// -// try -// { -// ancestors = tree.ancestorList(id); -// goto finish; -// } -// catch(std::exception& e) -// { -// throw e; -// } -// -//finish: -// return ancestors; -//} +node_t NodeManager::node(nodeid_t t) +{ + return tree.data(t); +} + +nodeid_t NodeManager::createId() +{ + return tree.createId(); +} void NodeManager::flushNodes() { @@ -292,122 +197,12 @@ void NodeManager::flushNodes() fclose(fp); } -#if 0 -NodeList nodelist; - -node_t create_node(std::string title, std::string desc) -{ - node_t t; - t.parent_id = current_id_count(); - t.attributes["title"] = title; - t.desc = desc; - t.id = id_count; id_count++; - - return t; -} - -NodeList load_nodelist_from_file(std::string file) -{ - NodeList list; - - // create MuniaDb class which handles nodes, db-flush and db-init. - - return list; -} - -bool save_nodelist_to_file(NodeList list, std::string file) -{ - FILE* fp; - - if(! (fp = fopen(file.c_str(), "w"))) - { - return false; - } - - if(!fprintf(fp, "\n")) - { - fclose(fp); - return false; - } - - NodeList::iterator it; - for(it = nodelist.begin(); it != nodelist.end(); it++) - { - node_t t = *it; - int r = 1; - - //printf("Flushing node %d\n", t.id); - r |= fprintf(fp, " \n", t.id, t.parent_id); - r |= fprintf(fp, " %s\n", xml_encode(t.attributes["title"]).c_str()); - r |= fprintf(fp, " %s\n", xml_encode(t.attributes["description"]).c_str()); - r |= fprintf(fp, " )\n"); - - if(!r) - { - fclose(fp); - return false; - } - } - - if(!fprintf(fp, "\n")) - { - fclose(fp); - return false; - } - - fclose(fp); - return true; -} - -static void delete_node(node_t t, NodeList& graveyard); - -static void delete_children(node_t t, NodeList& graveyard) -{ - NodeList::iterator it; - for(it = nodelist.begin(); it != nodelist.end(); it++) - { - if(it->parent_id == t.id) { - delete_node(*it, graveyard); - } - } -} - -static void delete_node(node_t t, NodeList& graveyard) +bool NodeManager::hasId(nodeid_t id) { - NodeList::iterator it; - for(it = nodelist.begin(); it != nodelist.end(); it++) - { - if(it->id == t.id) - { - break; - } - } - - if(it != nodelist.end()) - { - graveyard.push_back(*it); - delete_children(*it, graveyard); - } + return tree.hasId(id); } -void delete_node(int id, NodeList& graveyard) +bool NodeManager::isProtected(nodeid_t id) const { - node_t t; - t.id = id; - delete_node(t, graveyard); - - for(NodeList::iterator it = graveyard.begin(); - it != graveyard.end(); it++) - { - for(NodeList::iterator it_tl = nodelist.begin(); - it_tl != nodelist.end(); it_tl++) - { - if(it_tl->id == it->id) - { - nodelist.erase(it_tl); - break; - } - } - } + return id < FIRST_NODE_ID; } -#endif diff --git a/src/nodemanager.h b/src/nodemanager.h index 85b7955..8909eb1 100644 --- a/src/nodemanager.h +++ b/src/nodemanager.h @@ -27,10 +27,8 @@ */ #pragma once -#include #include -#include -#include +#include #include "node.h" #include "nodetree.h" @@ -43,173 +41,31 @@ public: NodeManager(); ~NodeManager(); - void init(std::string filename); + void init(const std::string& filename); - NodeIdList createNode(nodeid_t parentid, nodeid_t id, nodeid_t insertbeforeid) throw (std::exception); - NodeIdList updateNode(nodeid_t id, const std::string &name, - const std::string &value) throw (std::exception); - NodeIdList removeNode(nodeid_t id) throw (std::exception); + NodeIdList createNode(nodeid_t parentid, nodeid_t id, + nodeid_t insertbeforeid); - NodeIdListPair moveNode(nodeid_t id, nodeid_t newParent, nodeid_t beforeId) throw (std::exception); + NodeIdList updateNode(nodeid_t id, const std::string& name, + const std::string& value); - NodeIdList subNodes(nodeid_t) throw (std::exception); + NodeIdList removeNode(nodeid_t id); - NodeTree tree; + NodeIdListPair moveNode(nodeid_t id, nodeid_t newParent, nodeid_t beforeId); - node_t node(nodeid_t t); + NodeIdList subNodes(nodeid_t id); - void flushNodes(); + node_t node(nodeid_t t); nodeid_t createId(); - bool hasId(nodeid_t id); private: - // NodeIdList ancestorList(nodeid_t node); - nodeid_t idCount; - node_t nextNode(); + void flushNodes(); + bool hasId(nodeid_t id); + bool isProtected(nodeid_t id) const; std::string file; + NodeTree tree; }; -#ifndef FOOBAR extern NodeManager node_manager; -#endif - - -#if 0 - -/* -Node: - id - subnodes -// tags - title - description -// primary_assignment -// secondary_assignment - - -Protocol: - -Server -> client: - update [id] [title] [description]; - move [id] [x] [y]; - add [id] [title] [description] [x] [y]; - del [id] - -Client -> server: - update [id] [title] [description]; - move [id] [x] [y]; - add [title] [description] [x] [y]; - del [id] - -title and description are " encapsulated utf-8 string with " escaped with a backslash. -x and y are integers as strings -id are an integer as a string - */ - -/* -typedef struct { - int x, y; - int id; - std::string title; - std::string desc; -} node_t; -*/ -/* -protocol: - add_node title description parent_id - del_node id - update_node id title description - move_node id parent - copy_node id parent -*/ - - - -//typedef std::list NodeList; - -class CompareByParentid -{ -public: - bool operator()(const node_t &a, const node_t &b) const - { - return a.parent_id < b.parent_id; - } -}; - -class NodeList - : public std::list -{ -public: - NodeList() {} - ~NodeList(){} - - void insert(node_t t) - { - if(t.id == t.parent_id) - { - return; - } - printf("inserting node %d with parent %d\n", t.id, t.parent_id); - - if(t.parent_id == -1) - { - std::list::push_front(t); - return; - } - - std::list::iterator it; - for(it = begin(); it != end(); ++it) - { - printf("\tcomparing %d and %d\n", t.parent_id, it->id); - if(t.parent_id == it->id) - { - break; - } - } - assert(it != end()); - - std::list::insert(++it, t); - - //std::list::push_back(t); - //std::list::sort(CompareByParentid()); - } - - void move(node_t t) - { - std::list::iterator it; - for(it = begin(); it != end(); it++) - { - if(t.id == it->id) - { - break; - } - } - assert(it != end()); - //if(it != end()) - //{ - std::list::erase(it); - //} - insert(t); - } - - void push_back(node_t t) - { - insert(t); - } - -private: - std::list list; -}; - - -extern NodeList nodelist; - -//typedef std::priority_queue, CompareByParentid> NodeList; - -node_t create_node(std::string title, std::string desc, /*int x, int y*/ int parent_id); - -NodeList load_nodelist_from_file(std::string file); -bool save_nodelist_to_file(NodeList t, std::string file); -#endif/*0*/ diff --git a/src/nodetree.cc b/src/nodetree.cc index a2499ea..f0877ff 100644 --- a/src/nodetree.cc +++ b/src/nodetree.cc @@ -34,6 +34,7 @@ #include "hugin.hpp" #include "xml_encode_decode.h" +#include "exceptions.h" #define ROOT_PARENT_ID -1 @@ -75,22 +76,14 @@ std::string Node::toXML(std::string prefix) static void concatNodeIdLists(NodeIdList& pre, NodeIdList& post) { pre.insert(pre.end(), post.begin(), post.end()); - //for(NodeIdList::iterator it = post.begin(); - //it != post.end(); it++) - //{ - // pre.push_back( - //} } NodeTree::NodeTree() { - root = NULL; - nextid = 10; } NodeTree::~NodeTree() { - // cleanup tree } nodeid_t NodeTree::createId() @@ -106,7 +99,7 @@ nodeid_t NodeTree::createId() return nodeid; } -bool NodeTree::hasId(nodeid_t nodeid) +bool NodeTree::hasId(nodeid_t nodeid) const { return id2node.find(nodeid) != id2node.end(); } @@ -115,11 +108,15 @@ static nodeid_t rootid = -1; NodeIdList NodeTree::insertAsChild(nodeid_t parentid, nodeid_t id, node_t data, nodeid_t insertBeforeId) - throw (std::exception) { NodeIdList affectedNodes; - // Initialize + if(parentid == id) + { + throw Error::NewParentIsSelf(); // Node and new parent are the same node. + } + + // Initialized? if(!root) { rootid = id; @@ -130,62 +127,46 @@ NodeIdList NodeTree::insertAsChild(nodeid_t parentid, nodeid_t id, } else { - try + if(!hasId(parentid)) { - Node* parent = id2node.at(parentid); - Node* child = createNode(id); - //DEBUG(nodetree, "!!!!!!!id in insert: %d\n", data.id); - child->data = data; - insertChild(parent, child, insertBeforeId); - - //affectedNodes.push_back(parentid); - affectedNodes.push_back(id); - NodeIdList ancestors = ancestorList(id); - concatNodeIdLists(affectedNodes, ancestors); - } - catch(std::exception& e) - { - throw e; + throw Error::MissingParent(); } + + Node* parent = id2node.at(parentid); + Node* child = createNode(id); + + child->data = data; + insertChild(parent, child, insertBeforeId); + affectedNodes.push_back(id); + + NodeIdList ancestors = ancestorList(id); + concatNodeIdLists(affectedNodes, ancestors); } - //DEBUG(nodetree, "Child %d added to %d, affecting %d nodes\n", - // id, parentid, affectedNodes.size()); return affectedNodes; } NodeIdList NodeTree::remove(nodeid_t id) - throw (std::exception) { + if(!hasId(id)) + { + throw Error::NoSuchId(); + } + NodeIdList affectedNodes; affectedNodes.push_back(id); NodeIdList ancestors = ancestorList(id); concatNodeIdLists(affectedNodes, ancestors); - //DEBUG(nodetree, "Removing %d\n", id); - //DEBUG(nodetree, "!!!!!affected nodes %d\n", affectedNodes.size()); - - Node* node = id2node[id]; - - //DEBUG(nodetree, "node: %p, id %d, parent %p\n", node, node->data.id, node->parent); + Node* node = id2node.at(id); - //DEBUG(nodetree, "!!!!!size %d\n", node->parent->children.size()); node->parent->children.remove(node); - for(NodeList::iterator it = node->parent->children.begin(); - it != node->parent->children.end(); - it++) - { - //DEBUG(nodetree, "%p\n", *it); - } - //DEBUG(nodetree, "!!!!!size %d\n", node->parent->children.size()); NodeIdList idlist = bfs(id); NodeIdList::reverse_iterator it = idlist.rbegin(); while(it != idlist.rend()) { node_t node = data(*it); - //Node* n = id2node[node.id]; - //delete(n); it++; } @@ -208,112 +189,114 @@ static bool findNode(Node *needle, Node *haystack) } NodeIdList NodeTree::move(nodeid_t id, nodeid_t toid, nodeid_t beforeId) - throw (std::exception) { NodeIdList affectedNodes; - try + if(id == toid) { - Node* child = id2node.at(id); - Node* newparent = id2node.at(toid); + throw Error::NewParentIsSelf(); // Node and new parent are the same node. + } - // Test if new parent is a child of the node itself... - if(findNode(newparent, child)) - { - throw std::exception(); - } + if(!hasId(id)) + { + throw Error::NoSuchId(); + } - if(!child->parent) - { - throw std::exception(); - } + Node* child = id2node.at(id); - child->parent->children.remove(child); - bool inserted{false}; - for(auto it = newparent->children.begin(); - it != newparent->children.end(); - ++it) - { - if((*it)->id == beforeId) - { - newparent->children.insert(it, child); - inserted = true; - } - } - if(!inserted) - { - // beforeId was not found in parent - inserting last instead. - newparent->children.push_back(child); - } + if(!hasId(toid)) + { + throw Error::MissingParent(); + } - child->parent = newparent; + Node* newparent = id2node.at(toid); - affectedNodes.push_back(id); - // affectedNodes.push_back(child->parent->id); - NodeIdList ancestors = ancestorList(id); - concatNodeIdLists(affectedNodes, ancestors); - affectedNodes.push_back(toid); - ancestors = ancestorList(toid); + // Test if new parent is a child of the node itself... + if(findNode(newparent, child)) + { + throw Error::NewParentIsChildOfSelf(); + } + + if(!child->parent) + { + throw Error::System("Node does not have a parent."); + } + + child->parent->children.remove(child); + bool inserted{false}; + for(auto it = newparent->children.begin(); + it != newparent->children.end(); + ++it) + { + if((*it)->id == beforeId) + { + newparent->children.insert(it, child); + inserted = true; + } } - catch(std::exception& e) + if(!inserted) { - throw e; + // beforeId was not found in parent - inserting last instead. + newparent->children.push_back(child); } + child->parent = newparent; + + affectedNodes.push_back(id); + + NodeIdList ancestors = ancestorList(id); + concatNodeIdLists(affectedNodes, ancestors); + affectedNodes.push_back(toid); + ancestors = ancestorList(toid); + return affectedNodes; } NodeIdList NodeTree::updateData(nodeid_t id, const std::string &name, const std::string &value) - throw (std::exception) { + if(!hasId(id)) + { + throw Error::NoSuchId(); + } + NodeIdList affectedNodes; - try - { - Node* node = id2node.at(id); - node->data.attributes[name] = value; + Node* node = id2node.at(id); + node->data.attributes[name] = value; - affectedNodes.push_back(id); - NodeIdList ancestors = ancestorList(id); - concatNodeIdLists(affectedNodes, ancestors); - } - catch(std::exception& e) - { - throw e; - } + affectedNodes.push_back(id); + NodeIdList ancestors = ancestorList(id); + concatNodeIdLists(affectedNodes, ancestors); return affectedNodes; } node_t NodeTree::data(nodeid_t id) - throw (std::exception) { + if(!hasId(id)) + { + throw Error::NoSuchId(); + } + node_t t; - try + Node* node = id2node.at(id); + node_t tmp = node->data; + t.id = node->id; + t.attributes = tmp.attributes; + + if(node->parent) { - Node* node = id2node.at(id); - node_t tmp = node->data; - t.id = node->id; - t.attributes = tmp.attributes; - //DEBUG(nodetree, "!!!!t.id and tmp.id in data: %d and %d\n", t.id, tmp.id); - if(node->parent) - { - t.parentid = node->parent->id; - } - else - { - if(t.id != rootid) - { - throw std::exception(); - } - t.parentid = -1; - } + t.parentid = node->parent->id; } - catch(std::exception& e) + else { - throw e; + if(t.id != rootid) + { + throw Error::System("Node has no parent, but node is not root."); + } + t.parentid = -1; } return t; @@ -321,8 +304,12 @@ node_t NodeTree::data(nodeid_t id) // bfs search from id in tree NodeIdList NodeTree::bfs(nodeid_t id) - throw (std::exception) { + if(!hasId(id)) + { + throw Error::NoSuchId(); + } + NodeIdList lst; lst.push_back(id); @@ -353,35 +340,36 @@ NodeIdList NodeTree::bfs(nodeid_t id) } NodeIdList NodeTree::ancestorList(nodeid_t id) - throw (std::exception) { NodeIdList ancestors; - try + if(!hasId(id)) { - Node* current = id2node.at(id); - while(current->parent) - { - ancestors.push_back(current->parent->id); - current = current->parent; - } + throw Error::NoSuchId(); } - catch(std::exception& e) + + Node* current = id2node.at(id); + auto origin = current; + while(current->parent) { - throw e; + ancestors.push_back(current->parent->id); + current = current->parent; + if(current == origin) + { + throw Error::System("Cycle detected."); + } } - //DEBUG(nodetree, "Collected %d ancestors to %u\n", ancestors.size(), id); - //for(NodeIdList::iterator it = ancestors.begin(); - // it != ancestors.end(); it++) - //{ - // DEBUG(nodetree, "\tancestor %u\n", *it); - //} return ancestors; } Node* NodeTree::createNode(nodeid_t id) { + if(hasId(id)) + { + throw Error::IdAlreadyExists(); + } + Node* node = new Node(); node->parent = NULL; node->id = id; @@ -425,6 +413,7 @@ static void printNode(Node* node, std::string prefix) { return; } + node_t t = node->data; DEBUG(nodetree, "%s- %u - %s (%p)\n", prefix.c_str(), (int)node->id, t.attributes["title"].c_str(), node); @@ -444,12 +433,13 @@ void NodeTree::toStdOut() std::string NodeTree::toXML() { - Node *root = id2node.at(rootid); - std::string xml; xml += "\n"; xml += "\n"; - xml += root->toXML(" "); + if(root) + { + xml += root->toXML(" "); + } xml += ""; return xml; diff --git a/src/nodetree.h b/src/nodetree.h index f2f4600..41f7272 100644 --- a/src/nodetree.h +++ b/src/nodetree.h @@ -29,7 +29,6 @@ #include #include -#include #include "node.h" @@ -54,33 +53,57 @@ public: ~NodeTree(); nodeid_t createId(); - bool hasId(nodeid_t nodeid); + bool hasId(nodeid_t nodeid) const; + //! May throw: + //! - Error::MissingParent if the parent node id does not exist in the tree. + //! - Error::NoSuchId if the ancestorList cannot be made from the supplied id. + //! - Error::IdAlreadyExists if the supplied id already exists in the tree. + //! - Error::NewParentIsSelf if id and parentid are the same. NodeIdList insertAsChild(nodeid_t parentid, nodeid_t id, node_t data, - nodeid_t insertBeforeId) throw (std::exception); - NodeIdList remove(nodeid_t id) throw (std::exception); - NodeIdList move(nodeid_t id, nodeid_t newParentId, - nodeid_t insertBeforeId) throw (std::exception); + nodeid_t insertBeforeId); + + //! May throw: + //! - Error::NoSuchId if node id does not exist. + //! - Error::ProtectedNode if node is protected and cannot be removed. + NodeIdList remove(nodeid_t id); + + //! May throw: + //! - Error::NewParentIsSelf if id and parentid are the same. + //! - Error::NoSuchId if node id does not exist. + //! - Error::MissingParent if new parent node does not exist. + //! - Error::ProtectedNode if node is protected and cannot be (re)moved. + NodeIdList move(nodeid_t id, nodeid_t newParentId, nodeid_t insertBeforeId); + + //! May throw: + //! - Error::NoSuchId if node id does not exist. NodeIdList updateData(nodeid_t id, const std::string &name, - const std::string &value) throw (std::exception); - node_t data(nodeid_t id) throw (std::exception); + const std::string &value); - NodeIdList bfs(nodeid_t id) throw (std::exception); + //! May throw: + //! - Error::NoSuchId if node id does not exist. + node_t data(nodeid_t id); - NodeIdList ancestorList(nodeid_t id) throw (std::exception); + //! May throw: + //! - Error::NoSuchId if node id does not exist. + NodeIdList bfs(nodeid_t id); + + //! May throw: + //! - Error::NoSuchId if node id does not exist. + NodeIdList ancestorList(nodeid_t id); void toStdOut(); std::string toXML(); - void fromXML(const std::string& xml); - nodeid_t nextid; + void fromXML(const std::string& xml); private: friend class XmlParser; Node* createNode(nodeid_t id); void insertChild(Node* parent, Node* child, nodeid_t insertBeforeId); - Node* root; + nodeid_t nextid{10}; + Node* root{nullptr}; std::map id2node; }; -- cgit v1.2.3