TC65 : Settings management
December 11, 2009 — Florent ClairambaultSomeone 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).
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 | 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() > 0 ) { int c = is.read(); if ( c == '\n' ) { Load_TreatLine( 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 Load_TreatLine( Hashtable settings, String line ) { if ( Logger.E_VERBOSE ) Logger.Log( 78, "Load_TreatLine( [...], \"" + line + "\" );" ); String[] spl = Common.strSplit( '=', line ); String key = spl[0]; 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 ResetErything() { 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 ); String 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 ) != 0 ) { 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.