Skip to content

Commit

Permalink
Rework field-naming api to be more sensible
Browse files Browse the repository at this point in the history
  • Loading branch information
TBlueF committed Jun 23, 2024
1 parent 8b53ba1 commit d1aa42c
Show file tree
Hide file tree
Showing 7 changed files with 99 additions and 57 deletions.
8 changes: 1 addition & 7 deletions src/main/java/de/bluecolored/bluenbt/BlueNBT.java
Original file line number Diff line number Diff line change
Expand Up @@ -44,13 +44,7 @@ public class BlueNBT {
private final Map<TypeToken<?>, InstanceCreator<?>> instanceCreatorMap = new HashMap<>();

@Getter @Setter
private FieldNameTransformer fieldNameTransformer = s -> {
if (s.isEmpty()) return s;
char first = s.charAt(0);
if (Character.isUpperCase(first))
return Character.toLowerCase(first) + s.substring(1);
return s;
};
private NamingStrategy namingStrategy = NamingStrategy.FIELD_NAME;

public BlueNBT() {
register(ObjectAdapterFactory.INSTANCE);
Expand Down
30 changes: 0 additions & 30 deletions src/main/java/de/bluecolored/bluenbt/FieldNameTransformer.java

This file was deleted.

5 changes: 5 additions & 0 deletions src/main/java/de/bluecolored/bluenbt/NBTName.java
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,11 @@
@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.
*/
String[] value();

}
74 changes: 74 additions & 0 deletions src/main/java/de/bluecolored/bluenbt/NamingStrategy.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*
* This file is part of BlueNBT, licensed under the MIT License (MIT).
*
* Copyright (c) Blue (Lukas Rieger) <https://bluecolored.de>
* Copyright (c) contributors
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package de.bluecolored.bluenbt;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import java.util.function.UnaryOperator;

@FunctionalInterface
public interface NamingStrategy extends Function<Field, String> {

NamingStrategy FIELD_NAME = Field::getName;
NamingStrategy LOWER_CASE = field -> field.getName().toLowerCase();
NamingStrategy UPPER_CASE = field -> field.getName().toUpperCase();
NamingStrategy UPPER_CAMEL_CASE = field -> transformFirstLetter(field.getName(), Character::toUpperCase);

static NamingStrategy lowerCaseWithDelimiter(String delimiter) {
return field -> String.join(delimiter, splitCamelCase(field.getName())).toLowerCase();
}

static NamingStrategy upperCaseWithDelimiter(String delimiter) {
return field -> String.join(delimiter, splitCamelCase(field.getName())).toUpperCase();
}

static String[] splitCamelCase(String input) {
List<String> result = new ArrayList<>();
int start = 0;
for (int i = 1; i < input.length(); i++) {
char c = input.charAt(i);
if (Character.isUpperCase(c)) {
result.add(input.substring(start, i));
start = i;
}
}
result.add(input.substring(start));
return result.toArray(String[]::new);
}

static String transformFirstLetter(String input, UnaryOperator<Character> operation) {
for (int i = 0; i < input.length(); i++) {
char c = input.charAt(i);
if (!Character.isLetter(c)) continue;
return input.substring(0, i) + operation.apply(c) + input.substring(i + 1);
}

// no letters found
return input;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,6 @@ static class DefaultAdapter<T> implements TypeDeserializer<T> {

private final TypeToken<T> type;
private final InstanceCreator<T> constructor;
private final BlueNBT blueNBT;

private final Map<String, FieldAccessor> fields = new HashMap<>();

Expand All @@ -74,7 +73,6 @@ static class DefaultAdapter<T> implements TypeDeserializer<T> {
public DefaultAdapter(TypeToken<T> type, InstanceCreator<T> constructor, BlueNBT blueNBT) {
this.type = type;
this.constructor = constructor;
this.blueNBT = blueNBT;

TypeToken<?> typeToken = type;
Class<?> raw;
Expand All @@ -86,9 +84,9 @@ public DefaultAdapter(TypeToken<T> type, InstanceCreator<T> constructor, BlueNBT

field.setAccessible(true);

String[] names = new String[]{ field.getName() };
String[] names = new String[]{ blueNBT.getNamingStrategy().apply(field) };
NBTName nbtName = field.getAnnotation(NBTName.class);
if (nbtName != null) names = nbtName.value();
if (nbtName != null && nbtName.value().length > 0) names = nbtName.value();

TypeToken<?> fieldType = TypeToken.of(typeToken.resolve(field.getGenericType()));

Expand Down Expand Up @@ -140,11 +138,6 @@ public T read(NBTReader reader) throws IOException {
String name = reader.name();
FieldAccessor fieldInfo = fields.get(name);

if (fieldInfo == null) {
name = blueNBT.getFieldNameTransformer().apply(name);
fieldInfo = fields.get(name);
}

if (fieldInfo != null) {
fieldInfo.read(object, reader);
} else {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -83,9 +83,9 @@ public DefaultAdapter(TypeToken<T> type, BlueNBT blueNBT) {

field.setAccessible(true);

String[] names = new String[]{ field.getName() };
String name = blueNBT.getNamingStrategy().apply(field);
NBTName nbtName = field.getAnnotation(NBTName.class);
if (nbtName != null) names = nbtName.value();
if (nbtName != null && nbtName.value().length > 0) name = nbtName.value()[0];

TypeToken<?> fieldType = TypeToken.of(typeToken.resolve(field.getGenericType()));

Expand All @@ -107,16 +107,14 @@ public DefaultAdapter(TypeToken<T> type, BlueNBT blueNBT) {
});
} else if (SPECIAL_ACCESSORS.containsKey(fieldType.getType())) {
FieldWriter accessor = SPECIAL_ACCESSORS.get(fieldType.getType()).apply(field);
for (String name : names)
fields.put(name, accessor);
fields.put(name, accessor);
continue;
} else {
typeSerializer = blueNBT.getTypeSerializer(fieldType);
}

FieldWriter accessor = new TypeSerializerFieldWriter<>(field, typeSerializer);
for (String name : names)
fields.put(name, accessor);
fields.put(name, accessor);
}

Type superType = typeToken.resolve(raw.getGenericSuperclass());
Expand Down
18 changes: 13 additions & 5 deletions src/test/java/de/bluecolored/bluenbt/BlueNBTTest.java
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,8 @@ public class BlueNBTTest {
public void testBlueNBT() throws IOException {

BlueNBT blueNBT = new BlueNBT();
blueNBT.setNamingStrategy(NamingStrategy.UPPER_CAMEL_CASE);

try (InputStream in = NBTReaderTest.class.getResourceAsStream("/level.dat")) {
assert in != null;

Expand Down Expand Up @@ -110,6 +112,8 @@ public void testObjectAdapter() throws IOException {
public void testArrays() throws IOException {

BlueNBT blueNBT = new BlueNBT();
blueNBT.setNamingStrategy(NamingStrategy.lowerCaseWithDelimiter("_"));

try (InputStream in = loadMcaFileChunk(0, 0)) {
Chunk chunk = blueNBT.read(in, TypeToken.of(Chunk.class));

Expand All @@ -120,10 +124,10 @@ public void testArrays() throws IOException {
assertEquals(section.getBlockStates().getPalette().length, 26);

BlockState blockState = section.getBlockStates().getPalette()[6];
assertEquals(blockState.getProperties().size(), 7);
assertEquals(blockState.getProperties().get("down"), "true");
assertEquals(blockState.getProperties().get("east"), "false");
assertEquals(blockState.getName(), "minecraft:sculk_vein");
assertEquals(7, blockState.getProperties().size());
assertEquals("true", blockState.getProperties().get("down"));
assertEquals("false", blockState.getProperties().get("east"));
assertEquals("minecraft:sculk_vein", blockState.getName());
}

}
Expand Down Expand Up @@ -210,9 +214,10 @@ private static class Chunk {

@Data
private static class Section {
@NBTName("Y")
private int y;
@NBTName("block_states")
private BlockStates blockStates = new BlockStates();
@NBTName("BlockLight")
private byte[] blockLight = new byte[0];
}

Expand All @@ -224,7 +229,9 @@ private static class BlockStates {

@Data
private static class BlockState {
@NBTName("Name")
private String name = "minecraft:air";
@NBTName("Properties")
private Map<String, String> properties = Collections.emptyMap();
}

Expand All @@ -245,6 +252,7 @@ private static class DataTag extends DataTagSuper {
private static class DataTagSuper {
private int difficulty;
private boolean difficultyLocked;
@NBTName("rainTime")
private int rainTime;
}

Expand Down

0 comments on commit d1aa42c

Please sign in to comment.