Implementing the data model

DataModel

The DataModel class provides a storage for the guide data in the MIDlet.

The class implements the singleton pattern, and the getInstance method provides access to the singleton object.

    public static DataModel getInstance() {
        if (self == null) {
            self = new DataModel();
        }
        return self;
    }

The user's current position is stored in the singleton object. The distanceToAttraction method returns the distance from the user's current position, if known, to the given attraction. The method returns a string, so the distance can be returned either in meters or kilometers.

    private String distanceToAttraction(Attraction attraction) {
        final GeoCoordinate position = currentPosition;
        if (position == null) {
            return null;
        }
        double distance = position.distanceTo(attraction.getLocation());
        if (distance < 1000.0) {
            return ((int) (distance)) + " m";
        }
        else {
            String d = String.valueOf(distance % 1000).substring(0, 1);
            return (int) (distance / 1000) + "." + d + " km";
        }
    }

The updateDistances method updates the distances from the user's position to the attractions.

    private void updateDistances() {
        Vector attrs = attractions;
        if (attrs != null) {
            for (int i = 0; i < attrs.size(); i++) {
                Attraction a = (Attraction) attrs.elementAt(i);
                a.setDistance(distanceToAttraction(a));
            }
        }
    }

When the user exits the MIDlet, the saveGuides method stores the purchased guides to the record store.

    public final void saveGuides() {
        try {
            RecordStore store = RecordStore.openRecordStore("Guides", true);
            if (store.getNumRecords() == 0) {
                store.addRecord(null, 0, 0);
            }
            byte[] data = guidesState();
            store.setRecord(getRecordId(store), data, 0, data.length);
            store.closeRecordStore();
        }
        catch (Exception e) {
            try {
                RecordStore.deleteRecordStore("Guides");
            }
            catch (RecordStoreException rse) {
            }
        }
    }

    private int getRecordId(RecordStore store)
        throws RecordStoreException {
        RecordEnumeration e = store.enumerateRecords(null, null, false);
        try {
            return e.nextRecordId();
        }
        finally {
            e.destroy();
        }
    }

    private byte[] guidesState() {
        ByteArrayOutputStream bout = null;
        try {
            bout = new ByteArrayOutputStream();
            DataOutputStream dout = new DataOutputStream(bout);
            dout.writeInt(guides.size());
            for (int i = 0, size = guides.size(); i < size; i++) {
                ((Guide) guides.elementAt(i)).writeTo(dout);
            }
            dout.writeInt(currentGuideIndex);
            return bout.toByteArray();
        }
        catch (IOException e) {
        }
        finally {
            try {
                if (bout != null) {
                    bout.close();
                }
            }
            catch (IOException e) {
            }
        }
        return new byte[0];
    }

When the user starts the MIDlet, the loadGuides method loads the purchased guides from the record store.

    public final void loadGuides() {
        try {
            RecordStore store = RecordStore.openRecordStore("Guides", true);
            if (store.getNumRecords() == 0
                || !loadGuides(store.getRecord(getRecordId(store)))) {
                guides = new Vector();
            }
            store.closeRecordStore();
        }
        catch (RecordStoreException e) {
            guides = new Vector();
        }
    }

    private boolean loadGuides(byte[] record) {
        if (record == null) {
            return false;
        }
        try {
            DataInputStream din = new DataInputStream(new ByteArrayInputStream(
                record));
            guides = new Vector();
            int size = din.readInt();
            while (size > 0) {
                guides.addElement(Guide.readFrom(din));
                size--;
            }
            currentGuideIndex = din.readInt();
            return true;
        }
        catch (IOException e) {
        }
        return false;
    }

Guide

The Guide class represents a tourist guide.

The id field uniquely identifies guides, so the equals and hashCode methods are overwritten to use only the ID field.

    /**
     * @see Object#equals(java.lang.Object)
     */
    public final boolean equals(Object o) {
        if (o != null && o instanceof Guide) {
            return this.id.equals(((Guide) o).id);
        }
        return false;
    }

    /**
     * @see Object#hashCode()
     */
    public final int hashCode() {
        return this.id.hashCode();
    }

The writeTo method writes the fields that are needed to download a guide from the back-end server to a data output stream.

    public final void writeTo(DataOutputStream dout)
        throws IOException {
        dout.writeUTF(id);
        dout.writeUTF(purchaseTicket);
        dout.writeUTF(url);
        dout.writeUTF(imageUrl);
        dout.writeUTF(city);
        dout.writeUTF(account);
    }

The readFrom method creates the guide from the data input stream.

    public static Guide readFrom(DataInputStream din)
        throws IOException {
        Guide guide = new Guide();
        guide.id = din.readUTF();
        guide.purchaseTicket = din.readUTF();
        guide.url = din.readUTF();
        guide.imageUrl = din.readUTF();
        guide.city = din.readUTF();
        guide.account = din.readUTF();
        return guide;
    }

Attraction

The Attraction class represents an attraction in a tourist guide. An attraction contains an image, description text, and coordinates.