Database & MIDP, Part 5: Search Record Storage Submission Time: DEC 29, 2004 5:25:15 PM
Reply Send Messages in Part 4 of this Series, you learn how you learn a record storage, sort records in useful order, and select the desired record using a filter. This article explores various strategies for finding one or more records that meet the designated criteria. Search strategy ____________________________ is obvious, the simplest way to search for a specific record or record collection is to use a filter. This filter needs to know how the data is stored in a record, so you will want to reuse your data mapping classes in any possible case. For example, in Part 3 we define the FieldList and FieldBaseDStore classes to handle read and write arbitrary data to a record storage.
Take a refactor, we can move the code that is not specific to recorded to a new base class, FieldBaseDrecordMapper: package java.rms; import java.io. *; Import javax.microedition.rms. *; Import j2me.io . *; // A base class for writing and reading arbitrary // data as defined by a FieldListpublic abstract class FieldBasedRecordMapper {// Some useful constants public static Boolean TRUE = new Boolean (true); public static Boolean FALSE = new Boolean (false ); // Markers for the types of string we support private static final byte NULL_STRING_MARKER = 0; private static final byte UTF_STRING_MARKER = 1; // Constructs the mapper for the given list protected FieldBasedRecordMapper () {} // Prepares for input by setting the data buffer protected void prepareForInput (byte [] data). {if (_bin == null) {_bin = new DirectByteArrayInputStream (data); _din = new DataInputStream (_bin);} else {_bin.setByteArray (data);}} // Prepares The Store for Output. The Strea ms are reused protected void prepareForOutput () {if (_bout == null) {_bout = new DirectByteArrayOutputStream (); _dout = new DataOutputStream (_bout);} else {_bout.reset ();}}. // Reads a field from . the buffer protected Object readField (int type) throws IOException {switch (type) {case FieldList.TYPE_BOOLEAN: return _din.readBoolean () TRUE: FALSE; case FieldList.TYPE_BYTE:? return new Byte (_din.readByte ()); Case FieldList.Type_char: return new character (_din.readchar ());
case FieldList.TYPE_SHORT: return new Short (_din.readShort ()); case FieldList.TYPE_INT: return new Integer (_din.readInt ()); case FieldList.TYPE_LONG: return new Long (_din.readLong ()); case FieldList .TYPE_STRING: {byte marker = _din.readByte (); if (marker == UTF_STRING_MARKER) {return _din.readUTF ();}}} return null;} // Converts an object to a boolean value public static boolean toBoolean (. Object value) {if (value instanceof boolean) {return ((boolean) value) .BooleanValue ();} else if (value! = Null) {string str = value.toString (). Trim (); if (STR. Equals ("true")) Return True; if ("False")) Return False; Return (TOINT (Value)! = 0);} Return False;} // Converts An Object to a char. public s Tatic char tochar (ibject value) {if (value instanceof character) {return ((character) value) .Charvalue ();} else = value! = null) {string s = value.toString (); if (s. Length ()> 0) {RETURN S.CHARAT (0);}} Return 0;} // Converts an Object to an Int. this code // Would Be MUCH SIMPLER IF THE CLDC Supported // The java.lang.number Class. public static int tool {if (value instanceof integer) {return ("(integer) value) .intValue ();} else if (value instanceof boolean) {return (boolean) value) .BooleanValue () ? 1: 0;
} else if (value instanceof byte) {return ((byte) value) .Bytevalue ();} else if (value instanceof character) {return (character) value) .charvalue ();} else if (Value InstanceOf Short) {Return ((Short) value) .shortvalue ();} else if (value instanceof long) {return (ign) .longValue ();} else if (value! = Null) {Try {Return Integer.Parstring ());} catch (Numberformatexception E) {}} Return 0;} // Converts An Object to a long. This code // Would Be Much Simpler If The CLDC Supported // The Java. Lang.Number Class. Public Static long tolong (value instanceof integer {return (integer) .longvalue ();} else if (value instanceof boolean) {return ((Boolean). BooleanValue ()? 1: 0;} else if (value instanceof byte) { Return (Byte) Value) .Bytevalue ();} else if (value instanceof character) {return ((character) value) .Charvalue ();} else if (value instanceof short) {return ((Short) value). Shortvalue ();} else if (value instanceof long) {return ((long) value) .longvalue ();} else if (value! = null) {Try {Return long.parselope (value.tostring ());} Catch (NumberFormatexcection E) {}} Return 0;
} // Writes a field to the output buffer protected void writeField (int type, Object value) throws IOException {switch (type) {case FieldList.TYPE_BOOLEAN:. _Dout.writeBoolean (toBoolean (value)); break; case FieldList.TYPE_BYTE : _DOUT.WRITE ((byte) toint (value); break; case fieldlist.type_char: _dout.writechar (tochar (value)); Break; Case FieldList.Type_short: _dout.writeshort ((Short) Toint (Value)) ,;,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, String str = value.toString (); _do ut.writeByte (UTF_STRING_MARKER); _dout.writeUTF (str);} else {_dout.writeByte (NULL_STRING_MARKER);} break;.}} // Writes a set of fields to the output stream protected byte [] writeStream (FieldList list, Object [] Fields) throws ioException {int count = list.getfieldcount (); int LEN = (fields! = Null? Fields.Length: 0); prepareforoutput (); for (int i = 0; i } Return _bout.getByteArray ();} private DirectByteArrayInputStream _bin; private DirectByteArrayOutputStream _bout; private DataInputStream _din; private DataOutputStream _dout;} Now, we extend FieldBasedRecordMapper to create another abstract class, FieldBasedFilter, which is the base class our filters: package j2me.rms; import javax.microedition.rms *;. // A record filter for filtering records whose data // is mapped to a field list The actual filter will // extend this class and implement the matchFields // method appropriately.. public abstract class FieldBasedFilter extends FieldBasedRecordMapper implements RecordFilter {// Constructs the filter. The optional byte // array is an array that we want ignored, // usually the first record in the record store // where we store the field information. protected FieldBasedFilter () {This (null);} protected fieldbasedfilter (byte [] ignore) {_IGNORE = ignore;} // compares t Wo Byte Arrays. Private Boolean Equal (Byte [] A1, BYTE [] A2) {INT LEN = A1.LENGTH; IF (Len! = A2.LENGTH) Return False; for (INT i = 0; i new FieldList (5); empFields.setFieldType (0, FieldList.TYPE_INT); empFields.setFieldName (0, "ID"); empFields.setFieldType (1, FieldList.TYPE_STRING); empFields.setFieldName (1, "Given Name") ; empFields.setFieldType (2, FieldList.TYPE_STRING); empFields.setFieldName (2, "Last Name"); empFields.setFieldType (3, FieldList.TYPE_BOOLEAN); empFields.setFieldName (3, "Active"); empFields.setFieldType ( 4, FIELDLIST.TYPE_CHAR); Empfields.setfieldName (4, "SEX"); ... a filter matching a specific last name looks similar to: package j2me.rms; import java.ioException; // a filter That matches a specific last name // in an employee record.public class MatchLastName extends FieldBasedFilter {public MatchLastName (String name) {this (name, null);} public MatchLastName (String name, byte [] ignore) {super (ignore); _Name = name;} protected boilean matchfields () {try {readfield; readfield (fieldList.Type_String); string ln = (string) Readfield (FIELDLIST.TYPE_STRING); return ln.equals (_name);} catch false;}} private string _name;} Discovery matching record is a simple thing: ... RecordStore Employees =. .. // list of employeesRecordFilter lname = new MatchLastName ( "Smith"); RecordEnumeration enum = employees.enumerateRecords (lname, null, false); while (enum.hasNextElement ()) {int id = enum.nextRecordId ();. .. // etc. etc ./} enum.destroy (); ... carefully encoded, you can avoid opening any unwanted records. One way you can take is to cache the most recent records in memory. For example, whenever a filter matches a record, open the record and store it in the cache, which is usually a single entity, such as an employee. Use its record ID as a keyword - of course, you need to store ID in the record. When you traverse this enumeration, check the cache that has been opened before visiting the underlying recording store. In fact, use enumeration methods to collect and open the matching record and open to separate lists. Consider the Contact class we defined in Part 2, its simple TobyTearray () and frombyteaRray () methods are used to map between instances and byte arrays: package java.io. *; / / The contact information for a personpublic class Contact {private String _firstName; private String _lastName; private String _phoneNumber; public Contact () {} public Contact (String firstName, String lastName, String phoneNumber) {_firstName = firstName; _lastName = lastName; _phoneNumber = phoneNumber;} public String getFirstName () {return _firstName = null _firstName: "";} public String getLastName () {return _lastName = null _lastName:!? "!?";} public String getPhoneNumber () {return _phoneNumber =! null _phoneNumber:? "";} public void setFirstName (String name) {_firstName = name;} public void setLastName (String name) {_lastName = name;} public void setPhoneNumber (String number) {_phoneNumber = number; } Public void fromByteArray (byte [] data) throws IOException {ByteArrayInputStream bin = new ByteArrayInputStream (data); DataInputStream din = new DataInputStream (bin); fromDataStream (din); din.close ();} public byte [] toByteArray () throws IOException {ByteArrayOutputStream bout = new ByteArrayOutputStream (); DataOutputStream dout = new DataOutputStream (bout); toDataStream (dout); dout.close (); return bout.toByteArray (); } Public void fromDataStream (DataInputStream din) throws IOException {_firstName = din.readUTF (); _lastName = din.readUTF (); _phoneNumber = din.readUTF ();} public void toDataStream (DataOutputStream dout) throws IOException {dout.writeUTF ( getFirstname ()); Dout.Writeutf (getLastName ()); Dout.WriteUTF (getPhoneNumber ());}} The following class uses the recording filter as a discovery and storage matching record. It returns False to indicate an empty enumeration, which is immediately discarded: package java.Example; import java.io. *; Import java.util. *; Import javax.microedition.rms. *; // finds the contacts whose first and / or last // names match the given values.public class FindContacts {// Constructs the finder for the given names. If // both names are non-null, both names must match, // otherwise only the given name needs to match public FindContacts (String fname, String lname) {_fname = normalize (fname); _lname = normalize (lname);}.. // traverses the data in the record store and // returns a list of matching Contact objects public Vector list (RecordStore rs) throws RecordStoreException, IOException {Vector v = new Vector (); Filter f = new Filter (v); RecordEnumeration enum = rs.enumerateRecords (f, null, false); // The enum will never have any elements in it, // but call this to force it to travel erse // its list enum.hasNextElement ();. enum.destroy (); return v;} // Returns whether or not a given Contact // instance matches our criteria public boolean matchesContact (Contact c) {boolean sameFirst = false. Boolean Samelast = false; if (_fname! = Null) {SameFirst = C.GetFirstName (). TOLOWERCASE (). Equals (_fname);} if (_lname! = Null) {Samelast = c.getlastname (). TOLOWERCASE ). Equals (_lname);} if (_fname! = null& _lname! = null) {Return Samefirst && Samelast;