Ch08 code

From SCMAD Book

Jump to: navigation, search

Contents

Expense data access interface (Listing 8.1)

package com.scmadkit.ch08;
 
import javax.microedition.rms.*;
 
/**
 * This interface provides the API that must be implemented by the
 * DataAccessObject that would store/retrieve/delete records in the underlying
 * persistence system.
 * 
 * @author Sathya.Srinivasan
 * @version 1.0
 */
public interface IExpenseDAO {
	// ~ Methods --------------------------------------------------
 
	/**
	 * Adds a record into the record store.
	 * 
	 * @param expense The expense to be added into the record store.
	 * 
	 * @return The expense object containing newly-created ID in the record
	 *         store
	 * 
	 * 
	 * @throws RecordStoreException If the record cannot be added for unexpected
	 *         reasons.
	 * @throws RecordStoreNotOpenException If the record store cannot be
	 *         created.
	 * @throws RecordStoreFullException If the record store does not have enough
	 *         space.
	 */
	public Expense add(Expense expense) throws RecordStoreException,
			RecordStoreNotOpenException, RecordStoreFullException;
 
	/**
	 * Gets all the records in the record store.
	 * 
	 * @return All the stored expenses as an array.
	 * 
	 * @throws RecordStoreNotOpenException If the record store cannot be opened.
	 * @throws RecordStoreException If the array cannot be created for any other
	 *         reason.
	 */
	public Expense[] allRecords() throws RecordStoreNotOpenException,
			RecordStoreException;
 
	/**
	 * Deletes the passed record.
	 * 
	 * @param expense The expense to be deleted.
	 * 
	 * @throws RecordStoreException If the record cannot be deleted for
	 *         unexpected reasons.
	 * @throws RecordStoreNotOpenException If the record store cannot be opened.
	 * @throws InvalidRecordIDException If the expense passed contains an
	 *         invalid id.
	 */
	public void delete(Expense expense) throws RecordStoreException,
			RecordStoreNotOpenException, InvalidRecordIDException;
 
	/**
	 * Gets the most expensive expenses.
	 * 
	 * @param recordsToReturn The number of expenses to return.
	 * 
	 * @return An array of most expensive expenses. If the number of records in
	 *         the record store is less than the records requested, all records
	 *         in the record store sorted by the expense amount in descending
	 *         order will be returned.
	 * 
	 * @throws RecordStoreException If the records cannot be retrieved for
	 *         unexpected reasons.
	 * @throws RecordStoreNotOpenException If the record store cannot be opened.
	 */
	public Expense[] mostExpensiveExpenses(int recordsToReturn)
			throws RecordStoreException, RecordStoreNotOpenException;
 
	/**
	 * Gets the records based on a certain record type.
	 * 
	 * @param type The type of expense to be used as a filter.
	 * 
	 * @return An array of expenses that match the passed criteria.
	 * 
	 * @throws RecordStoreException If the records cannot be retrieved for
	 *         unexpected reasons.
	 * @throws RecordStoreNotOpenException If record store cannot be opened.
	 */
	public Expense[] recordsFor(ExpenseType type) throws RecordStoreException,
			RecordStoreNotOpenException;
}

Expense data structure (Listing 8.2)

package com.scmadkit.ch08;
 
/**
 * This class is a data structure that represents an Expense.
 * 
 * @author Sathya.Srinivasan
 * @version 1.0
 */
public class Expense {
	// ~ Instance fields ----------------------------------------------
 
	private ExpenseType type;
	private double amount;
	private int id;
	private long date;
	private String description;
 
	// ~ Constructors -------------------------------------------------
 
	/**
	 * Creates a new Expense object.
	 * 
	 * @param type Type of expense.
	 * @param amount The amount incurred in this expense.
	 * @param date The date when the expense happened.
	 */
	public Expense(ExpenseType type, double amount, long date) {
		this.setType(type);
		this.setAmount(amount);
		this.setDate(date);
		this.setDescription(description);
	}
 
	// ~ Methods ------------------------------------------------------
 
	/**
	 * Sets the amount for this expense.
	 * 
	 * @param amount The amount incurred in this expense.
	 */
	public void setAmount(double amount) {
		this.amount = amount;
	}
 
	/**
	 * Gets the amount for this expense.
	 * 
	 * @return Amount incurred in this expense.
	 */
	public double getAmount() {
		return amount;
	}
 
	/**
	 * Sets the date when this expense happened.
	 * 
	 * @param date The date when this expense happened.
	 */
	public void setDate(long date) {
		this.date = date;
	}
 
	/**
	 * Gets the date when this expense happened.
	 * 
	 * @return Date when this expense happened.
	 */
	public long getDate() {
		return date;
	}
 
	/**
	 * Sets the ID of this expense.
	 * 
	 * @param id ID of this expense.
	 */
	public void setId(int id) {
		this.id = id;
	}
 
	/**
	 * Gets the id for this expense.
	 * 
	 * @return ID for this expense.
	 */
	public int getId() {
		return id;
	}
 
	/**
	 * Sets the type of this expense.
	 * 
	 * @param type Type of this expense.
	 */
	public void setType(ExpenseType type) {
		this.type = type;
	}
 
	/**
	 * Gets the type of this expense.
	 * 
	 * @return Type of this expense.
	 */
	public ExpenseType getType() {
		return type;
	}
 
	/**
	 * Sets the description for this expense.
	 * 
	 * @param description Description for this expense.
	 */
	public void setDescription(String description) {
		this.description = description;
	}
 
	/**
	 * Gets the description for this expense.
	 * 
	 * @return Description for this expense.
	 */
	public String getDescription() {
		return this.description;
	}
 
	/**
	 * A readable version of this Expense.
	 * 
	 * @return Readable version of this Expense.
	 */
	public String toString() {
		StringBuffer buffer = new StringBuffer();
		buffer.append(this.getDate());
		buffer.append(',');
		buffer.append(this.getType().toString());
		buffer.append(',');
		buffer.append(this.getDescription());
		buffer.append(',');
		buffer.append(this.getAmount());
		return buffer.toString();
	}
}

Expense Type enumeration (Listing 8.3)

package com.scmadkit.ch08;
 
/**
 * This class represents an enumeration of the expense types used in this
 * application.
 * 
 * @author Sathya.Srinivasan
 * @version 1.0
 */
public class ExpenseType {
	// ~ Static fields/initializers ----------------------------------
 
	/** Indicates a breakfast expense. */
	public static final ExpenseType BREAKFAST = new ExpenseType(1, "Breakfast");
 
	/** Indicates a lunch expense. */
	public static final ExpenseType LUNCH = new ExpenseType(2, "Lunch");
 
	/** Indicates a dinner expense. */
	public static final ExpenseType DINNER = new ExpenseType(3, "Dinner");
 
	/** Indicates a gas expense. */
	public static final ExpenseType GAS = new ExpenseType(4, "Gas");
 
	/** Indicates an entertainment expense. */
	public static final ExpenseType ENTERTAINMENT = new ExpenseType(5,
			"Entertainment");
 
	/** Indicates a miscellaneous expense. */
	public static final ExpenseType MISCELLANEOUS = new ExpenseType(6,
			"Miscellaneous");
 
	// ~ Instance fields ---------------------------------------------
 
	private String description;
	private int id;
 
	// ~ Constructors -------------------------------------------------
 
	/**
	 * Creates a new ExpenseType object.
	 * 
	 * @param id ID for this instance.
	 * @param description Description for this instance.
	 */
	public ExpenseType(int id, String description) {
		this.setId(id);
		this.setDescription(description);
	}
 
	// ~ Methods ------------------------------------------------------
 
	/**
	 * Sets the description for this instance.
	 * 
	 * @param description Description for this instance.
	 */
	public void setDescription(String description) {
		this.description = description;
	}
 
	/**
	 * Gets the id of this instance.
	 * 
	 * @return ID of this instance.
	 */
	public int getId() {
		return id;
	}
 
	/**
	 * Gets an instance of the ExpenseType class based on the passed id. This is
	 * used to reconstruct the object when getting it from the database.
	 * 
	 * @param id The id of the type.
	 * 
	 * @return The instance representing the id.
	 * 
	 * @throws IllegalArgumentException If the passed id is invalid.
	 */
	public static ExpenseType getInstance(int id) {
		switch (id) {
		case 1:
			return BREAKFAST;
		case 2:
			return LUNCH;
		case 3:
			return DINNER;
		case 4:
			return GAS;
		case 5:
			return ENTERTAINMENT;
		case 6:
			return MISCELLANEOUS;
		default:
			throw new IllegalArgumentException("Invalid ID " + id);
		}
	}
 
	/**
	 * Provides a readable version of this instance.
	 * 
	 * @return A human-readable version of this instance.
	 */
	public String toString() {
		return description;
	}
 
	/**
	 * Sets the id for this instance.
	 * 
	 * @param id ID for this instance.
	 */
	private void setId(int id) {
		this.id = id;
	}
}

Expense DAO (Listing 8.4)

package com.scmadkit.ch08;
 
import java.io.IOException;
import java.util.Vector;
 
import javax.microedition.rms.InvalidRecordIDException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;
import javax.microedition.rms.RecordStoreFullException;
import javax.microedition.rms.RecordStoreNotFoundException;
import javax.microedition.rms.RecordStoreNotOpenException;
 
public class ExpenseDAO implements IExpenseDAO {
	private static final String STORE_NAME = "expenseTracker";
	private ExpenseTypeFilter filter = new ExpenseTypeFilter();
	private MostExpensiveComparator comparator = new MostExpensiveComparator();
 
	public Expense add(Expense expense) throws RecordStoreException,
			RecordStoreNotOpenException, RecordStoreFullException {
		if (expense == null) {
			throw new IllegalArgumentException("Expense cannot be null");
		}
 
		RecordStore store = RecordStore.openRecordStore(STORE_NAME, true);
		try {
			byte bytes[] = ExpenseUtility.convertToBytes(expense);
			int id = store.addRecord(bytes, 0, bytes.length);
			expense.setId(id);
		} catch (IOException e) {
			throw new RecordStoreException("Could not process record."
					+ " REASON: " + e.getMessage());
		}
		store.closeRecordStore();
		return expense;
	}
 
	public void delete(Expense expense) throws RecordStoreException,
			RecordStoreNotFoundException, InvalidRecordIDException {
		if (expense == null) {
			throw new IllegalArgumentException("Expense cannot be null");
		}
		int id = expense.getId();
		RecordStore store = RecordStore.openRecordStore(STORE_NAME, false);
		store.deleteRecord(id);
		store.closeRecordStore();
	}
 
	public Expense[] allRecords() throws RecordStoreNotOpenException,
			RecordStoreException {
		RecordStore store = RecordStore.openRecordStore(STORE_NAME, false);
		RecordEnumeration enumeration = store.enumerateRecords(null, null,
				false);
		int size = store.getNumRecords();
		Expense expenses[] = new Expense[size];
		int count = 0;
		try {
			while (enumeration.hasNextElement()) {
				int id = enumeration.nextRecordId();
				byte record[] = store.getRecord(id);
				expenses[count++] = ExpenseUtility.convertToObject(record);
			}
		} catch (IOException e) {
			throw new RecordStoreException("Could not read records."
					+ " REASON: " + e.getMessage());
		}
 
		enumeration.destroy();
		store.closeRecordStore();
		return expenses;
	}
 
	/**
	 * Gets records based on a certain record type.
	 * 
	 * @param type The type of expense to be used as a filter.
	 * 
	 * @return An array of expenses that match the passed criteria.
	 * 
	 * @throws RecordStoreException If the records cannot be retrieved for
	 *         unexpected reasons.
	 * @throws RecordStoreNotOpenException If the record store cannot be opened.
	 */
	public Expense[] recordsFor(ExpenseType type) throws RecordStoreException,
			RecordStoreNotOpenException {
		if (type == null) {
			throw new IllegalArgumentException("Expense cannot be null");
		}
 
		filter.setExpenseType(type);
		RecordStore store = RecordStore.openRecordStore(STORE_NAME, false);
		RecordEnumeration enumeration = store.enumerateRecords(filter, null,
				false);
		Vector matchedRecords = new Vector();
 
		try {
			while (enumeration.hasNextElement()) {
				int id = enumeration.nextRecordId();
				byte record[] = store.getRecord(id);
				matchedRecords.add(ExpenseUtility.convertToObject(record));
			}
		} catch (IOException e) {
			throw new RecordStoreException("Could not read records."
					+ " REASON: " + e.getMessage());
		}
 
		enumeration.destroy();
		store.closeRecordStore();
		Expense[] expenses = new Expense[matchedRecords.size()];
		for (int i = 0; i < expenses.length; i++) {
			expenses[i] = (Expense) matchedRecords.elementAt(i);
		}
		return expenses;
	}
 
	/**
	 * Gets the most expensive expenses.
	 * 
	 * @param recordsToReturn The number of expenses to return.
	 * 
	 * @return An array of most expensive expenses. If the number of records in
	 *         the record store is less than the records requested, all records
	 *         in the record store sorted by the expense amount in descending
	 *         order will be returned.
	 * 
	 * @throws RecordStoreException If the records cannot be retrieved for
	 *         unexpected reasons.
	 * @throws RecordStoreNotOpenException If the record store cannot be opened.
	 */
	public Expense[] mostExpensiveExpenses(int recordsToReturn)
			throws RecordStoreException, RecordStoreNotOpenException {
		if (recordsToReturn <= 0) {
			throw new IllegalArgumentException(
					"Records to return must be greater than 0.");
		}
 
		RecordStore store = RecordStore.openRecordStore(STORE_NAME, false);
		int totalRecords = store.getNumRecords();
		Expense expenses[] = new Expense[(recordsToReturn < totalRecords) ? recordsToReturn
				: totalRecords];
		RecordEnumeration enumeration = store.enumerateRecords(null,
				comparator, false);
		int count = 0;
		try {
			while (enumeration.hasNextElement()) {
				int id = enumeration.nextRecordId();
				byte record[] = store.getRecord(id);
				expenses[count++] = ExpenseUtility.convertToObject(record);
				if (count == expenses.length)
					break;
			}
		} catch (IOException e) {
			throw new RecordStoreException("Could not read records. "
					+ " REASON: " + e.getMessage());
		}
		enumeration.destroy();
		store.closeRecordStore();
		return expenses;
	}
}

Expense Utility class (Listing 8.5)

package com.scmadkit.ch08;
 
import java.io.*;
 
/**
 * This is an utility class that can perform some generic functions on an
 * Expense object.
 * 
 * @author Sathya.Srinivasan
 * @version 1.0
 */
public final class ExpenseUtility {
	/**
	 * Converts an Expense object to a byte array.
	 * 
	 * @param expense The expense object to be converted.
	 * 
	 * @return An array of bytes representing the passed object.
	 * 
	 * @throws IOException If the object cannot be converted.
	 */
	public static final byte[] convertToBytes(Expense expense)
			throws IOException {
		ByteArrayOutputStream bout = new ByteArrayOutputStream();
		DataOutputStream out = new DataOutputStream(bout);
		out.writeInt(expense.getType().getId());
		out.writeLong(expense.getDate());
		out.writeUTF(expense.getDescription());
		out.writeDouble(expense.getAmount());
		return bout.toByteArray();
	}
 
	/**
	 * Converts a byte array into an Expense object.
	 * 
	 * @param record The byte array to be used.
	 * 
	 * @return An Expense object constructed from the passed byte array.
	 * 
	 * @throws IOException If the object cannot be created from the byte array.
	 */
	public static final Expense convertToObject(byte record[])
			throws IOException {
		ByteArrayInputStream bin = new ByteArrayInputStream(record);
		DataInputStream in = new DataInputStream(bin);
		ExpenseType type = ExpenseType.getInstance(in.readInt());
		long date = in.readLong();
		String description = in.readUTF();
		double amount = in.readDouble();
 
		Expense expense = new Expense(type, amount, date);
		expense.setDescription(description);
		return expense;
	}
 
}

ExpenseType filter (Listing 8.9)

package com.scmadkit.ch08;
 
import java.io.IOException;
import javax.microedition.rms.RecordFilter;
 
/**
 * Filters a record based on a given expense type.
 * 
 * @author Sathya.Srinivasan
 * @version 1.0
 */
public class ExpenseTypeFilter implements RecordFilter {
	// ~ Instance fields ----------------------------------------------
 
	private ExpenseType expenseType;
 
	// ~ Methods ------------------------------------------------------
 
	/**
	 * Sets the expense type to be used in this filter.
	 * 
	 * @param type The {@link ExpenseType} to be used in this filter.
	 */
	public void setExpenseType(ExpenseType type) {
		this.expenseType = type;
	}
 
	/**
	 * Checks if the passed byte array matches the criteria.
	 * 
	 * @param record The record to be checked if it meets the criteria.
	 * 
	 * @return true if the record matches the criteria. false otherwise.
	 */
	public boolean matches(byte record[]) {
		try {
			Expense expense = ExpenseUtility.convertToObject(record);
			return (expense.getType() == expenseType);
		} catch (IOException e) {
			return false;
		}
	}
}

MostExpensive comparator (Listing 8.10)

package com.scmadkit.ch08;
 
import java.io.IOException;
import javax.microedition.rms.RecordComparator;
 
/**
 * Compares records and provides the order according to the amount in the
 * Expense in descending order.
 * 
 * @author Sathya.Srinivasan
 * @version 1.0
 */
public class MostExpensiveComparator implements RecordComparator {
	// ~ Methods ------------------------------------------------------
 
	/**
	 * Compares two expense records.
	 * 
	 * @param record1 The first record to be compared.
	 * @param record2 The second record to be compared.
	 * 
	 * @return PRECEDES if the first record is more expensive than the second
	 *         record. FOLLOWS if the first records is less expensive than the
	 *         second record. EQUIVALENT if the expenses are the same.
	 */
	public int compare(byte record1[], byte record2[]) {
		Expense expense1 = null;
		Expense expense2 = null;
		try {
			expense1 = ExpenseUtility.convertToObject(record1);
		} catch (IOException e) {
			return FOLLOWS;
		}
 
		try {
			expense2 = ExpenseUtility.convertToObject(record2);
		} catch (IOException e) {
			return PRECEDES;
		}
 
		double amount1 = expense1.getAmount();
		double amount2 = expense2.getAmount();
 
		if (amount1 > amount2) {
			return PRECEDES;
		} else if (amount1 < amount2) {
			return FOLLOWS;
		} else {
			return EQUIVALENT;
		}
	}
}
Personal tools