/*
* The MIT License (MIT)
*
* Copyright (c) 2016. Diorite (by Bartłomiej Mazur (aka GotoFinal))
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package org.diorite.config.serialization;
import static org.diorite.config.serialization.SerializationData.DEFAULT_KEY_PROPERTY;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collection;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
/**
* Interface used in serialization methods to help with serializaing different types of data.
*/
@SuppressWarnings("MagicNumber")
public interface DeserializationData
{
/**
* Returns type of serializer data.
*
* @return type of serializer data.
*/
SerializationType getSerializationType();
/**
* Returns serialization object that started serialization.
*
* @return serialization object that started serialization.
*/
Serialization getSerializationInstance();
/**
* Add string values that can be interpreted as {@link Boolean#TRUE} value when reading from config. <br>
* Note that this value must be supported by deserializer too.
*
* @param strings
* string representations of {@link Boolean#TRUE} value.
*/
void addTrueValues(String... strings);
/**
* Add string values that can be interpreted as {@link Boolean#FALSE} value when reading from config. <br>
* Note that this value must be supported by deserializer too.
*
* @param strings
* string representations of {@link Boolean#FALSE} value.
*/
void addFalseValues(String... strings);
/**
* Returns set of root level keys.
*
* @return set of root level keys.
*/
Set<String> getKeys();
/**
* Check if this data instance contains given key, note that value on that key might be still a null value.
*
* @param key
* key to check.
*
* @return true if key exists.
*/
boolean containsKey(String key);
/**
* Get and deserialize given object from this data instance. <br>
* If there is no value, or it is null, throw error.
*
* @param key
* value to deserialize.
* @param type
* type of value.
* @param <T>
* type of value.
*
* @return deserialized value.
*/
default <T> T getOrThrow(String key, Class<T> type)
{
T t = this.get(key, type, null);
if (t == null)
{
throw new IllegalStateException("Missing value: " + key);
}
return t;
}
/**
* Get and deserialize given object from this data instance.
*
* @param key
* value to deserialize.
* @param type
* type of value.
* @param <T>
* type of value.
*
* @return deserialized value.
*/
@Nullable
default <T> T get(String key, Class<T> type)
{
return this.get(key, type, null);
}
/**
* Get and deserialize given object from this data instance, or default value if there is no value on given key.
*
* @param key
* value to deserialize.
* @param type
* type of value.
* @param def
* default value.
* @param <T>
* type of value.
*
* @return deserialized value.
*/
@Nullable
<T> T get(String key, Class<T> type, @Nullable T def);
/**
* Get and deserialize given boolean value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default boolean getAsBoolean(String key)
{
Boolean val = this.get(key, Boolean.class, null);
if (val == null)
{
throw new NullPointerException("Can't get boolean value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given byte value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default byte getAsByte(String key)
{
Byte val = this.get(key, Byte.class, null);
if (val == null)
{
throw new NullPointerException("Can't get byte value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given char value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default char getAsChar(String key)
{
Character val = this.get(key, Character.class, null);
if (val == null)
{
throw new NullPointerException("Can't get char value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given short value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default short getAsShort(String key)
{
Short val = this.get(key, Short.class, null);
if (val == null)
{
throw new NullPointerException("Can't get short value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given int value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default int getAsInt(String key)
{
Integer val = this.get(key, Integer.class, null);
if (val == null)
{
throw new NullPointerException("Can't get int value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given long value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default long getAsLong(String key)
{
Long val = this.get(key, Long.class, null);
if (val == null)
{
throw new NullPointerException("Can't get long value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given float value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default float getAsFloat(String key)
{
Float val = this.get(key, Float.class, null);
if (val == null)
{
throw new NullPointerException("Can't get float value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given double value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default double getAsDouble(String key)
{
Double val = this.get(key, Double.class, null);
if (val == null)
{
throw new NullPointerException("Can't get double value on: " + key + ", as value is null, but primitive can't be a null.");
}
return val;
}
/**
* Get and deserialize given boolean value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default boolean getAsBoolean(String key, boolean def)
{
Boolean v = this.get(key, Boolean.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given byte value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default byte getAsByte(String key, byte def)
{
Byte v = this.get(key, Byte.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given char value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default char getAsChar(String key, char def)
{
Character v = this.get(key, Character.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given short value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default short getAsShort(String key, short def)
{
Short v = this.get(key, Short.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given int value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default int getAsInt(String key, int def)
{
Integer v = this.get(key, Integer.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given long value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default long getAsLong(String key, long def)
{
Long v = this.get(key, Long.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given float value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default float getAsFloat(String key, float def)
{
Float v = this.get(key, Float.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given double value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default double getAsDouble(String key, double def)
{
Double v = this.get(key, Double.class, def);
if (v == null)
{
return def;
}
return v;
}
/**
* Get and deserialize given number value from this data instance, method will throw {@link NullPointerException} if type is primitive and value don't
* exists or it's null.
*
* @param key
* value to deserialize.
* @param type
* type of number.
* @param def
* default value.
* @param <T>
* type of number.
*
* @return deserialized value.
*
* @throws NullPointerException
* when type is primitive and value don't exists or it's null.
*/
@SuppressWarnings("unchecked")
@Nullable
default <T extends Number> T getAsHexNumber(String key, Class<T> type, @Nullable T def)
{
if (type == byte.class)
{
return (T) (Byte) this.getAsHexByte(key);
}
if (type == short.class)
{
return (T) (Short) this.getAsHexShort(key);
}
if (type == int.class)
{
return (T) (Integer) this.getAsHexInt(key);
}
if (type == long.class)
{
return (T) (Long) this.getAsHexLong(key);
}
String val = this.get(key, String.class, null);
if (val == null)
{
return def;
}
val = val.substring(2);
try
{
Object r;
if (type == Byte.class)
{
r = Byte.parseByte(val, 16);
}
else if (type == Short.class)
{
r = Short.parseShort(val, 16);
}
else if (type == Integer.class)
{
r = Integer.parseInt(val, 16);
}
else if (type == Long.class)
{
r = Long.parseLong(val, 16);
}
else
{
throw new RuntimeException("Can't deserialize " + type + " as hex number.");
}
return (T) r;
}
catch (NumberFormatException e)
{
return def;
}
}
/**
* Get and deserialize given byte value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default byte getAsHexByte(String key)
{
String val = this.get(key, String.class, null);
if (val == null)
{
throw new NullPointerException("Can't get byte value on: " + key + ", as value is null, but primitive can't be a null.");
}
val = val.substring(2);
return Byte.parseByte(val, 16);
}
/**
* Get and deserialize given short value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default short getAsHexShort(String key)
{
String val = this.get(key, String.class, null);
if (val == null)
{
throw new NullPointerException("Can't get short value on: " + key + ", as value is null, but primitive can't be a null.");
}
val = val.substring(2);
return Short.parseShort(val, 16);
}
/**
* Get and deserialize given int value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default int getAsHexInt(String key)
{
String val = this.get(key, String.class, null);
if (val == null)
{
throw new NullPointerException("Can't get int value on: " + key + ", as value is null, but primitive can't be a null.");
}
val = val.substring(2);
return Integer.parseInt(val, 16);
}
/**
* Get and deserialize given long value from this data instance, method will throw {@link NullPointerException} if value don't exists or it's null.
*
* @param key
* value to deserialize.
*
* @return deserialized value.
*
* @throws NullPointerException
* when value don't exists or it's null.
*/
default long getAsHexLong(String key)
{
String val = this.get(key, String.class, null);
if (val == null)
{
throw new NullPointerException("Can't get long value on: " + key + ", as value is null, but primitive can't be a null.");
}
val = val.substring(2);
return Long.parseLong(val, 16);
}
/**
* Get and deserialize given byte value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default byte getAsHexByte(String key, byte def)
{
String v = this.get(key, String.class, null);
if (v == null)
{
return def;
}
v = v.substring(2);
return Byte.parseByte(v, 16);
}
/**
* Get and deserialize given short value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default short getAsHexShort(String key, short def)
{
String v = this.get(key, String.class, null);
if (v == null)
{
return def;
}
v = v.substring(2);
return Short.parseShort(v, 16);
}
/**
* Get and deserialize given int value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default int getAsHexInt(String key, int def)
{
String v = this.get(key, String.class, null);
if (v == null)
{
return def;
}
v = v.substring(2);
return Integer.parseInt(v, 16);
}
/**
* Get and deserialize given long value from this data instance, or return default value if value don't exists or it's null.
*
* @param key
* value to deserialize.
* @param def
* default value.
*
* @return deserialized value.
*/
default long getAsHexLong(String key, long def)
{
String v = this.get(key, String.class, null);
if (v == null)
{
return def;
}
v = v.substring(2);
return Long.parseLong(v, 16);
}
/**
* Deserialize list on given key, if key contains Map instead of List, it will be deserialized and returned as list of values.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param type
* type of elements.
* @param <T>
* type of elements.
*
* @return deserialized value.
*/
default <T> List<T> getAsList(String key, Class<T> type)
{
ArrayList<T> objects = new ArrayList<>(20);
this.getAsCollection(key, type, objects);
objects.trimToSize();
return objects;
}
/**
* Deserialize list on given key, if key contains Map instead of List, it will be deserialized as list of values. <br>
* All deserialized values are added to given collection.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param type
* type of elements.
* @param collection
* target collection for deserialized elements.
* @param <T>
* type of elements.
* @param <C>
* type of collection.
*/
<T, C extends Collection<T>> void getAsCollection(String key, Class<T> type, C collection);
/**
* Deserialize map on given key, if key contains Collection instead of Map, all elements are added to map using given key mapper. <br>
* If key contains Map value, keyMapper is still used, and all keys are updated.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param type
* type of elements.
* @param keyMapper
* function that returns key name for given element.
* @param <T>
* type of elements.
*
* @return deserialized value.
*/
default <T> Map<String, T> getAsMap(String key, Class<T> type, Function<T, String> keyMapper)
{
LinkedHashMap<String, T> map = new LinkedHashMap<>(20);
this.getAsMap(key, type, keyMapper, map);
return map;
}
/**
* Deserialize map on given key, if key contains Collection instead of Map, all elements are added to map using {@link
* SerializationData#DEFAULT_KEY_PROPERTY} value as map key. <br>
* If key contains Map value, key will be still updated if key property exists.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyType
* type of keys.
* @param type
* type of elements.
* @param <T>
* type of elements.
* @param <K>
* type of keys.
*
* @return deserialized value.
*/
default <K, T> Map<K, T> getAsMapWithKeys(String key, Class<K> keyType, Class<T> type)
{
LinkedHashMap<K, T> map = new LinkedHashMap<>(20);
this.getAsMapWithKeys(key, keyType, type, DEFAULT_KEY_PROPERTY, map);
return map;
}
/**
* Deserialize map on given key, if key contains Collection instead of Map, all elements are added to map using given property value as map key. <br>
* If key contains Map value, key will be still updated if key property exists.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyType
* type of keys.
* @param type
* type of elements.
* @param keyPropertyName
* name of key property for map.
* @param <T>
* type of elements.
* @param <K>
* type of keys.
*
* @return deserialized value.
*/
default <K, T> Map<K, T> getAsMapWithKeys(String key, Class<K> keyType, Class<T> type, String keyPropertyName)
{
LinkedHashMap<K, T> map = new LinkedHashMap<>(20);
this.getAsMapWithKeys(key, keyType, type, keyPropertyName, map);
return map;
}
/**
* Deserialize map on given key, key type must be serializable to string.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyType
* type of keys, must be string serializable.
* @param type
* type of elements.
* @param <K>
* type of keys, must be string serializable.
* @param <T>
* type of elements.
*
* @return deserialized value.
*/
default <K, T> Map<K, T> getMap(String key, Class<K> keyType, Class<T> type)
{
LinkedHashMap<K, T> map = new LinkedHashMap<>(20);
this.getMap(key, keyType, type, map);
return map;
}
/**
* Deserialize map on given key using keyMapper to change string keys to key objects. <br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyMapper
* function that change string key to key object.
* @param type
* type of elements.
* @param <K>
* type of keys.
* @param <T>
* type of elements.
*
* @return deserialized value.
*/
default <K, T> Map<K, T> getMap(String key, Function<String, K> keyMapper, Class<T> type)
{
LinkedHashMap<K, T> map = new LinkedHashMap<>(20);
this.getMap(key, keyMapper, type, map);
return map;
}
/**
* Deserialize map on given key, if key contains Collection instead of Map, all elements are added to map using given key mapper. <br>
* If key contains Map value, keyMapper is still used, and all keys are updated.<br>
* All deserialized values are added to given map.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param type
* type of elements.
* @param keyMapper
* function that returns key name for given element.
* @param map
* target map for deserialized elements.
* @param <T>
* type of elements.
* @param <M>
* type of map.
*/
<T, M extends Map<String, T>> void getAsMap(String key, Class<T> type, Function<T, String> keyMapper, M map);
/**
* Deserialize map on given key, if key contains Collection instead of Map, all elements are added to map using {@link
* SerializationData#DEFAULT_KEY_PROPERTY} value as map key. <br>
* If key contains Map value, key will be still updated if key property exists.<br>
* All deserialized values are added to given map.
*
* @param key
* value to deserialize.
* @param keyType
* type of keys.
* @param type
* type of elements
* @param map
* target map for deserialized elements.
* @param <K>
* type of keys
* @param <T>
* type of elements
* @param <M>
* type of map.
*/
default <K, T, M extends Map<K, T>> void getAsMapWithKeys(String key, Class<K> keyType, Class<T> type, M map)
{
this.getAsMapWithKeys(key, keyType, type, DEFAULT_KEY_PROPERTY, map);
}
/**
* Deserialize map on given key, if key contains Collection instead of Map, all elements are added to map using given property value as map key. <br>
* If key contains Map value, key will be still updated if key property exists.<br>
* All deserialized values are added to given map.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyType
* type of keys.
* @param type
* type of elements.
* @param map
* target map for deserialized elements.
* @param keyPropertyName
* name of key property for map.
* @param <K>
* type of keys
* @param <T>
* type of elements
* @param <M>
* type of map.
*/
<K, T, M extends Map<K, T>> void getAsMapWithKeys(String key, Class<K> keyType, Class<T> type, String keyPropertyName, M map);
/**
* Deserialize map on given key, key type must be serializable to string.<br>
* All deserialized values are added to given map.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyType
* type of keys, must be string serializable.
* @param type
* type of elements
* @param map
* target map for deserialized elements.
* @param <K>
* type of keys, must be string serializable.
* @param <T>
* type of elements
* @param <M>
* type of map.
*/
<K, T, M extends Map<K, T>> void getMap(String key, Class<K> keyType, Class<T> type, M map);
/**
* Deserialize map on given key using keyMapper to change string keys to key objects.<br>
* All deserialized values are added to given map.<br>
* Use empty key to deserialize map from root element.
*
* @param key
* value to deserialize.
* @param keyMapper
* function that change string key to key object.
* @param type
* type of elements
* @param map
* target map for deserialized elements.
* @param <K>
* type of keys.
* @param <T>
* type of elements
* @param <M>
* type of map.\
*/
<K, T, M extends Map<K, T>> void getMap(String key, Function<String, K> keyMapper, Class<T> type, M map);
// /**
// * Return copy of raw data as map instance.
// *
// * @return simple map of values.
// */
// Map<String, Object> asMap();
//
// /**
// * Create deserialization data instance for given manager.
// *
// * @param serialization
// * serialization manager to use.
// * @param dataMap
// * map with configuration values.
// *
// * @return created deserialization data instance.
// */
// static DeserializationData create(Serialization serialization, Map<String, Object> dataMap)
// {
// return new SimpleDeserializationData(serialization, dataMap);
// }
}