395 lines
10 KiB
C++
395 lines
10 KiB
C++
/*
|
|
* Copyright (c) 2003-2013, 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 <system.hh>
|
|
|
|
#include "generate.h"
|
|
#include "session.h"
|
|
|
|
namespace ledger {
|
|
|
|
generate_posts_iterator::generate_posts_iterator
|
|
(session_t& _session,
|
|
unsigned int _seed,
|
|
std::size_t _quantity,
|
|
bool _allow_invalid)
|
|
: session(_session), seed(_seed), quantity(_quantity),
|
|
allow_invalid(_allow_invalid),
|
|
|
|
rnd_gen(seed == 0 ? static_cast<unsigned int>(std::time(0)) : seed),
|
|
|
|
year_range(1900, 2300), year_gen(rnd_gen, year_range),
|
|
mon_range(1, 12), mon_gen(rnd_gen, mon_range),
|
|
day_range(1, 28), day_gen(rnd_gen, day_range),
|
|
|
|
upchar_range('A', 'Z'), upchar_gen(rnd_gen, upchar_range),
|
|
downchar_range('a', 'z'), downchar_gen(rnd_gen, downchar_range),
|
|
numchar_range('0', '9'), numchar_gen(rnd_gen, numchar_range),
|
|
|
|
truth_range(0, 1), truth_gen(rnd_gen, truth_range),
|
|
three_range(1, 3), three_gen(rnd_gen, three_range),
|
|
six_range(1, 6), six_gen(rnd_gen, six_range),
|
|
two_six_range(2, 6), two_six_gen(rnd_gen, two_six_range),
|
|
strlen_range(1, 40), strlen_gen(rnd_gen, strlen_range),
|
|
|
|
neg_number_range(-10000, -1), neg_number_gen(rnd_gen, neg_number_range),
|
|
pos_number_range(1, 10000), pos_number_gen(rnd_gen, pos_number_range)
|
|
{
|
|
std::ostringstream next_date_buf;
|
|
generate_date(next_date_buf);
|
|
next_date = parse_date(next_date_buf.str());
|
|
|
|
std::ostringstream next_aux_date_buf;
|
|
generate_date(next_aux_date_buf);
|
|
next_aux_date = parse_date(next_aux_date_buf.str());
|
|
|
|
TRACE_CTOR(generate_posts_iterator, "bool");
|
|
}
|
|
|
|
void generate_posts_iterator::generate_string(std::ostream& out, int len,
|
|
bool only_alpha)
|
|
{
|
|
DEBUG("generate.post.string",
|
|
"Generating string of length " << len << ", only alpha " << only_alpha);
|
|
|
|
int last = -1;
|
|
bool first = true;
|
|
for (int i = 0; i < len; i++) {
|
|
int next = only_alpha ? 3 : three_gen();
|
|
bool output = true;
|
|
switch (next) {
|
|
case 1: // colon
|
|
if (! first && last == 3 && strlen_gen() % 10 == 0 && i + 1 != len)
|
|
out << ':';
|
|
else {
|
|
i--;
|
|
output = false;
|
|
}
|
|
break;
|
|
case 2: // space
|
|
if (! first && last == 3 && strlen_gen() % 20 == 0 && i + 1 != len)
|
|
out << ' ';
|
|
else {
|
|
i--;
|
|
output = false;
|
|
}
|
|
break;
|
|
case 3: // character
|
|
switch (three_gen()) {
|
|
case 1: // uppercase
|
|
out << char(upchar_gen());
|
|
break;
|
|
case 2: // lowercase
|
|
out << char(downchar_gen());
|
|
break;
|
|
case 3: // number
|
|
if (! only_alpha && ! first)
|
|
out << char(numchar_gen());
|
|
else {
|
|
i--;
|
|
output = false;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
if (output) {
|
|
last = next;
|
|
first = false;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool generate_posts_iterator::generate_account(std::ostream& out,
|
|
bool no_virtual)
|
|
{
|
|
bool must_balance = true;
|
|
bool is_virtual = false;
|
|
|
|
if (! no_virtual) {
|
|
switch (three_gen()) {
|
|
case 1:
|
|
out << '[';
|
|
is_virtual = true;
|
|
break;
|
|
case 2:
|
|
out << '(';
|
|
must_balance = false;
|
|
is_virtual = true;
|
|
break;
|
|
case 3:
|
|
break;
|
|
}
|
|
}
|
|
|
|
generate_string(out, strlen_gen());
|
|
|
|
if (is_virtual) {
|
|
if (must_balance)
|
|
out << ']';
|
|
else
|
|
out << ')';
|
|
}
|
|
|
|
return must_balance;
|
|
}
|
|
|
|
void generate_posts_iterator::generate_commodity(std::ostream& out,
|
|
const string& exclude)
|
|
{
|
|
string comm;
|
|
do {
|
|
std::ostringstream buf;
|
|
generate_string(buf, six_gen(), true);
|
|
comm = buf.str();
|
|
}
|
|
while (comm == exclude || comm == "h" || comm == "m" || comm == "s" ||
|
|
comm == "and" || comm == "any" || comm == "all" || comm == "div" ||
|
|
comm == "false" || comm == "or" || comm == "not" ||
|
|
comm == "true" || comm == "if" || comm == "else");
|
|
|
|
out << comm;
|
|
}
|
|
|
|
string generate_posts_iterator::generate_amount(std::ostream& out,
|
|
value_t not_this_amount,
|
|
bool no_negative,
|
|
const string& exclude)
|
|
{
|
|
std::ostringstream buf;
|
|
|
|
if (truth_gen()) { // commodity goes in front
|
|
generate_commodity(buf, exclude);
|
|
if (truth_gen())
|
|
buf << ' ';
|
|
if (no_negative || truth_gen())
|
|
buf << pos_number_gen();
|
|
else
|
|
buf << neg_number_gen();
|
|
} else {
|
|
if (no_negative || truth_gen())
|
|
buf << pos_number_gen();
|
|
else
|
|
buf << neg_number_gen();
|
|
if (truth_gen())
|
|
buf << ' ';
|
|
generate_commodity(buf, exclude);
|
|
}
|
|
|
|
// Possibly generate an annotized commodity, but make it rarer
|
|
if (! no_negative && three_gen() == 1) {
|
|
if (three_gen() == 1) {
|
|
buf << " {";
|
|
generate_amount(buf, value_t(), true);
|
|
buf << '}';
|
|
}
|
|
if (six_gen() == 1) {
|
|
buf << " [";
|
|
generate_date(buf);
|
|
buf << ']';
|
|
}
|
|
if (six_gen() == 1) {
|
|
buf << " (";
|
|
generate_string(buf, six_gen());
|
|
buf << ')';
|
|
}
|
|
}
|
|
|
|
if (! not_this_amount.is_null() &&
|
|
value_t(buf.str()).as_amount().commodity() ==
|
|
not_this_amount.as_amount().commodity())
|
|
return "";
|
|
|
|
out << buf.str();
|
|
|
|
return buf.str();
|
|
}
|
|
|
|
bool generate_posts_iterator::generate_post(std::ostream& out, bool no_amount)
|
|
{
|
|
out << " ";
|
|
bool must_balance = generate_account(out, no_amount);
|
|
out << " ";
|
|
|
|
if (! no_amount) {
|
|
value_t amount(generate_amount(out));
|
|
if (truth_gen())
|
|
generate_cost(out, amount);
|
|
}
|
|
if (truth_gen())
|
|
generate_note(out);
|
|
out << '\n';
|
|
|
|
return must_balance;
|
|
}
|
|
|
|
void generate_posts_iterator::generate_cost(std::ostream& out, value_t amount)
|
|
{
|
|
std::ostringstream buf;
|
|
|
|
if (truth_gen())
|
|
buf << " @ ";
|
|
else
|
|
buf << " @@ ";
|
|
|
|
if (! generate_amount(buf, amount, true,
|
|
amount.as_amount().commodity().symbol()).empty())
|
|
out << buf.str();
|
|
}
|
|
|
|
void generate_posts_iterator::generate_date(std::ostream& out)
|
|
{
|
|
out.width(4);
|
|
out.fill('0');
|
|
out << year_gen();
|
|
|
|
out.width(1);
|
|
out << '/';
|
|
|
|
out.width(2);
|
|
out.fill('0');
|
|
out << mon_gen();
|
|
|
|
out.width(1);
|
|
out << '/';
|
|
|
|
out.width(2);
|
|
out.fill('0');
|
|
out << day_gen();
|
|
}
|
|
|
|
void generate_posts_iterator::generate_state(std::ostream& out)
|
|
{
|
|
switch (three_gen()) {
|
|
case 1:
|
|
out << "* ";
|
|
break;
|
|
case 2:
|
|
out << "! ";
|
|
break;
|
|
case 3:
|
|
out << "";
|
|
break;
|
|
}
|
|
}
|
|
|
|
void generate_posts_iterator::generate_code(std::ostream& out)
|
|
{
|
|
out << '(';
|
|
generate_string(out, six_gen());
|
|
out << ") ";
|
|
}
|
|
|
|
void generate_posts_iterator::generate_payee(std::ostream& out)
|
|
{
|
|
generate_string(out, strlen_gen());
|
|
}
|
|
|
|
void generate_posts_iterator::generate_note(std::ostream& out)
|
|
{
|
|
out << "\n ; ";
|
|
generate_string(out, strlen_gen());
|
|
}
|
|
|
|
void generate_posts_iterator::generate_xact(std::ostream& out)
|
|
{
|
|
out << format_date(next_date, FMT_WRITTEN);
|
|
next_date += gregorian::days(six_gen());
|
|
if (truth_gen()) {
|
|
out << '=';
|
|
out << format_date(next_aux_date, FMT_WRITTEN);
|
|
next_aux_date += gregorian::days(six_gen());
|
|
}
|
|
out << ' ';
|
|
|
|
generate_state(out);
|
|
generate_code(out);
|
|
generate_payee(out);
|
|
if (truth_gen())
|
|
generate_note(out);
|
|
out << '\n';
|
|
|
|
int count = three_gen() * 2;
|
|
bool has_must_balance = false;
|
|
for (int i = 0; i < count; i++) {
|
|
if (generate_post(out))
|
|
has_must_balance = true;
|
|
}
|
|
if (has_must_balance)
|
|
generate_post(out, true);
|
|
|
|
out << '\n';
|
|
}
|
|
|
|
void generate_posts_iterator::increment()
|
|
{
|
|
post_t * post = *posts++;
|
|
|
|
if (post == NULL && quantity > 0) {
|
|
std::ostringstream buf;
|
|
generate_xact(buf);
|
|
|
|
DEBUG("generate.post", "The post we intend to parse:\n" << buf.str());
|
|
|
|
try {
|
|
shared_ptr<std::istringstream> in(new std::istringstream(buf.str()));
|
|
|
|
parse_context_stack_t parsing_context;
|
|
parsing_context.push(in);
|
|
parsing_context.get_current().journal = session.journal.get();
|
|
parsing_context.get_current().scope = &session;
|
|
|
|
if (session.journal->read(parsing_context) != 0) {
|
|
VERIFY(session.journal->xacts.back()->valid());
|
|
posts.reset(*session.journal->xacts.back());
|
|
post = *posts++;
|
|
}
|
|
}
|
|
catch (std::exception&) {
|
|
add_error_context(_f("While parsing generated transaction (seed %1%):")
|
|
% seed);
|
|
add_error_context(buf.str());
|
|
throw;
|
|
}
|
|
catch (int) {
|
|
add_error_context(_f("While parsing generated transaction (seed %1%):")
|
|
% seed);
|
|
add_error_context(buf.str());
|
|
throw;
|
|
}
|
|
|
|
quantity--;
|
|
}
|
|
|
|
m_node = post;
|
|
}
|
|
|
|
} // namespace ledger
|