|
27 | 27 | import de.bluecolored.bluenbt.adapter.*; |
28 | 28 | import lombok.Getter; |
29 | 29 | import lombok.Setter; |
| 30 | +import org.jetbrains.annotations.Nullable; |
30 | 31 |
|
31 | 32 | import java.io.IOException; |
32 | 33 | import java.io.InputStream; |
|
42 | 43 | */ |
43 | 44 | public class BlueNBT { |
44 | 45 |
|
| 46 | + @SuppressWarnings("rawtypes") |
| 47 | + private static final TypeResolver NO_TYPE_RESOLVER = new TypeResolver() { |
| 48 | + @Override public TypeToken getBaseType() { throw new UnsupportedOperationException(); } |
| 49 | + @Override public TypeToken resolve(Object base) { throw new UnsupportedOperationException(); } |
| 50 | + @Override public Iterable getPossibleTypes() { throw new UnsupportedOperationException(); } |
| 51 | + }; |
| 52 | + |
45 | 53 | private final List<TypeSerializerFactory> serializerFactories = new ArrayList<>(); |
46 | 54 | private final List<TypeDeserializerFactory> deserializerFactories = new ArrayList<>(); |
47 | 55 | private final List<InstanceCreatorFactory> instanceCreatorFactories = new ArrayList<>(); |
| 56 | + private final List<TypeResolverFactory> typeResolverFactories = new ArrayList<>(); |
48 | 57 | private final Map<TypeToken<?>, TypeSerializer<?>> typeSerializerMap = new ConcurrentHashMap<>(); |
49 | 58 | private final Map<TypeToken<?>, TypeDeserializer<?>> typeDeserializerMap = new ConcurrentHashMap<>(); |
50 | 59 | private final Map<TypeToken<?>, InstanceCreator<?>> instanceCreatorMap = new ConcurrentHashMap<>(); |
| 60 | + private final Map<TypeToken<?>, TypeResolver<?, ?>> typeResolverMap = new ConcurrentHashMap<>(); |
51 | 61 |
|
52 | 62 | /** |
53 | 63 | * The {@link NamingStrategy} this BlueNBT instance uses to determine the NBT-name |
@@ -99,6 +109,13 @@ public synchronized void register(InstanceCreatorFactory instanceCreatorFactory) |
99 | 109 | instanceCreatorFactories.add(instanceCreatorFactory); |
100 | 110 | } |
101 | 111 |
|
| 112 | + /** |
| 113 | + * Registers a {@link TypeResolverFactory} for this BlueNBT instance to use for resolving types. |
| 114 | + */ |
| 115 | + public synchronized void register(TypeResolverFactory typeResolverFactory) { |
| 116 | + typeResolverFactories.add(typeResolverFactory); |
| 117 | + } |
| 118 | + |
102 | 119 | /** |
103 | 120 | * Registers a {@link TypeAdapter} for this BlueNBT instance to use for (de)serialization of the specified type. |
104 | 121 | */ |
@@ -159,6 +176,21 @@ public <U> Optional<? extends InstanceCreator<U>> create(TypeToken<U> createType |
159 | 176 | }); |
160 | 177 | } |
161 | 178 |
|
| 179 | + /** |
| 180 | + * Registers a {@link TypeResolver} for this BlueNBT instance to use for instance-creation of the specified type. |
| 181 | + */ |
| 182 | + public <T> void register(TypeToken<T> type, TypeResolver<T, ?> typeResolver) { |
| 183 | + register(new TypeResolverFactory() { |
| 184 | + @Override |
| 185 | + @SuppressWarnings("unchecked") |
| 186 | + public <U> Optional<? extends TypeResolver<U, ?>> create(TypeToken<U> createType, BlueNBT blueNBT) { |
| 187 | + if (createType.equals(type)) |
| 188 | + return Optional.of((TypeResolver<U, ?>) typeResolver); |
| 189 | + return Optional.empty(); |
| 190 | + } |
| 191 | + }); |
| 192 | + } |
| 193 | + |
162 | 194 | /** |
163 | 195 | * Returns the {@link TypeSerializer} for the given type |
164 | 196 | */ |
@@ -252,6 +284,37 @@ public <T> InstanceCreator<T> getInstanceCreator(TypeToken<T> type) { |
252 | 284 | return instanceCreator; |
253 | 285 | } |
254 | 286 |
|
| 287 | + /** |
| 288 | + * Returns the {@link InstanceCreator} for the given type or null if there is none |
| 289 | + */ |
| 290 | + @SuppressWarnings({"unchecked", "rawtypes"}) |
| 291 | + public <T> @Nullable TypeResolver<T, ?> getTypeResolver(TypeToken<T> type) { |
| 292 | + TypeResolver<T, ?> typeResolver = (TypeResolver<T, ?>) typeResolverMap.get(type); |
| 293 | + if (typeResolver != null && (!(typeResolver instanceof FutureInstanceCreator))) return typeResolver; |
| 294 | + |
| 295 | + synchronized (this) { |
| 296 | + typeResolver = (TypeResolver<T, ?>) typeResolverMap.get(type); |
| 297 | + if (typeResolver != null) return typeResolver; |
| 298 | + |
| 299 | + FutureTypeResolver<T, ?> future = new FutureTypeResolver<>(); |
| 300 | + typeResolverMap.put(type, future); // set future before creation of new deserializers to avoid recursive creation |
| 301 | + |
| 302 | + for (int i = typeResolverFactories.size() - 1; i >= 0; i--) { |
| 303 | + TypeResolverFactory factory = typeResolverFactories.get(i); |
| 304 | + typeResolver = factory.create(type, this).orElse(null); |
| 305 | + if (typeResolver != null) break; |
| 306 | + } |
| 307 | + |
| 308 | + if (typeResolver == null) |
| 309 | + typeResolver = NO_TYPE_RESOLVER; |
| 310 | + |
| 311 | + future.complete((TypeResolver) typeResolver); |
| 312 | + typeResolverMap.put(type, typeResolver); |
| 313 | + } |
| 314 | + |
| 315 | + return typeResolver == NO_TYPE_RESOLVER ? null : typeResolver; |
| 316 | + } |
| 317 | + |
255 | 318 | /** |
256 | 319 | * Serializes an object to NBT using the specified type for serialization, and writes it to the given |
257 | 320 | * {@link OutputStream} |
@@ -433,16 +496,45 @@ private static class FutureInstanceCreator<T> implements InstanceCreator<T> { |
433 | 496 | private InstanceCreator<T> value; |
434 | 497 |
|
435 | 498 | public void complete(InstanceCreator<T> value) { |
436 | | - if (this.value != null) throw new IllegalStateException("FutureObjectConstructor already completed!"); |
| 499 | + if (this.value != null) throw new IllegalStateException("FutureInstanceCreator already completed!"); |
437 | 500 | this.value = Objects.requireNonNull(value); |
438 | 501 | } |
439 | 502 |
|
440 | 503 | @Override |
441 | 504 | public T create() { |
442 | | - if (this.value == null) throw new IllegalStateException("FutureObjectConstructor is not ready!"); |
| 505 | + if (this.value == null) throw new IllegalStateException("FutureInstanceCreator is not ready!"); |
443 | 506 | return this.value.create(); |
444 | 507 | } |
445 | 508 |
|
446 | 509 | } |
447 | 510 |
|
| 511 | + private static class FutureTypeResolver<T, B> implements TypeResolver<T, B> { |
| 512 | + |
| 513 | + private TypeResolver<T, B> value; |
| 514 | + |
| 515 | + public void complete(TypeResolver<T, B> value) { |
| 516 | + if (this.value != null) throw new IllegalStateException("FutureTypeResolver already completed!"); |
| 517 | + this.value = Objects.requireNonNull(value); |
| 518 | + } |
| 519 | + |
| 520 | + @Override |
| 521 | + public TypeToken<B> getBaseType() { |
| 522 | + if (this.value == null) throw new IllegalStateException("FutureTypeResolver is not ready!"); |
| 523 | + return this.value.getBaseType(); |
| 524 | + } |
| 525 | + |
| 526 | + @Override |
| 527 | + public TypeToken<? extends T> resolve(B base) { |
| 528 | + if (this.value == null) throw new IllegalStateException("FutureTypeResolver is not ready!"); |
| 529 | + return this.value.resolve(base); |
| 530 | + } |
| 531 | + |
| 532 | + @Override |
| 533 | + public Iterable<TypeToken<? extends T>> getPossibleTypes() { |
| 534 | + if (this.value == null) throw new IllegalStateException("FutureTypeResolver is not ready!"); |
| 535 | + return this.value.getPossibleTypes(); |
| 536 | + } |
| 537 | + |
| 538 | + } |
| 539 | + |
448 | 540 | } |
0 commit comments