ledger/src/transform.cc

357 lines
8.8 KiB
C++

/*
* Copyright (c) 2003-2007, John Wiegley. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are
* met:
*
* - Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
*
* - Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* - Neither the name of New Artisans LLC nor the names of its
* contributors may be used to endorse or promote products derived from
* this software without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "transform.h"
namespace ledger {
#if 0
void populate_account(account_t& acct, xml::document_t& document)
{
if (! acct.parent)
return;
account_repitem_t * acct_item;
if (acct.data == NULL) {
acct.data = acct_item =
static_cast<account_repitem_t *>(repitem_t::wrap(&acct));
if (acct.parent) {
if (acct.parent->data == NULL)
populate_account(*acct.parent, acct_item);
else
static_cast<account_repitem_t *>(acct.parent->data)->
add_child(acct_item);
}
} else {
acct_item = static_cast<account_repitem_t *>(acct.data);
}
if (item->kind == repitem_t::ACCOUNT)
acct_item->add_child(item);
else
acct_item->add_content(item);
}
class populate_accounts : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
if (item->kind == repitem_t::TRANSACTION) {
item->extract();
populate_account(*static_cast<xact_repitem_t *>(item)->account(), item);
}
}
};
class clear_account_data : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
if (item->kind == repitem_t::ACCOUNT)
static_cast<account_repitem_t *>(item)->account->data = NULL;
}
};
void accounts_transform::execute(xml::document_t& document)
{
populate_accounts cb1;
items->select_all(cb1);
for (repitem_t * j = items->children; j; j = j->next) {
assert(j->kind == repitem_t::JOURNAL);
j->clear();
for (accounts_map::iterator i = j->journal->master->accounts.begin();
i != j->journal->master->accounts.end();
i++) {
assert((*i).second->data);
j->add_child(static_cast<account_repitem_t *>((*i).second->data));
(*i).second->data = NULL;
}
}
clear_account_data cb2;
items->select_all(cb2);
}
void compact_transform::execute(xml::document_t& document)
{
for (repitem_t * i = items; i; i = i->next) {
if (i->kind == repitem_t::ACCOUNT) {
while (! i->contents &&
i->children && ! i->children->next) {
account_repitem_t * p = static_cast<account_repitem_t *>(i);
i = p->children;
p->children = NULL;
p->last_child = NULL;
i->set_parent(p->parent);
p->set_parent(NULL);
i->prev = p->prev;
if (p->prev)
p->prev->next = i;
p->prev = NULL;
i->next = p->next;
if (p->next)
p->next->prev = i;
p->next = NULL;
if (i->parent->children == p)
i->parent->children = i;
if (i->parent->last_child == p)
i->parent->last_child = i;
account_repitem_t * acct = static_cast<account_repitem_t *>(i);
acct->parents_elided = p->parents_elided + 1;
checked_delete(p);
}
}
if (i->children)
execute(i->children);
}
}
void clean_transform::execute(xml::document_t& document)
{
repitem_t * i = items;
while (i) {
if (i->kind == repitem_t::ACCOUNT) {
value_t temp;
i->add_total(temp);
if (! temp) {
repitem_t * next = i->next;
checked_delete(i);
i = next;
continue;
}
}
#if 0
else if (i->kind == repitem_t::ENTRY && ! i->contents) {
assert(! i->children);
repitem_t * next = i->next;
checked_delete(i);
i = next;
continue;
}
#endif
if (i->children)
execute(i->children);
i = i->next;
}
}
void entries_transform::execute(xml::document_t& document)
{
}
void optimize_transform::execute(xml::document_t& document)
{
for (repitem_t * i = items; i; i = i->next) {
if (i->kind == repitem_t::ENTRY) {
if (i->contents &&
i->contents->next &&
! i->contents->next->next) { // exactly two transactions
xact_repitem_t * first =
static_cast<xact_repitem_t *>(i->contents);
xact_repitem_t * second =
static_cast<xact_repitem_t *>(i->contents->next);
if (first->xact->amount == - second->xact->amount)
;
}
}
if (i->children)
execute(i->children);
}
}
void split_transform::execute(xml::document_t& document)
{
for (repitem_t * i = items; i; i = i->next) {
if (i->contents && i->contents->next) {
repitem_t * j;
switch (i->kind) {
case repitem_t::TRANSACTION:
assert(false);
j = new xact_repitem_t(static_cast<xact_repitem_t *>(i)->xact);
break;
case repitem_t::ENTRY:
j = new entry_repitem_t(static_cast<entry_repitem_t *>(i)->entry);
break;
case repitem_t::ACCOUNT:
j = new account_repitem_t(static_cast<account_repitem_t *>(i)->account);
break;
default:
j = new repitem_t(i->kind);
break;
}
j->set_parent(i->parent);
j->prev = i;
j->next = i->next;
i->next = j;
j->contents = i->contents->next;
j->contents->prev = NULL;
j->contents->set_parent(j);
i->contents->next = NULL;
j->last_content = i->last_content;
if (j->contents == i->last_content)
i->last_content = i->contents;
}
if (i->children)
execute(i->children);
}
}
void merge_transform::execute(xml::document_t& document)
{
for (repitem_t * i = items; i; i = i->next) {
if (i->next) {
assert(i->kind == i->next->kind);
bool merge = false;
switch (i->kind) {
case repitem_t::TRANSACTION:
assert(false);
break;
case repitem_t::ENTRY:
if (static_cast<entry_repitem_t *>(i)->entry ==
static_cast<entry_repitem_t *>(i->next)->entry)
merge = true;
break;
case repitem_t::ACCOUNT:
#if 0
if (static_cast<account_repitem_t *>(i)->account ==
static_cast<account_repitem_t *>(i->next)->account)
merge = true;
#endif
break;
default:
break;
}
if (merge) {
repitem_t * j = i->next;
i->next = i->next->next;
if (i->next)
i->next->prev = i;
for (repitem_t * k = j->contents; k; k = k->next)
k->set_parent(i);
i->last_content->next = j->contents;
i->last_content = j->last_content;
j->contents = NULL;
assert(! j->children);
checked_delete(j);
}
}
if (i->children)
execute(i->children);
}
}
namespace {
#define REPITEM_FLAGGED 0x1
class mark_selected : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
item->flags |= REPITEM_FLAGGED;
}
};
class mark_selected_and_ancestors : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
while (item->parent) {
item->flags |= REPITEM_FLAGGED;
item = item->parent;
}
}
};
class delete_unmarked : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
if (item->parent && ! (item->flags & REPITEM_FLAGGED))
checked_delete(item);
}
};
class delete_marked : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
if (item->flags & REPITEM_FLAGGED)
checked_delete(item);
}
};
class clear_flags : public repitem_t::select_callback_t {
virtual void operator()(xml::document_t& document) {
item->flags = 0;
}
};
}
void select_transform::execute(xml::document_t& document)
{
if (! path) {
items->clear();
return;
}
mark_selected_and_ancestors cb1;
items->select(path, cb1);
delete_unmarked cb2;
items->select_all(cb2);
clear_flags cb3;
items->select_all(cb3);
}
void remove_transform::execute(xml::document_t& document)
{
if (! path)
return;
mark_selected cb1;
items->select(path, cb1);
delete_marked cb2;
items->select_all(cb2);
clear_flags cb3;
items->select_all(cb3);
}
#endif
} // namespace ledger