package au.net.causal.projo.prefs.javapreferences;
import java.lang.annotation.Annotation;
import java.util.Collections;
import java.util.Set;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import au.net.causal.projo.prefs.AbstractPreferenceNode;
import au.net.causal.projo.prefs.PreferenceKeyMetadata;
import au.net.causal.projo.prefs.PreferenceNode;
import au.net.causal.projo.prefs.PreferencesException;
import au.net.causal.projo.prefs.UnsupportedDataTypeException;
import com.google.common.collect.ImmutableSet;
import com.google.common.primitives.Primitives;
import com.google.common.reflect.TypeToken;
/**
* Preference node implementation backed by a standard {@linkplain Preferences Java preferences node}.
* <p>
*
* Natively supports all the data types that the standard preferences API supports, including String, int, long and byte[].
*
* @author prunge
*/
public class JavaPreferenceNode extends AbstractPreferenceNode
{
private static final Set<? extends TypeToken<?>> SUPPORTED_DATA_TYPES = ImmutableSet.<TypeToken<?>>builder()
.add(TypeToken.of(String.class),
TypeToken.of(Integer.class),
TypeToken.of(Long.class),
TypeToken.of(Float.class),
TypeToken.of(Double.class),
TypeToken.of(Boolean.class),
TypeToken.of(byte[].class)).build();
private final Preferences pref;
/**
* Creates a <code>JavaPreferenceNode</code>.
*
* @param pref the Java preferences node to wrap.
*
* @throws NullPointerException if <code>pref</code> is null.
*/
public JavaPreferenceNode(Preferences pref)
{
super(Collections.<Class<? extends Annotation>>emptySet());
if (pref == null)
throw new NullPointerException("pref == null");
this.pref = pref;
}
@Override
protected <T> T getValueImpl(String key, PreferenceKeyMetadata<T> metadata)
throws UnsupportedDataTypeException, PreferencesException
{
if (key == null)
throw new NullPointerException("key == null");
if (metadata == null)
throw new NullPointerException("metadata == null");
TypeToken<T> dataType = metadata.getDataType();
Class<? super T> rawType = dataType.getRawType();
rawType = Primitives.wrap(rawType);
if (String.class.equals(rawType))
return((T)pref.get(key, null));
if (byte[].class.equals(rawType))
return((T)pref.getByteArray(key, null));
//For the primitive types, there is no way to work out the difference between the key not existing and the default value being returned
//so we need explicit key check
//Sigh - unfortunately that means we need to do the lookup twice
boolean valueExists = (pref.get(key, null) != null);
if (!valueExists)
return(null);
if (Integer.class.equals(rawType))
return((T)Integer.valueOf(pref.getInt(key, 0)));
else if (Long.class.equals(rawType))
return((T)Long.valueOf(pref.getLong(key, 0L)));
else if (Float.class.equals(rawType))
return((T)Float.valueOf(pref.getFloat(key, 0.0f)));
else if (Double.class.equals(rawType))
return((T)Double.valueOf(pref.getDouble(key, 0.0)));
else if (Boolean.class.equals(rawType))
return((T)Boolean.valueOf(pref.getBoolean(key, false)));
//If we get here, some other data type was used
throw new UnsupportedDataTypeException(dataType, dataType + " is not supported by Java preferences.");
}
@Override
protected <T> void putValueImpl(String key, T value, PreferenceKeyMetadata<T> metadata)
throws UnsupportedDataTypeException, PreferencesException
{
if (key == null)
throw new NullPointerException("key == null");
if (value == null)
throw new NullPointerException("value == null");
if (metadata == null)
throw new NullPointerException("metadata == null");
TypeToken<T> dataType = metadata.getDataType();
Class<? super T> rawType = dataType.getRawType();
rawType = Primitives.wrap(rawType);
if (String.class.equals(rawType))
pref.put(key, (String)value);
else if (byte[].class.equals(rawType))
pref.putByteArray(key, (byte[])value);
else if (Integer.class.equals(rawType))
pref.putInt(key, (Integer)value);
else if (Long.class.equals(rawType))
pref.putLong(key, (Long)value);
else if (Float.class.equals(rawType))
pref.putFloat(key, (Float)value);
else if (Double.class.equals(rawType))
pref.putDouble(key, (Double)value);
else if (Boolean.class.equals(rawType))
pref.putBoolean(key, (Boolean)value);
}
@Override
protected <T> void removeValueImpl(String key, PreferenceKeyMetadata<T> metadata)
throws UnsupportedDataTypeException, PreferencesException
{
if (key == null)
throw new NullPointerException("key == null");
if (metadata == null)
throw new NullPointerException("metadata == null");
pref.remove(key);
}
@Override
protected boolean isDataTypeSupportedImpl(PreferenceKeyMetadata<?> keyType) throws PreferencesException
{
return(isDataTypeSupported(keyType.getDataType()));
}
private boolean isDataTypeSupported(TypeToken<?> type)
throws PreferencesException
{
if (type == null)
throw new NullPointerException("type == null");
return(getSupportedDataTypes().contains(type));
}
private Set<? extends TypeToken<?>> getSupportedDataTypes()
throws PreferencesException
{
return(SUPPORTED_DATA_TYPES);
}
@Override
public PreferenceNode getChildNode(String name) throws PreferencesException
{
if (name == null)
throw new NullPointerException("name == null");
return(new JavaPreferenceNode(pref.node(name)));
}
@Override
public void removeChildNode(String name) throws PreferencesException
{
if (name == null)
throw new NullPointerException("name == null");
Preferences child = pref.node(name);
try
{
child.removeNode();
}
catch (BackingStoreException e)
{
throw new PreferencesException(e);
}
}
@Override
public void removeAllValues() throws PreferencesException
{
try
{
pref.clear();
}
catch (BackingStoreException e)
{
throw new PreferencesException(e);
}
}
@Override
public Set<String> getKeyNames()
throws PreferencesException
{
try
{
return(ImmutableSet.<String>builder().add(pref.keys()).build());
}
catch (BackingStoreException e)
{
throw new PreferencesException(e);
}
}
@Override
public Set<String> getNodeNames() throws PreferencesException
{
try
{
return(ImmutableSet.<String>builder().add(pref.childrenNames()).build());
}
catch (BackingStoreException e)
{
throw new PreferencesException(e);
}
}
@Override
public void flush() throws PreferencesException
{
try
{
pref.flush();
}
catch (BackingStoreException e)
{
throw new PreferencesException(e);
}
}
@Override
public void close() throws PreferencesException
{
flush();
}
}