Skip to content

Commit

Permalink
Fix some final bugs and add javadocs
Browse files Browse the repository at this point in the history
  • Loading branch information
TBlueF committed Jun 23, 2024
1 parent 6d75c48 commit 02d01da
Show file tree
Hide file tree
Showing 30 changed files with 500 additions and 125 deletions.
20 changes: 20 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,23 @@ try (
> Both times we GZIP(De)Compressed our streams before writing/reading. This is because usually all nbt-files are
> GZIP-Compressed, BlueNBT does **not** do this for us to allow more flexibility.
> Also, make sure to use buffered streams before (de)compression to greatly improve compression-performance.
### Using TypeTokens
Sometimes you have a type with generic type-variables that you want to deserialize, for example `HashMap<String, Integer>`.
However, you can't easily create a `Class<?>` or `Type` for such a type that you can use to call `BlueNBT#read(InputStream in, Class<T> type)`.
This is where `TypeToken`s come in.
You can create a `TypeToken` in multiple ways:
```java
// simply from a Class<?> or Type
TypeToken.of(String.class) // String

// for an array of a certain type
TypeToken.array(String.class) // String[]

// for a raw Class<?> with additional type-parameters
TypeToken.of(Map.class, String.class, Integer.class) // Map<String, Integer>

// or by creating a generic anonymous subclass of TypeToken
new TypeToken< Map<String, Collection<Integer>> >() {} // Map<String, Collection<Integer>>
```
You can then pass this `TypeToken` to e.g. `BlueNBT#read(InputStream in, TypeToken<T> type)`.
136 changes: 134 additions & 2 deletions src/main/java/de/bluecolored/bluenbt/BlueNBT.java
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,11 @@
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;

/**
* The heart of BlueNBT.
* <p>Use this class to register your {@link TypeSerializer}s, {@link TypeDeserializer}s and {@link InstanceCreator}s and
* (de)serialize any object to/from NBT.</p>
*/
public class BlueNBT {

private final List<TypeSerializerFactory> serializerFactories = new ArrayList<>();
Expand All @@ -44,36 +49,59 @@ public class BlueNBT {
private final Map<TypeToken<?>, TypeDeserializer<?>> typeDeserializerMap = new ConcurrentHashMap<>();
private final Map<TypeToken<?>, InstanceCreator<?>> instanceCreatorMap = new ConcurrentHashMap<>();

/**
* The {@link NamingStrategy} this BlueNBT instance uses to determine the NBT-name
* from a given {@link java.lang.reflect.Field}.
* <p>Defaults to {@link NamingStrategy#FIELD_NAME}</p>
*/
@Getter @Setter
private NamingStrategy namingStrategy = NamingStrategy.FIELD_NAME;

/**
* Creates a fresh BlueNBT instance with the default configuration and default (de)serializers.
*/
public BlueNBT() {
register(ObjectAdapterFactory.INSTANCE);
register(TypeToken.of(Object.class), ObjectDeserializer.INSTANCE);
register(ArrayAdapterFactory.INSTANCE);
register(PrimitiveSerializerFactory.INSTANCE);
register(PrimitiveDeserializerFactory.INSTANCE);
register(CollectionAdapterFactory.INSTANCE);
register(MapAdapterFactory.INSTANCE);
}

/**
* Registers a {@link TypeAdapterFactory} for this BlueNBT instance to use for (de)serialization.
*/
public synchronized void register(TypeAdapterFactory typeAdapterFactory) {
serializerFactories.add(typeAdapterFactory);
deserializerFactories.add(typeAdapterFactory);
typeSerializerMap.clear();
}

/**
* Registers a {@link TypeSerializerFactory} for this BlueNBT instance to use for serialization.
*/
public synchronized void register(TypeSerializerFactory typeSerializerFactory) {
serializerFactories.add(typeSerializerFactory);
}

/**
* Registers a {@link TypeDeserializerFactory} for this BlueNBT instance to use for deserialization.
*/
public synchronized void register(TypeDeserializerFactory typeDeserializerFactory) {
deserializerFactories.add(typeDeserializerFactory);
}

/**
* Registers a {@link InstanceCreatorFactory} for this BlueNBT instance to use for instance-creation.
*/
public synchronized void register(InstanceCreatorFactory instanceCreatorFactory) {
instanceCreatorFactories.add(instanceCreatorFactory);
}

/**
* Registers a {@link TypeAdapter} for this BlueNBT instance to use for (de)serialization of the specified type.
*/
public <T> void register(TypeToken<T> type, TypeAdapter<T> typeAdapter) {
register(new TypeAdapterFactory() {
@Override
Expand All @@ -86,6 +114,9 @@ public <U> Optional<TypeAdapter<U>> create(TypeToken<U> createType, BlueNBT blue
});
}

/**
* Registers a {@link TypeSerializer} for this BlueNBT instance to use for serialization of the specified type.
*/
public <T> void register(TypeToken<T> type, TypeSerializer<T> typeSerializer) {
register(new TypeSerializerFactory() {
@Override
Expand All @@ -98,6 +129,9 @@ public <U> Optional<TypeSerializer<U>> create(TypeToken<U> createType, BlueNBT b
});
}

/**
* Registers a {@link TypeDeserializer} for this BlueNBT instance to use for deserialization of the specified type.
*/
public <T> void register(TypeToken<T> type, TypeDeserializer<T> typeDeserializer) {
register(new TypeDeserializerFactory() {
@Override
Expand All @@ -110,7 +144,10 @@ public <U> Optional<TypeDeserializer<U>> create(TypeToken<U> createType, BlueNBT
});
}

public <T> void register(Type type, InstanceCreator<T> instanceCreator) {
/**
* Registers a {@link InstanceCreator} for this BlueNBT instance to use for instance-creation of the specified type.
*/
public <T> void register(TypeToken<T> type, InstanceCreator<T> instanceCreator) {
register(new InstanceCreatorFactory() {
@Override
@SuppressWarnings("unchecked")
Expand All @@ -122,6 +159,9 @@ public <U> Optional<? extends InstanceCreator<U>> create(TypeToken<U> createType
});
}

/**
* Returns the {@link TypeSerializer} for the given type
*/
@SuppressWarnings("unchecked")
public <T> TypeSerializer<T> getTypeSerializer(TypeToken<T> type) {
TypeSerializer<T> serializer = (TypeSerializer<T>) typeSerializerMap.get(type);
Expand Down Expand Up @@ -150,6 +190,9 @@ public <T> TypeSerializer<T> getTypeSerializer(TypeToken<T> type) {
return serializer;
}

/**
* Returns the {@link TypeDeserializer} for the given type
*/
@SuppressWarnings("unchecked")
public <T> TypeDeserializer<T> getTypeDeserializer(TypeToken<T> type) {
TypeDeserializer<T> deserializer = (TypeDeserializer<T>) typeDeserializerMap.get(type);
Expand Down Expand Up @@ -178,6 +221,9 @@ public <T> TypeDeserializer<T> getTypeDeserializer(TypeToken<T> type) {
return deserializer;
}

/**
* Returns the {@link InstanceCreator} for the given type
*/
@SuppressWarnings("unchecked")
public <T> InstanceCreator<T> getInstanceCreator(TypeToken<T> type) {
InstanceCreator<T> instanceCreator = (InstanceCreator<T>) instanceCreatorMap.get(type);
Expand Down Expand Up @@ -206,52 +252,138 @@ public <T> InstanceCreator<T> getInstanceCreator(TypeToken<T> type) {
return instanceCreator;
}

/**
* Serializes an object to NBT using the specified type for serialization, and writes it to the given
* {@link OutputStream}
* @param object The Object that should be serialized
* @param out The {@link OutputStream} to write the data to
* @param type The type that should be used during serialization
* @throws IOException If an {@link IOException} occurred
*/
public <T> void write(T object, OutputStream out, TypeToken<T> type) throws IOException {
write(object, new NBTWriter(out), type);
}

/**
* Serializes an object to NBT using the specified type for serialization, and writes it to the given
* {@link NBTWriter}
* @param object The Object that should be serialized
* @param out The {@link NBTWriter} to write the data to
* @param type The type that should be used during serialization
* @throws IOException If an {@link IOException} occurred
*/
public <T> void write(T object, NBTWriter out, TypeToken<T> type) throws IOException {
getTypeSerializer(type).write(object, out);
}

/**
* Serializes an object to NBT using the specified {@link Class} for serialization, and writes it to the given
* {@link OutputStream}
* @param object The Object that should be serialized
* @param out The {@link OutputStream} to write the data to
* @param type The {@link Class} that should be used during serialization
* @throws IOException If an {@link IOException} occurred
*/
public <T> void write(T object, OutputStream out, Class<T> type) throws IOException {
write(object, out, TypeToken.of(type));
}

/**
* Serializes an object to NBT using the specified {@link Class} for serialization, and writes it to the given
* {@link NBTWriter}
* @param object The Object that should be serialized
* @param out The {@link NBTWriter} to write the data to
* @param type The {@link Class} that should be used during serialization
* @throws IOException If an {@link IOException} occurred
*/
public <T> void write(T object, NBTWriter out, Class<T> type) throws IOException {
write(object, out, TypeToken.of(type));
}

/**
* Serializes an object to NBT and writes it to the given {@link OutputStream}
* @param object The Object that should be serialized
* @param out The {@link OutputStream} to write the data to
* @throws IOException If an {@link IOException} occurred
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public void write(Object object, OutputStream out) throws IOException {
write(object, out, (Class) object.getClass());
}

/**
* Serializes an object to NBT and writes it to the given {@link NBTWriter}
* @param object The Object that should be serialized
* @param out The {@link NBTWriter} to write the data to
* @throws IOException If an {@link IOException} occurred
*/
@SuppressWarnings({"unchecked", "rawtypes"})
public void write(Object object, NBTWriter out) throws IOException {
write(object, out, (Class) object.getClass());
}

/**
* Reads an object from the given {@link InputStream} and deserializes it from NBT to the given type.
* @param in The {@link InputStream} to read from
* @param type The type of the object
* @return The deserialized object
* @throws IOException If an {@link IOException} occurred
*/
public <T> T read(InputStream in, TypeToken<T> type) throws IOException {
return read(new NBTReader(in), type);
}

/**
* Reads an object from the given {@link NBTReader} and deserializes it from NBT to the given type.
* @param in The {@link NBTReader} to read from
* @param type The type of the object
* @return The deserialized object
* @throws IOException If an {@link IOException} occurred
*/
public <T> T read(NBTReader in, TypeToken<T> type) throws IOException {
return getTypeDeserializer(type).read(in);
}

/**
* Reads an object from the given {@link InputStream} and deserializes it from NBT to the given type.
* @param in The {@link InputStream} to read from
* @param type The type of the object
* @return The deserialized object
* @throws IOException If an {@link IOException} occurred
*/
public <T> T read(InputStream in, Class<T> type) throws IOException {
return read(in, TypeToken.of(type));
}

/**
* Reads an object from the given {@link NBTReader} and deserializes it from NBT to the given type.
* @param in The {@link NBTReader} to read from
* @param type The type of the object
* @return The deserialized object
* @throws IOException If an {@link IOException} occurred
*/
public <T> T read(NBTReader in, Class<T> type) throws IOException {
return read(in, TypeToken.of(type));
}

/**
* Reads an object from the given {@link InputStream} and deserializes it from NBT to the given type.
* @param in The {@link InputStream} to read from
* @param type The type of the object
* @return The deserialized object
* @throws IOException If an {@link IOException} occurred
*/
public Object read(InputStream in, Type type) throws IOException {
return read(in, TypeToken.of(type));
}

/**
* Reads an object from the given {@link NBTReader} and deserializes it from NBT to the given type.
* @param in The {@link NBTReader} to read from
* @param type The type of the object
* @return The deserialized object
* @throws IOException If an {@link IOException} occurred
*/
public Object read(NBTReader in, Type type) throws IOException {
return read(in, TypeToken.of(type));
}
Expand Down
7 changes: 7 additions & 0 deletions src/main/java/de/bluecolored/bluenbt/InstanceCreator.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,15 @@
*/
package de.bluecolored.bluenbt;

/**
* An {@link InstanceCreator} is able to create instances of a certain type T
*/
@FunctionalInterface
public interface InstanceCreator<T> {

/**
* Creates and returns a new instance of T
*/
T create();

}
11 changes: 11 additions & 0 deletions src/main/java/de/bluecolored/bluenbt/InstanceCreatorFactory.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,19 @@

import java.util.Optional;

/**
* A factory for creating {@link InstanceCreator}s
*/
@FunctionalInterface
public interface InstanceCreatorFactory {

/**
* Attempts to create and return an {@link InstanceCreator} for the given type, returning an empty {@link Optional} if the type
* is not supported by this factory.
* @param type The type the new {@link InstanceCreator} should be able to produce
* @param blueNBT The currently used BlueNBT-instance
* @return An {@link Optional} with the {@link InstanceCreator} or an empty Optional if the type is not supported
*/
<T> Optional<? extends InstanceCreator<T>> create(TypeToken<T> type, BlueNBT blueNBT);

}
9 changes: 9 additions & 0 deletions src/main/java/de/bluecolored/bluenbt/NBTAdapter.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Use this annotation on any field or class definition to set a specific {@link TypeAdapter} that should be used
* when (de)serializing this field or type.
* <p>Field-annotations have a higher priority than class-annotations.</p>
* <p>{@link NBTSerializer} or {@link NBTDeserializer} have a higher priority than {@link NBTAdapter} annotations.</p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface NBTAdapter {

/**
* The {@link TypeAdapter} that should be used for (de)serialization
*/
Class<? extends TypeAdapter<?>> value();

}
9 changes: 9 additions & 0 deletions src/main/java/de/bluecolored/bluenbt/NBTDeserializer.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,19 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Use this annotation on any field or class definition to set a specific {@link TypeDeserializer} that should be used
* when deserializing this field or type.
* <p>Field-annotations have a higher priority than class-annotations.</p>
* <p>{@link NBTDeserializer} has a higher priority than {@link NBTAdapter} annotations.</p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD})
public @interface NBTDeserializer {

/**
* The {@link TypeDeserializer} that should be used for deserialization
*/
Class<? extends TypeDeserializer<?>> value();

}
10 changes: 7 additions & 3 deletions src/main/java/de/bluecolored/bluenbt/NBTName.java
Original file line number Diff line number Diff line change
Expand Up @@ -29,14 +29,18 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

/**
* Defines one or more fixed nbt-names for the annotated field.<br>
* All names of the list will be considered during deserialization, but only the first name will be used for
* serialization.
* <p>The defined names will ignore any configured {@link NamingStrategy}s.</p>
*/
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
public @interface NBTName {

/**
* Defines one or more nbt-names for the annotated field.<br>
* All names of the list will be considered during deserialization, but only the first name will be used for
* serialization.
* The name(s) that should be used for (de)serialization
*/
String[] value();

Expand Down
Loading

0 comments on commit 02d01da

Please sign in to comment.