summaryrefslogtreecommitdiff
path: root/src/traversal/transform.cc
diff options
context:
space:
mode:
Diffstat (limited to 'src/traversal/transform.cc')
-rw-r--r--src/traversal/transform.cc357
1 files changed, 357 insertions, 0 deletions
diff --git a/src/traversal/transform.cc b/src/traversal/transform.cc
new file mode 100644
index 00000000..3331c2f3
--- /dev/null
+++ b/src/traversal/transform.cc
@@ -0,0 +1,357 @@
+/*
+ * 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