Index: lutinutil/src/java/org/codelutin/util/MyProperties.java
diff -u /dev/null lutinutil/src/java/org/codelutin/util/MyProperties.java:1.1
--- /dev/null Sun Dec 9 20:01:47 2007
+++ lutinutil/src/java/org/codelutin/util/MyProperties.java Sun Dec 9 20:01:42 2007
@@ -0,0 +1,593 @@
+/*
+* ##% Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007 Code Lutin,
+* Benjamin Poussin, Tony Chemit, and others
+*
+*
+* This program is free software; you can redistribute it and/or
+* modify it under the terms of the GNU General Public License
+* as published by the Free Software Foundation; either version 2
+* of the License, or (at your option) any later version.
+*
+* This program 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. See the
+* GNU General Public License for more details.
+*
+* You should have received a copy of the GNU General Public License
+* along with this program; if not, write to the Free Software
+* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
+* ##% */
+package org.codelutin.util;
+
+import org.apache.commons.beanutils.ConvertUtils;
+import static org.codelutin.i18n.I18n._;
+import org.codelutin.util.MyPropertiesUtil.MyPropertyDef;
+import org.codelutin.util.MyPropertiesUtil.MyPropertyKey;
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.EnumSet;
+import java.util.List;
+import java.util.Map;
+import java.util.Properties;
+import java.util.SortedMap;
+import java.util.TreeMap;
+
+/**
+ * This class encapsulate a {@link Properties} object with more
+ * friendly methods to obtain or set some valued values automaticly.
+ *
+ * The class is parametrized by the class of an enum which contains keys to use.
+ *
+ * And more the typed class must also be a {@link MyPropertyKey} class.
+ *
+ * Use Factory methods {@link #newInstance(Class)} and variants one.
+ *
+ * Date: 8 déc. 2007
+ *
+ * @author tony
+ * @see org.codelutin.util.MyPropertiesUtil.MyPropertyKey
+ */
+public class MyProperties> {
+
+ public static > MyProperties newInstance(Class keyType) {
+ MyProperties myProperties;
+ myProperties = new MyProperties(keyType);
+ return myProperties;
+ }
+
+ public static > MyProperties newInstance(Class keyType, Properties data) {
+ MyProperties myProperties = newInstance(keyType);
+ myProperties.load(data);
+ return myProperties;
+ }
+
+ public static > MyProperties newInstance(Class keyType, Reader reader) throws IOException {
+ MyProperties myProperties = newInstance(keyType);
+ myProperties.load(reader);
+ return myProperties;
+ }
+
+ public static > MyProperties newInstance(Class keyType, InputStream inStream) throws IOException {
+ MyProperties myProperties = newInstance(keyType);
+ myProperties.load(inStream);
+ return myProperties;
+ }
+
+ public static > MyProperties newInstanceFromXml(Class keyType, InputStream inStream) throws IOException {
+ MyProperties myProperties = newInstance(keyType);
+ myProperties.loadFromXML(inStream);
+ return myProperties;
+ }
+
+ /** the internal Properties object used for io operations */
+ protected Properties tmp;
+
+ /** the internal map of data indexed by their key */
+ protected SortedMap data;
+
+ /** the internal map of rejected data indexed by their key found */
+ protected SortedMap unsafeData;
+
+ /** the type of key to use, this class must also implements {@link MyPropertyDef} */
+ protected final Class keyType;
+
+ /** the set of all authorized keys */
+ protected EnumSet universe;
+
+ /** the current set of registred keys */
+ protected EnumSet keys;
+
+ /** key manager */
+ protected MyPropertyKeyManager manager;
+
+ public Class getKeyType() {
+ return keyType;
+ }
+
+ public EnumSet getUniverse() {
+ return universe;
+ }
+
+ public EnumSet getKeys() {
+ return keys;
+ }
+
+ public EnumSet getMissingKeys() {
+ return EnumSet.complementOf(keys);
+ }
+
+ public boolean isEmpty() {
+ return size() == 0;
+ }
+
+ public boolean isFull() {
+ return !isEmpty() && getMissingKeys().isEmpty();
+ }
+
+ public boolean isSafe() {
+ return unsafeData == null || unsafeData.isEmpty();
+ }
+
+ public int size() {
+ return data == null ? 0 : data.size();
+ }
+
+ /**
+ * Obtain the value for a given key, with no default value
+ * if not found.
+ *
+ * @param key the key to use
+ * @return the value for the given key, or null if not found
+ */
+ public Object getProperty(T key) {
+ return getProperty(key, null);
+ }
+
+ /**
+ * Obtain the value for a given key,or defaultValue
+ * if not found.
+ *
+ * @param key the key to use
+ * @param defaultValue default value to return if not found
+ * @return the value for the given key, or null if not found
+ */
+ public Object getProperty(T key, Object defaultValue) {
+ checkAuthorizedKey(key);
+ Class> klass = getManager().getType((MyPropertyDef) key);
+ if (!containsKey(key)) {
+ return defaultValue;
+ }
+ Object result;
+ result = getData().get(key);
+ return result;
+ }
+
+ /**
+ * Set safely a value for a given key.
+ *
+ * @param key the key to use
+ * @param value the value to set
+ * @see #setProperty(Enum, Object, boolean)
+ */
+ public void setProperty(T key, Object value) {
+ setProperty(key, value, true);
+ }
+
+ /**
+ * @param key the given key
+ * @return true if key is legal and used
+ * ,false otherwise.
+ */
+ public boolean containsKey(T key) {
+ return universe.contains(key) && keys.contains(key);
+ }
+
+ /**
+ * @param key the given unsafe key, this is the original key of unsafe
+ * property (if a load was done), this is not connected with MyPropertyKey.
+ * @return the value of the given unsafe value, or null if there is not a
+ * such unsafe property.
+ */
+ public Object getUnsafeData(Object key) {
+ if (isSafe()) {
+ return null;
+ }
+ Object result;
+ result = getUnsafeData().get(key);
+ return result;
+ }
+
+ /**
+ * Remove a unsafe property using a given unsage key, if there is a such
+ * unsafe property.
+ *
+ * @param key the key to use
+ */
+ public void removeUnsafeData(Object key) {
+ if (isSafe()) {
+ return;
+ }
+ getUnsafeData().remove(key);
+ }
+
+ /**
+ * clear the instance (data, tmp,unsafeData and keys).
+ *
+ * After the method invocation, the {@link #isEmpty()}'s method's return is
+ * always true.
+ */
+ public void clear() {
+ if (data != null) {
+ data.clear();
+ }
+ if (tmp != null) {
+ tmp.clear();
+ }
+ if (unsafeData != null) {
+ unsafeData.clear();
+ }
+ keys.clear();
+ }
+
+ /**
+ * Load some properties from a given {@link Properties} object.
+ *
+ * @param data the data to inject
+ * @return the current instance
+ */
+ public MyProperties load(Properties data) {
+ if (data == null || data.isEmpty()) {
+ return this;
+ }
+ getTmp().putAll(data);
+ updateUniverse();
+ return this;
+ }
+
+ /**
+ * Load some properties from a given Properties files via a {@link Reader} object.
+ *
+ * @param reader the data to inject
+ * @return the current instance
+ * @throws java.io.IOException if problem while grabbing data.
+ */
+ public MyProperties load(Reader reader) throws IOException {
+ if (reader == null) {
+ return this;
+ }
+ getTmp().load(reader);
+ updateUniverse();
+ return this;
+ }
+
+ /**
+ * Load some properties from a given properties files via a {@link InputStream} object.
+ *
+ * @param inputStream the data to inject
+ * @return the current instance
+ * @throws java.io.IOException if problem while grabbing data.
+ */
+ public MyProperties load(InputStream inputStream) throws IOException {
+ if (inputStream == null) {
+ return this;
+ }
+ getTmp().load(inputStream);
+ updateUniverse();
+ return this;
+ }
+
+ /**
+ * Load some properties from a given xml properties file via a {@link InputStream} object.
+ *
+ * @param inputStream the data to inject
+ * @return the current instance
+ * @throws java.io.IOException if problem while grabbing data.
+ */
+ public MyProperties loadFromXML(InputStream inputStream) throws IOException {
+ if (inputStream == null) {
+ return this;
+ }
+ getTmp().loadFromXML(inputStream);
+ updateUniverse();
+ return this;
+ }
+
+ /**
+ * Try to fill the cuurent instance missing properties using the given
+ * otherProperties object.
+ *
+ * @param otherProperties the other properties object to use
+ * @return the current instance
+ */
+ public MyProperties fillMissingKeys(MyProperties otherProperties) {
+ for (T key : getMissingKeys()) {
+ Object value = otherProperties.getProperty(key);
+ setProperty(key, value);
+ }
+ return this;
+ }
+
+ @Override
+ public String toString() {
+ StringBuilder s = new StringBuilder(super.toString()).append("');
+ if (!isEmpty()) {
+ for (T key : keys) {
+ MyPropertyKey entry = getManager().getEntry((MyPropertyDef) key, false);
+ s.append('\n').append(entry).append(" = ").append(getData().get(key));
+ }
+ }
+ if (!isSafe()) {
+ s.append("\nunsafe:");
+ for (Map.Entry entry : unsafeData.entrySet()) {
+ s.append('\n').append(entry.getKey()).append(" = ").append(entry.getValue());
+ }
+ }
+ return s.toString();
+ }
+
+ /** @return the lazy manager instance */
+ protected MyPropertyKeyManager getManager() {
+ if (manager == null) {
+ manager = MyPropertyKeyManager.getInstance();
+ }
+ return manager;
+ }
+
+ /** @return the lazy tmp instance */
+ protected Properties getTmp() {
+ if (tmp == null) {
+ tmp = new Properties();
+ }
+ return tmp;
+ }
+
+ /** @return the lazy data instance */
+ protected Map getData() {
+ if (data == null) {
+ data = new TreeMap();
+ }
+ return data;
+ }
+
+ /** @return the lazy unsafeData instance */
+ protected Map getUnsafeData() {
+ if (unsafeData == null) {
+ unsafeData = new TreeMap();
+ }
+ return unsafeData;
+ }
+
+ /**
+ * @param def def to use
+ * @return the {@link MyPropertyKey#getKey()} value for the given entry
+ * associated to the given def
+ */
+ protected String getKey(T def) {
+ return getManager().getKey((MyPropertyDef) def);
+ }
+
+ /**
+ * @param def def to use
+ * @return the {@link MyPropertyKey#getType()} value for the given entry
+ * associated to the given def
+ */
+ protected Class> getType(T def) {
+ return getManager().getType((MyPropertyDef) def);
+ }
+
+ /**
+ * Test if a def is authorized in the current{@link #universe}.
+ *
+ * If not throw an {@link IllegalArgumentException}.
+ *
+ * @param def the given def to test
+ */
+ protected void checkAuthorizedKey(T def) {
+ if (def == null || !universe.contains(def)) {
+ throw new IllegalArgumentException(_("lutinutil.error.config.unauthorized.key", def, universe.toString()));
+ }
+ }
+
+ /**
+ * Set the value for a given property using his def.
+ *
+ * @param def the given key to use
+ * @param value the given valuer to set
+ * @param safe the flag to say do it safely or not (if safe then it
+ * will throw exceptions if found errors).
+ */
+ protected void setProperty(T def, Object value, boolean safe) {
+ checkAuthorizedKey(def);
+ if (value == null) {
+ removeProperty(def);
+ return;
+ }
+ MyPropertyDef def2 = (MyPropertyDef) def;
+ MyPropertyKey entry = getManager().getEntry(def2, true);
+ Class> klass = entry.getType();
+
+ if (klass == String.class) {
+ addProperty(def, String.valueOf(value));
+ return;
+ }
+
+ if (value instanceof String) {
+ // make a conversion from String
+ Object typeValue = convert(entry.getType(), value);
+ if (typeValue == null) {
+ addUnsafeData(entry.getKey(), klass, value);
+ } else {
+ addProperty(def, typeValue);
+ }
+ return;
+ }
+
+ if (value.getClass() == klass) {
+ // add a matching typed value
+ addProperty(def, value);
+ return;
+ }
+
+ Object value2 = value;
+ if (klass.isEnum()) {
+ // try an Enum conversion from ordinal
+ value2 = ConvertUtils.lookup(klass).convert(klass, value);
+ if (value2 != null && value2.getClass() == klass) {
+ // add a matching typed value after conversion
+ addProperty(def, value2);
+ }
+ }
+ if (value2 == null) {
+ addUnsafeData(entry.getKey(), klass, value);
+ }
+ }
+
+ /**
+ * Update unvierse after a load(XXX) method.
+ *
+ * The data to inject are stored in the {@link #tmp}.
+ */
+ protected void updateUniverse() {
+ if (tmp == null || tmp.isEmpty()) {
+ return;
+ }
+ try {
+ // tmp contains new data to digest
+ for (Object o : tmp.keySet()) {
+ String kStr = o + "";
+ T def = getEnumName(kStr);
+ Object value = tmp.get(o);
+ MyPropertyKey key = getManager().getEntry((MyPropertyDef) def, false);
+ if (def != null && key != null && universe.contains(def)) {
+ setProperty(def, value, false);
+ } else {
+ addUnsafeData(kStr, key == null ? null : key.getType(), value);
+ }
+ }
+ } finally {
+ tmp.clear();
+ }
+ }
+
+ /**
+ * Set a value for a given property using his def and update
+ * also the list of keys used.
+ *
+ * @param def the def to use
+ * @param value the value to set
+ */
+ protected void addProperty(T def, Object value) {
+ getData().put(def, value);
+ if (!keys.contains(def)) {
+ keys.add(def);
+ }
+ }
+
+ /**
+ * Remove a given property using his def and update
+ * also the list of keys used.
+ *
+ * @param def the def to use
+ */
+ protected void removeProperty(T def) {
+ getData().remove(def);
+ if (keys.contains(def)) {
+ keys.remove(def);
+ }
+ }
+
+ /**
+ * Set a unsafe property.
+ *
+ * @param key unsafe key
+ * @param klass the unsafe property value's type
+ * @param value the unsafe property value to set
+ */
+ protected void addUnsafeData(Object key, Class klass, Object value) {
+ getUnsafeData().put(key, value);
+ }
+
+
+ /**
+ * Convert the given o object to the type klass
+ *
+ * @param klass the class of value to extract
+ * @param o the original value
+ * @return the converted value
+ */
+ protected Object convert(Class> klass, Object o) {
+ if (o == null) {
+ return null;
+ }
+ Object result;
+ if (klass == String.class) {
+ result = o;
+ } else {
+ if (o.getClass() == String.class) {
+ result = ConvertUtils.convert(String.valueOf(o), klass);
+ } else {
+ result = o;
+ }
+ }
+ if (result != null && !klass.isAssignableFrom(result.getClass())) {
+ result = null;
+ }
+ return result;
+ }
+
+ /**
+ * @param kStr the key to used
+ * @return the MyPropertyKey instance for the
+ * {@link MyPropertyKey#key} for the given key, or null if not found.
+ */
+ protected T getEnumName(String kStr) {
+ for (T t : universe) {
+ MyPropertyKey entry = getManager().getEntry((MyPropertyDef) t, false);
+ if (entry.getKey().equals(kStr)) {
+ return t;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * Instanciate an empty instance.
+ *
+ * Private constructor, use Factory newInstance(XXX)
+ * methods instead.
+ *
+ * @param keyType the type of key to use
+ */
+ private MyProperties(Class keyType) {
+ if (!(MyPropertyDef.class.isAssignableFrom(keyType))) {
+ throw new IllegalArgumentException(_("lutinutil.error.config.unauthorized.type.key", keyType));
+ }
+ this.keyType = keyType;
+ this.universe = EnumSet.allOf(this.keyType);
+ this.keys = EnumSet.noneOf(this.keyType);
+ this.manager = MyPropertyKeyManager.getInstance();
+ }
+
+ public MyPropertyKey getEntry(T def) {
+ return getManager().getEntry((MyPropertyDef) def, false);
+ }
+
+ public java.util.List getUnsafeDataKeys() {
+ if (isSafe()) {
+ return Collections.emptyList();
+ }
+ List result = new ArrayList(getUnsafeData().size());
+ for (Object o : unsafeData.keySet()) {
+ result.add(o);
+ }
+ return result;
+ }
+}