// OFXTransactionExtracter
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.model.dynamic.transaction.TransactionTypeKeys.EXPENSE;
import static org.javamoney.examples.ez.money.model.dynamic.transaction.TransactionTypeKeys.INCOME;
import static org.javamoney.examples.ez.money.utility.TransactionHelper.isExpense;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.PushbackReader;
import java.text.SimpleDateFormat;
import java.util.Date;
import org.javamoney.examples.ez.money.locale.CurrencyFormat;
import org.javamoney.examples.ez.money.locale.CurrencyFormatKeys;
import org.javamoney.examples.ez.money.model.persisted.transaction.Transaction;
import org.javamoney.moneta.Money;
/**
* This class facilitates extracting transactions from an OFX file.
*/
final class OFXTransactionExtracter extends TransactionExtracter {
/**
* This method returns the account balance specified in the file.
*
* @return The account balance specified in the file.
*/
protected Money getAccountBalance() {
return itsAccountBalance;
}
/**
* 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 {
PushbackReader reader = new PushbackReader(stream, 32);
Transaction trans = null;
String tag = null;
while ((tag = getNextTag(reader)) != null) {
if (tag.equals(TRANSACTION_START) == true) {
trans = getNextTransaction(reader);
break;
} else if (tag.equals(ACCOUNT_BALANCE) == true) {
String value = getNextValue(reader);
value = removeNonmoneyValues(value);
setAccountBalance(Money.of(getCurrencyFormat(value)
.parse(value), UI_CURRENCY_SYMBOL.getCurrency()));
}
}
return trans;
}
// ////////////////////////////////////////////////////////////////////////////
// Start of private methods.
// ////////////////////////////////////////////////////////////////////////////
private static CurrencyFormat getCurrencyFormat(String value) {
CurrencyFormatKeys formatKey = null;
// OFX specifies strict rules for the amounts. No need to specify the
// decimal format.
if (value.lastIndexOf('.') != -1) {
formatKey = CurrencyFormatKeys.US_DOLLAR;
} else {
formatKey = CurrencyFormatKeys.OTHER;
}
return formatKey.getFormat();
}
private static String getNextTag(PushbackReader stream) throws IOException {
String tag = null;
int ch = 0;
while ((ch = stream.read()) != -1) {
if (ch == START_TAG) {
tag = getTagName(stream);
break;
}
}
return tag;
}
private Transaction getNextTransaction(PushbackReader stream)
throws Exception {
Transaction trans = null;
String tag = null;
Date date = new Date();
String notes = "";
String number = "";
String payee = "";
double amount = 0.0;
while ((tag = getNextTag(stream)) != null) {
if (tag.equals(TRANSACTION_END) == true) {
break;
} else if (tag.equals(TRANSACTION_AMOUNT) == true) {
String value = getNextValue(stream);
value = removeNonmoneyValues(value);
amount = getCurrencyFormat(value).parse(value);
if (isExpense(amount) == true) {
setType(EXPENSE);
} else {
setType(INCOME);
}
} else if (tag.equals(TRANSACTION_DATE) == true) {
// OFX dates contain extra information that needs to be
// truncated.
date = DATE_FORMAT.parse(getNextValue(stream).substring(0, 8));
} else if (tag.equals(TRANSACTION_CHECK_NUMBER) == true) {
number = extractCheckNumber(getNextValue(stream));
} else if (tag.equals(TRANSACTION_NOTES) == true) {
notes = extractNotes(getNextValue(stream));
} else if (tag.equals(TRANSACTION_PAYEE) == true) {
payee = extractPayee(getNextValue(stream));
}
}
// Create the transaction and assume it is reconciled.
trans = new Transaction(number, date, payee, Money.of(amount,
UI_CURRENCY_SYMBOL.getCurrency()), "", notes);
trans.setIsReconciled(true);
return trans;
}
private static String getNextValue(PushbackReader stream)
throws IOException {
StringBuffer value = new StringBuffer();
int ch = 0;
while ((ch = stream.read()) != -1) {
if (ch == START_TAG) {
stream.unread(ch);
break;
}
value.append((char) ch);
}
return value.toString().trim();
}
private static String getTagName(PushbackReader stream) throws IOException {
StringBuffer tag = new StringBuffer();
int ch = 0;
while ((ch = stream.read()) != -1) {
if (ch == END_TAG) {
break;
}
tag.append((char) ch);
}
return tag.toString().trim();
}
private void setAccountBalance(Money value) {
itsAccountBalance = value;
}
// ////////////////////////////////////////////////////////////////////////////
// Start of class members.
// ////////////////////////////////////////////////////////////////////////////
private Money itsAccountBalance;
private static SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
"yyyyMMdd");
private static final int END_TAG = '>';
private static final int START_TAG = '<';
private static final String ACCOUNT_BALANCE = "BALAMT";
private static final String TRANSACTION_AMOUNT = "TRNAMT";
private static final String TRANSACTION_DATE = "DTPOSTED";
private static final String TRANSACTION_CHECK_NUMBER = "CHECKNUM";
private static final String TRANSACTION_END = "/STMTTRN";
private static final String TRANSACTION_PAYEE = "NAME";
private static final String TRANSACTION_NOTES = "MEMO";
private static final String TRANSACTION_START = "STMTTRN";
}