All Downloads are FREE. Search and download functionalities are using the official Maven repository.

net.yapbam.data.GlobalData Maven / Gradle / Ivy

There is a newer version: 1.9.1
Show newest version
package net.yapbam.data;

import java.math.BigInteger;
import java.net.URI;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Currency;
import java.util.Date;
import java.util.List;
import java.util.Locale;

import org.slf4j.LoggerFactory;

import net.yapbam.data.event.*;
import net.yapbam.date.helpers.DateStepper;
import net.yapbam.util.NullUtils;

/** The whole Yapbam data.
 *  
You can also have a look at FilteredData which presents a filtered view of Yapbam data. * @see FilteredData */ public class GlobalData extends DefaultListenable { private static final char DEFAULT_CATEGORY_SEPARATOR = '.'; private List accounts; private List categories; private List periodicals; private List transactions; private boolean archive; private boolean locked; private URI uri; private String password; private char subCategorySeparator; private boolean somethingChanged; private boolean eventsPending; private static Currency defaultCurrency; private static double defaultPrecision; static { setDefaultCurrency(Currency.getInstance(Locale.getDefault())); } private static final Comparator COMPARATOR = new Comparator() { @Override public int compare(Transaction o1, Transaction o2) { return Long.signum(o1.getId()-o2.getId()); } }; private static final Comparator PERIODICAL_COMPARATOR = new Comparator() { @Override public int compare(PeriodicalTransaction o1, PeriodicalTransaction o2) { int result = o1.getDescription().compareToIgnoreCase(o2.getDescription()); if (result==0) { result = Long.signum(o1.getId()-o2.getId()); } return result; } }; /** A comparator dedicated to amount comparisons. *
As amount are represented by doubles, and doubles are unable to represent exactly decimal numbers, * we have to take care when we compare two amounts, especially, if we intend to know if two amounts are equals. *
This comparator returns that the doubles are equals if their difference is less than the current currency precision. * @see #setDefaultCurrency(Currency) */ public static final Comparator AMOUNT_COMPARATOR = new Comparator() { @Override public int compare(Double o1, Double o2) { // o1.equals(o2) is here because if the doubles are positive or negative infinity, their difference is not defined if (o1.equals(o2) || (Math.abs(o1-o2)Builds a new empty instance. */ public GlobalData() { super(); this.clear(); } /** Sets the isArchive attribute of this data. *
This attribute indicates that this globalData contains transactions from archived statements. * @param isArchive true to lock the data, false to unlock. */ public void setArchive(boolean isArchive) { if (isArchive!=this.archive) { this.archive = isArchive; this.fireEvent(new IsArchivedChangedEvent(this)); this.setChanged(); } } /** Gets the isArchive attribute of this data. * @return true if the data is an archive. */ public boolean isArchive() { return this.archive; } /** Sets the locked attribute of this data. *
Currently, this attribute is ignored, but in future releases this could change. * @param locked true to lock the data, false to unlock. * @see IsLockedChangedEvent */ public void setLocked(boolean locked) { if (locked!=this.locked) { this.locked = locked; fireEvent(new IsLockedChangedEvent(this)); this.setChanged(); } } /** Gets the locked attribute of this data. * @return true if the data is locked. */ public boolean isLocked() { return this.locked; } /** Sets the currency to be used in Yapbam. * As amounts are represented by doubles, and doubles are unable to represent exactly decimal numbers, * "amount is the same" is related to the currency precision. * @param currency The currency to be used. * @see #AMOUNT_COMPARATOR */ public static void setDefaultCurrency(Currency currency) { defaultCurrency = currency; defaultPrecision = Math.pow(10, -currency.getDefaultFractionDigits())/2; } /** Gets the default currency. * @return a currency. */ public static Currency getDefaultCurrency() { return defaultCurrency; } /** Tests if the data is empty (no accounts, no transactions, no category, etc... , really nothing !) * @return true if the data is empty */ public boolean isEmpty() { return this.accounts.isEmpty() && this.getCategoriesNumber()==1 && this.getSubCategorySeparator()==DEFAULT_CATEGORY_SEPARATOR; } /** Tests if the data needs to be saved. * @return true if the data needs to be saved, false, if there's nothing to change (no changes since last save). */ public boolean somethingHasChanged() { return this.somethingChanged; } /** Gets the URI where the data is saved. * @return an URI or null the data isn't attach to any location. */ public URI getURI() { return uri; } /** Gets the password that protects the data. * @return a string or null if data is not password protected. */ public String getPassword() { return this.password; } /** Sets the URI attached to the data. * @param uri The new URI (null to clear current URI). * @see URIChangedEvent */ public void setURI(URI uri) { URI old = this.uri; this.uri = uri; if (!NullUtils.areEquals(this.uri,old)) { fireEvent(new URIChangedEvent(this)); } } /** Sets the password used to protect the data (to encrypt the file containing it). * @param password a string (null or an empty string if the data is not protected). * @see PasswordChangedEvent */ public void setPassword(String password) { if ((password!=null) && (password.length()==0)) { password = null; } if (!NullUtils.areEquals(this.password, password)) { String old = this.password; this.password = password; fireEvent(new PasswordChangedEvent(this, old, this.password)); this.setChanged(); } } /** Enables/Disables the event throwing. *
When events are enabled, every modification on the data results in a fired event. *
If you want to perform a lot of modifications on the data, this will results in a large * amount of events, and a poor performance. Then, you can disable events, do the modifications, * then enable events. *
When events are turned on again, a {@link EverythingChangedEvent} is sent if some modification * occurred since events has been disabled. * @param enabled true to enable events, false to disable events. */ @Override public void setEventsEnabled(boolean enabled) { if (isEventsEnabled()) { eventsPending = false; } super.setEventsEnabled(enabled); if (enabled && (eventsPending)) { fireEvent(new EverythingChangedEvent(this)); } } /** Tests whether the events are enabled or not. * @return true if the events are enabled. */ public boolean isEventsEnabled() { return super.isEventsEnabled(); } @Override protected void fireEvent(DataEvent event) { if (isEventsEnabled()) { super.fireEvent(event); } else { eventsPending = true; } } /** Gets an account by its name. * @param name The account's name * @return an Account or null if no account with this name exists */ public Account getAccount(String name) { for (Account account : this.accounts) { if (account.getName().equalsIgnoreCase(name)) { return account; } } return null; } /** Gets an account by its index. * @param index The account's index * @return an Account * @throws IndexOutOfBoundsException if the index is negative or >= {@link #getTransactionsNumber()} */ public Account getAccount(int index) { return this.accounts.get(index); } /** Gets the number of accounts. * @return a positive or null int */ public int getAccountsNumber() { return this.accounts.size(); } /** Gets the index of an account. * @param account The account to search for * @return 0 or more if the account is found, a negative integer if the account is unknown */ public int indexOf(Account account) { return this.accounts.indexOf(account); } /** Adds an account. * @param account The account to add * @see AccountAddedEvent */ public void add(Account account) { if (getAccount(account.getName())!=null) { throw new IllegalArgumentException("Duplicate account name : "+account); //$NON-NLS-1$ } this.accounts.add(account); fireEvent(new AccountAddedEvent(this, account)); this.setChanged(); } /** Gets the number of transactions. * @return the number of transactions in this. */ public int getTransactionsNumber() { return this.transactions.size(); } /** Gets a transaction by its index. * @param index The index of the transaction. * @return a transaction. * @throws IndexOutOfBoundsException if the index is negative or >= {@link #getTransactionsNumber()} */ public Transaction getTransaction(int index) { return this.transactions.get(index); } /** Adds some transactions. * @param transactions The transactions to add * @throws IllegalArgumentException if one of the transactions already exists or its account is unknown. * @see TransactionsAddedEvent */ public void add(Transaction[] transactions) { LoggerFactory.getLogger(getClass()).trace("start adding transactions to global data"); if (transactions.length==0) { return; } // In order to optimize the number of events fired, we will group transactions by account before // adding them to their accounts (so, we will generate a maximum of one event per account). // Initialize the lists of transactions per account. List> accountTransactions = new ArrayList>(this.getAccountsNumber()); for (int i = 0; i < this.getAccountsNumber(); i++) { accountTransactions.add(new ArrayList()); } for (Transaction transaction : transactions) { int accountIndex = indexOf(transaction.getAccount()); if (accountIndex<0) { throw new IllegalArgumentException("Unknown account: "+transaction.getAccount().getName()); } accountTransactions.get(accountIndex).add(transaction); } for (int i=0;i= 0; j--) { this.transactions.remove(indexOf(transaction)); } throw new IllegalArgumentException("Duplicated transaction"); } this.transactions.add(index, transaction); } LoggerFactory.getLogger(getClass()).trace("start adding transactions to accounts"); for (Collection collection : accountTransactions) { // For each account (there's one collection per account) if (!collection.isEmpty()) { // If this account has some transactions added Transaction[] addedAccountTransactions = collection.toArray(new Transaction[collection.size()]); addedAccountTransactions[0].getAccount().add(addedAccountTransactions); } } fireEvent(new TransactionsAddedEvent(this, transactions)); this.setChanged(); LoggerFactory.getLogger(getClass()).trace("Start looking for checkbooks updates"); for (Transaction transaction : transactions) { // Let's examine if this new transaction is a check and has a number behind next check available // If so, detach the checks between current "next check" and this one (included). if (transaction.getMode().isUseCheckBook() && (transaction.getAmount()<=0)) { // If transaction use checkbook // Detach check String number = transaction.getNumber(); if (number!=null) { Account account = transaction.getAccount(); for (int j = 0; j < account.getCheckbooksNumber(); j++) { Checkbook checkbook = account.getCheckbook(j); BigInteger shortNumber = checkbook.getNumber(number); if (!checkbook.isEmpty() && (shortNumber!=null)) { if (shortNumber.compareTo(checkbook.getNext())>=0) { Checkbook newOne = new Checkbook(checkbook.getPrefix(), checkbook.getFirst(), checkbook.size(), shortNumber.equals(checkbook.getLast())?null:shortNumber.add(BigInteger.ONE)); setCheckbook(account, checkbook, newOne); } break; } } } } } LoggerFactory.getLogger(getClass()).trace("End adding transactions"); } /** Adds a transaction. * @param transaction The transaction to add. * @see TransactionsAddedEvent * @throws IllegalArgumentException if transaction already exists or its account is unknown. */ public void add (Transaction transaction) { add(new Transaction[]{transaction}); } /** Removes some transactions from this. *
Note : that if one or more transactions are not in this, they are ignored. *
If one or more transactions are not ignored, a TransactionsRemovedEvent is fired. * @param transactions The transactions to be removed. * @see TransactionsRemovedEvent */ public void remove(Transaction[] transactions) { Collection removed = new ArrayList(transactions.length); // In order to optimize the number of events fired, we will group transactions by account before // removing them from their accounts (so, we will generate a maximum of one event per account). // Initialize the lists of transactions per account. List> accountTransactions = new ArrayList>(this.getAccountsNumber()); for (int i = 0; i < this.getAccountsNumber(); i++) { accountTransactions.add(new ArrayList()); } for (Transaction transaction: transactions) { int index = indexOf(transaction); if (index>=0) { Transaction removedTransaction = this.transactions.remove(index); removed.add(removedTransaction); accountTransactions.get(indexOf(transaction.getAccount())).add(removedTransaction); } } if (!removed.isEmpty()) { for (Collection collection : accountTransactions) { // For each account (there's one collection per account) if (!collection.isEmpty()) { // If this account has some transactions removed Transaction[] removedAccountTransactions = collection.toArray(new Transaction[collection.size()]); removedAccountTransactions[0].getAccount().remove(removedAccountTransactions); } } this.fireEvent(new TransactionsRemovedEvent(this, removed.toArray(new Transaction[removed.size()]))); setChanged(); } } /** Removes a transaction from this. * If the transaction is not in this, does nothing. * @param transaction The transaction to remove * @see TransactionsRemovedEvent */ public void remove(Transaction transaction) { remove(new Transaction[]{transaction}); } /** Gets the index of a transaction. * @param transaction The transaction to search for * @return 0 or more if the transaction is found, a negative integer if the transaction is unknown */ public int indexOf(Transaction transaction) { return Collections.binarySearch(this.transactions, transaction, COMPARATOR); } /** Gets the number of categories. * @return an integer. */ public int getCategoriesNumber() { return this.categories.size(); } /** Gets a category by its index. * @param index category index * @return the category (note : categories are always sorted by their name) * @throws IndexOutOfBoundsException if the index is negative or >= {@link #getTransactionsNumber()} */ public Category getCategory(int index) { return this.categories.get(index); } /** Gets a category by its name. * @param categoryName The category's name, or null which identifies the UNDEFINED category. * @return a category */ public Category getCategory(String categoryName) { if (categoryName==null) { return Category.UNDEFINED; } int index = Collections.binarySearch(categories, new Category(categoryName)); if (index<0) { return null; } else { return this.categories.get(index); } } /** Gets the index of a category. * @param category a category * @return a integer (<0 if the category is unknown) */ public int indexOf(Category category) { return Collections.binarySearch(categories, category); } /** Adds a category. * @param category The category to add. * @throws IllegalArgumentException if this already contains a category with the same name as the argument. * @see CategoryAddedEvent */ public void add(Category category) { if (category.getName()==null) { throw new IllegalArgumentException(); } int index = -Collections.binarySearch(categories, category)-1; this.categories.add(index, category); fireEvent(new CategoryAddedEvent(this, category)); setChanged(); } /** Gets the character used to separate the category from sub category in category names *
For instance in "Leisures/Sports", '/' means Sports is a subcategory of "Leisures". * @return a char */ public char getSubCategorySeparator() { return this.subCategorySeparator; } /** Sets the character used to separate the category from sub category in category names *
For instance in "Leisures/Sports", '/' means Sports is a subcategory of "Leisures". * @param separator The separator between subcategories. * @see SubCategorySeparatorChangedEvent */ public void setSubCategorySeparator(char separator) { if (separator!=this.subCategorySeparator) { char old = this.subCategorySeparator; this.subCategorySeparator = separator; fireEvent(new SubCategorySeparatorChangedEvent(this, old, separator)); setChanged(); } } /** Clears all data in this instance. * @see EverythingChangedEvent */ public void clear() { this.categories = new ArrayList(); this.categories.add(Category.UNDEFINED); this.subCategorySeparator = DEFAULT_CATEGORY_SEPARATOR; this.accounts = new ArrayList(); this.periodicals = new ArrayList(); this.transactions = new ArrayList(); this.uri = null; this.password = null; this.somethingChanged = false; fireEvent(new EverythingChangedEvent(this)); } /** Adds a periodical transaction. * @param periodical The periodical transaction to add * @see PeriodicalTransactionsAddedEvent */ public void add(PeriodicalTransaction periodical) { int index = -Collections.binarySearch(this.periodicals, periodical, PERIODICAL_COMPARATOR)-1; this.periodicals.add(index, periodical); fireEvent(new PeriodicalTransactionsAddedEvent(this, new PeriodicalTransaction[]{periodical})); setChanged(); } /** Adds periodical transactions. * @param transactions The transactions to add * @see PeriodicalTransactionsAddedEvent */ public void add(PeriodicalTransaction[] transactions) { if (transactions.length==0) { return; } for (PeriodicalTransaction transaction : transactions) { int index = -Collections.binarySearch(this.periodicals, transaction, PERIODICAL_COMPARATOR)-1; this.periodicals.add(index, transaction); } fireEvent(new PeriodicalTransactionsAddedEvent(this, transactions)); setChanged(); } /** Gets the number of periodicals transactions. * @return an positive or null integer. */ public int getPeriodicalTransactionsNumber() { return this.periodicals.size(); } /** Gets a periodical transaction by its index. * @param index The index of the periodical transaction. * @return a periodical transaction. * @throws IndexOutOfBoundsException if the index is negative or >= {@link #getTransactionsNumber()} */ public PeriodicalTransaction getPeriodicalTransaction(int index) { return this.periodicals.get(index); } /** Removes some periodical transactions. *
Note : that if one or more transactions are not in this, they are ignored. * @param periodicals The periodical transactions to remove * @see PeriodicalTransactionsRemovedEvent */ public void remove (PeriodicalTransaction[] periodicals) { // nb is the number of effectively removed transactions (the ones found in this). int nb = 0; int[] indexes = new int[periodicals.length]; for (PeriodicalTransaction transaction : periodicals) { indexes[nb] = Collections.binarySearch(this.periodicals, transaction, PERIODICAL_COMPARATOR); if (indexes[nb]>=0) { nb++; } } if (nb>0) { // If some were found PeriodicalTransaction[] removed = new PeriodicalTransaction[nb]; int[] removedIndexes = (nb==periodicals.length)?indexes:Arrays.copyOf(indexes, nb); Arrays.sort(removedIndexes); for (int i = removedIndexes.length-1; i >=0 ; i--) { removed[i] = this.periodicals.remove(removedIndexes[i]); } this.fireEvent(new PeriodicalTransactionsRemovedEvent(this, removedIndexes, removed)); setChanged(); } } /** Removes a periodical transaction. *
If the transaction is not in this, makes nothing. * @param periodical The periodical transaction to remove */ public void remove (PeriodicalTransaction periodical) { int index = Collections.binarySearch(this.periodicals, periodical, PERIODICAL_COMPARATOR); if (index>=0) { this.removePeriodicalTransaction(index); } } /** Removes a periodical transaction identified by its index. * @param index the periodical transaction index */ private void removePeriodicalTransaction(int index) { PeriodicalTransaction removed = this.periodicals.remove(index); this.fireEvent(new PeriodicalTransactionsRemovedEvent(this, new int[]{index}, new PeriodicalTransaction[]{removed})); setChanged(); } /** Increments some periodical transactions next date until it becomes greater than a date. *
If some periodical transactions have no next date, they are ignored. * @param transactions the periodical transactions to update * @param dates the limit dates each periodical transaction have to pass * @see PeriodicalTransactionsRemovedEvent * @see PeriodicalTransactionsAddedEvent */ public void setPeriodicalTransactionNextDate(PeriodicalTransaction[] transactions, Date[] dates) { Collection removed = new ArrayList(transactions.length); Collection updated = new ArrayList(transactions.length); for (int i = 0; i < dates.length; i++) { PeriodicalTransaction pt = transactions[i]; Date date = dates[i]; Date nextDate = pt.getNextDate(); if (nextDate!=null) { DateStepper ds = pt.getNextDateBuilder(); if (ds == null) { nextDate = date; } else { while ((nextDate!=null) && (nextDate.compareTo(date)<=0)) { nextDate = ds.getNextStep(nextDate); } } removed.add(pt); updated.add(new PeriodicalTransaction(pt.getDescription(), pt.getComment(), pt.getAmount(), pt.getAccount(), pt.getMode(), pt.getCategory(), Arrays.asList(pt.getSubTransactions()), nextDate, pt.isEnabled() && (nextDate!=null), ds)); } } this.remove(removed.toArray(new PeriodicalTransaction[removed.size()])); this.add(updated.toArray(new PeriodicalTransaction[updated.size()])); } private void setChanged() { setChanged(true); } public void setChanged(boolean changed) { if (changed!=this.somethingChanged) { this.somethingChanged = changed; this.fireEvent(new NeedToBeSavedChangedEvent(this)); } } /** Removes an account from the data. * If some transactions were attached to the account, all these transactions will be also removed. * @param account the account to be removed * @see AccountRemovedEvent */ public void remove(Account account) { int index = this.accounts.indexOf(account); if (index>=0){ if (account.getTransactionsNumber()!=0) { List removed = new ArrayList(account.getTransactionsNumber()); for (Transaction transaction : this.transactions) { if (transaction.getAccount()==account) { removed.add(transaction); } } this.remove(removed.toArray(new Transaction[removed.size()])); } List removed = new ArrayList(); for (PeriodicalTransaction transaction : this.periodicals) { if (transaction.getAccount()==account) { removed.add(transaction); } } this.remove(removed.toArray(new PeriodicalTransaction[removed.size()])); this.accounts.remove(index); this.fireEvent(new AccountRemovedEvent(this, index, account)); this.setChanged(); } } /** Changes the name of an account. * @param account the account to be changed * @param value the new account name * @throws IllegalArgumentException if the name is already used for another account. * @see AccountPropertyChangedEvent */ public void setName(Account account, String value) { String old = account.getName(); if (!old.equals(value)) { // Check that this account name is not already used Account accountByName = getAccount(value); if ((accountByName != null) && (accountByName!=account)) { throw new IllegalArgumentException("Account name already exists"); //$NON-NLS-1$ } account.setName(value); this.fireEvent(new AccountPropertyChangedEvent(this, AccountPropertyChangedEvent.NAME, account, old,value)); this.setChanged(); } } /** Changes the initial balance of an account. * @param account the account to be changed * @param value the new initial balance * @throws IllegalArgumentException if the name is already used for another account. * @see AccountPropertyChangedEvent */ public void setInitialBalance(Account account, double value) { double old = account.getInitialBalance(); if (old != value) { account.setInitialBalance(value); this.fireEvent(new AccountPropertyChangedEvent(this, AccountPropertyChangedEvent.INITIAL_BALANCE, account, old, value)); this.setChanged(); } } /** Changes the alert threshold for this account. * @param account the account to be changed * @param threshold the alert threshold to apply to this account * @see AccountPropertyChangedEvent */ public void setAlertThreshold (Account account, AlertThreshold threshold) { AlertThreshold old = account.getAlertThreshold(); if (!old.equals(threshold)) { account.setAlertThreshold(threshold); this.fireEvent(new AccountPropertyChangedEvent(this, AccountPropertyChangedEvent.ALERT_THRESHOLD, account, old, threshold)); this.setChanged(); } } /** Sets the comment attached to an account. * @param account the account * @param comment the comment * @see AccountPropertyChangedEvent */ public void setComment (Account account, String comment) { String old = account.getComment(); if (!NullUtils.areEquals(old, comment)) { account.setComment(comment); this.fireEvent(new AccountPropertyChangedEvent(this, AccountPropertyChangedEvent.COMMENT, account, old, comment)); this.setChanged(); } } class CategoryUpdater extends AbstractTransactionUpdater { private Category oldCategory; private Category newCategory; CategoryUpdater (Category oldMode, Category newMode) { super(GlobalData.this); this.oldCategory = oldMode; this.newCategory = newMode; } @Override protected Transaction change(Transaction t) { return t.change(oldCategory, newCategory); } @Override protected PeriodicalTransaction change(PeriodicalTransaction t) { return t.change(oldCategory, newCategory); } } /** Removes a category from the data. * All the transactions and the subtransactions attached to the deleted category are moved to the "undefined" category. * @param category The category to remove */ public void remove(Category category) { int index = this.categories.indexOf(category); if (index>=0){ new CategoryUpdater(category, Category.UNDEFINED).doIt(); this.categories.remove(index); this.fireEvent(new CategoryRemovedEvent(this, index, category)); this.setChanged(); } } /** Changes the name of a category. * @param category the category to update. * @param name The new name of the category. * @throws IllegalArgumentException if a category with the same name exists * @see CategoryPropertyChangedEvent */ public void setName(Category category, String name) { String old = category.getName(); if (!old.equals(name)) { // Check that this category name is not already used if (getCategory(name) != null) { throw new IllegalArgumentException("Category name already exists"); //$NON-NLS-1$ } // Category list is sorted by name => we have to change the category position this.categories.remove(indexOf(category)); category.setName(name); int index = -Collections.binarySearch(categories, category)-1; this.categories.add(index, category); this.fireEvent(new CategoryPropertyChangedEvent(this, CategoryPropertyChangedEvent.NAME, category, old,name)); this.setChanged(); } } public void add(Account account, Mode mode) { account.add(mode); this.fireEvent(new ModeAddedEvent(this, account, mode)); this.setChanged(); } class ModeUpdater extends AbstractTransactionUpdater { private Account account; private Mode oldMode; private Mode newMode; ModeUpdater (Account account, Mode oldMode, Mode newMode) { super(GlobalData.this); this.account = account; this.oldMode = oldMode; this.newMode = newMode; } @Override protected Transaction change(Transaction t) { return t.change(account, oldMode, newMode); } @Override protected PeriodicalTransaction change(PeriodicalTransaction t) { return t.change(account, oldMode, newMode); } } /** Removes a payment mode from an account. * @param account the account * @param mode the payment mode to remove from the account * @see ModeRemovedEvent */ public void remove(Account account, Mode mode) { int index = account.indexOf(mode); if (index>=0){ new ModeUpdater(account, mode, Mode.UNDEFINED).doIt(); account.remove(mode); this.fireEvent(new ModeRemovedEvent(this, index, account, mode)); this.setChanged(); } } /** Replaces a payment mode. * @param account the account of the payment mode * @param oldMode the mode to replace * @param newMode the new mode * @see ModePropertyChangedEvent */ public void setMode(Account account, Mode oldMode, Mode newMode) { if (oldMode.equals(Mode.UNDEFINED)) { throw new IllegalArgumentException("Undefined mode can't be modified"); } ModePropertyChangedEvent event = new ModePropertyChangedEvent(this, account, oldMode, newMode); if (event.getChanges()!=0) { // oldMode object will be updated. In order to send the right event data, we have to remember it // So, we'll store it in a new fresh mode object : oldVanished. Mode oldVanished = new Mode(oldMode.getName(), oldMode.getReceiptVdc(), oldMode.getExpenseVdc(), oldMode.isUseCheckBook()); account.replace(oldMode, newMode); event = new ModePropertyChangedEvent(this, account, oldVanished, oldMode); this.fireEvent(event); this.setChanged(); } } /** Adds a checkbook to an account. * @param account the account * @param book the checkbook to add to the account * @see CheckbookAddedEvent */ public void add(Account account, Checkbook book) { account.add(book); this.fireEvent(new CheckbookAddedEvent(this, account, book)); this.setChanged(); } /** Removes a checkbook from an account. * @param account the account * @param book the checkbook to remove from the account * @see CheckbookRemovedEvent */ public void remove(Account account, Checkbook book) { int index = account.indexOf(book); if (index>=0){ account.remove(book); this.fireEvent(new CheckbookRemovedEvent(this, index, account, book)); this.setChanged(); } } /** Updates a checkbook with the data of another checkbook. * @param account The account that contains the checkbook. * @param old The checkbook we want to update * @param checkbook The checkbook that contains new data * @see CheckbookPropertyChangedEvent */ public void setCheckbook(Account account, Checkbook old, Checkbook checkbook) { CheckbookPropertyChangedEvent event = new CheckbookPropertyChangedEvent(this, account, old, checkbook); if (event.getChanges()!=0) { // old object will be updated. In order to send the right event data, we have to remember it // So, we'll store it in a new fresh mode object : oldVanished. Checkbook oldVanished = new Checkbook(old.getPrefix(), old.getFirst(), old.size(), old.getNext()); old.copy (checkbook); event = new CheckbookPropertyChangedEvent(this, account, oldVanished, old); this.fireEvent(event); this.setChanged(); } } /** Copies source data into this. *
Warning: There are side effects between this and src. * somethingHasChanged returns true after call to this method. * @param src The data that will be copied into this * @see EverythingChangedEvent */ public void copy(GlobalData src) { archive = src.archive; locked = src.isLocked(); accounts = src.accounts; categories = src.categories; subCategorySeparator = src.subCategorySeparator; periodicals = src.periodicals; transactions = src.transactions; password = src.password; uri = src.uri; this.fireEvent(new EverythingChangedEvent(this)); this.setChanged(); } /** Tests whether there is periodical transactions with pending transactions at a date. * @param date The date to consider while looking for pending transactions * @return true if there is one or more pending transaction * @see PeriodicalTransaction#hasPendingTransactions(Date) */ public boolean hasPendingPeriodicalTransactions(Date date) { for (int i = 0; i < getPeriodicalTransactionsNumber(); i++) { if (getPeriodicalTransaction(i).hasPendingTransactions(date)) { return true; } } return false; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy