From 9f962123be30da94add3f966bb46962f8bfdea88 Mon Sep 17 00:00:00 2001 From: Matthias Schiffer Date: Thu, 24 Sep 2009 21:50:11 +0200 Subject: UserList: Encapsulate list --- src/Client/UserListCommands.cpp | 87 ++++++++++++++++++++++++- src/Client/UserListCommands.h | 3 + src/Common/CMakeLists.txt | 2 +- src/Common/UserLists/UserList.cpp | 129 ++++++++++++++++++++++++++++++++++++++ src/Common/UserLists/UserList.h | 125 +++++++++++++++++++++++++++++++++++- src/Common/UserLists/Util.cpp | 4 +- src/Server/UserListManager.cpp | 2 +- 7 files changed, 344 insertions(+), 8 deletions(-) create mode 100644 src/Common/UserLists/UserList.cpp diff --git a/src/Client/UserListCommands.cpp b/src/Client/UserListCommands.cpp index eb4aee1..420202e 100644 --- a/src/Client/UserListCommands.cpp +++ b/src/Client/UserListCommands.cpp @@ -162,10 +162,79 @@ void UserListCommands::showListCommand(CommandParser *commandParser, const std:: boost::shared_ptr userList = Common::UserLists::Util::deserializeUserList(result.first.get()); - std::cout << "User list '" << args[2] << "':" << std::endl; + std::cout << "User list '" << args[2]; - for(Common::UserLists::UserList::iterator user = userList->begin(); user != userList->end(); ++user) { - std::cout << " " << user->getName() << std::endl; + if(userList->isEmpty()) { + std::cout << " is empty." << std::endl; + } + else { + std::cout << "' contains " << userList->getLength() << " user"; + if(userList->getLength() > 1) + std::cout << "s"; + std::cout << "." << std::endl << std::endl; + + std::map lengths; + + static const std::string USER_NAME("User name"); + lengths.insert(std::make_pair(USER_NAME, USER_NAME.length())); + static const std::string GROUP_NAME("Group name"); + lengths.insert(std::make_pair(GROUP_NAME, GROUP_NAME.length())); + + std::set details = userList->getDetails(); + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + lengths.insert(std::make_pair(*detail, detail->length())); + } + + for(Common::UserLists::UserList::iterator user = userList->begin(); user != userList->end(); ++user) { + std::map::iterator it = lengths.find(USER_NAME); + if(user->getName().length() > it->second) + it->second = user->getName().length(); + + it = lengths.find(GROUP_NAME); + if(user->getGroup().length() > it->second) + it->second = user->getGroup().length(); + + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + it = lengths.find(*detail); + std::string detailStr = user->getDetail(*detail); + if(detailStr.length() > it->second) + it->second = detailStr.length(); + } + } + + printPadded(USER_NAME, lengths.find(USER_NAME)->second); + printPadded(GROUP_NAME, lengths.find(GROUP_NAME)->second); + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + printPadded(*detail, lengths.find(*detail)->second); + } + std::cout << std::endl; + + printDelimiter(lengths.find(USER_NAME)->second); + printDelimiter(lengths.find(GROUP_NAME)->second); + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + printDelimiter(lengths.find(*detail)->second); + } + std::cout << std::endl; + + for(Common::UserLists::UserList::iterator user = userList->begin(); user != userList->end(); ++user) { + static const std::string UNSET(""); + + if(!user->getName().empty()) + printPadded(user->getName(), lengths.find(USER_NAME)->second); + else + printPadded(UNSET, lengths.find(USER_NAME)->second); + + if(!user->getGroup().empty()) + printPadded(user->getGroup(), lengths.find(GROUP_NAME)->second); + else + printPadded(UNSET, lengths.find(GROUP_NAME)->second); + + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + printPadded(user->getDetail(*detail), lengths.find(*detail)->second); + } + + std::cout << std::endl; + } } std::cout << std::endl; @@ -177,6 +246,18 @@ void UserListCommands::showListCommand(CommandParser *commandParser, const std:: } } +void UserListCommands::printPadded(const std::string &str, unsigned length) { + static const int EXTRA_PADDING = 3; + + std::cout << str; + if(str.length() < length + EXTRA_PADDING) + std::cout << std::string(length-str.length()+EXTRA_PADDING, ' '); +} + +void UserListCommands::printDelimiter(unsigned length) { + printPadded(std::string(length, '-'), length); +} + void UserListCommands::userListCommand(CommandParser *commandParser, const std::vector &args) { std::string cmd; diff --git a/src/Client/UserListCommands.h b/src/Client/UserListCommands.h index 17fcb4e..51a714b 100644 --- a/src/Client/UserListCommands.h +++ b/src/Client/UserListCommands.h @@ -42,6 +42,9 @@ class MAD_CLIENT_EXPORT UserListCommands { static void listCommand(CommandParser *commandParser, const std::vector &args); static void showListCommand(CommandParser *commandParser, const std::vector &args); + static void printPadded(const std::string &str, unsigned length); + static void printDelimiter(unsigned length); + public: static void userListCommand(CommandParser *commandParser, const std::vector &args); }; diff --git a/src/Common/CMakeLists.txt b/src/Common/CMakeLists.txt index a99f9be..d82c0d9 100644 --- a/src/Common/CMakeLists.txt +++ b/src/Common/CMakeLists.txt @@ -18,7 +18,7 @@ mad_library(Common Requests/SimpleRequest.cpp Requests/SimpleRequest.h Requests/StatusRequest.h - UserLists/UserList.h + UserLists/UserList.cpp UserLists/UserList.h UserLists/UserListDiff.cpp UserLists/UserListDiff.h UserLists/UserListEntry.h UserLists/Util.cpp UserLists/Util.h diff --git a/src/Common/UserLists/UserList.cpp b/src/Common/UserLists/UserList.cpp new file mode 100644 index 0000000..a14111d --- /dev/null +++ b/src/Common/UserLists/UserList.cpp @@ -0,0 +1,129 @@ +/* + * UserList.cpp + * + * Copyright (C) 2009 Matthias Schiffer + * + * This program is free software: you can redistribute it and/or modify it + * under the terms of the GNU Lesser General Public License as published by the + * Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, but + * WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public License along + * with this program. If not, see . + */ + +#include "UserList.h" + +namespace Mad { +namespace Common { +namespace UserLists { + +std::list::iterator UserList::findEntry(const std::string &name) { + for(std::list::iterator it = list.begin(); it != list.end(); ++it) { + if(it->getName() == name) { + return it; + } + } + + return list.end(); +} + +std::list::const_iterator UserList::findEntry(const std::string &name) const { + for(std::list::const_iterator it = list.begin(); it != list.end(); ++it) { + if(it->getName() == name) { + return it; + } + } + + return list.end(); +} + +UserList::iterator UserList::find(const std::string &name) { + if(!names.count(name)) + return end(); + + return iterator(findEntry(name)); +} + +UserList::const_iterator UserList::find(const std::string &name) const { + if(!names.count(name)) + return end(); + + return const_iterator(findEntry(name)); +} + +void UserList::registerEntry(const UserListEntry &entry) { + std::set details = entry.getDetailList(); + + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + std::map::iterator it = detailCounts.find(*detail); + if(it == detailCounts.end()) + detailCounts.insert(make_pair(*detail, 1)); + else + ++it->second; + } +} + +void UserList::unregisterEntry(const UserListEntry &entry) { + std::set details = entry.getDetailList(); + + for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { + std::map::iterator it = detailCounts.find(*detail); + // TODO Assert + if(it == detailCounts.end()) + continue; + + if(it->second > 1) + --it->second; + else + detailCounts.erase(it); + } +} + + +bool UserList::addUser(const UserListEntry &entry) { + return insertUser(entry, end()); +} + +bool UserList::insertUser(const UserListEntry &entry, iterator after) { + if(!names.insert(entry.getName()).second) + return false; + + list.insert(after.it, entry); + registerEntry(entry); + return true; +} + +bool UserList::removeUser(const std::string &name) { + if(!names.erase(name)) + return false; + + std::list::iterator it = findEntry(name); + // TODO Assert + + unregisterEntry(*it); + list.erase(it); + return true; +} + +bool UserList::updateUser(const UserListEntry &entry) { + if(!names.count(entry.getName())) + return false; + + std::list::iterator it = findEntry(entry.getName()); + // TODO Assert + + unregisterEntry(*it); + *it = entry; + registerEntry(entry); + return true; +} + +} +} +} diff --git a/src/Common/UserLists/UserList.h b/src/Common/UserLists/UserList.h index e80abaf..9c5985d 100644 --- a/src/Common/UserLists/UserList.h +++ b/src/Common/UserLists/UserList.h @@ -20,16 +20,139 @@ #ifndef MAD_COMMON_USERLISTS_USERLIST_H_ #define MAD_COMMON_USERLISTS_USERLIST_H_ +#include "../export.h" + #include "UserListEntry.h" #include +#include +#include namespace Mad { namespace Common { namespace UserLists { -class UserList : public std::list { +class MAD_COMMON_EXPORT UserList { + private: + template + class iterator_base { + public: + friend class UserList; + + typedef Type value_type; + + typedef value_type &reference; + typedef value_type *pointer; + + typedef long difference_type; + + private: + IteratorType it; + + iterator_base(IteratorType it0) : it(it0) {} + + public: + iterator_base() {} + + reference operator*() const { + return *it; + } + + iterator_base operator++(int) { + return iterator(it++); + } + + iterator_base& operator++() { + ++it; + return *this; + } + + iterator_base operator--(int) { + return iterator(it--); + } + + iterator_base& operator--() { + --it; + return *this; + } + + bool operator==(const iterator_base &it2) { + return it2.it == it; + } + + bool operator!=(const iterator_base &it2) { + return it2.it != it; + } + + pointer operator->() const { + return &*it; + } + }; + + std::set names; + std::list list; + + std::map detailCounts; + + void registerEntry(const UserListEntry &entry); + void unregisterEntry(const UserListEntry &entry); + + std::list::iterator findEntry(const std::string &name); + std::list::const_iterator findEntry(const std::string &name) const; + public: + typedef iterator_base::iterator> iterator; + typedef iterator_base::const_iterator> const_iterator; + + iterator find(const std::string &name); + const_iterator find(const std::string &name) const; + + iterator begin() { + return iterator(list.begin()); + } + + const_iterator begin() const { + return const_iterator(list.begin()); + } + + iterator end() { + return list.end(); + } + + const_iterator end() const { + return const_iterator(list.end()); + } + + bool isEmpty() const { + return list.empty(); + } + + size_t getLength() const { + return list.size(); + } + + std::set getDetails() const { + std::set ret; + + for(std::map::const_iterator it = detailCounts.begin(); it != detailCounts.end(); ++it) + ret.insert(it->first); + + return ret; + } + + unsigned getDetailUsage(const std::string &detail) const { + std::map::const_iterator it = detailCounts.find(detail); + if(it == detailCounts.end()) + return 0; + else + return it->second; + } + + bool addUser(const UserListEntry &entry); + bool insertUser(const UserListEntry &entry, iterator after); + bool removeUser(const std::string &name); + bool updateUser(const UserListEntry &entry); + UserList() {} }; diff --git a/src/Common/UserLists/Util.cpp b/src/Common/UserLists/Util.cpp index 812c2c7..3c15956 100644 --- a/src/Common/UserLists/Util.cpp +++ b/src/Common/UserLists/Util.cpp @@ -39,7 +39,7 @@ UserListEntry Util::deserializeUserListEntry(XmlData::List::const_iterator entry std::set details = entry->getChildren(); for(std::set::iterator detail = details.begin(); detail != details.end(); ++detail) { - if(*detail == "user" || *detail == "group") + if(*detail == "name" || *detail == "group") continue; user.setDetail(*detail, entry->get(*detail)); @@ -62,7 +62,7 @@ boost::shared_ptr Util::deserializeUserList(const XmlData *data) { if(userList) { for(XmlData::List::const_iterator user = userList->begin(); user != userList->end(); ++user) - users->push_back(deserializeUserListEntry(user)); + users->addUser(deserializeUserListEntry(user)); } return users; diff --git a/src/Server/UserListManager.cpp b/src/Server/UserListManager.cpp index d1a6e31..d520c96 100644 --- a/src/Server/UserListManager.cpp +++ b/src/Server/UserListManager.cpp @@ -141,7 +141,7 @@ boost::shared_ptr UserListManager::getCurrentUserLi Common::UserLists::UserListEntry entry(user->second.getUsername(), groupname); entry.setDetail("Full name", user->second.getFullName()); - list->push_back(entry); + list->addUser(entry); } return list; -- cgit v1.2.3