Persistent Ranking MIDlet : Database Persistence « J2ME « Java






Persistent Ranking MIDlet


/*
J2ME in a Nutshell
By Kim Topley
ISBN: 0-596-00253-X

*/


import java.io.IOException;
import java.io.InputStreamReader;
import java.io.InputStream;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.DataInputStream;
import java.io.DataOutputStream;

import javax.microedition.rms.RecordComparator;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordFilter;
import javax.microedition.rms.RecordListener;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;


import java.util.Vector;
import javax.microedition.lcdui.Alert;
import javax.microedition.lcdui.AlertType;
import javax.microedition.lcdui.Command;
import javax.microedition.lcdui.CommandListener;
import javax.microedition.lcdui.Display;
import javax.microedition.lcdui.Displayable;
import javax.microedition.lcdui.Form;
import javax.microedition.lcdui.List;
import javax.microedition.lcdui.Screen;
import javax.microedition.lcdui.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;
import javax.microedition.rms.RecordEnumeration;
import javax.microedition.rms.RecordListener;
import javax.microedition.rms.RecordStore;
import javax.microedition.rms.RecordStoreException;


public class PersistentRankingMIDlet extends MIDlet
                implements CommandListener, RecordListener, Runnable {

    private Command exitCommand;
    private Command okCommand;
    private Command cancelCommand;
    private Command newCommand;
    private Command checkCommand;
    private Command detailsCommand;
    private Command backCommand;
    private Command deleteCommand;
    private Display display;
    private TextField isbnField;
    private StringItem isbnDisplay;
    private StringItem titleDisplay;
    private StringItem rankingDisplay;
    private StringItem reviewDisplay;
    private StringItem checkTitle;
    private Form isbnForm;
    private Form searchForm;
    private Form resultForm;
    private Form checkForm;
    private List bookList;
    private Vector bookInfoList;
    private Thread searchThread;
    private BookStore bookStore;
    private BookInfo searchBookInfo;

    protected void startApp() throws MIDletStateChangeException {
        if (display == null) {
            initialize();

            // If there are any books in the
            // book store, display the list.
            // Otherwise, start with the ISBN form.
            display.setCurrent(getSelectionScreen());
        }
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean unconditional)
                        throws MIDletStateChangeException {
        // Close the book store
        if (bookStore != null) {
            try {
                bookStore.removeRecordListener(this);
                bookStore.close();
            } catch (RecordStoreException ex) {
            }
        }
    }

    public void commandAction(Command cmd, Displayable d) {
        if (cmd == exitCommand) {
            try {
                destroyApp(true);
            } catch (MIDletStateChangeException ex) {
            }
            notifyDestroyed();
        } else if (cmd == okCommand) {
            String isbn = isbnField.getString().trim();
            if (!isbn.equals("")) {
                isbnDisplay.setText(isbn);
                display.setCurrent(searchForm);
                searchForBook(new BookInfo(isbn));
            }
        } else if (cmd == cancelCommand) {
            searchThread = null;
            isbnField.setString(null);
            display.setCurrent(getSelectionScreen());
        } else if (cmd == newCommand) {
            isbnField.setString(null);
            display.setCurrent(isbnForm);
        } else if (cmd == detailsCommand || cmd == List.SELECT_COMMAND) {
            int index = bookList.getSelectedIndex();
            searchBookInfo = (BookInfo)bookInfoList.elementAt(index);
            isbnDisplay.setText(searchBookInfo.getIsbn());
            showBookInfo(searchBookInfo);
        } else if (cmd == deleteCommand) {
            int index = bookList.getSelectedIndex();
            BookInfo bookInfo = (BookInfo)bookInfoList.elementAt(index);
            try {
                bookStore.deleteBook(bookInfo);
            } catch (RecordStoreException ex) {
                System.out.println("Delete failed: " + ex);
            }
        } else if (cmd == checkCommand) {
            String isbn = searchBookInfo.getIsbn();
            checkTitle.setText(searchBookInfo.getTitle());
            display.setCurrent(checkForm);
            searchForBook(searchBookInfo);
        } else if (cmd == backCommand) {
            display.setCurrent(getSelectionScreen());
        }
    }

    public void searchForBook(BookInfo info) {
        searchBookInfo = info;
        searchThread = new Thread(this);
        searchThread.start();
    }

    public void recordAdded(RecordStore recordStore, int recordId) {
        // Update the book list
        populateBookList();
    }

    public void recordChanged(RecordStore recordStore, int recordId) {
        // Update the book list
        populateBookList();
    }

    public void recordDeleted(RecordStore recordStore, int recordId) {
        // Update the book list
        populateBookList();
    }

    public void run() {
        try {
            boolean found = Fetcher.fetch(searchBookInfo);
            if (searchThread == Thread.currentThread()) {
                if (found && searchBookInfo.getTitle() != null) {
                    // Display the book details
                    showBookInfo(searchBookInfo);

                    // Add the new book to the book store
                    bookStore.saveBookInfo(searchBookInfo);
                } else {
                    Alert alert = new Alert("Book not found", null,
                                        null, AlertType.ERROR);
                    alert.setTimeout(Alert.FOREVER);
                    alert.setString("No book with ISBN " +
                                        searchBookInfo.getIsbn() +
                                        " was found.");
                    isbnField.setString(null);
                    display.setCurrent(alert, getSelectionScreen());
                }
            }
        } catch (Throwable ex) {
            if (searchThread == Thread.currentThread()) {
                Alert alert = new Alert("Search Failed", null,
                                        null, AlertType.ERROR);
                alert.setTimeout(Alert.FOREVER);
                alert.setString("Search failed:\n" + ex.getMessage());
                isbnField.setString(null);
                display.setCurrent(alert, getSelectionScreen());
            }
        }
    }

    // Shows book details on the result screen
    private void showBookInfo(BookInfo info) {
        titleDisplay.setText(info.getTitle());

        int ranking = info.getRanking();
        int lastRanking = info.getLastRanking();
        int change = ranking - lastRanking;
        String rankingText =
                ranking == 0 ? "" :
                    String.valueOf(ranking);
        if (change > 0) {
            rankingText += ", down by " + change;
        } else if (change < 0) {
            rankingText += ", UP by " + (-change);
        }
        rankingDisplay.setText(rankingText);

        int reviews = info.getReviews();
        int lastReviews = info.getLastReviews();
        change = reviews - lastReviews;
        String reviewText =
            reviews == 0 ? "" :
                    String.valueOf(reviews);
        if (change > 0) {
            reviewText += ", up by " + change;
        } 
        reviewDisplay.setText(reviewText);

        display.setCurrent(resultForm);
    }

    // If there are any books in the
    // book store, display the list.
    // Otherwise, start with the ISBN form.
    private Screen getSelectionScreen() {
        return
            bookInfoList.isEmpty() ? (Screen)isbnForm : (Screen)bookList;
    }

    // Populates the list of books
    private void populateBookList() {
        // Clear out any existing content
        int count = bookList.size();
        for (int i = 0; i < count; i++) {
            bookList.delete(0);
        }
        bookInfoList.removeAllElements();

        // Add an entry for each book in the store
        try {
            RecordEnumeration e = bookStore.getBooks();
            while (e.hasNextElement()) {
                int id = e.nextRecordId();
                BookInfo info = bookStore.getBookInfo(id);
                bookInfoList.addElement(info);
                String title = info.getTitle();
                if (title == null || title.equals("")) {
                    title = info.getIsbn();
                }
                bookList.append(title, null);
            }
            e.destroy();
        } catch (Exception ex) {
            // Just leave an empty list.
        }

        // The ISBN list should have an exit command
        // only if the List screen is empty.
        isbnForm.removeCommand(exitCommand);
        if (bookInfoList.isEmpty()) {
            isbnForm.addCommand(exitCommand);
        }
    }

    private void initialize() {
        display = Display.getDisplay(this);

        // Open the book store
        bookStore = new BookStore();
 
        exitCommand = new Command("Exit", Command.EXIT, 0);
        okCommand = new Command("OK", Command.OK, 0);
        cancelCommand = new Command("Cancel", Command.CANCEL, 0);
        newCommand = new Command("New", Command.SCREEN, 1);
        detailsCommand = new Command("Details", Command.OK, 0);
        checkCommand = new Command("Check", Command.OK, 0);
        backCommand = new Command("Back", Command.CANCEL, 1);
        deleteCommand = new Command("Delete", Command.SCREEN, 1);

        bookList = new List("Books", List.IMPLICIT);
        bookList.addCommand(detailsCommand);
        bookList.addCommand(newCommand);
        bookList.addCommand(deleteCommand);
        bookList.addCommand(exitCommand);
        bookInfoList = new Vector();

        isbnForm = new Form("Book Query");
        isbnForm.append("Enter an ISBN and press OK:");
        isbnField = new TextField("", null, 10, TextField.ANY);
        isbnForm.append(isbnField);
        isbnForm.addCommand(okCommand);
        isbnForm.addCommand(exitCommand);
        isbnForm.addCommand(backCommand);

        searchForm = new Form("Book Search");
        searchForm.append("Searching for ISBN\n");
        isbnDisplay = new StringItem(null, null);
        searchForm.append(isbnDisplay);
        searchForm.append("\nPlease wait....");
        searchForm.addCommand(cancelCommand);

        checkForm = new Form("Details Update");
        checkForm.append("Getting details for\n");
        checkTitle = new StringItem(null, null);
        checkForm.append(checkTitle);
        checkForm.append(" from amazon.com\nPlease wait....");
        checkForm.addCommand(cancelCommand);

        resultForm = new Form("Search Results");
        titleDisplay = new StringItem("Book title: ", null);
        rankingDisplay = new StringItem("Ranking:    ", null);
        reviewDisplay = new StringItem("Reviews:    ", null);
        resultForm.append(titleDisplay);
        resultForm.append(rankingDisplay);
        resultForm.append(reviewDisplay);
        resultForm.addCommand(backCommand);
        resultForm.addCommand(checkCommand);

        // Register for events from all of the forms
        isbnForm.setCommandListener(this);
        searchForm.setCommandListener(this);
        resultForm.setCommandListener(this);
        bookList.setCommandListener(this);

        // Listen for changes in the content of the book store
        bookStore.addRecordListener(this);

        // Install the books held in the record store
        populateBookList();
    }
}

// A class that implements a persistent store
// of books, keyed by ISBN.
class BookStore implements RecordComparator, RecordFilter {

    // The name of the record store used to hold books
    private static final String STORE_NAME = "BookStore";

    // The record store itself
    private RecordStore store;

    // ISBN to be used during a filter operation
    private String searchISBN;

    // Creates a bookstore and opens it
    public BookStore() {
        try {
            store = RecordStore.openRecordStore(STORE_NAME, true);
        } catch (RecordStoreException ex) {
            System.err.println(ex);
        }
    }

    // Closes the bookstore
    public void close() throws RecordStoreException {
        if (store != null) {
            store.closeRecordStore();
        }
    }

    // Gets the number of books in the book store
    public int getBookCount() throws RecordStoreException {
        if (store != null) {
            return store.getNumRecords();
        }
        return 0;
    }

    // Adds a listener to the book store
    public void addRecordListener(RecordListener l) {
        if (store != null) {
            store.addRecordListener(l);
        }
    }

    // Removes a listener from the book store
    public void removeRecordListener(RecordListener l) {
        if (store != null) {
            store.removeRecordListener(l);
        }
    }

    // Gets a sorted list of all of the books in
    // the store.
    public RecordEnumeration getBooks() throws RecordStoreException {
        if (store != null) {
            return store.enumerateRecords(null, this, false);
        }
        return null;
    }

    
    // Gets a BookInfo from a store record
    // given its ISBN
    public BookInfo getBookInfo(String isbn) throws RecordStoreException,
                                                    IOException {
        BookInfo bookInfo = null;
        searchISBN = isbn; 
        
        // Look for a book with the given ISBN
        RecordEnumeration e = store.enumerateRecords(
                                        this, null, false);
        
        // If found, get its identifier and
        // fetch its BookInfo object
        if (e.numRecords() > 0) {
            int id = e.nextRecordId();
            bookInfo = getBookInfo(id);
        }
        
        // Release the enumeration
        e.destroy();
        
        return bookInfo;
    }
        
    // Gets a BookInfo from a store record
    // given its record identifier
    
    
    public BookInfo getBookInfo(int id) throws RecordStoreException,
                                                    IOException {
        byte[] bytes = store.getRecord(id);
        DataInputStream is = new DataInputStream(
                            new ByteArrayInputStream(bytes));

        String isbn = is.readUTF();
        BookInfo info = new BookInfo(isbn);
        info.id = id;
        info.title = is.readUTF();
        info.ranking = is.readInt();
        info.reviews = is.readInt();
        info.lastRanking = is.readInt();
        info.lastReviews = is.readInt();

        return info;
    }
    
    // Adds an entry to the store or modifies the existing
    // entry if a matching ISBN exists.
    public void saveBookInfo(BookInfo bookInfo)
                                throws IOException, RecordStoreException {
        if (store != null) {
            searchISBN = bookInfo.getIsbn();
            RecordEnumeration e = store.enumerateRecords(
                                        this, null, false);
            if (e.numRecords() > 0) {
                // A matching record exists. Set the id
                // of the BookInfo to match the existing record
                bookInfo.id = e.nextRecordId();
                byte[] bytes = toByteArray(bookInfo);
                store.setRecord(bookInfo.id, bytes, 0, bytes.length);
            } else {
                // Create a new record
                bookInfo.id = store.getNextRecordID();
                byte[] bytes = toByteArray(bookInfo);
                store.addRecord(bytes, 0, bytes.length);
            }

            // Finally, destroy the RecordEnumeration
            e.destroy();
        }
    }

    // Deletes the entry for a book from the store
    public void deleteBook(BookInfo bookInfo) throws RecordStoreException {
        if (store != null) {
            store.deleteRecord(bookInfo.id);
        }
    }

    // RecordComparator implementation
    public int compare(byte[] book1, byte[] book2) {
        try {
            DataInputStream stream1 =
                new DataInputStream(new ByteArrayInputStream(book1));
            DataInputStream stream2 =
                new DataInputStream(new ByteArrayInputStream(book2));

            // Match based on the ISBN, but sort based on the title.
            String isbn1 = stream1.readUTF();
            String isbn2 = stream2.readUTF();
            if (isbn1.equals(isbn2)) {
                return RecordComparator.EQUIVALENT;
            }
            String title1 = stream1.readUTF();
            String title2 = stream2.readUTF();
            int result = title1.compareTo(title2);
            if (result == 0) {
                return RecordComparator.EQUIVALENT;
            }
            return result < 0 ? RecordComparator.PRECEDES :
                                RecordComparator.FOLLOWS;
        } catch (IOException ex) {
            return RecordComparator.EQUIVALENT;
        }
    }

    // RecordFilter implementation
    public boolean matches(byte[] book) {
        if (searchISBN != null) {
            try {
                DataInputStream stream =
                    new DataInputStream(new ByteArrayInputStream(book));

                // Match based on the ISBN.
                return searchISBN.equals(stream.readUTF());
            } catch (IOException ex) {
                System.err.println(ex);
            }
        }

        // Default is not to match
        return false;
    }
    
    // Writes a record into a byte array.
    private byte[] toByteArray(BookInfo bookInfo) throws IOException {
        ByteArrayOutputStream baos = new ByteArrayOutputStream();
        DataOutputStream os = new DataOutputStream(baos);

        os.writeUTF(bookInfo.isbn);
        os.writeUTF(bookInfo.title == null ? "" : bookInfo.title);
        os.writeInt(bookInfo.ranking);
        os.writeInt(bookInfo.reviews);
        os.writeInt(bookInfo.lastRanking);
        os.writeInt(bookInfo.lastReviews);

        return baos.toByteArray();
    }
}




/**
 * A class that represents a book listing
 * at an online book set, including the number
 * of reviews for the book and its sales ranking.
 */
class BookInfo {

    int    id;          // Used when persisting
    String isbn;        // The book ISBN
    String title;       // The book title
    int    reviews;     // Number of reviews
    int    ranking;     // Current ranking
    int    lastReviews; // Last review count
    int    lastRanking;  // Last ranking

    public BookInfo(String isbn) {
        this.isbn = isbn;
    }

    public String getIsbn() {
        return isbn;
    }

    public String getTitle() {
        return title;
    }

    public int getReviews() {
        return reviews;
    }

    public int getRanking() {
        return ranking;
    }

    public int getLastReviews() {
        return lastReviews;
    }

    public int getLastRanking() {
        return lastRanking;
    }

    // Installs details parsed from an input stream
    public void setFromInputStream(InputStream is) {
        // Use an InputHelper to search the input
        InputHelper helper = new InputHelper(is);
        try {

            // Default new values to current values
            int newRanking = this.ranking;
            int newReviews = this.reviews;

            boolean found = helper.moveAfterString("buying info: ");
            if (!found) {
                return;
            }

            // Gather the title from the rest of this line
            StringBuffer titleBuffer = helper.getRestOfLine();

            // Look for the number of reviews
            found = helper.moveAfterString("Based on ");
            if (!found) {
                return;
            }

            // Gather the number of reviews from the current location
            String reviewString = helper.gatherNumber();

            // Look for the sales rank
            found = helper.moveAfterString("Sales Rank: ");
            if (!found) {
                return;
            }

            // Gather the number from the current location
            String rankingString = helper.gatherNumber();

            // Having safely found everything, set the new title
            title = titleBuffer.toString().trim();

            // Now convert the reviews and ranking to integers.
            // If they fail to convert, just leave the existing
            // values.
            try {
                newRanking = Integer.parseInt(rankingString);
            } catch (NumberFormatException ex) {
            }

            if (newRanking != ranking) {
                lastRanking = ranking;
                ranking = newRanking;
                if (lastRanking == 0) {
                    // First time, set last and current
                    // to the same value
                    lastRanking = ranking;
                }
            }

            try {
                newReviews = Integer.parseInt(reviewString);
            } catch (NumberFormatException ex) {
            }

            if (newReviews != reviews) {
                lastReviews = reviews;
                reviews = newReviews;
                if (lastReviews == 0) {
                    // First time, set last and current
                    // to the same value
                    lastReviews = reviews;
                }
            }
        } catch (IOException ex) {
        } finally {
            // Allow garbage collection
            helper.dispose();
            helper = null;
        }
    }
}

// A class that scans through an input
// stream for strins without reading the
// entire stream into a large string.
class InputHelper {

    // Size of the input buffer
    private static final int BUFFER_SIZE = 1024;

    // The input buffer
    private final char[] buffer = new char[BUFFER_SIZE];

    // Number of characters left in the buffer
    private int charsLeft;

    // Index of the next character in the buffer
    private int nextChar;

    // InputStreamReader used to map to Unicode
    private InputStreamReader reader;

    // Constructs a helper to read a given stream
    public InputHelper(InputStream is) {
        reader = new InputStreamReader(is);
    }
    
    // Cleans up when no longer needed
    public void dispose() {
        if (reader != null) {
            try {
                reader.close();
            } catch (IOException ex) {
            }
            reader = null;
        }
    }

    // Looks for a given string in the input
    // stream and positions the stream so that the
    // next character read is one beyond the string.
    // Returns true if the string was found, false if
    // not (and the stream will have been completely read).
    public boolean moveAfterString(String str) throws IOException {
        char[] chars = str.toCharArray();
        int count = chars.length;
        char firstChar = chars[0];

        char c = (char)0;
        for (;;) {
            if (c != firstChar && !findNext(firstChar)) {
                // Reached the end of the input stream
                return false;
            }

            boolean mismatch = false;
            for (int i = 1; i < count; i++) {
                c = getNext();
                if (c != chars[i]) {
                    mismatch = true;
                    break;
                }
            }

            if (!mismatch) {
                return true;
            }

            // Mismatch. 'c' has the first mismatched
            // character - start the loop again with
            // that character. This is necessary because we
            // could have found "wweb" while looking for "web"
        }
    }
    
    // Gets the characters for a number, ignoring
    // the grouping separator. The number starts at the
    // current input position, but any leading non-numerics
    // are skipped.
    public String gatherNumber() throws IOException {
        StringBuffer sb = new StringBuffer();
        boolean gotNumeric = false;
        for (;;) {
            char c = getNext();
            
            // Skip until we find a digit.
            boolean isDigit = Character.isDigit(c);
            if (!gotNumeric && !isDigit) {
                continue;
            }
            gotNumeric = true;
            if (!isDigit) {
                if (c == '.' || c == ',') {
                    continue;
                }
                break;
            }
            sb.append(c);
        }
        return sb.toString();
    }
    
    // Gets the balance of the current line
    // and returns it as a StringBuffer
    public StringBuffer getRestOfLine() throws IOException {
    
        StringBuffer sb = new StringBuffer();
        char c;
        for (;;) {
            c = getNext();
            if (c == '\n' || c == (char)0) {
                break;
            }
            sb.append(c);
        }
        return sb;
    } 
    
    // Gets the next character from the stream,
    // returning (char)0 when all input has been read.
    private char getNext() throws IOException {
        if (charsLeft == 0) {
            charsLeft = reader.read(buffer, 0, BUFFER_SIZE);
            if (charsLeft < 0) {
                return (char)0;
            }
            nextChar = 0;
        }
        charsLeft--;
        return buffer[nextChar++];
    }
       
    // Finds the next instance of a given character in the
    // input stream. The input stream is positioned after
    // the located character. If EOF is reached without
    // finding the character, false is returned.
    private boolean findNext(char c) throws IOException {
        for (;;) {
            if (charsLeft == 0) {
                charsLeft = reader.read(buffer, 0, BUFFER_SIZE);
                if (charsLeft < 0) {
                    return false;
                }
                nextChar = 0;
            }
            charsLeft--;
            if (c == buffer[nextChar++]) {
                return true;
            }
        }
    }
}
           
       








Related examples in the same category

1.Write Read Mixed Data Types Example Write Read Mixed Data Types Example
2.Record Enumeration Example Record Enumeration Example
3.Mixed Record Enumeration Example Mixed Record Enumeration Example
4.Sort Record Example
5.Sort Mixed Record Data Type Example Sort Mixed Record Data Type Example
6.Search ExampleSearch Example
7.Search Mixed Record Data Type ExampleSearch Mixed Record Data Type Example
8.Record MIDletRecord MIDlet
9.Store DatabaseStore Database
10.Test the RMS listener methodsTest the RMS listener methods
11.Use streams to read and write Java data types to the record store.Use streams to read and write Java data types to the record store.
12.Read and write to the record store.Read and write to the record store.
13.Persistence: storing and showing game scores
14.Read Display FileRead Display File