Mega Code Archive

 
Categories / Java / J2ME
 

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;             }         }     } }