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