package pluginbase.config.serializers; import org.jetbrains.annotations.NotNull; import org.jetbrains.annotations.Nullable; import pluginbase.config.SerializableConfig; import pluginbase.config.field.Field; import pluginbase.config.field.FieldMap; import pluginbase.config.field.FieldMapper; import pluginbase.config.field.PropertyVetoException; import java.util.Collection; import java.util.Map; /** * Implemented by helper classes that are used to serialize objects. * <p> * @param <T> The type of the object to be serialized. */ public interface Serializer<T> { /** * Transforms the specified object of type {@code T} to a type recognized by a serialization format. * <p/> * The most common format for serialization is YAML. Types typically recognized by YAML include: * <ul> * <li>all primitives and their wrapper classes</li> * <li>{@link String}</li> * <li>{@link java.util.List} of primitives or {@link String}</li> * <li>{@link java.util.Map} of primitives or {@link String}</li> * <li>arrays of primitives or {@link String}</li> * </ul> * * @param object the object to serialize. * @param serializerSet the serializerSet that contains serializers that may need to be used to serialize child objects. * @return the serialized form of the object. * @throws IllegalArgumentException if the deserialized object's type is not serializable. */ @Nullable Object serialize(@Nullable T object, @NotNull SerializerSet serializerSet) throws IllegalArgumentException; /** * Transforms the specified object to the type specified by wantedType. * * @param serialized The object to transform. * <p/> * The type of the serialized object <em>should</em> be one of the following: * <ul> * <li>all primitives and their wrapper classes</li> * <li>{@link String}</li> * <li>{@link java.util.List} of primitives or {@link String}</li> * <li>{@link java.util.Map} of primitives or {@link String}</li> * <li>arrays of primitives or {@link String}</li> * </ul> * However, it is possible that serialization could take other forms.<p/> * @param wantedType The {@link Class} of the object that should be returned. * @param serializerSet the serializerSet that contains serializers that may need to be used to deserialize child objects. * @return The deserialized form of the serialized object. * @throws IllegalArgumentException if the serialized object's type is not deserializable. */ @Nullable T deserialize(@Nullable Object serialized, @NotNull Class wantedType, @NotNull SerializerSet serializerSet) throws IllegalArgumentException; @Nullable default Object serialize(@Nullable T object) throws IllegalArgumentException { return serialize(object, SerializerSet.defaultSet()); } @Nullable default T deserialize(@Nullable Object serialized, @NotNull Class wantedType) throws IllegalArgumentException { return deserialize(serialized, wantedType, SerializerSet.defaultSet()); } /** * This method attempts to map the values contained in the given data Map to the fields of the given target object. * <p/> * The keys in the data map should represent the field names that will be set with the deserialized value. * * @param data the Map of serialized data with keys representing field names. * @param target the object to populate the fields of. * @param serializerSet the serializerSet that contains serializers that may need to be used to deserialize child objects. * @return The given target object is returned with its fields populated with deserialized data. */ @NotNull default T deserializeToObject(@NotNull Map data, @NotNull T target, @NotNull SerializerSet serializerSet) { FieldMap fieldMap = FieldMapper.getFieldMap(target.getClass()); for (Object key : data.keySet()) { if (key.equals(SerializableConfig.SERIALIZED_TYPE_KEY)) { continue; } Field field = fieldMap.getField(key.toString()); if (field != null) { Object fieldValue = field.getValue(target); Object serializedFieldData = data.get(key); if (serializedFieldData == null) { fieldValue = null; } else { Class asClass = fieldValue != null ? fieldValue.getClass() : field.getType(); if (Collection.class.isAssignableFrom(field.getType()) && serializedFieldData instanceof Collection) { fieldValue = DefaultSerializer.deserializeCollection(field, (Collection<?>) serializedFieldData, asClass, serializerSet); } else if (Map.class.isAssignableFrom(field.getType()) && serializedFieldData instanceof Map) { fieldValue = DefaultSerializer.deserializeMap(field, (Map<?, ?>) serializedFieldData, asClass, serializerSet); } else if (fieldValue != null && serializedFieldData instanceof Map) { fieldValue = DefaultSerializer.deserializeFieldAs(field, serializedFieldData, fieldValue.getClass(), serializerSet); } else { fieldValue = DefaultSerializer.deserializeFieldAs(field, serializedFieldData, field.getType(), serializerSet); } } try { field.forceSet(target, fieldValue); } catch (PropertyVetoException e) { e.printStackTrace(); } } else { // TODO fail silently or no? } } return target; } @NotNull default T deserializeToObject(@NotNull Map data, @NotNull T object) { return deserializeToObject(data, object, SerializerSet.defaultSet()); } }