001/* 002 * Configurate 003 * Copyright (C) zml and Configurate contributors 004 * 005 * Licensed under the Apache License, Version 2.0 (the "License"); 006 * you may not use this file except in compliance with the License. 007 * You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017package org.spongepowered.configurate.serialize; 018 019import io.leangen.geantyref.GenericTypeReflector; 020import io.leangen.geantyref.TypeToken; 021import org.checkerframework.checker.nullness.qual.Nullable; 022import org.spongepowered.configurate.ConfigurationNode; 023 024import java.lang.reflect.AnnotatedType; 025import java.lang.reflect.Type; 026import java.util.List; 027import java.util.function.Predicate; 028 029/** 030 * Serialize a value that can be represented as a scalar value within a node. 031 * Implementations must be able to serialize when one of the accepted types is 032 * a {@link String}, and may support any other types as desired. 033 * 034 * <p>When serializing to a node, null values will be passed through directly. 035 * If the type serialized by this serializer is one of the native types of the 036 * backing node, it will be written directly to the node without 037 * any transformation. 038 * 039 * <p>Any serialized value must be deserializable by the same serializer. 040 * 041 * @param <T> the object type to serialize 042 * @since 4.0.0 043 */ 044public abstract class ScalarSerializer<T> implements TypeSerializer.Annotated<T> { 045 046 private final TypeToken<T> type; 047 048 /** 049 * Create a new scalar serializer that handles the provided type. 050 * 051 * @param type type to handle 052 * @since 4.0.0 053 */ 054 @SuppressWarnings("unchecked") 055 protected ScalarSerializer(final TypeToken<T> type) { 056 final Type boxed = GenericTypeReflector.box(type.getType()); 057 this.type = boxed == type.getType() ? type : (TypeToken<T>) TypeToken.get(boxed); 058 } 059 060 /** 061 * Create a new scalar serializer that handles the provided type. 062 * 063 * <p>{@code type} must not be a raw parameterized type.</p> 064 * 065 * @param type type to handle 066 * @since 4.0.0 067 */ 068 protected ScalarSerializer(final Class<T> type) { 069 if (type.getTypeParameters().length > 0) { 070 throw new IllegalArgumentException("Provided type " + type + " has type parameters but was not provided as a TypeToken!"); 071 } 072 this.type = TypeToken.get(type); 073 } 074 075 @SuppressWarnings("unchecked") 076 ScalarSerializer(final Type type) { 077 this.type = (TypeToken<T>) TypeToken.get(type); 078 } 079 080 /** 081 * Get the general type token applicable for this serializer. This token may 082 * be parameterized. 083 * 084 * @return the type token for this serializer 085 * @since 4.0.0 086 */ 087 public final TypeToken<T> type() { 088 return this.type; 089 } 090 091 @Override 092 public final T deserialize(final Type type, final ConfigurationNode node) throws SerializationException { 093 return TypeSerializer.Annotated.super.deserialize(type, node); 094 } 095 096 @Override 097 public final T deserialize(AnnotatedType type, final ConfigurationNode node) throws SerializationException { 098 ConfigurationNode deserializeFrom = node; 099 if (node.isList()) { 100 final List<? extends ConfigurationNode> children = node.childrenList(); 101 if (children.size() == 1) { 102 deserializeFrom = children.get(0); 103 } 104 } 105 106 if (deserializeFrom.isList() || deserializeFrom.isMap()) { 107 throw new SerializationException(type, "Value must be provided as a scalar!"); 108 } 109 110 final @Nullable Object value = deserializeFrom.rawScalar(); 111 if (value == null) { 112 throw new SerializationException(type, "No scalar value present"); 113 } 114 115 type = GenericTypeReflector.toCanonicalBoxed(type); // every primitive type should be boxed (cuz generics!) 116 final @Nullable T possible = this.cast(value); 117 if (possible != null) { 118 return possible; 119 } 120 121 return this.deserialize(type, value); 122 } 123 124 /** 125 * Attempt to deserialize the provided object using an unspecialized type. 126 * This may fail on more complicated deserialization processes such as with 127 * enum types. 128 * 129 * @param value the object to deserialize. 130 * @return the deserialized object, if possible 131 * @throws SerializationException if unable to coerce the value to the 132 * requested type. 133 * @since 4.0.0 134 */ 135 public final T deserialize(final Object value) throws SerializationException { 136 final @Nullable T possible = this.cast(value); 137 if (possible != null) { 138 return possible; 139 } 140 141 return this.deserialize(this.type().getAnnotatedType(), value); 142 } 143 144 /** 145 * Given an object of unknown type, attempt to convert it into the given 146 * type. 147 * 148 * @param type the specific type of the type's usage 149 * @param obj the object to convert 150 * @return a converted object 151 * @throws SerializationException if the object could not be converted for 152 * any reason 153 * @since 4.2.0 154 */ 155 public T deserialize(final AnnotatedType type, final Object obj) throws SerializationException { 156 return this.deserialize(type.getType(), obj); 157 } 158 159 /** 160 * Given an object of unknown type, attempt to convert it into the given 161 * type. 162 * 163 * @param type the specific type of the type's usage 164 * @param obj the object to convert 165 * @return a converted object 166 * @throws SerializationException if the object could not be converted for 167 * any reason 168 * @since 4.0.0 169 */ 170 public abstract T deserialize(Type type, Object obj) throws SerializationException; 171 172 @Override 173 public final void serialize(final AnnotatedType type, final @Nullable T obj, final ConfigurationNode node) { 174 if (obj == null) { 175 node.raw(null); 176 return; 177 } 178 179 if (node.options().acceptsType(obj.getClass())) { 180 node.raw(obj); 181 return; 182 } 183 184 node.raw(this.serialize(type, obj, node.options()::acceptsType)); 185 } 186 187 @Override 188 public final void serialize(final Type type, final @Nullable T obj, final ConfigurationNode node) { 189 this.serialize(GenericTypeReflector.annotate(type), obj, node); 190 } 191 192 /** 193 * Serialize the provided value to a supported type, testing against the 194 * provided predicate. 195 * 196 * <p>Annotated type information is provided for reference.</p> 197 * 198 * @param type the annotated type of the field being serialized 199 * @param item the value to serialize 200 * @param typeSupported a predicate to allow choosing which types are 201 * supported 202 * @return a serialized form of this object 203 * @since 4.2.0 204 */ 205 protected Object serialize(final AnnotatedType type, final T item, final Predicate<Class<?>> typeSupported) { 206 return this.serialize(item, typeSupported); 207 } 208 209 /** 210 * Serialize the provided value to a supported type, testing against the 211 * provided predicate. 212 * 213 * @param item the value to serialize 214 * @param typeSupported a predicate to allow choosing which types are 215 * supported 216 * @return a serialized form of this object 217 * @since 4.0.0 218 */ 219 protected abstract Object serialize(T item, Predicate<Class<?>> typeSupported); 220 221 @SuppressWarnings("unchecked") 222 private @Nullable T cast(final Object value) { 223 final Class<?> rawType = GenericTypeReflector.erase(this.type().getType()); 224 if (rawType.isInstance(value)) { 225 return (T) value; 226 } 227 return null; 228 } 229 230 /** 231 * Attempt to deserialize the provided object, but rather than throwing an 232 * exception when a deserialization error occurs, return null instead. 233 * 234 * @param obj the object to try to deserialize 235 * @return an instance of the appropriate type, or null 236 * @see #deserialize(Object) 237 * @since 4.0.0 238 */ 239 public final @Nullable T tryDeserialize(final @Nullable Object obj) { 240 if (obj == null) { 241 return null; 242 } 243 244 try { 245 return this.deserialize(obj); 246 } catch (final SerializationException ex) { 247 return null; 248 } 249 } 250 251 /** 252 * Serialize the item to a {@link String}, in a representation that can be 253 * interpreted by this serializer again. 254 * 255 * @param item the item to serialize 256 * @return the serialized form of the item 257 * @since 4.0.0 258 */ 259 public final String serializeToString(final T item) { 260 if (item instanceof CharSequence) { 261 return item.toString(); 262 } 263 // Otherwise, use the serializer 264 return (String) this.serialize(GenericTypeReflector.annotate(item.getClass()), item, clazz -> clazz.isAssignableFrom(String.class)); 265 } 266 267 /** 268 * A specialization of the scalar serializer that favors 269 * annotated type methods over unannotated methods. 270 * 271 * @param <V> the value to deserialize 272 * @since 4.2.0 273 */ 274 public abstract static class Annotated<V> extends ScalarSerializer<V> { 275 276 /** 277 * Create a new annotated scalar serializer 278 * that handles the provided type. 279 * 280 * <p>{@code type} must not be a raw parameterized type.</p> 281 * 282 * @param type type to handle 283 * @since 4.2.0 284 */ 285 protected Annotated(final Class<V> type) { 286 super(type); 287 } 288 289 /** 290 * Create a new annotated scalar serializer 291 * that handles the provided type. 292 * 293 * @param type type to handle 294 * @since 4.2.0 295 */ 296 protected Annotated(final TypeToken<V> type) { 297 super(type); 298 } 299 300 @Override 301 public abstract V deserialize( 302 AnnotatedType type, 303 Object obj 304 ) throws SerializationException; 305 306 @Override 307 public V deserialize( 308 final Type type, 309 final Object obj 310 ) throws SerializationException { 311 return this.deserialize(GenericTypeReflector.annotate(type), obj); 312 } 313 314 @Override 315 protected abstract Object serialize(AnnotatedType type, V item, Predicate<Class<?>> typeSupported); 316 317 @Override 318 protected Object serialize(final V item, final Predicate<Class<?>> typeSupported) { 319 return this.serialize(GenericTypeReflector.annotate(item.getClass()), item, typeSupported); 320 } 321 322 } 323 324}