MorkaLork Development

Interesting stuff I've picked up over the years...

Using the RecordStore class

2009-07-18 21:36:09 | 423 views | java recordstore database project midlet class

The difference between a database and the Records



In mobile devices you don't always have databases with a file system, instead you have the RMS (Record Management System) which saves data in records.
Without fields and a proper language to your help, working with the RMS is very basic, and a bit tougher then your standard database system to handle.

What is a record?


A record is an individual data item placed in the database. It can consist of a string, an array or whatever that you can turn in to bytes.
Records are stored in bytes, so whatever can be encoded to bytes (and decoded) can be stored.
As stated earlier, there are no fields, just bytes. But every record also has a unique identifier which increments for every inserted record.

An example:

IdBytes
10101
2101101011001
30001



What is a record store


A record store is a collection of records. A record can not exist independently but must belong a collection of records, a record store.
The record store assigns the unique id which is unique to that particular store.

[invDivClosed]
More information about the Record Management System:

Sun on RMS
IBM on RMS
[/invDivClosed]


Our example project


In this article we will be creating a very simple phone book. A user will be able to add and display contacts. To keep it short, editing will be left out.

The application will contain four files:

RecordStoreMIDlet.java: The MIDlet
ContactForm.java: The contact form where you add your contacts
DBManager.java: Our record store manager
DisplayContacts.java: A canvas where we display our contacts


ContactForm.Java




package org.morkalork.recordStore;

import javax.microedition.lcdui.*;
/**
* Displays a Form where you can enter a contacts
* name and phone number.
* @author maffelu
*/
public class ContactForm extends Form{
//==================FIELDS===================
private TextField txtName;
private TextField txtPhone;
private Command cmdAdd;
private Command cmdExit;
private Command cmdReset;
private Command cmdShow;


//==================PROPERTIES===============
/**
* Returns an add command
* @return A Command object
*/
public Command getCmdAdd(){
if(cmdAdd == null){
cmdAdd = new Command("Add", Command.SCREEN, 0);
}
return cmdAdd;
}

/**
* Returns an exit command
* @return A Command object
*/
public Command getCmdExit(){
if(cmdExit == null){
cmdExit = new Command("Exit", Command.EXIT, 0);
}
return cmdExit;
}

/**
* Returns a reset command
* @return A Command object
*/
public Command getCmdReset(){
if(cmdReset == null){
cmdReset = new Command("Reset form", Command.OK, 1);
}
return cmdReset;
}

/**
* Returns a show command
* @return A Command object
*/
public Command getCmdShow(){
if(cmdShow == null){
cmdShow = new Command("Show contacts", Command.OK, 1);
}
return cmdShow;
}

/**
* Returns a TextField object
* @return a TextField object
*/
public TextField getTxtName(){
if(txtName == null){
txtName = new TextField("Name: ", "", 45, TextField.ANY);
}
return txtName;
}

/**
* Returns a TextField object
* @return a TextField object
*/
public TextField getTxtPhone(){
if(txtPhone == null){
txtPhone = new TextField("Phone: ", "", 25, TextField.ANY);
}
return txtPhone;
}

//=====================CONSTRUCTOR=========================
/**
* Creates the form
* @param title The Form object title
* @param parent A parent to set as command listener
*/
public ContactForm(String title, RecordStoreMIDlet parent){
super(title);

this.append(getTxtName());
this.append(getTxtPhone());
this.addCommand(getCmdAdd());
this.addCommand(getCmdReset());
this.addCommand(getCmdShow());
this.addCommand(getCmdExit());
this.setCommandListener(parent);
}

//======================METHODS============================
/**
* Reset the form controls
*/
public void reset(){
this.txtName.setString("");
this.txtPhone.setString("");
}
}


There is nothing much to say about this class, it's a standard form that displays two TextFields and a menu that lets the user add a contact, reset the form or show all contacts.
It has a reset method that clears the form.


DBManager.java




package org.morkalork.recordStore;

import javax.microedition.rms.*;
import javax.microedition.midlet.*;
/**
* A class to handle the record store
* @author maffelu
*/
public class DBManager {
//======================FIELDS=====================
RecordStore db = null;
String fileName;

//======================CONSTRUCTOR================
/**
* Constructor
* @param fileName The name of the database
*/
public DBManager(String fileName){
this.fileName = fileName;
}

//======================METHODS====================
/**
* Safely open the database
*/
public void openDB(){
try{
db = RecordStore.openRecordStore(fileName, true);
} catch (RecordStoreException rse){
System.err.println(rse.getMessage());
}
}

/**
* Safely close the database
*/
public void closeDB(){
try{
//If recordstore is empty, remove it
if(db.getNumRecords() == 0){
String fName = db.getName();
db.closeRecordStore();
db.deleteRecordStore(fName);
} else {
db.closeRecordStore();
}
} catch (RecordStoreException rse){
System.err.println(rse.getMessage());
}
}

/**
* Insert data into the database
* @param record The data to input
* @return int The new record id
*/
public synchronized int insert(String record){
try{
//Open database connection
this.openDB();
//Convert the string to bytes
byte[] data = record.getBytes();
//Insert the byte array and return the new record id
return db.addRecord(data, 0, data.length);
} catch (RecordStoreException rse){
System.err.println(rse.getMessage());
} finally {
//Close database connection
this.closeDB();
}
//If failed, return -1
return -1;
}

/**
* Reads the database into an array and returns it
* @return A string array containing the records from the database
*/
public String[] readDB(){
//Initialize an empty array
String[] data = null;
try{
//Open a connection to the database
this.openDB();
//Create an array
data = new String[db.getNumRecords()];

//Loop through the records and add them to the array
for(int i = 1; i <= db.getNumRecords(); i++){
byte[] record = db.getRecord(i);
data[i - 1] = new String(record);
}
} catch (Exception e){
System.err.println(e.getMessage());
} finally {
//Close database connection
this.closeDB();
}
//If something went wrong, return the nulled string array
return data;
}

/**
* Returns the number of records in the database
* @return An int representing the number of records in the database
*/
public int getNumberOfRecords(){
try{
//Open database connection
this.openDB();
//Return the number of records
return db.getNumRecords();
} catch (RecordStoreNotOpenException rsnoe){
System.err.println(rsnoe.getMessage());
} finally {
//Close database connection
this.closeDB();
}
//If something went wrong, return 0
return 0;
}
}


Allright, this is the class that handles the record store. This class could have alot more properties and methods and could get alot more complex, but this is just an example, so it's very very basic.
The class has two fields, 'db' and 'fileName'. 'db' is a RecordStore object which means it is used to handle the record store communications. 'fileName' is the name of the record store to communicate with.
The name of the record store to use is set in the constructor (an improvement would obviosly be to set this as a property).
We have two methods to handle the connection, openDB and closeDB.

The most interesting methods are insert and readDB.

insert takes a string parameter which is the string that you want inserted into the database.
What the method does is that it opens a connection to the database, converts the string into a byte array (since bytes are the only values allowed into the record set) and it enters the byte array into the record store.

readDB reads the database into a String array (converting it from bytes) and returns that array. Notice that the records start at indexer 1, not 0.


DisplayContacts.java




package org.morkalork.recordStore;

import java.io.IOException;
import javax.microedition.lcdui.*;

/**
* A screen to display the contacts in the database
* @author maffelu
*/
public class DisplayContacts extends Canvas{
//===============FIELDS=========================
private Image image;
private Command cmdBack;
private DBManager dbm;
private String[] contacts;

//===============PROPERTIES=====================
/**
* Returns a back command
* @return A command object
*/
public Command getCmdBack(){
if(cmdBack == null){
cmdBack = new Command("Back", Command.EXIT, 0);
}
return cmdBack;
}
/**
* Returns a database managing object
* @return A DBManager object
*/
public DBManager getDBManager(){
if(dbm == null){
dbm = new DBManager("MorkaDB");
}
return dbm;
}

//===================CONSTRUCTOR=======================
/**
* constructor, creates an image object
*/
public DisplayContacts(RecordStoreMIDlet parent, String[] contacts) {
this.contacts = contacts;

//Get image
try{
image = Image.createImage("/org/morkalork/recordStore/images/contactsBackground.jpg");
} catch (IOException ioe){
System.err.println(ioe.getMessage());
}

cmdBack = getCmdBack();
this.addCommand(cmdBack);
this.setCommandListener(parent);
}


//==================METHODS===========================
/**
* paint
*/
public void paint(Graphics g) {
//Set background image
g.setGrayScale(255);
g.fillRect(0, 0, g.getClipWidth() -1, g.getClipHeight() -1);
g.drawImage(image, 0, 0, Graphics.TOP | Graphics.LEFT);

//Output the contacts
g.setGrayScale(0);

//Only attempt to output contacts if the contacts array is really an array
if(contacts.getClass().isArray()){

int delimiter; //We used a comma ',' as delimiter in the database
String name; //Will hold the contact name
String phone; //Will hold the contact phone number
int top = 60; //The distance to draw from the top (60 covering the title)
int left = 5; //The distance to draw from the left, wont' change

for(int i = 0; i < contacts.length; i++){
//Get contact data
delimiter = contacts[ i].indexOf(",");
name = contacts[ i].substring(0, delimiter);
phone = contacts[ i].substring(delimiter + 1, contacts[ i].length());

//Output it
g.drawString("Name: " + name, left, top, Graphics.TOP | Graphics.LEFT);
top = top + 12; //Add a linebreak
g.drawString("Phone: " + phone, left, top, Graphics.TOP | Graphics.LEFT);
top = top + 24; //Add a linebreak (12) and an extra break (12)
//between this and the next record.
}
} else {
System.err.println("Contacts is not an array...");
}
}
}


Interesting here is the paint method. In the constructor the contacts to display were set (as an array), and the paint method loops through that array and draws the content on the canvas. Only problem is that we've saved the data in one array with a comma ',' as a delimiter. So what we do first is that we separate the name and the phone number.
The drawing is quite simple, we have a integer called top that keeps track of how far from the top we move. We start 60 pixels from the top since our background image has a title ('contacts') and we want the output to start beneath it.
After that, we jump down 12 pixels to the next row. After the phone number we jump down 24 pixels (two rows) in order to get a one row empty space between each contact.

The background image we use is a small thing I did in GIMP:
The contacts background...



RecordStoreMIDlet.java




package org.morkalork.recordStore;

import javax.microedition.midlet.*;
import javax.microedition.lcdui.*;

/**
* A phone book
* @author maffelu
*/
public class RecordStoreMIDlet extends MIDlet implements CommandListener{
//====================FIELDS=============================
private ContactForm contactForm;
private DisplayContacts displayContacts;
private DBManager dbm;


//===================PROPERTIES==========================
/**
* Returns a ContactForm object
* @return a ContactForm object
*/
public ContactForm getContactForm(){
if(contactForm == null){
contactForm = new ContactForm("Contact form", this);
}
return contactForm;
}

/**
* Returns a database manager object
* @return a DBManager object
*/
public DBManager getDBManager(){
if(dbm == null){
dbm = new DBManager("MorkaDB");
}
return dbm;
}
//===================CONSTRUCTOR=========================
/**
* Constructor
*/
public RecordStoreMIDlet(){
contactForm = getContactForm();
dbm = getDBManager();
}


//===================METHODS=============================
/**
* Returns a Display object
* @return a Display object
*/
public Display getDisplay(){
return Display.getDisplay(this);
}

/**
* Switches the current screen to a new screen
* @param alert An Alert screen, or NULL
* @param displayable The new display object
*/
public void switchDisplay(Alert alert, Displayable displayable){
Display display = getDisplay();

if(alert == null){
display.setCurrent(displayable);
} else {
display.setCurrent(alert, displayable);
}
}

public void commandAction(Command command, Displayable displayable){
if(displayable == contactForm){
if(command == contactForm.getCmdAdd()){
String record = contactForm.getTxtName().getString();
record += ",";
record += contactForm.getTxtPhone().getString();
dbm.insert(record);
contactForm.reset();
} else if(command == contactForm.getCmdShow()){
String[] data = dbm.readDB();
switchDisplay(null, new DisplayContacts(this, data));
} else if(command == contactForm.getCmdReset()){
contactForm.reset();
} else if(command == contactForm.getCmdExit()){
exitMIDlet();
}
} else if(displayable == displayContacts){
if(command == displayContacts.getCmdBack()){
switchDisplay(null, contactForm);
}
}
}

public void startMIDlet(){
switchDisplay(null, getContactForm());
}

public void exitMIDlet(){
destroyApp(true);
notifyDestroyed();
}

public void startApp() {
startMIDlet();
}

public void pauseApp() {
}

public void destroyApp(boolean unconditional) {
}
}


As we can see in the commandAction method, when we add a record we add it like this:
"name" + "," + "phone number" which gives us something like "Bob Smith,555-12345".
This was noted in the DisplayContacts.java file where we split that string on the comma delimiter.

An improvement here would be not to use a comma as delimiter as someone could by misstake use that character. Perhaps a '*' or '#' would be better.



Screenshots



Adding a contact
Adding a person to the record store


Displaying the contacts
Contacts retrieved from the record store



Download the project:



Article comments

Feel free to comment this article using a facebook profile.

I'm using facebook accounts for identification since even akismet couldn't handle all the spam I receive every day.