You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
kmymoney/kmymoney2/mymoney/storage/mymoneymap.h

329 lines
7.8 KiB

#include <stdint.h>
#include <tqmap.h>
#include <tqptrstack.h>
#include <kmymoney/mymoneyexception.h>
#ifndef MYMONEYMAP_H
#define MYMONEYMAP_H
#define MY_OWN_DEBUG 0
/**
* @author Thomas Baumgart
*
* This template class adds transaction security to the TQMap<> class.
* The interface is very simple. Before you perform any changes,
* you have to call the startTransaction() method. Then you can use
* the insert(), modify() and remove() methods to modify the map.
* Changes are recorded and if you are finished, use the
* commitTransaction() to finish the transaction. If you want to go
* back before you have committed the transaction, use
* rollbackTransaction() to set the container to the state it was
* in before you called startTransaction().
*
* The implementation is based on the command pattern, in case
* someone is interested.
*/
template <class Key, class T>
class MyMoneyMap : protected TQMap<Key, T>
{
public:
// typedef TQMapConstIterator<Key, T> const_iterator;
MyMoneyMap() : TQMap<Key, T>() {}
virtual ~MyMoneyMap() {}
void startTransaction(unsigned long* id = 0)
{
m_stack.push(new MyMoneyMapStart(this, id));
}
void rollbackTransaction(void)
{
if(m_stack.count() == 0)
throw new MYMONEYEXCEPTION("No transaction started to rollback changes");
// undo all actions
MyMoneyMapAction* action;
while(m_stack.count()) {
action = m_stack.pop();
action->undo();
delete action;
}
}
bool commitTransaction(void)
{
if(m_stack.count() == 0)
throw new MYMONEYEXCEPTION("No transaction started to commit changes");
bool rc = m_stack.count() > 1;
m_stack.setAutoDelete(true);
m_stack.clear();
return rc;
}
void insert(const Key& key, const T& obj)
{
if(m_stack.count() == 0)
throw new MYMONEYEXCEPTION("No transaction started to insert new element into container");
// store object in
m_stack.push(new MyMoneyMapInsert(this, key, obj));
}
void modify(const Key& key, const T& obj)
{
if(m_stack.count() == 0)
throw new MYMONEYEXCEPTION("No transaction started to modify element in container");
#if 0
// had to take this out, because we use TQPair in one instance as key
if(key.isEmpty())
throw new MYMONEYEXCEPTION("No key to update object");
#endif
m_stack.push(new MyMoneyMapModify(this, key, obj));
}
void remove(const Key& key)
{
if(m_stack.count() == 0)
throw new MYMONEYEXCEPTION("No transaction started to remove element from container");
#if 0
// had to take this out, because we use TQPair in one instance as key
if(key.isEmpty())
throw new MYMONEYEXCEPTION("No key to remove object");
#endif
m_stack.push(new MyMoneyMapRemove(this, key));
}
MyMoneyMap<Key, T>& operator= (const TQMap<Key, T>& m)
{
if(m_stack.count() != 0) {
throw new MYMONEYEXCEPTION("Cannot assign whole container during transaction");
}
TQMap<Key, T>::operator=(m);
return *this;
}
inline TQValueList<T> values(void) const
{
return TQMap<Key,T>::values();
}
inline TQValueList<Key> keys(void) const
{
return TQMap<Key,T>::keys();
}
const T& operator[] ( const Key& k ) const
{ TQT_CHECK_INVALID_MAP_ELEMENT; return TQMap<Key,T>::operator[](k); }
inline TQ_TYPENAME TQMap<Key, T>::const_iterator tqfind(const Key& k) const
{
return TQMap<Key,T>::tqfind(k);
}
inline TQ_TYPENAME TQMap<Key, T>::const_iterator begin(void) const
{
return TQMap<Key,T>::begin();
}
inline TQ_TYPENAME TQMap<Key, T>::const_iterator end(void) const
{
return TQMap<Key,T>::end();
}
inline bool tqcontains(const Key& k) const
{
return tqfind(k) != end();
}
inline void map(TQMap<Key, T>& that) const
{
//TQMap<Key, T>* ptr = dynamic_cast<TQMap<Key, T>* >(this);
//that = *ptr;
that = *(dynamic_cast<TQMap<Key, T>* >(const_cast<MyMoneyMap<Key, T>* >(this)));
}
inline size_t count(void) const
{
return TQMap<Key, T>::count();
}
#if MY_OWN_DEBUG
void dump(void) const
{
printf("Container dump\n");
printf(" items in container = %d\n", count());
printf(" items on stack = %d\n", m_stack.count());
const_iterator it;
for(it = begin(); it != end(); ++it) {
printf(" %s \n", it.key().data());
}
}
#endif
private:
class MyMoneyMapAction
{
public:
MyMoneyMapAction(TQMap<Key, T>* container) :
m_container(container) {}
MyMoneyMapAction(TQMap<Key, T>* container, const Key& key, const T& obj) :
m_container(container),
m_obj(obj),
m_key(key) {}
virtual ~MyMoneyMapAction() {}
virtual void undo(void) = 0;
protected:
TQMap<Key, T>* m_container;
T m_obj;
Key m_key;
};
class MyMoneyMapStart : public MyMoneyMapAction
{
public:
MyMoneyMapStart(TQMap<Key, T>* container, unsigned long* id) :
MyMoneyMapAction(container),
m_idPtr(id)
{
if(id != 0)
m_id = *id;
}
virtual ~MyMoneyMapStart() {}
void undo(void)
{
if(m_idPtr != 0)
*m_idPtr = m_id;
}
private:
unsigned long* m_idPtr;
unsigned long m_id;
};
class MyMoneyMapInsert : public MyMoneyMapAction
{
public:
MyMoneyMapInsert(TQMap<Key, T>* container, const Key& key, const T& obj) :
MyMoneyMapAction(container, key, obj)
{
(*container)[key] = obj;
}
virtual ~MyMoneyMapInsert() {}
void undo(void)
{
// m_container->remove(m_key) does not work on GCC 4.0.2
// using this-> to access those member does the trick
this->m_container->remove(this->m_key);
}
};
class MyMoneyMapRemove : public MyMoneyMapAction
{
public:
MyMoneyMapRemove(TQMap<Key, T>* container, const Key& key) :
MyMoneyMapAction(container, key, (*container)[key])
{
container->remove(key);
}
virtual ~MyMoneyMapRemove() {}
void undo(void)
{
(*(this->m_container))[this->m_key] = this->m_obj;
}
};
class MyMoneyMapModify : public MyMoneyMapAction
{
public:
MyMoneyMapModify(TQMap<Key, T>* container, const Key& key, const T& obj) :
MyMoneyMapAction(container, key, (*container)[key])
{
(*container)[key] = obj;
}
virtual ~MyMoneyMapModify() {}
void undo(void)
{
(*(this->m_container))[this->m_key] = this->m_obj;
}
};
protected:
TQPtrStack<MyMoneyMapAction> m_stack;
};
#if MY_OWN_DEBUG
#include <kmymoney/mymoneyaccount.h>
#include <kmymoney/mymoneytransaction.h>
main()
{
MyMoneyMap<TQString, MyMoneyAccount> container;
MyMoneyMap<TQString, MyMoneyTransaction> ct;
MyMoneyAccount acc;
acc.setName("Test");
// this should not be possible
// container["a"] = acc;
TQValueList<MyMoneyAccount> list;
list = container.values();
MyMoneyAccount b;
b.setName("Thomas");
try {
container.startTransaction();
container.insert("001", acc);
container.dump();
container.commitTransaction();
acc.setName("123");
container.startTransaction();
container.modify("001", acc);
container.dump();
container.rollbackTransaction();
container.dump();
container.startTransaction();
container.remove(TQString("001"));
container.dump();
container.rollbackTransaction();
container.dump();
b = container["001"];
printf("b.name() = %s\n", b.name().data());
TQMap<TQString, MyMoneyAccount>::ConstIterator it;
it = container.tqfind("001");
it = container.begin();
} catch(MyMoneyException *e) {
printf("Caught exception: %s\n", e->what().data());
delete e;
}
TQMap<TQString, MyMoneyAccount> map;
map["005"] = b;
container = map;
printf("b.name() = %s\n", container["001"].name().data());
printf("b.name() = %s\n", container["005"].name().data());
}
#endif
#endif