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 static io.leangen.geantyref.GenericTypeReflector.annotate; 020import static io.leangen.geantyref.GenericTypeReflector.isSuperType; 021import static java.util.Objects.requireNonNull; 022import static org.spongepowered.configurate.util.Types.requireCompleteParameters; 023 024import io.leangen.geantyref.GenericTypeReflector; 025import io.leangen.geantyref.TypeToken; 026import org.checkerframework.checker.nullness.qual.Nullable; 027import org.spongepowered.configurate.objectmapping.ConfigSerializable; 028import org.spongepowered.configurate.objectmapping.ObjectMapper; 029import org.spongepowered.configurate.util.UnmodifiableCollections; 030 031import java.lang.reflect.Type; 032import java.lang.reflect.WildcardType; 033import java.util.ArrayList; 034import java.util.List; 035import java.util.Map; 036import java.util.Objects; 037import java.util.concurrent.ConcurrentHashMap; 038import java.util.function.Predicate; 039 040/** 041 * A calculated collection of {@link TypeSerializer}s. 042 * 043 * @since 4.0.0 044 */ 045public final class TypeSerializerCollection { 046 047 private static final TypeSerializerCollection DEFAULTS; 048 049 static { 050 DEFAULTS = TypeSerializerCollection.builder() 051 .registerExact(Scalars.STRING) 052 .registerExact(Scalars.BOOLEAN) 053 .register(MapSerializer.TYPE, new MapSerializer()) 054 .register(ListSerializer.TYPE, new ListSerializer()) 055 .registerExact(Scalars.BYTE) 056 .registerExact(Scalars.SHORT) 057 .registerExact(Scalars.INTEGER) 058 .registerExact(Scalars.LONG) 059 .registerExact(Scalars.FLOAT) 060 .registerExact(Scalars.DOUBLE) 061 .registerAnnotatedObjects(ObjectMapper.factory()) 062 .register(Scalars.ENUM) 063 .registerExact(Scalars.CHAR) 064 .registerExact(Scalars.URI) 065 .registerExact(Scalars.URL) 066 .registerExact(Scalars.UUID) 067 .registerExact(Scalars.PATTERN) 068 .register(ArraySerializer.Objects::accepts, new ArraySerializer.Objects()) 069 .registerExact(ArraySerializer.Booleans.TYPE, new ArraySerializer.Booleans()) 070 .registerExact(ArraySerializer.Bytes.TYPE, new ArraySerializer.Bytes()) 071 .registerExact(ArraySerializer.Chars.TYPE, new ArraySerializer.Chars()) 072 .registerExact(ArraySerializer.Shorts.TYPE, new ArraySerializer.Shorts()) 073 .registerExact(ArraySerializer.Ints.TYPE, new ArraySerializer.Ints()) 074 .registerExact(ArraySerializer.Longs.TYPE, new ArraySerializer.Longs()) 075 .registerExact(ArraySerializer.Floats.TYPE, new ArraySerializer.Floats()) 076 .registerExact(ArraySerializer.Doubles.TYPE, new ArraySerializer.Doubles()) 077 .register(SetSerializer.TYPE, new SetSerializer()) 078 .register(ConfigurationNodeSerializer.TYPE, new ConfigurationNodeSerializer()) 079 .register(PathSerializer.TYPE, PathSerializer.INSTANCE) 080 .registerExact(FileSerializer.TYPE, FileSerializer.INSTANCE) 081 .build(); 082 } 083 084 private final @Nullable TypeSerializerCollection parent; 085 private final List<RegisteredSerializer> serializers; 086 private final Map<Type, TypeSerializer<?>> typeMatches = new ConcurrentHashMap<>(); 087 088 private TypeSerializerCollection(final @Nullable TypeSerializerCollection parent, final List<RegisteredSerializer> serializers) { 089 this.parent = parent; 090 this.serializers = UnmodifiableCollections.copyOf(serializers); 091 } 092 093 /** 094 * Resolve a type serializer. 095 * 096 * <p>First, all registered serializers from this collection are queried 097 * then if a parent collection is set, that collection is queried. 098 * 099 * @param token the type a serializer is required for 100 * @param <T> the type to serialize 101 * @return a serializer if any is present, or null if no applicable 102 * serializer is found 103 * @since 4.0.0 104 */ 105 @SuppressWarnings("unchecked") 106 public <T> @Nullable TypeSerializer<T> get(final TypeToken<T> token) { 107 requireNonNull(token, "type"); 108 return (TypeSerializer<T>) get(token.getType()); 109 } 110 111 /** 112 * Resolve a type serializer. 113 * 114 * <p>First, all registered serializers from this collection are queried 115 * then if a parent collection is set, that collection is queried. 116 * 117 * <p>This method will fail when provided a raw parameterized type</p> 118 * 119 * @param token the type a serializer is required for 120 * @param <T> the type to serialize 121 * @return a serializer if any is present, or null if no applicable 122 * serializer is found 123 * @since 4.0.0 124 */ 125 @SuppressWarnings("unchecked") 126 public <T> @Nullable TypeSerializer<T> get(final Class<T> token) { 127 requireNonNull(token, "type"); 128 requireCompleteParameters(token); 129 130 return (TypeSerializer<T>) get((Type) token); 131 } 132 133 /** 134 * Resolve a type serializer. 135 * 136 * <p>First, all registered serializers from this collection are queried 137 * then if a parent collection is set, that collection is queried. 138 * 139 * @param type the type a serializer is required for 140 * @return a serializer if any is present, or null if no applicable 141 * serializer is found 142 * @since 4.0.0 143 */ 144 public @Nullable TypeSerializer<?> get(Type type) { 145 type = GenericTypeReflector.toCanonicalBoxed(annotate(requireNonNull(type, "type"))).getType(); 146 @Nullable TypeSerializer<?> serial = this.typeMatches.computeIfAbsent(type, param -> { 147 for (RegisteredSerializer ent : this.serializers) { 148 if (ent.predicate.test(param)) { 149 return ent.serializer; 150 } 151 } 152 return null; 153 }); 154 155 if (serial == null && this.parent != null) { 156 serial = this.parent.get(type); 157 } 158 return serial; 159 } 160 161 /** 162 * Create a new builder to begin building a collection of type serializers 163 * that inherits from this collection. 164 * 165 * @return the new builder 166 * @since 4.0.0 167 */ 168 public Builder childBuilder() { 169 return new Builder(this); 170 } 171 172 @Override 173 public String toString() { 174 return "TypeSerializerCollection{" 175 + "parent=" + this.parent 176 + ", serializers=" + this.serializers 177 + '}'; 178 } 179 180 @Override public boolean equals(final Object other) { 181 if (this == other) { 182 return true; 183 } 184 if (!(other instanceof TypeSerializerCollection)) { 185 return false; 186 } 187 final TypeSerializerCollection that = (TypeSerializerCollection) other; 188 return Objects.equals(this.parent, that.parent) 189 && this.serializers.equals(that.serializers); 190 } 191 192 @Override 193 public int hashCode() { 194 return Objects.hash(this.parent, this.serializers); 195 } 196 197 /** 198 * Create a builder for a new type serializer collection without a 199 * parent set. 200 * 201 * <p>If <em>any</em> of the standard serializers provided by Configurate 202 * are desired, either the default collection or a collection inheriting 203 * from the default collection should be applied. 204 * 205 * @return the builder 206 * @since 4.0.0 207 */ 208 public static Builder builder() { 209 return new Builder(null); 210 } 211 212 /** 213 * Get a collection containing all of Configurate's built-in 214 * type serializers. 215 * 216 * @return the collection 217 * @since 4.0.0 218 */ 219 public static TypeSerializerCollection defaults() { 220 return DEFAULTS; 221 } 222 223 /** 224 * A builder to construct new serializer collections. 225 * 226 * @since 4.0.0 227 */ 228 public static class Builder { 229 private final @Nullable TypeSerializerCollection parent; 230 private final List<RegisteredSerializer> serializers = new ArrayList<>(); 231 232 Builder(final @Nullable TypeSerializerCollection parent) { 233 this.parent = parent; 234 } 235 236 /** 237 * Register a type serializer for a given type. 238 * 239 * <p>Serializers registered will match all subclasses of the provided 240 * type, as well as unwrapped primitive equivalents of the type. 241 * 242 * @param type the type to accept 243 * @param serializer the serializer that will be serialized with 244 * @param <T> the type to generify around 245 * @return this builder 246 * @since 4.0.0 247 */ 248 public <T> Builder register(final TypeToken<T> type, final TypeSerializer<? super T> serializer) { 249 return register0(type.getType(), serializer); 250 } 251 252 /** 253 * Register a type serializer for a given type. 254 * 255 * <p>Serializers registered will match all subclasses of the provided 256 * type, as well as unboxed primitive equivalents of the type. 257 * 258 * @param type the type to accept 259 * @param serializer the serializer that will be serialized with 260 * @param <T> the type to generify around 261 * @return this builder 262 * @since 4.0.0 263 */ 264 public <T> Builder register(final Class<T> type, final TypeSerializer<? super T> serializer) { 265 return register0(type, serializer); 266 } 267 268 /** 269 * Register a type serializer matching against a given predicate. 270 * 271 * @param test the predicate to match types against 272 * @param serializer the serializer to serialize matching types with 273 * @param <T> the type parameter 274 * @return this builder 275 * @since 4.0.0 276 */ 277 public <T> Builder register(final Predicate<Type> test, final TypeSerializer<? super T> serializer) { 278 requireNonNull(test, "test"); 279 requireNonNull(serializer, "serializer"); 280 this.serializers.add(new RegisteredSerializer(test, serializer)); 281 return this; 282 } 283 284 /** 285 * Register a scalar serializer with its own attached type token. 286 * 287 * <p>Serializers registered will match all subclasses of the provided 288 * type, as well as unboxed primitive equivalents of the type.</p> 289 * 290 * @param serializer serializer to register 291 * @param <T> value type 292 * @return this builder 293 * @since 4.0.0 294 */ 295 public <T> Builder register(final ScalarSerializer<T> serializer) { 296 requireNonNull(serializer, "serializer"); 297 return register(serializer.type(), serializer); 298 } 299 300 private Builder register0(final Type type, final TypeSerializer<?> serializer) { 301 requireNonNull(type, "type"); 302 requireNonNull(serializer, "serializer"); 303 this.serializers.add(new RegisteredSerializer(test -> { 304 // Test direct type 305 if (GenericTypeReflector.isSuperType(type, test)) { 306 return true; 307 } 308 309 // And upper bounds 310 if (test instanceof WildcardType) { 311 final Type[] upperBounds = ((WildcardType) test).getUpperBounds(); 312 if (upperBounds.length == 1) { 313 return isSuperType(type, upperBounds[0]); 314 } 315 } 316 return false; 317 }, serializer)); 318 return this; 319 } 320 321 /** 322 * Register an <em>exact</em> type serializer for a given type. 323 * 324 * <p>Serializers will only match exact object types. For example, a 325 * serializer registered for {@code List<String>} would not match when 326 * {@code ArrayList<String>} is queried.</p> 327 * 328 * @param type the type to accept 329 * @param serializer the serializer that will be serialized with 330 * @param <T> the type to generify around 331 * @return this builder 332 * @since 4.0.0 333 */ 334 public <T> Builder registerExact(final TypeToken<T> type, final TypeSerializer<? super T> serializer) { 335 return registerExact0(type.getType(), serializer); 336 } 337 338 /** 339 * Register an <em>exact</em> type serializer for a given type. 340 * 341 * <p>Serializers will only match exact object types. For example, a 342 * serializer registered for {@code List<String>} would not match when 343 * {@code ArrayList<String>} is queried.</p> 344 * 345 * @param type the type to accept 346 * @param serializer the serializer that will be serialized with 347 * @param <T> the type to generify around 348 * @return this builder 349 * @since 4.0.0 350 */ 351 public <T> Builder registerExact(final Class<T> type, final TypeSerializer<? super T> serializer) { 352 return registerExact0(type, serializer); 353 } 354 355 /** 356 * Register a scalar serializer with its own attached type token. 357 * 358 * <p>Serializers will only match exact object types. For example, a 359 * serializer registered for {@code List<String>} would not match when 360 * {@code ArrayList<String>} is queried.</p> 361 * 362 * @param serializer serializer to register 363 * @param <T> value type 364 * @return this builder 365 * @since 4.0.0 366 */ 367 public <T> Builder registerExact(final ScalarSerializer<T> serializer) { 368 requireNonNull(serializer, "serializer"); 369 return registerExact(serializer.type(), serializer); 370 } 371 372 private Builder registerExact0(final Type type, final TypeSerializer<?> serializer) { 373 requireNonNull(type, "type"); 374 requireNonNull(serializer, "serializer"); 375 this.serializers.add(new RegisteredSerializer(test -> test.equals(type), serializer)); 376 return this; 377 } 378 379 /** 380 * Register all serializers from {@code other} into this collection. 381 * 382 * <p>Creating a child collection should be preferred, but when merging 383 * multiple sets of serializers together, directly adding other 384 * collections may be the best choice.</p> 385 * 386 * @param other source collection 387 * @return this builder 388 * @since 4.0.0 389 */ 390 public Builder registerAll(final TypeSerializerCollection other) { 391 this.serializers.addAll(requireNonNull(other, "other").serializers); 392 return this; 393 } 394 395 /** 396 * Register a customized object mapper to handle 397 * {@link ConfigSerializable}-annotated objects. 398 * 399 * @param factory factory to retrieve object mappers from 400 * @return this builder 401 * @since 4.0.0 402 */ 403 public Builder registerAnnotatedObjects(final ObjectMapper.Factory factory) { 404 return register(Builder::isAnnotatedTarget, factory.asTypeSerializer()); 405 } 406 407 /** 408 * A predicate to restrict the type serializer created by 409 * {@link ObjectMapper.Factory#asTypeSerializer()} to annotated types. 410 * 411 * @return whether a type is annotated with {@link ConfigSerializable} 412 * @since 4.0.0 413 */ 414 static boolean isAnnotatedTarget(final Type type) { 415 return GenericTypeReflector.annotate(type).isAnnotationPresent(ConfigSerializable.class); 416 } 417 418 /** 419 * Create a new type serializer collection. 420 * 421 * @return a newly created collection 422 * @since 4.0.0 423 */ 424 public TypeSerializerCollection build() { 425 return new TypeSerializerCollection(this.parent, this.serializers); 426 } 427 } 428 429 private static final class RegisteredSerializer { 430 431 private final Predicate<Type> predicate; 432 private final TypeSerializer<?> serializer; 433 434 private RegisteredSerializer(final Predicate<Type> predicate, final TypeSerializer<?> serializer) { 435 this.predicate = predicate; 436 this.serializer = serializer; 437 } 438 439 } 440 441}