package au.net.causal.projo.prefs.transform;
import au.net.causal.projo.prefs.DataTypeSupport;
import au.net.causal.projo.prefs.PreferenceKeyMetadata;
import au.net.causal.projo.prefs.PreferencesException;
import au.net.causal.projo.prefs.TransformDataTypeSupportChain;
import au.net.causal.projo.prefs.TransformGetChain;
import au.net.causal.projo.prefs.TransformPutChain;
import au.net.causal.projo.prefs.TransformRemoveChain;
import au.net.causal.projo.prefs.TransformResult;
/**
* Converts between enum values and strings.
* <p>
*
* {@link Enum#name()} is used as the string representation of enum values.
*
* @author prunge
*/
public class EnumToStringTransformer implements PreferenceTransformer
{
@Override
public <T> TransformResult<T> applyGet(String key, PreferenceKeyMetadata<T> keyMetadata, TransformGetChain chain)
throws PreferencesException
{
if (!isSupported(keyMetadata, chain))
return(null);
//Safe because enum types aren't parameterized
Class<T> enumType = (Class<T>)keyMetadata.getDataType().getRawType();
//Read as string from underlying store, convert to integer and pass up
String storeValue = chain.getValue(key, keyMetadata.withDataType(String.class));
T enumValue;
if (storeValue == null)
enumValue = null;
else
{
//Safe because we know isSupported would only return true if enumType was an actual enum
try
{
@SuppressWarnings({ "unchecked", "rawtypes" })
T theValue = (T)Enum.valueOf((Class)enumType, storeValue);
enumValue = theValue;
}
catch (IllegalArgumentException e)
{
throw new PreferencesException("Invalid store value for enum type " + enumType.getCanonicalName() + ": " + storeValue);
}
}
TransformResult<T> result = new TransformResult<>(enumValue);
return(result);
}
@Override
public <T> boolean applyPut(String key, T value, PreferenceKeyMetadata<T> keyMetadata, TransformPutChain chain)
throws PreferencesException
{
if (!isSupported(keyMetadata, chain))
return(false);
Enum<?> enumValue = (Enum<?>)value;
//Convert to string
String storeValue;
if (enumValue == null)
storeValue = null;
else
storeValue = enumValue.name();
chain.putValue(key, storeValue, keyMetadata.withDataType(String.class));
return(true);
}
@Override
public <T> boolean applyRemove(String key, PreferenceKeyMetadata<T> keyMetadata, TransformRemoveChain chain)
throws PreferencesException
{
if (!isSupported(keyMetadata, chain))
return(false);
chain.removeValue(key, keyMetadata.withDataType(String.class));
return(true);
}
@Override
public DataTypeSupport applyDataTypeSupport(PreferenceKeyMetadata<?> keyMetadata, TransformDataTypeSupportChain chain)
throws PreferencesException
{
//Only for enum keys
if (!Enum.class.isAssignableFrom(keyMetadata.getDataType().getRawType()))
return(null);
//If underlying store supports strings then add support, otherwise we can't touch it
if (!chain.isDataTypeSupported(keyMetadata.withDataType(String.class)))
return(null);
//If we get here the transform will work
return(DataTypeSupport.ADD_SUPPORT);
}
/**
* Only use this transformer if:
*
* <ul>
* <li>Store does not support the enum data type natively</li>
* <li>Store supports String data type</li>
* <li>Key is an enum data type</li>
* </ul>
*
* @throws PreferencesException if the underlying store fails to retrieve data type support information.
*/
private boolean isSupported(PreferenceKeyMetadata<?> keyMetadata, TransformDataTypeSupportChain chain)
throws PreferencesException
{
if (!Enum.class.isAssignableFrom(keyMetadata.getDataType().getRawType()))
return(false);
if (chain.isDataTypeSupportedNatively(keyMetadata))
return(false);
if (!chain.isDataTypeSupported(keyMetadata.withDataType(String.class)))
return(false);
return(true);
}
}