J2ME Java

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