Java tutorial
/*-------------------------------------------------- * TodoMIDlet.java * * The main class for todo list MIDlet * * Example from the book: Core J2ME Technology * Copyright John W. Muchow http://www.CoreJ2ME.com * You may use/modify for any non-commercial purpose *-------------------------------------------------*/ import java.io.*; import java.util.*; import javax.microedition.midlet.*; import javax.microedition.lcdui.*; import javax.microedition.rms.*; public class TodoMIDlet extends MIDlet implements ItemStateListener, CommandListener { private Display display; // Our display private Form fmMain; // Main Form private FormAdd fmAdd; // Form to add todo item private Command cmAdd, // Command to add todo cmPrefs, // Command to set preferences cmExit; // Command to exit private Vector vecTodo; // The "master" list of todo items private ChoiceGroup cgTodo; // Todo items (read from vecTodo) protected DisplayManager displayMgr; // Class to help manage screens //------------------------------------------------- // Record Stores. One for todo, one for preferences //------------------------------------------------- private RecordStore rsTodo; private RecordStore rsPref; private static final String REC_STORE_TODO = "TodoList"; private static final String REC_STORE_PREF = "TodoPrefs"; private boolean flagSortByPriority = false, // Sort by priority? flagShowPriority = true; // Show priorities ? //------------------------------------------------- // Re-use these input streams throughout the MIDlet //------------------------------------------------- // Read from a specified byte array private ByteArrayInputStream istrmBytes = null; // Read Java data types from the above byte array private DataInputStream istrmDataType = null; // If you change length of a todo entry, bump this byte[] recData = new byte[25]; //------------------------------------------------- // Re-use these output streams throughout the MIDlet //------------------------------------------------- // Write data into an internal byte array ByteArrayOutputStream ostrmBytes = null; // Write Java data types into the above byte array DataOutputStream ostrmDataType = null; //------------------------------------------------- // Record Enumerator and compare class //------------------------------------------------- private RecordEnumeration e = null; private ComparatorInt comp = null; /*-------------------------------------------------- * MIDlet constructor. *-------------------------------------------------*/ public TodoMIDlet() { display = Display.getDisplay(this); // Create 'main' and 'add todo item' forms // Form for setting prefs is in commandAction() fmMain = new Form("Todo List"); fmAdd = new FormAdd("Add Todo", this); // Todo list and vector cgTodo = new ChoiceGroup("", Choice.MULTIPLE); vecTodo = new Vector(); // Commands cmAdd = new Command("Add Todo", Command.SCREEN, 2); cmPrefs = new Command("Preferences", Command.SCREEN, 3); cmExit = new Command("Exit", Command.EXIT, 1); // Add all to form and listen for events fmMain.addCommand(cmAdd); fmMain.addCommand(cmPrefs); fmMain.addCommand(cmExit); fmMain.append(cgTodo); fmMain.setCommandListener(this); fmMain.setItemStateListener(this); // Create a display manager object displayMgr = new DisplayManager(display, fmMain); // Open/create the record stores rsTodo = openRecStore(REC_STORE_TODO); rsPref = openRecStore(REC_STORE_PREF); // Initialize the streams initInputStreams(); initOutputStreams(); // Read preferences from rms refreshPreferences(); // Initialize the enumeration for todo rms initEnumeration(); // Read rms into vector writeRMS2Vector(); // Build the todo list (choice group) rebuildTodoList(); } /*-------------------------------------------------- * Show the main Form *-------------------------------------------------*/ public void startApp() { display.setCurrent(fmMain); } /*-------------------------------------------------- * Shutting down. Cleanup all we created *-------------------------------------------------*/ public void destroyApp(boolean unconditional) { // Cleanup for enumerator if (comp != null) comp.compareIntClose(); if (e != null) e.destroy(); // Cleanup streams try { if (istrmDataType != null) istrmDataType.close(); if (istrmBytes != null) istrmBytes.close(); if (ostrmDataType != null) ostrmDataType.close(); if (ostrmBytes != null) ostrmBytes.close(); } catch (Exception e) { db(e.toString()); } // Cleanup rms closeRecStore(rsTodo); closeRecStore(rsPref); } /*-------------------------------------------------- * No pause code necessary *-------------------------------------------------*/ public void pauseApp() { } /*-------------------------------------------------- * Open input streams *-------------------------------------------------*/ private void initInputStreams() { istrmBytes = new ByteArrayInputStream(recData); istrmDataType = new DataInputStream(istrmBytes); } /*-------------------------------------------------- * Open output streams *-------------------------------------------------*/ private void initOutputStreams() { ostrmBytes = new ByteArrayOutputStream(); ostrmDataType = new DataOutputStream(ostrmBytes); } /*-------------------------------------------------- * Initialize enumeration for todo rms *-------------------------------------------------*/ private void initEnumeration() { // Are we to bother with sorting? if (flagSortByPriority) comp = new ComparatorInt(); else // We must set this to null to clear out // any previous setting comp = null; try { e = rsTodo.enumerateRecords(null, comp, false); } catch (Exception e) { db(e.toString()); } } /*-------------------------------------------------- * Open a record store *-------------------------------------------------*/ private RecordStore openRecStore(String name) { try { // Open the Record Store, creating it if necessary return RecordStore.openRecordStore(name, true); } catch (Exception e) { db(e.toString()); return null; } } /*-------------------------------------------------- * Close a record store *-------------------------------------------------*/ private void closeRecStore(RecordStore rs) { try { rs.closeRecordStore(); } catch (Exception e) { db(e.toString()); } } /*-------------------------------------------------- * Delete a record store *-------------------------------------------------*/ private void deleteRecStore(String name) { try { RecordStore.deleteRecordStore(name); } catch (Exception e) { db(e.toString()); } } /*-------------------------------------------------- * Write new todo item * - Write a new record into the rms * - Write a new item into the vector * - Recreate the vector from the rms (which will * use the proper sort (using rms enumeration) * - Rebuild todo list from vector *-------------------------------------------------*/ protected void addTodoItem(int priority, String text) { try { // Toss any data in the internal array so writes // starts at beginning (of the internal array) ostrmBytes.reset(); // Write priority and todo text ostrmDataType.writeInt(priority); ostrmDataType.writeUTF(text); // Clear any buffered data ostrmDataType.flush(); // Get stream data into byte array and write record byte[] record = ostrmBytes.toByteArray(); int recordId = rsTodo.addRecord(record, 0, record.length); // Create a new Todo item and insert it into our Vector TodoItem item = new TodoItem(priority, text, recordId); vecTodo.addElement(item); } catch (Exception e) { db(e.toString()); } // Read rms into vector writeRMS2Vector(); // Rebuild todo list rebuildTodoList(); } /*-------------------------------------------------- * Save preferences to record store *-------------------------------------------------*/ protected void savePreferences(boolean sort, boolean showSort) { // No changes we made if (sort == flagSortByPriority && showSort == flagShowPriority) return; // Save the current sort status boolean previouslySorted = flagSortByPriority; boolean previouslyShowPriority = flagShowPriority; try { // Update prefs in private variables flagSortByPriority = sort; flagShowPriority = showSort; // Toss any data in the internal array so writes // starts at beginning (of the internal array) ostrmBytes.reset(); // Write the sort order and keep completed flags ostrmDataType.writeBoolean(flagSortByPriority); ostrmDataType.writeBoolean(flagShowPriority); // Clear any buffered data ostrmDataType.flush(); // Get stream data into byte array and write record byte[] record = ostrmBytes.toByteArray(); // Always write preferences at first record // We cannot request to set the first record unless // the record store has contents. // If empty => add a record // If not => overwrite the first record if (rsPref.getNumRecords() == 0) rsPref.addRecord(record, 0, record.length); else rsPref.setRecord(1, record, 0, record.length); } catch (Exception e) { db(e.toString()); } // If the sort order was changed, rebuild enumeration if (previouslySorted != flagSortByPriority) initEnumeration(); // If we are going from non-sorted to sorted // or changing whether or not to show priority // then we must update what's currently displayed if ((!previouslySorted && flagSortByPriority) || (previouslyShowPriority != flagShowPriority)) { // Read rms into vector writeRMS2Vector(); // Rebuild todo list rebuildTodoList(); } } /*-------------------------------------------------- * Read preferences from record store *-------------------------------------------------*/ private void refreshPreferences() { try { // Record store is empty if (rsPref.getNumRecords() == 0) { // Write into the store the default preferences savePreferences(flagSortByPriority, flagShowPriority); return; } // Reset input back to the beginning istrmBytes.reset(); // Read configuration data stored in the first record rsPref.getRecord(1, recData, 0); flagSortByPriority = istrmDataType.readBoolean(); flagShowPriority = istrmDataType.readBoolean(); System.out.println("Sort: " + flagSortByPriority); System.out.println("Show: " + flagShowPriority); } catch (Exception e) { db(e.toString()); } } /*-------------------------------------------------- * Create the vector from record store contents *-------------------------------------------------*/ private void writeRMS2Vector() { // Cleanout the vector vecTodo.removeAllElements(); try { // Rebuild enumeration for any changes e.rebuild(); while (e.hasNextElement()) { // Reset input back to the beginning istrmBytes.reset(); // Get data into the byte array int id = e.nextRecordId(); rsTodo.getRecord(id, recData, 0); // Create a new Todo item and insert it into our Vector TodoItem item = new TodoItem(istrmDataType.readInt(), istrmDataType.readUTF(), id); vecTodo.addElement(item); } } catch (Exception e) { db(e.toString()); } } /*-------------------------------------------------- * Store the current vector contents to the rms *-------------------------------------------------*/ private void writeVector2RMS() { try { byte[] record; for (int i = 0; i < vecTodo.size(); i++) { TodoItem item = (TodoItem) vecTodo.elementAt(i); int priority = item.getPriority(); String text = item.getText(); // Toss any data in the internal array so writes // starts at beginning (of the internal array) ostrmBytes.reset(); // Write priority and todo text ostrmDataType.writeInt(priority); ostrmDataType.writeUTF(text); // Clear any buffered data ostrmDataType.flush(); // Get stream data into byte array and write record record = ostrmBytes.toByteArray(); rsTodo.addRecord(record, 0, record.length); } } catch (Exception e) { db(e.toString()); } } /*-------------------------------------------------- * Rebuild todo list (ChoiceGroup) from the Vector *-------------------------------------------------*/ protected void rebuildTodoList() { // Clear out the ChoiceGroup. for (int i = cgTodo.size(); i > 0; i--) cgTodo.delete(i - 1); TodoItem item; int priority; String text; StringBuffer strb; for (int i = 0; i < vecTodo.size(); i++) { // Get a todo item from vector item = (TodoItem) vecTodo.elementAt(i); // Read values from todoitem class priority = item.getPriority(); text = item.getText(); // Are we are to show priority as part of the text? strb = new StringBuffer((flagShowPriority ? (Integer.toString(priority) + "-") : "")); strb.append(text); // Append to todo choicegroup cgTodo.append(strb.toString(), null); } } /*-------------------------------------------------- * This method is called when a todo item has been * selected in the choicegroup. We treat this as * a request to delete the item *-------------------------------------------------*/ public void itemStateChanged(Item item) { ChoiceGroup cg; // Cast the item to a ChoiceGroup type cg = (ChoiceGroup) item; // Create an array that mirrors the ChoiceGroup // and populate with those items selected boolean selected[] = new boolean[cg.size()]; cg.getSelectedFlags(selected); // Unfortunately, there is no (easy) way to determine // which item in the choiceGroup was "clicked" to // initiate this event. The best we can do is look at // each entry and determine its current selection state // For each element, see if it is selected // If so, delete it. Once we have found a selected // item, we can exit - there will never be more than // one item selected at any time for (int i = 0; i < cg.size(); i++) { if (selected[i]) { // Get the record id from the todoItem // and delete record from rms TodoItem todoitem = (TodoItem) vecTodo.elementAt(i); try { rsTodo.deleteRecord(todoitem.getRecordId()); } catch (Exception e) { db(e.toString()); } break; } } // Read rms into vector writeRMS2Vector(); // Rebuild todo list rebuildTodoList(); } /*-------------------------------------------------- * Process events for the main form *-------------------------------------------------*/ public void commandAction(Command c, Displayable d) { if (c == cmExit) { destroyApp(false); notifyDestroyed(); } else { if (c == cmAdd) { // Reset the textfield and choicegroup // on the 'add todo' form fmAdd.tfTodo.setString(""); fmAdd.cgPriority.setSelectedIndex(0, true); // Push current displayable and activate 'add todo' form displayMgr.pushDisplayable(fmAdd); } else if (c == cmPrefs) { boolean flags[] = { flagSortByPriority, flagShowPriority }; // Push current displayable and show preferences form // passing in current preference settings displayMgr.pushDisplayable(new FormPrefs("Preferences", this, flags)); } } } /*-------------------------------------------------- * Simple message to console for debug/errors *-------------------------------------------------*/ private void db(String str) { System.err.println("Msg: " + str); } } /*-------------------------------------------------- * Use a stack to push and pop displayable objects * * public void pushDisplayable(Displayable) * public void popDisplayable() * public void home() * * Example from the book: Core J2ME Technology * Copyright John W. Muchow http://www.CoreJ2ME.com * You may use/modify for any non-commercial purpose *-------------------------------------------------*/ class DisplayManager extends Stack { private Display display; // Reference to Display object private Displayable mainDisplayable; // Main displayable for MIDlet private Alert alStackError; // Alert for error conditions /*-------------------------------------------------- * Display manager constructor *-------------------------------------------------*/ public DisplayManager(Display display, Displayable mainDisplayable) { // Only one display object per midlet, this is it this.display = display; this.mainDisplayable = mainDisplayable; // Create an alert displayed when an error occurs alStackError = new Alert("Displayable Stack Error"); alStackError.setTimeout(Alert.FOREVER); // Modal } /*-------------------------------------------------- * Push the current displayable onto stack and set * the passed in displayable as active *-------------------------------------------------*/ public void pushDisplayable(Displayable newDisplayable) { push(display.getCurrent()); display.setCurrent(newDisplayable); } /*-------------------------------------------------- * Return to the main displayable object of MIDlet *-------------------------------------------------*/ public void home() { while (elementCount > 1) pop(); display.setCurrent(mainDisplayable); } /*-------------------------------------------------- * Pop displayable from stack and set as active *-------------------------------------------------*/ public void popDisplayable() { // If the stack is not empty, pop next displayable if (empty() == false) display.setCurrent((Displayable) pop()); else // On error show an alert // Once acknowldeged, set 'mainDisplayable' as active display.setCurrent(alStackError, mainDisplayable); } } /*-------------------------------------------------- * FormAdd.java * * Form for adding new todoitems * Supporting class for TodoMIDlet * * Example from the book: Core J2ME Technology * Copyright John W. Muchow http://www.CoreJ2ME.com * You may use/modify for any non-commercial purpose *-------------------------------------------------*/ class FormAdd extends Form implements CommandListener { private Command cmBack, cmSave; protected TextField tfTodo; protected ChoiceGroup cgPriority; private TodoMIDlet midlet; public FormAdd(String title, TodoMIDlet midlet) { // Call the Form constructor super(title); // Save reference to MIDlet so we can access // the display manager class and rms this.midlet = midlet; // Commands cmSave = new Command("Save", Command.SCREEN, 1); cmBack = new Command("Back", Command.BACK, 2); // Create textfield for entering todo items tfTodo = new TextField("Todo", null, 15, TextField.ANY); // Create choicegroup and append options (no images) cgPriority = new ChoiceGroup("Priority", Choice.EXCLUSIVE); cgPriority.append("Today", null); cgPriority.append("Tomorrow", null); cgPriority.append("This Week", null); cgPriority.append("This Month", null); cgPriority.append("Someday", null); // Add stuff to form and listen for events addCommand(cmSave); addCommand(cmBack); append(tfTodo); append(cgPriority); setCommandListener(this); } public void commandAction(Command c, Displayable s) { if (c == cmSave) { // Add a new todo item // Notice we bump priority by 1. This is because the // choicegroup entries start at zero. We would like // the records in the rms to store priorities starting // at 1. Thus, if a user requests to display priorities // on the todo list, the highest priority is 1 (not zero) midlet.addTodoItem(cgPriority.getSelectedIndex() + 1, tfTodo.getString()); } // Any other event and we go back to the main form... // Pop the last displayable off the stack midlet.displayMgr.popDisplayable(); } } /*-------------------------------------------------- * ComparatorInt.java * * Sorts rms records based on todo item priority * Provides compare() method for RecordEnumerator * Supporting class for TodoMIDlet * * Example from the book: Core J2ME Technology * Copyright John W. Muchow http://www.CoreJ2ME.com * You may use/modify for any non-commercial purpose *-------------------------------------------------*/ class ComparatorInt implements RecordComparator { private byte[] record = new byte[10]; // Read from a specified byte array private ByteArrayInputStream strmBytes = null; // Read Java data types from the above byte array private DataInputStream strmDataType = null; public void compareIntClose() { try { if (strmBytes != null) strmBytes.close(); if (strmDataType != null) strmDataType.close(); } catch (Exception e) { } } public int compare(byte[] rec1, byte[] rec2) { int x1, x2; try { // If either record is larger than our buffer, reallocate int maxsize = Math.max(rec1.length, rec2.length); if (maxsize > record.length) record = new byte[maxsize]; // Read record #1 // We want the priority which is first "field" strmBytes = new ByteArrayInputStream(rec1); strmDataType = new DataInputStream(strmBytes); x1 = strmDataType.readInt(); // Read record #2 strmBytes = new ByteArrayInputStream(rec2); strmDataType = new DataInputStream(strmBytes); x2 = strmDataType.readInt(); // Compare record #1 and #2 if (x1 == x2) return RecordComparator.EQUIVALENT; else if (x1 < x2) return RecordComparator.PRECEDES; else return RecordComparator.FOLLOWS; } catch (Exception e) { return RecordComparator.EQUIVALENT; } } } /*-------------------------------------------------- * TodoItem.java * * Holds data/methods for a single todo item * Supporting class for todoMIDlet * * Example from the book: Core J2ME Technology * Copyright John W. Muchow http://www.CoreJ2ME.com * You may use/modify for any non-commercial purpose *-------------------------------------------------*/ class TodoItem { private int priority; private String text; private int recordId; public TodoItem(int priority, String text, int recordId) { this.priority = priority; this.text = text; this.recordId = recordId; } public int getPriority() { return priority; } public void setPriority(int priority) { this.priority = priority; } public String getText() { return text; } public void setText(String text) { this.text = text; } public int getRecordId() { return recordId; } } /*-------------------------------------------------- * FormPrefs.java * * Form for specifying user preferences * Supporting class for TodoMIDlet * * Example from the book: Core J2ME Technology * Copyright John W. Muchow http://www.CoreJ2ME.com * You may use/modify for any non-commercial purpose *-------------------------------------------------*/ class FormPrefs extends Form implements CommandListener { private Command cmBack, cmSave; private TodoMIDlet midlet; private ChoiceGroup cgPrefs; public FormPrefs(String title, TodoMIDlet midlet, boolean flags[]) { // Call the Form constructor super(title); // Save reference to MIDlet so we can access // the display manager class and rms this.midlet = midlet; // Commands cmSave = new Command("Save", Command.SCREEN, 1); cmBack = new Command("Back", Command.BACK, 2); // Choicegroup for sort order and showing priority cgPrefs = new ChoiceGroup("Preferences", Choice.MULTIPLE); cgPrefs.append("Sort by Priority", null); cgPrefs.append("Show Priority", null); // Set the current status of each entry cgPrefs.setSelectedFlags(flags); // Add to form and listen for events append(cgPrefs); addCommand(cmBack); addCommand(cmSave); setCommandListener(this); } public void commandAction(Command c, Displayable s) { if (c == cmSave) { // Save the preferences midlet.savePreferences(cgPrefs.isSelected(0), cgPrefs.isSelected(1)); } // Any other event and we go back to the main form... // Pop the last displayable off the stack midlet.displayMgr.popDisplayable(); } }