Hashtable Interface to MIDP RecordStore

The javax.microedition.rms package provides the Record Management System (RMS) implementation. It is a kind of DBMS, but its API doesnot resemble JDBC or any of other J2SE APIs. The primary concept of RMS is the RecordStore. The RecordStore element is the Record, and Record's essence is a byte array.

I have created a special adaptor to the RMS. It doesnot cover all the RMS capabilities but allows us to use RMS as a backend for a Map-like object structure. There is no java.util.Map interface in MIDP, so our "RecordMap" adapter uses java.util.Hashtable as a pattern. Even more, it extends java.util.Hashtable. It is a pure interface inheritance, all the methods are overridden in our RecordMap.

Our RecordMap is configurable in that it can be parameterized with different implementations of our own EntryMapper interface to provide the concrete functionality of the data mapping between Hashtable key-value pairs and RecordStore byte arrays.

package j2mefancier;

public interface EntryMapper {

byte[] bytes(Object key, Object value);

Object key(byte[] bytes);

Object value(byte[] bytes);

}

Then, here is a couple of small utility classes used by our RecordMap:

package j2mefancier;

import java.util.*;
import javax.microedition.rms.*;

public class EnumAdapter implements Enumeration {

private RecordEnumeration re;
private EntryMapper mapper;
private boolean isKey;

public EnumAdapter(RecordStore rs, EntryMapper mapper, boolean isKey) throws RecordStoreException {
this.re = rs.enumerateRecords(null, null, false);
this.mapper = mapper;
this.isKey = isKey;
}

public boolean hasMoreElements() {
return re.hasNextElement();
}

public Object nextElement() {
try {
byte[] bytes = re.nextRecord();
return isKey ? mapper.key(bytes) : mapper.value(bytes);
} catch (RecordStoreException e) {
return null;
}
}

}
package j2mefancier;

import javax.microedition.rms.*;

public class Filter implements RecordFilter {

private Object obj;
private EntryMapper mapper;
private boolean isKey;

public Filter(Object obj, EntryMapper mapper, boolean isKey) {
if (obj == null) throw new NullPointerException();
this.obj = obj;
this.mapper = mapper;
this.isKey = isKey;
}

public boolean matches(byte[] candidate) {
return obj.equals(isKey ? mapper.key(candidate) : mapper.value(candidate));
}

}

Now, look at the implementation of the EntryMapper interface I use for own purposes. Note that it allows only String keys and values, so the best use case of this implementation is for RMS backend of Properties-like data. Again, there is no java.util.Properties in MIDP, otherwise I'd prefer to use Properties API instead of Hashtable one. Other use cases require more intelligent entry mappers.

package j2mefancier;

import java.io.*;

public class EntryMapperImpl implements EntryMapper {

public byte[] bytes(Object key, Object value) {
if (! (key instanceof String) || ! (value instanceof String))
throw new IllegalArgumentException();
ByteArrayOutputStream baos = new ByteArrayOutputStream();
DataOutputStream dos = new DataOutputStream(baos);
try {
dos.writeUTF(key.toString());
dos.writeUTF(value.toString());
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
return baos.toByteArray();
}

public Object key(byte[] bytes) {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
try {
return dis.readUTF();
} catch (IOException e) {
return null;
}
}

public Object value(byte[] bytes) {
DataInputStream dis = new DataInputStream(new ByteArrayInputStream(bytes));
try {
dis.readUTF();
return dis.readUTF();
} catch (IOException e) {
return null;
}
}

}

And here is the heart of the thing: the RecordMap class. I don't insist on its implementation optimality etc., the main purpose is to demonstrate the idea. Probably you can develop more efficient implementations for some use cases and also add more clever exception handling. The usual Hashtable toString() functionality is omitted to keep the code more compact.

package j2mefancier;

import java.util.*;
import javax.microedition.rms.*;

public class RecordMap extends Hashtable {

private RecordStore rs;
private EntryMapper mapper;

public RecordMap(RecordStore rs, EntryMapper mapper) {
this.rs = rs;
this.mapper = mapper;
}

public void clear() {
try {
RecordEnumeration re = rs.enumerateRecords(null, null, false);
while (re.hasNextElement()) rs.deleteRecord(re.nextRecordId());
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public boolean contains(Object value) {
try {
RecordEnumeration re = rs.enumerateRecords(new Filter(value, mapper, false), null, false);
return re.hasNextElement();
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public boolean containsKey(Object key) {
return get(key) != null;
}

public Enumeration elements() {
try {
return new EnumAdapter(rs, mapper, false);
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public Object get(Object key) {
try {
RecordEnumeration re = rs.enumerateRecords(new Filter(key, mapper, true), null, false);
if (re.hasNextElement()) return mapper.value(re.nextRecord());
return null;
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public boolean isEmpty() {
return size() == 0;
}

public Enumeration keys() {
try {
return new EnumAdapter(rs, mapper, true);
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public Object put(Object key, Object value) {
if (value == null) throw new NullPointerException();
try {
Object old = remove(key);
byte[] bytes = mapper.bytes(key, value);
rs.addRecord(bytes, 0, bytes.length);
return old;
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public Object remove(Object key) {
try {
RecordEnumeration re = rs.enumerateRecords(new Filter(key, mapper, true), null, false);
if (re.hasNextElement()) {
Object old = mapper.value(re.nextRecord());
rs.deleteRecord(re.previousRecordId());
return old;
}
return null;
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public int size() {
try {
return rs.getNumRecords();
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

public String toString() {
try {
return "RecordMap-" + rs.getName();
} catch (RecordStoreException e) {
throw wrapped(e);
}
}

private RuntimeException wrapped(RecordStoreException e) {
return new RuntimeException(e.getMessage());
}

}
 
發佈了11 篇原創文章 · 獲贊 0 · 訪問量 2萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章