This weekend I'll be adding some posts on what's happened to the design and implementation over the past months.
Saturday, May 16, 2009
Busy as a....ooh shiny!
So yeah. I've been neglecting this little corner of the interweb. On the other hand, I can show you a reason why (other than WoW being utterly addictive).

This weekend I'll be adding some posts on what's happened to the design and implementation over the past months.
This weekend I'll be adding some posts on what's happened to the design and implementation over the past months.
Monday, February 16, 2009
Rupiz: Core Concepts
At the core of Rupiz are one interface and two classes:
Amount
Note that anything that implements Amount must be comparable to any other implementation of Amount. This lets us mix implementations without worrying about their details.
The implementation I'm using for Rupiz will be backed by an instance of BigDecimal with a scale of 2 decimal digits.
Transaction
The major design decision with the Transaction class was whether to make it immutable. For now, I'm leaning towards immutability mainly because it's a value object and it will be put into ordered sets, at which point modifying the object is a Very Bad Thing.
Account
Account is pretty straightforward. It keeps track of the transactions that involve the account and provides methods to move amounts in and out of the acount.
With these classes complete, I can begin working on the actual application.
The source for the classes above can be downloaded here.
Amount
public interface Amount extends Comparable<Amount>{
public Amount add(Amount value);
public Amount subtract(Amount value);
@Override
public int compareTo(Amount amount);
}
Note that anything that implements Amount must be comparable to any other implementation of Amount. This lets us mix implementations without worrying about their details.
The implementation I'm using for Rupiz will be backed by an instance of BigDecimal with a scale of 2 decimal digits.
Transaction
public class Transaction implements Comparable<Transaction>{
public final Amount amount;
public final Account source;
public final Account destination;
public final String description;
public final LocalDate dateStamp;
public Transaction(Amount amt, Account src, Account dest) {
this("", amt, src, dest, new LocalDate());
}
public Transaction(Amount amt, Account src,Account dest, LocalDate date){
this("", amt, src, dest, date);
}
public Transaction(String desc, Amount amt, Account src, Account dest) {
this(desc, amt, src, dest, new LocalDate());
}
public Transaction(String desc, Amount amt, Account src, Account dest, LocalDate date) {
description = desc;
amount = amt;
source = src;
destination = dest;
dateStamp = date;
}
@Override
public int compareTo(Transaction trans) {
int value = dateStamp.compareTo(trans.dateStamp);
if(value != 0)
return value;
value = source.getDescription().compareTo(trans.source.getDescription());
if(value != 0)
return value;
value = destination.getDescription().compareTo(trans.destination.getDescription());
if(value != 0)
return value;
value = amount.compareTo(trans.amount);
if(value != 0)
return value;
return description.compareTo(trans.description);
}
}
The major design decision with the Transaction class was whether to make it immutable. For now, I'm leaning towards immutability mainly because it's a value object and it will be put into ordered sets, at which point modifying the object is a Very Bad Thing.
Account
public class Account {
private String description;
private Amount amount;
private Set<Transaction> transactions;
// Global source account for deposits from unspecified sources
static Account globalSource = new Account("Opening Balances",
new DecimalAmount(new BigDecimal("0.00"))) {...};
// Constructors snipped
// Setters & Getters snipped
public Amount depositFor(Amount amount, String description) {
return depositForOn(amount, description, new LocalDate());
}
public Amount depositForOn(Amount amount, String description, LocalDate date) {
return globalSource.transferToForOn(amount, this, description, date);
}
public Amount transferToFor(Amount amount, Account destination, String description) {
return transferToForOn(amount, destination, description, new LocalDate());
}
public Amount transferToForOn(Amount amount, Account destination,
String description, LocalDate date) {
Transaction trans = new Transaction(description, amount,
this, destination, date);
destination.amount = destination.amount.add(amount);
amount = amount.subtract(amount);
transactions.add(trans);
destination.transactions.add(trans);
return amount;
}Account is pretty straightforward. It keeps track of the transactions that involve the account and provides methods to move amounts in and out of the acount.
With these classes complete, I can begin working on the actual application.
The source for the classes above can be downloaded here.
Labels:
rupiz
Sunday, January 25, 2009
Rupiz: A basic design
What do we need for the back end of a personal finance application?
So for our model, we will have an Account class that holds a BigDecimal for the current balance. This Account will have methods deposit(Amount) and withdraw(Amount) that both return an implementation of Amount representing the updated account balance. To track our account balance changes, we will have a Transaction interface with implementations BasicTransaction and CompoundTransaction, where CompoundTransaction will allow us to group a number of transactions together. We will maintain a List<Transaction> for each account we're dealing with.
For our GUI, we will need a way to show our list of Transactions for a given account. A JTable will do nicely until we add support for compound transactions. Our JTable will need columns for Date, Description, Transaction Type, Source/Destination, Amount, and whether the transaction has been reconciled.
- Some way to represent Accounts. An Account has a description, current balance, and some methods to modify the current balance.
- We want to encapsulate modifications to accounts in Transactions, which would have a description, source account, destination account, and amount. In addition, we will want a version of Transaction that holds a number of subtransactions.
- We want users of our Account class to not care how the account is implemented (in particular how the current balance is represented). To make this possible, we will define an Amount interface to hide these details. To start, we will use an implementation that uses BigDecimal to hold the amount info. If we want to change this later, we can do so easily and without a lot of search-and-replacing.
So for our model, we will have an Account class that holds a BigDecimal for the current balance. This Account will have methods deposit(Amount) and withdraw(Amount) that both return an implementation of Amount representing the updated account balance. To track our account balance changes, we will have a Transaction interface with implementations BasicTransaction and CompoundTransaction, where CompoundTransaction will allow us to group a number of transactions together. We will maintain a List<Transaction> for each account we're dealing with.
For our GUI, we will need a way to show our list of Transactions for a given account. A JTable will do nicely until we add support for compound transactions. Our JTable will need columns for Date, Description, Transaction Type, Source/Destination, Amount, and whether the transaction has been reconciled.
Labels:
rupiz
A new year, a new project
Happy new year! I figured this year I'd resolve to finish a programming project that's been kicking around the back of my head for a while now and has had an abortive start or two.
For the past year, I've been using GnuCash as my way of keeping track of my spending. While it's a very well written program, it has one minor irritant. I split my purchases into categories to better track where my money goes, but that makes reconciliation tricky at the end of the month. While I could preface the description for each transaction with the store, I'd rather be able to group transactions and be able to treat the group as a whole as a single entity during reconciliation.
So, with no pomp and somewhat random circumstance, I will be using this blog as a way to guilt me into working on a personal finance app I'm going to call Rupiz (Roo-peez).
For the past year, I've been using GnuCash as my way of keeping track of my spending. While it's a very well written program, it has one minor irritant. I split my purchases into categories to better track where my money goes, but that makes reconciliation tricky at the end of the month. While I could preface the description for each transaction with the store, I'd rather be able to group transactions and be able to treat the group as a whole as a single entity during reconciliation.
So, with no pomp and somewhat random circumstance, I will be using this blog as a way to guilt me into working on a personal finance app I'm going to call Rupiz (Roo-peez).
Labels:
rupiz
Wednesday, August 13, 2008
GridBag, Dude...
If you've ever done Swing programming, you've likely had this experience.
GridBagLayout is one of the more complicated layout managers, but as with most complicated things, it gives you a lot of power and flexibility.
GridBag gets its power from a set of GridBagConstraints provided with each Component you add to the layout. The GridBagConstraint attributes we are interested in are:
Now, keeping those constraint attributes in mind, let's see if that dialog could be made to work. For a starting point, we'll use the code as entered in the animation (with a few assumptions)
As you saw in the animation, this mostly works and results in the following layout.

The only major issue is the text fields resizing to zero width when the window gets too small. The buttons could also use some spacing.
Fixing the button spacing is as simple as adding some horizontal glue between the buttons to take up space.
To fix the text fields, we have three options.
Next time, I'll take a look at GroupLayout and FormLayout, two layout managers designed to make setting up this type of layout easier.
GridBagLayout is one of the more complicated layout managers, but as with most complicated things, it gives you a lot of power and flexibility.
GridBag gets its power from a set of GridBagConstraints provided with each Component you add to the layout. The GridBagConstraint attributes we are interested in are:
- gridx & gridy : These attributes set the x and y coordinates in the grid that you want the component to appear in.
- gridwidth & gridheight : These attributes set the number of grid cells the component should take up.
- anchor : The side or corner you want the Component to attach to.
Now, keeping those constraint attributes in mind, let's see if that dialog could be made to work. For a starting point, we'll use the code as entered in the animation (with a few assumptions)
public class TotallyGridBag {
public static void main(final String[] args) {
SwingUtilities.invokeLater(new Runnable() {
public void run() { buildAndShowGui(); }
});
}
static void buildAndShowGui() {
JFrame frame = new JFrame("Totally GridBag");
frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
frame.setLayout(new GridBagLayout());
JComboBox colors = new JComboBox(new String[]{"Red", "Blue", "Green", "Yellow"});
// Constraints for left column
GridBagConstraints left = new GridBagConstraints();
left.anchor = NORTHEAST;
left.gridx = 0;
// Constraints for right column
GridBagConstraints right = new GridBagConstraints();
right.anchor = NORTHWEST;
right.gridx = 1;
frame.add(new JLabel("Name"), left);
frame.add(new JTextField(10), right);
frame.add(new JLabel("Age"), left);
frame.add(new JTextField(3), right);
frame.add(new JLabel("Favorite Color"), left);
frame.add(colors, right);
JPanel buttons = new JPanel();
// Constraints for button panel
GridBagConstraints gbc2 = new GridBagConstraints();
gbc2.anchor = SOUTHEAST;
gbc2.gridx = 0;
gbc2.gridwidth = REMAINDER;
buttons.add(new JButton("OK"));
buttons.add(new JButton("Cancel"));
frame.add(buttons, gbc2);
frame.pack();
frame.setLocation(200, 200);
frame.setVisible(true);
}
}
As you saw in the animation, this mostly works and results in the following layout.
The only major issue is the text fields resizing to zero width when the window gets too small. The buttons could also use some spacing.
Fixing the button spacing is as simple as adding some horizontal glue between the buttons to take up space.
To fix the text fields, we have three options.
- Set the fill attribute on the right constraint. This will tell the layout manager to set the widths of the text fields so they fill the column. Doing this prevents the fields from dropping to zero width, but will resize the name field to the same width as age when the window gets too narrow
- Set the minimum size of the text fields after the frame has been packed. This prevents the layout manager from resizing the name field smaller than the minimum columns we specified, but results in the text field spilling off the frame.
- Set the minimum size of the frame after it has been packed. This is probably the best choice if we want to keep the entire layout as a GridBag. It keeps the preferred sizes we set on the text fields and prevents the frame from being shrunk below the minimum size needed to lay out the components.
...
frame.pack();
frame.setMinimumSize(frame.getSize());
frame.setLocation(200, 200);
frame.setVisible(true);
...
Next time, I'll take a look at GroupLayout and FormLayout, two layout managers designed to make setting up this type of layout easier.
Tuesday, August 12, 2008
Format Testing
Testing SyntaxHighlighter Prettify. Actual content will be forthcoming.
code:
pre:
import java.util.List;
public class CommonUtils {
@SuppressWarnings("unsafe")
public static <T> List<T> coerce(List list){
return (List<T>) list;
}
}pre:
import java.util.List;
public class CommonUtils {
// Coerces a List to a List
@SuppressWarnings("unsafe")
public static <T> List<T> coerce(List list){
return (List<T>) list;
}
}
Subscribe to:
Posts (Atom)