// QIFTransactionExtracter
package org.javamoney.examples.ez.money.importexport;
import static org.javamoney.examples.ez.money.ApplicationProperties.UI_CURRENCY_SYMBOL;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.ACCOUNT_BALANCE;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.ACCOUNT_HEADER;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.ACCOUNT_NAME;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.ACCOUNT_TYPE;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.ACCOUNT_TYPE_CASH;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.ACCOUNT_TYPE_CREDIT;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.AMOUNT;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.AMOUNT_IN_SPLIT;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.CATEGORY;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.CATEGORY_IN_SPLIT;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.CHECK_NUMBER;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.CLEARED;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.CLEARED_STATUS;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.DATE;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.END_OF_ENTRY;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.NEW_TYPE;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.NOTES;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.PAYEE;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.TRANSACTION_TYPE_CASH;
import static org.javamoney.examples.ez.money.importexport.QIFConstants.TRANSACTION_TYPE_CREDIT;
import static org.javamoney.examples.ez.money.model.dynamic.transaction.Split.AMOUNT_SEPARATOR;
import static org.javamoney.examples.ez.money.model.dynamic.transaction.Split.ITEM_SEPARATOR;
import static org.javamoney.examples.ez.money.model.dynamic.transaction.TransactionTypeKeys.EXPENSE;
import static org.javamoney.examples.ez.money.model.dynamic.transaction.TransactionTypeKeys.INCOME;
import static org.javamoney.examples.ez.money.model.dynamic.transaction.TransactionTypeKeys.TRANSFER;
import static org.javamoney.examples.ez.money.utility.TransactionHelper.isExpense;
import static org.javamoney.examples.ez.money.utility.TransactionHelper.isSplit;
import java.io.BufferedReader;
import java.util.Date;
import org.javamoney.examples.ez.money.model.persisted.account.AccountTypeKeys;
import org.javamoney.examples.ez.money.model.persisted.transaction.Transaction;
import org.javamoney.moneta.Money;
/**
* This class facilitates extracting transactions from a QIF file.
*/
final class QIFTransactionExtracter extends TransactionExtracter {
/**
* Constructs a new transaction extractor.
*/
protected QIFTransactionExtracter() {
setAccountBalance(Money.of(Double.valueOf(0),
UI_CURRENCY_SYMBOL.getCurrency()));
setAccountKey(null);
setAccountUID(null);
}
/**
* This method returns the account's balance.
*
* @return The account's balance.
*/
protected Money getAccountBalance() {
return itsAccountBalance;
}
/**
* This method returns the account's unique identifier for importing the
* transactions into, or null if one was not specified in the file.
*
* @return The account's key or null if one was not specified in the file.
*/
protected AccountTypeKeys getAccountKey() {
return itsAccountKey;
}
/**
* This method returns the account's unique identifier for importing the
* transactions into, or null if one was not specified in the file.
*
* @return The account's unique identifier or null if one was not specified
* in the file.
*/
protected String getAccountUID() {
return itsAccountUID;
}
/**
* This method returns true if an account for importing the transactions
* into has been specified from within the file, otherwise false.
*
* @return true or false.
*/
protected boolean hasAccountForImport() {
return getAccountUID() != null && getAccountKey() != null;
}
/**
* This method extracts and returns the next transaction from the stream. If
* there are no more transactions in the stream, then null is returned.
*
* @param stream
* The input stream that has the transactions.
*
* @return The next transaction or null if there are no more transactions in
* the stream.
*
* @throws Exception
* If an error occurs.
*/
@Override
protected Transaction next(BufferedReader stream) throws Exception {
Transaction trans = null;
String category = "";
Date date = new Date();
String line = "";
String notes = "";
String number = "";
String payee = "";
double amount = 0.0;
boolean reconciled = false;
char key = 0;
setType(null);
while ((line = stream.readLine()) != null) {
// Skip over any blank lines.
if (line.length() == 0) {
continue;
}
key = Character.toUpperCase(line.charAt(0));
// Remove the key from the line.
line = line.substring(1);
if (key == END_OF_ENTRY) {
break;
} else if (key == NEW_TYPE) {
if (line.equalsIgnoreCase(ACCOUNT_HEADER) == true) {
extractAccountInfo(stream);
break;
}
} else if (key == AMOUNT_IN_SPLIT) {
category += extractAmountForSplit(line);
} else if (key == AMOUNT) {
amount = extractAmount(line);
// Don't change the type if it is already a transfer.
if (getType() == null) {
if (isExpense(amount) == true) {
setType(EXPENSE);
} else {
setType(INCOME);
}
}
} else if (key == CATEGORY) {
// Can be either a category or an account.
if (isAccount(line) == true) {
payee = extractAccount(line);
setType(TRANSFER);
} else {
category = extractCategory(line);
}
} else if (key == CATEGORY_IN_SPLIT) {
// If the category isn't already formatted to be a split, then
// reset it.
if (isSplit(category) == false) {
category = "";
}
category += ITEM_SEPARATOR + extractCategory(line)
+ AMOUNT_SEPARATOR;
} else if (key == CHECK_NUMBER) {
number = extractCheckNumber(line);
} else if (key == DATE) {
date = extractDate(line);
} else if (key == NOTES) {
notes = extractNotes(line);
} else if (key == PAYEE) {
payee = extractPayee(line);
} else if (key == CLEARED_STATUS) {
reconciled = line.equals(CLEARED);
} else {
// Ignored.
}
}
if (line != null) {
// Ensure a split ends in a money value.
if (category.endsWith(AMOUNT_SEPARATOR) == true) {
category += getCurrency().format(0, false);
}
// Create the transaction.
trans = new Transaction(number, date, payee, Money.of(amount,
UI_CURRENCY_SYMBOL.getCurrency()), category, notes);
trans.setIsReconciled(reconciled);
}
return trans;
}
// ////////////////////////////////////////////////////////////////////////////
// Start of private methods.
// ////////////////////////////////////////////////////////////////////////////
private void extractAccountInfo(BufferedReader stream) throws Exception {
String line = "";
char key;
while ((line = stream.readLine()) != null) {
// Skip over any blank lines.
if (line.length() == 0) {
continue;
}
key = Character.toUpperCase(line.charAt(0));
// Remove the key from the line.
line = line.substring(1);
if (key == END_OF_ENTRY) {
break;
} else if (key == ACCOUNT_BALANCE) {
setAccountBalance(Money.of(extractAmount(line),
UI_CURRENCY_SYMBOL.getCurrency()));
} else if (key == ACCOUNT_NAME) {
setAccountUID(extractPayee(line));
} else if (key == ACCOUNT_TYPE) {
setAccountKey(extractAccountKey(line));
} else {
// Ignored.
}
}
}
private static AccountTypeKeys extractAccountKey(String line) {
AccountTypeKeys type = null;
if (line.equals(ACCOUNT_TYPE_CASH) == true
|| line.equals(TRANSACTION_TYPE_CASH) == true) {
type = AccountTypeKeys.CASH;
} else if (line.equals(ACCOUNT_TYPE_CREDIT) == true
|| line.equals(TRANSACTION_TYPE_CREDIT) == true) {
type = AccountTypeKeys.CREDIT;
} else {
type = AccountTypeKeys.DEPOSIT;
}
return type;
}
private void setAccountBalance(Money balance) {
itsAccountBalance = balance;
}
private void setAccountKey(AccountTypeKeys key) {
itsAccountKey = key;
}
private void setAccountUID(String uid) {
itsAccountUID = uid;
}
// ////////////////////////////////////////////////////////////////////////////
// Start of class members.
// ////////////////////////////////////////////////////////////////////////////
private Money itsAccountBalance;
private String itsAccountUID;
private AccountTypeKeys itsAccountKey;
}