/********************************************************************************* * TotalCross Software Development Kit * * Copyright (C) 2000-2012 SuperWaba Ltda. * * All Rights Reserved * * * * This library and virtual machine is distributed in the hope that it will * * be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of * * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. * * * * This file is covered by the GNU LESSER GENERAL PUBLIC LICENSE VERSION 3.0 * * A copy of this license is located in file license.txt at the root of this * * SDK or can be downloaded here: * * http://www.gnu.org/licenses/lgpl-3.0.txt * * * *********************************************************************************/ package totalcross.util; import totalcross.io.*; import totalcross.sys.Convert; /** * Used to store properties pairs (key,value). A hashtable is used to * store them. Currently, the key must be a String and the value must be a Value * The properties can be saved and loaded to/from a DataStream. * Here's a sample: * <pre> Properties props = new Properties(); File file; //*************************************** // load properties that already exists //*************************************** if (existe_arquivo(arquivo)) { file = new File(arquivo, File.READ_WRITE); props.load(new DataStream(file)); file.close(); } //*************************************** props.put(propriedade,new Properties.Str(valor)); file = new File(arquivo, File.CREATE); props.save(new DataStream(file)); file.close(); * </pre> * @see totalcross.util.Properties.Value * @see totalcross.util.Properties.Str * @see totalcross.util.Properties.Int * @see totalcross.util.Properties.Double * @see totalcross.util.Properties.Boolean * @see totalcross.util.Properties.Long */ public class Properties { private Hashtable props; /** * Avoids that the load method gets into an infinite loop if the file is * empty or corrupted. This constant limits the number of properties to 1000. * If you are saving more than 1000 props, just change this max value. * */ public static int MAX_PROPS = 1000; // guich@556_2 public Properties() { props = new Hashtable(13); } /** Stores the given keys/values pairs in a new Properties. */ public Properties(String[] keys, Value[] values) { int n = keys.length; props = new Hashtable(n << 1); for (int i = 0; i < n; i++) props.put(keys[i], values[i]); } /** Represents a generic value that can be stored here. */ public abstract static class Value { /** Read-only property, which identifies the value type */ public char type; public abstract String toString(); /** The full name of the type. */ public String typeStr; } /** Implements a value of type String */ public static class Str extends Value { public final static char TYPE = 'S'; public String value; public Str(String value) { this.value = value; type = TYPE; typeStr = "String"; } public String toString() { return value; } /** * Returns a hash code for this Str. * * @return the hash code of the enclosed String value. * @since TotalCross 1.25 */ public int hashCode() { return value.hashCode(); } /** * Compares this object to the specified object. The result is true if and only if the argument is not null and is * a String object, or another Str object, that represents the same sequence of characters as this object. * * @since TotalCross 1.25 */ public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof String) return value.equals(obj); if (obj instanceof Str) return value.equals(((Str) obj).value); return false; } } /** Implements a value of type int */ public static class Int extends Value { public final static char TYPE = 'I'; public int value; public Int(int value) { this.value = value; type = TYPE; typeStr = "int"; } public String toString() { return Convert.toString(value); } /** * Returns a hash code for this Int. * * @return a hash code value for this object, equal to the primitive int value represented by this Int object. * @since TotalCross 1.25 */ public int hashCode() { return value; } /** * Compares this object to the specified object. The result is true if and only if the argument is not null and is * an Int object that contains the same int value as this object. * * @since TotalCross 1.25 */ public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof Int) return value == ((Int) obj).value; return false; } } /** Implements a value of type double */ public static class Double extends Value { public final static char TYPE = 'D'; public double value; public Double(double value) { this.value = value; type = TYPE; typeStr = "double"; } public String toString() { return Convert.toString(value); } /** * Returns a hash code for this <code>Double</code> object. The result is the exclusive OR of the two halves of * the <code>long</code> integer bit representation, exactly as produced by the method * {@link Convert#doubleToLongBits(double) doubleToLongBits(double)}, of the primitive <code>double</code> value * represented by this <code>Double</code> object. That is, the hash code is the value of the expression: * <blockquote> * * <pre> * (int) (v ˆ (v >>> 32)) * </pre> * * </blockquote> where <code>v</code> is defined by: <blockquote> * * <pre> * long v = Convert.doubleToLongBits(this.value); * </pre> * * </blockquote> * * @since TotalCross 1.25 */ public int hashCode() { long v = Convert.doubleToLongBits(value); return (int)(v^(v>>>32)); } /** * Compares this object against the specified object. The result is <code>true</code> if and only if the argument * is not <code>null</code> and is a <code>Double</code> object that represents a <code>double</code> that has the * same value as the <code>double</code> represented by this object. For this purpose, two <code>double</code> * values are considered to be the same if and only if the method {@link Convert#doubleToLongBits(double) * doubleToLongBits(double)} returns the identical <code>long</code> value when applied to each. * <p> * Note that in most cases, for two instances of class <code>Double</code>, <code>d1</code> and <code>d2</code>, * the value of <code>d1.equals(d2)</code> is <code>true</code> if and only if <blockquote> * * <pre> * Convert.doubleToLongBits(d1.value) == Convert.doubleToLongBits(d2.value) * </pre> * * </blockquote> * <p> * also has the value <code>true</code>. However, there are two exceptions: * <ul> * <li>If <code>d1</code> and <code>d2</code> both represent <code>Convert.DOUBLE_NAN_BITS</code>, then the * <code>equals</code> method returns <code>true</code>, even though * <code>Convert.doubleToLongBits(Convert.DOUBLE_NAN_BITS) == Convert.doubleToLongBits(Convert.DOUBLE_NAN_BITS)</code> * has the value <code>false</code>. * <li>If <code>d1</code> represents <code>+0.0</code> while <code>d2</code> represents <code>-0.0</code>, or vice * versa, the <code>equal</code> test has the value <code>false</code>, even though <code>+0.0==-0.0</code> has * the value <code>true</code>. * </ul> * This definition allows hash tables to operate properly. * * @since TotalCross 1.25 */ public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof Double) return Convert.doubleToLongBits(value) == Convert.doubleToLongBits(((Double) obj).value); return false; } } /** Implements a value of type boolean */ public static class Boolean extends Value { public final static char TYPE = 'B'; public boolean value; public Boolean(boolean value) { this.value = value; type = TYPE; typeStr = "boolean"; } public String toString() { return value ? "1" : "0"; } /** * Returns a hash code for this Boolean object. * * @return the integer 1231 if this object represents true; returns the integer 1237 if this object represents * false. * @since TotalCross 1.25 */ public int hashCode() { return value ? 1231 : 1237; } /** * Returns true if and only if the argument is not null and is a Boolean object that represents the same boolean * value as this object. * * @since TotalCross 1.25 */ public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof Boolean) return value == ((Boolean) obj).value; return false; } } /** Implements a value of type long */ public static class Long extends Value { public final static char TYPE = 'L'; public long value; public Long(long value) { this.value = value; type = TYPE; typeStr = "long"; } public String toString() { return Convert.toString(value); } /** * Returns a hash code for this <code>Long</code>. The result is the exclusive OR of the two halves of the * primitive <code>long</code> value held by this <code>Long</code> object. That is, the hashcode is the value of * the expression: <blockquote> * * <pre> * (int) (this.value ˆ (this.value >>> 32)) * </pre> * * </blockquote> * * @since TotalCross 1.25 */ public int hashCode() { return (int)(value^(value>>>32)); } /** * Compares this object to the specified object. The result is true if and only if the argument is not null and is * a Long object that contains the same long value as this object. * * @since TotalCross 1.25 */ public boolean equals(Object obj) { if (this == obj) return true; if (obj instanceof Long) return value == ((Long) obj).value; return false; } } /** Put the given key/value pair in the hashtable that stores the properties. */ public void put(String key, Value v) { props.put(key, v); } /** Get the value given the key from the hashtable that stores the properties. */ public Value get(String key) { return (Value) props.get(key); } /** Returns the number of properties */ public int size() { return props.size(); } /** Returns a Vector with the current keys */ public Vector getKeys() { return props.getKeys(); } /** Clears this property */ public void clear() { props.clear(); } /** Remove a value from the property */ public void remove(String key) { props.remove(key); } /** Save all properties in the given DataStream * @throws totalcross.io.IOException */ public void save(DataStream ds) throws totalcross.io.IOException { // get all keys, store everything in database Vector vec = props.getKeys(); int n = vec.size(); for (int i = 0; i < n; i++) { String key = (String) vec.items[i]; Value v = (Value) props.get(key); byte type = (byte) v.type; ds.writeByte(type); ds.writeString(key); switch (type) { case Str.TYPE: ds.writeString(((Str) v).value); break; case Int.TYPE: ds.writeInt(((Int) v).value); break; case Double.TYPE: ds.writeDouble(((Double) v).value); break; case Boolean.TYPE: ds.writeBoolean(((Boolean) v).value); break; case Long.TYPE: ds.writeLong(((Long) v).value); break; } } ds.writeByte(255); } /** * Load all properties from the given DataStream. Before calling this method, * be sure that there's something to be read (ie, that the file is not * empty), otherwise it may run in an infinite loop and will freeze your * device. * * @throws totalcross.io.IOException */ public void load(DataStream ds) throws totalcross.io.IOException { load(ds, true); } /** * Load properties from the given DataStream. If cleanBeforeLoad is true, the contents of this object will be cleared * before reading from the DataStream. * * @param ds * @param cleanBeforeLoad * @throws totalcross.io.IOException */ public void load(DataStream ds, boolean cleanBeforeLoad) throws totalcross.io.IOException { if (cleanBeforeLoad) props.clear(); // read and populate the options hashtable from pdb try { for (int i = MAX_PROPS; i >= 0; i--) { byte type = ds.readByte(); String key = ds.readString(); Value v = null; switch (type) { case Str.TYPE: v = new Str(ds.readString()); break; case Int.TYPE: v = new Int(ds.readInt()); break; case Double.TYPE: v = new Double(ds.readDouble()); break; case Boolean.TYPE: v = new Boolean(ds.readBoolean()); break; case Long.TYPE: v = new Long(ds.readLong()); break; } if (v != null) props.put(key, v); } } catch (EOFException e) { // Don't throw EOF. } } /** * Dumps the keys and values into the given StringBuffer. The values are dumped as Strings, exactly like the * Hashtable implementation. * * @param sb * The StringBuffer where the data will be dumped to * @param keyvalueSeparator * The separator between the key and the value (E.G.: ": ") * @param lineSeparator * The separator placed after each key+value pair (E.G.: "\r\n"). The last separator is cut from the * StringBuffer. * @return the received StringBuffer sb * @since TotalCross 1.25 */ public StringBuffer dumpKeysValues(StringBuffer sb, String keyvalueSeparator, String lineSeparator) { return props.dumpKeysValues(sb, keyvalueSeparator, lineSeparator); } }