Skip to content

Commit 99ef370

Browse files
committed
Add TypeResolver functionallity
1 parent f25fd49 commit 99ef370

File tree

7 files changed

+327
-12
lines changed

7 files changed

+327
-12
lines changed

src/main/java/de/bluecolored/bluenbt/BlueNBT.java

Lines changed: 94 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@
2727
import de.bluecolored.bluenbt.adapter.*;
2828
import lombok.Getter;
2929
import lombok.Setter;
30+
import org.jetbrains.annotations.Nullable;
3031

3132
import java.io.IOException;
3233
import java.io.InputStream;
@@ -42,12 +43,21 @@
4243
*/
4344
public class BlueNBT {
4445

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+
4553
private final List<TypeSerializerFactory> serializerFactories = new ArrayList<>();
4654
private final List<TypeDeserializerFactory> deserializerFactories = new ArrayList<>();
4755
private final List<InstanceCreatorFactory> instanceCreatorFactories = new ArrayList<>();
56+
private final List<TypeResolverFactory> typeResolverFactories = new ArrayList<>();
4857
private final Map<TypeToken<?>, TypeSerializer<?>> typeSerializerMap = new ConcurrentHashMap<>();
4958
private final Map<TypeToken<?>, TypeDeserializer<?>> typeDeserializerMap = new ConcurrentHashMap<>();
5059
private final Map<TypeToken<?>, InstanceCreator<?>> instanceCreatorMap = new ConcurrentHashMap<>();
60+
private final Map<TypeToken<?>, TypeResolver<?, ?>> typeResolverMap = new ConcurrentHashMap<>();
5161

5262
/**
5363
* The {@link NamingStrategy} this BlueNBT instance uses to determine the NBT-name
@@ -99,6 +109,13 @@ public synchronized void register(InstanceCreatorFactory instanceCreatorFactory)
99109
instanceCreatorFactories.add(instanceCreatorFactory);
100110
}
101111

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+
102119
/**
103120
* Registers a {@link TypeAdapter} for this BlueNBT instance to use for (de)serialization of the specified type.
104121
*/
@@ -159,6 +176,21 @@ public <U> Optional<? extends InstanceCreator<U>> create(TypeToken<U> createType
159176
});
160177
}
161178

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+
162194
/**
163195
* Returns the {@link TypeSerializer} for the given type
164196
*/
@@ -252,6 +284,37 @@ public <T> InstanceCreator<T> getInstanceCreator(TypeToken<T> type) {
252284
return instanceCreator;
253285
}
254286

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+
255318
/**
256319
* Serializes an object to NBT using the specified type for serialization, and writes it to the given
257320
* {@link OutputStream}
@@ -433,16 +496,45 @@ private static class FutureInstanceCreator<T> implements InstanceCreator<T> {
433496
private InstanceCreator<T> value;
434497

435498
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!");
437500
this.value = Objects.requireNonNull(value);
438501
}
439502

440503
@Override
441504
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!");
443506
return this.value.create();
444507
}
445508

446509
}
447510

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+
448540
}
Lines changed: 83 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
package de.bluecolored.bluenbt;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
5+
import java.io.ByteArrayOutputStream;
6+
import java.io.IOException;
7+
import java.io.InputStream;
8+
9+
class DataLogInputStream extends InputStream {
10+
11+
protected final InputStream in;
12+
13+
protected boolean isLogging;
14+
protected ByteArrayOutputStream log;
15+
16+
public DataLogInputStream(InputStream in) {
17+
this.in = in;
18+
this.isLogging = false;
19+
this.log = new ByteArrayOutputStream();
20+
}
21+
22+
public void startLog() {
23+
log.reset();
24+
isLogging = true;
25+
}
26+
27+
public byte[] stopLog() {
28+
isLogging = false;
29+
return log.toByteArray();
30+
}
31+
32+
@Override
33+
public int read() throws IOException {
34+
int b = in.read();
35+
if (isLogging && b != -1) log.write(b);
36+
return b;
37+
}
38+
39+
@Override
40+
public int read(byte @NotNull [] b) throws IOException {
41+
int l = in.read(b);
42+
if (isLogging && l != -1) log.write(b, 0, l);
43+
return l;
44+
}
45+
46+
@Override
47+
public int read(byte @NotNull [] b, int off, int len) throws IOException {
48+
int l = in.read(b, off, len);
49+
if (isLogging && l != -1) log.write(b, off, l);
50+
return l;
51+
}
52+
53+
@Override
54+
public byte[] readNBytes(int len) throws IOException {
55+
byte[] d = in.readNBytes(len);
56+
if (isLogging) log.write(d, 0, Math.min(d.length, len));
57+
return d;
58+
}
59+
60+
@Override
61+
public int readNBytes(byte[] b, int off, int len) throws IOException {
62+
int l = in.readNBytes(b, off, len);
63+
if (isLogging && l != -1) log.write(b, off, l);
64+
return l;
65+
}
66+
67+
@Override
68+
public long skip(long n) throws IOException {
69+
if (!isLogging) return in.skip(n);
70+
return super.skip(n);
71+
}
72+
73+
@Override
74+
public int available() throws IOException {
75+
return in.available();
76+
}
77+
78+
@Override
79+
public void close() throws IOException {
80+
in.close();
81+
}
82+
83+
}

src/main/java/de/bluecolored/bluenbt/NBTReader.java

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,19 +39,22 @@
3939
public class NBTReader implements Closeable {
4040
private static final String UNKNOWN_NAME = "<unknown>";
4141

42+
private final DataLogInputStream log;
4243
private final DataInputStream in;
4344

4445
private int stackPosition = 0;
4546
private TagType[] stack = new TagType[32];
4647
private String[] nameStack = new String[32];
4748
private int[] listStack = new int[32];
4849

50+
public NBTReader(byte @NotNull [] data) {
51+
this(new ByteArrayInputStream(data));
52+
}
53+
4954
public NBTReader(@NotNull InputStream in) {
5055
Objects.requireNonNull(in);
51-
if (in instanceof DataInputStream)
52-
this.in = (DataInputStream) in;
53-
else
54-
this.in = new DataInputStream(in);
56+
this.log = new DataLogInputStream(in);
57+
this.in = new DataInputStream(log);
5558
}
5659

5760
public TagType peek() throws IOException {
@@ -302,6 +305,25 @@ private void readLongArray(int length, Object bufferArray) throws IOException {
302305
skipNBytes((long) (length - readLength) * TagType.LONG.getSize());
303306
}
304307

308+
/**
309+
* Reads the entire next element and returns it as a raw nbt-data byte-array.
310+
*/
311+
public byte[] raw() throws IOException {
312+
checkState();
313+
log.startLog();
314+
315+
// write tag-id and name back into log
316+
DataOutputStream dOut = new DataOutputStream(log.log);
317+
dOut.write(peek().getId());
318+
dOut.writeUTF(name());
319+
dOut.flush();
320+
321+
// skip element, writing it into the log
322+
skip();
323+
324+
return log.stopLog();
325+
}
326+
305327
/**
306328
* Skips over the next element.
307329
*/
Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
package de.bluecolored.bluenbt;
2+
3+
public interface TypeResolver<T, B> {
4+
5+
TypeToken<B> getBaseType();
6+
7+
TypeToken<? extends T> resolve(B base);
8+
9+
Iterable<TypeToken<? extends T>> getPossibleTypes();
10+
11+
}
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
/*
2+
* This file is part of BlueNBT, licensed under the MIT License (MIT).
3+
*
4+
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
5+
* Copyright (c) contributors
6+
*
7+
* Permission is hereby granted, free of charge, to any person obtaining a copy
8+
* of this software and associated documentation files (the "Software"), to deal
9+
* in the Software without restriction, including without limitation the rights
10+
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11+
* copies of the Software, and to permit persons to whom the Software is
12+
* furnished to do so, subject to the following conditions:
13+
*
14+
* The above copyright notice and this permission notice shall be included in
15+
* all copies or substantial portions of the Software.
16+
*
17+
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18+
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19+
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20+
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21+
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22+
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23+
* THE SOFTWARE.
24+
*/
25+
package de.bluecolored.bluenbt;
26+
27+
import java.util.Optional;
28+
29+
/**
30+
* A factory for creating {@link TypeResolver}s
31+
*/
32+
@FunctionalInterface
33+
public interface TypeResolverFactory {
34+
35+
<T> Optional<? extends TypeResolver<T, ?>> create(TypeToken<T> type, BlueNBT blueNBT);
36+
37+
}

0 commit comments

Comments
 (0)