RankingMIDlet.java Source code

Java tutorial

Introduction

Here is the source code for RankingMIDlet.java

Source

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

import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.OutputStream;

import javax.microedition.io.Connector;
import javax.microedition.io.HttpConnection;
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.StringItem;
import javax.microedition.lcdui.TextField;
import javax.microedition.midlet.MIDlet;
import javax.microedition.midlet.MIDletStateChangeException;

public class RankingMIDlet extends MIDlet implements CommandListener, Runnable {

    private Command exitCommand;

    private Command okCommand;

    private Command cancelCommand;

    private Command newCommand;

    private Display display;

    private TextField isbnField;

    private StringItem isbnDisplay;

    private StringItem titleDisplay;

    private StringItem rankingDisplay;

    private StringItem reviewDisplay;

    private Form isbnForm;

    private Form searchForm;

    private Form resultForm;

    private BookInfo searchBookInfo;

    private Thread searchThread;

    protected void startApp() throws MIDletStateChangeException {
        if (display == null) {
            initialize();
            display.setCurrent(isbnForm);
        }
    }

    protected void pauseApp() {
    }

    protected void destroyApp(boolean unconditional) throws MIDletStateChangeException {

    }

    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("")) {
                searchForBook(new BookInfo(isbn));
            }
        } else if (cmd == cancelCommand) {
            searchThread = null;
            isbnField.setString(null);
            display.setCurrent(isbnForm);
        } else if (cmd == newCommand) {
            isbnField.setString(null);
            display.setCurrent(isbnForm);
        }
    }

    public void searchForBook(BookInfo info) {
        searchBookInfo = info;
        isbnDisplay.setText(info.getIsbn().trim());
        display.setCurrent(searchForm);
        searchThread = new Thread(this);
        searchThread.start();
    }

    public void run() {
        try {
            boolean found = Fetcher.fetch(searchBookInfo);
            if (searchThread == Thread.currentThread()) {
                if (found && searchBookInfo.getTitle() != null) {
                    titleDisplay.setText(searchBookInfo.getTitle());
                    rankingDisplay.setText(
                            searchBookInfo.getRanking() == 0 ? "" : String.valueOf(searchBookInfo.getRanking()));
                    reviewDisplay.setText(
                            searchBookInfo.getReviews() == 0 ? "" : String.valueOf(searchBookInfo.getReviews()));
                    display.setCurrent(resultForm);
                } 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, isbnForm);
                }
            }
        } 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, isbnForm);
            }
        }
    }

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

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

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

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

        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(newCommand);
        resultForm.addCommand(exitCommand);

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

class Fetcher {

    private static final String BASE_URL = "http://www.amazon.com";

    private static final String QUERY_URL = BASE_URL + "/exec/obidos/search-handle-form/0";

    private static final int MAX_REDIRECTS = 5;

    // Fetches the title, ranking and review count
    // for a book with a given ISBN.
    public static boolean fetch(BookInfo info) throws IOException {
        InputStream is = null;
        OutputStream os = null;
        HttpConnection conn = null;
        int redirects = 0;
        try {
            String isbn = info.getIsbn();
            String query = "index=books&field-keywords=" + isbn + "\r\n";
            String requestMethod = HttpConnection.POST;
            String name = QUERY_URL;

            while (redirects < MAX_REDIRECTS) {
                conn = (HttpConnection) Connector.open(name, Connector.READ_WRITE);

                // Send the ISBN number to perform the query
                conn.setRequestMethod(requestMethod);
                conn.setRequestProperty("Connection", "Close");
                if (requestMethod.equals(HttpConnection.POST)) {
                    conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
                    os = conn.openOutputStream();
                    os.write(query.getBytes());
                    os.close();
                    os = null;
                }

                // Read the response from the server
                is = conn.openInputStream();
                int code = conn.getResponseCode();

                // If we get a redirect, try again at the new location
                if ((code >= HttpConnection.HTTP_MOVED_PERM && code <= HttpConnection.HTTP_SEE_OTHER)
                        || code == HttpConnection.HTTP_TEMP_REDIRECT) {
                    // Get the URL of the new location (always absolute)
                    name = conn.getHeaderField("Location");
                    is.close();
                    conn.close();
                    is = null;
                    conn = null;

                    if (++redirects > MAX_REDIRECTS) {
                        // Too many redirects - give up.
                        break;
                    }

                    // Choose the appropriate request method
                    requestMethod = HttpConnection.POST;
                    if (code == HttpConnection.HTTP_MOVED_TEMP || code == HttpConnection.HTTP_SEE_OTHER) {
                        requestMethod = HttpConnection.GET;
                    }
                    continue;
                }
                String type = conn.getType();
                if (code == HttpConnection.HTTP_OK && type.equals("text/html")) {
                    info.setFromInputStream(is);
                    return true;
                }
            }
        } catch (Throwable t) {
            System.out.println(t);
        } finally {
            if (is != null) {
                try {
                    is.close();
                } catch (IOException ex) {
                }
            }
            if (os != null) {
                try {
                    os.close();
                } catch (IOException ex) {
                }
            }
            if (conn != null) {
                try {
                    conn.close();
                } catch (IOException ex) {
                }
            }
        }
        return false;
    }
}

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