Someone recently asked me and the javacint group how we should handle settings. So I’ll give you two answers :

  • ejw’s reply : You should use some simple DataInputStream and DataOutputStream objects.
  • Mine : If you only store simple values (numbers and text), you should use a simple text file. It enables you to easily see and modify settings outside the TC65.

So, my little gift of today will be a simple settings management class. The idea is that this file is in a very simple format (that can be read in any PC) and it only stores settings that have changed. This is very important, it allows you to change the default behavior at next software update but also enable you to override some of the settings for each chip (like the last IMSI identifier of the SIM card).

package Common;
 
import java.io.*;
import java.util.*;
import javax.microedition.io.*;
import com.siemens.icm.io.file.*;
 
/**
 * Settings management class
 * @author Florent Clairambault
 */
public class Settings {
 
        // The cached settings
    private Hashtable _settings;
 
        // File of the settings
    private final String _fileName = "a:/settings.txt";
 
        // Singleton instance of the settings
        private static Settings _instance;
 
    /**
     * Get Default instance of the Settings class
     * @return The default instance of the Settings class
     */
    public static synchronized Settings getInstance() {
        if ( _instance == null )
            _instance = new Settings();
        return _instance;
    }
 
    /**
     * Free the singleton instance
     */
    public static synchronized void freeInstance() {
        _instance = null;
    }
 
    /**
     * Load settings
     */
    public synchronized void load() {
        StringBuffer buffer = new StringBuffer();
        Hashtable settings = getDefaultSettings();
        try {
 
 
            FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ );
            InputStream is = fc.openInputStream();
 
            while ( is.available() >  ) {
                int c = is.read();
 
                if ( c == '\n' ) {
                    loadTreatLine( settings, buffer.toString() );
                    buffer = new StringBuffer();
                } else
                    buffer.append( (char) c );
            }
            is.close();
            fc.close();
 
        } catch ( IOException ex ) {
            // The exception we shoud have is at first launch : 
            // There shouldn't be any file to read from
 
            if ( Logger.E_CRITICAL )
                Logger.Log( 19, "Settings.Load", ex );
        }
        _settings = settings;
    }
 
    /**
     * Treat each line of the file
     * @param def Default settings
     * @param line Line to parse
     */
    private static void loadTreatLine( Hashtable settings, String line ) {
        if ( Logger.E_VERBOSE )
            Logger.Log( 78, "loadTreatLine( [...], \"" + line + "\" );" );
        String[] spl = Common.strSplit( '=', line );
        String key = spl[];
        String value = spl[1];
 
        // If default settings hashTable contains this key
        // we can use this value
        if ( settings.containsKey( key ) ) {
            settings.remove( key );
            settings.put( key, value );
        }
 
    }
 
 
    /**
     * Get default settings
     * @return Default settings Hashtable
     */
    private Hashtable getDefaultSettings() {
        Hashtable defaultSettings = new Hashtable();
 
        // General M2MSoft settings :
        defaultSettings.put( "code", "8888" );
        defaultSettings.put( "servers", "87.106.206.30:3000" );
        defaultSettings.put( "apn", "gprs,m2minternet,\"\",\"\",,0" );
        defaultSettings.put( "imsi", "0000" );
        defaultSettings.put( "watchdogtimer", "20" );
        defaultSettings.put( "version", "0" );
        defaultSettings.put( "phoneManager", "+33686955405" );
 
        return defaultSettings;
    }
 
    /**
     * Reset everything
     */
    public synchronized void resetEverything() {
        try {
            FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ_WRITE );
            if ( fc.exists() )
                fc.delete();
                        _settings = new Hashtable();
        } catch ( Exception ex ) {
            if ( Logger.E_CRITICAL )
                Logger.Log( 16725, "Settings.ResetEverything", ex );
        }
    }
 
    /** 
     * save setttings
     */
    public synchronized void save() {
 
        // If there's no settings, we shouldn't have to save anything
        if ( _settings == null )
            return;
 
        try {
            Hashtable defSettings = getDefaultSettings();
            Enumeration e = defSettings.keys();
            FileConnection fc = (FileConnection) Connector.open( "file:///" + _fileName, Connector.READ_WRITE );
            if ( fc.exists() )
                fc.delete();
            //fc = (FileConnection) Connector.open("file:///" + _fileName, Connector.READ_WRITE);
            fc.create();
            OutputStream os = fc.openOutputStream();
 
            while ( e.hasMoreElements() ) {
                String key = (String) e.nextElement();
                String value = (String) _settings.get( key );
                <a href="http://www.google.com/search?hl=en&q=allinurl%3Astring+java prix viagra pharmacie france.sun.com&btnI=I%27m%20Feeling%20Lucky">String</a> defValue = (String) defSettings.get( key );
 
                if ( // if there is a default value
                    defValue != null && // and
                    // the value isn't the same as the default value
                    defValue.compareTo( value ) !=  ) {
                    String line = key + "=" + value + '\n';
 
                    if ( Logger.E_DEBUG )
                        Logger.Log( 131, "Settings.save.line = \"" + line + "\"" );
 
                    os.write( line.getBytes() );
                }
 
            }
            os.flush();
            os.close();
            fc.close();
        } catch ( Exception ex ) {
            if ( Logger.E_CRITICAL )
                Logger.Log( 131, "Settings.save", ex );
        }
 
    }
 
    /**
     * Init (and ReInit) method
     */
    private void checkLoad() {
        if ( _settings == null )
            load();
    }
 
    /**
     * Get a setting's value as a String
     * @param key Key Name of the setting
     * @return String value of the setting
     */
    public synchronized String getSetting( String key ) {
        checkLoad();
        if ( _settings.containsKey( key ) )
            return (String) _settings.get( key );
        else
            return null;
    }
 
    /**
     * Sets a setting
     * @param key Setting to set
     * @param value Value of the setting
     */
    public synchronized void setSetting( String key, String value ) {
        checkLoad();
        if ( _settings.containsKey( key ) )
            _settings.remove( key );
 
        _settings.put( key, value );
 
        OnSettingChanged( key );
    }
 
    /**
     * Get a setting's value as an int
     * @param key Key Name of setting
     * @return Integer value of the setting
     * @throws java.lang.NumberFormatException When the int cannot be parsed
     */
    public int getSettingInt( String key ) throws NumberFormatException {
        String value = getSetting( key );
 
        if ( value == null )
            return -1;
 
        return Integer.parseInt( value );
    }
}

Latter on, I added some settings consumer capacities. It allows to use this class in some sort of “third party” developed components. Each component had to register itself to the settings management class, the settings management class could then request the default value settings it will provide, and it could set/get settings. The settings event class also launched an event when a setting was changed.