Skip to content

Commit

Permalink
Implemented support for arrays
Browse files Browse the repository at this point in the history
  • Loading branch information
mikigal committed Apr 15, 2021
1 parent a39b07d commit 4d5be63
Show file tree
Hide file tree
Showing 11 changed files with 164 additions and 35 deletions.
4 changes: 2 additions & 2 deletions README.MD
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ maven {
url = 'https://repo.mikigal.pl/releases'
}
compile group: 'pl.mikigal', name: 'ConfigAPI', version: '1.1.4'
compile group: 'pl.mikigal', name: 'ConfigAPI', version: '1.1.5'
```

#### Maven
Expand All @@ -33,7 +33,7 @@ compile group: 'pl.mikigal', name: 'ConfigAPI', version: '1.1.4'
<dependency>
<groupId>pl.mikigal</groupId>
<artifactId>ConfigAPI</artifactId>
<version>1.1.4</version>
<version>1.1.5</version>
<scope>compile</scope>
</dependency>
```
Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ plugins {
}

group 'pl.mikigal'
version '1.1.4'
version '1.1.5'

publishing {
repositories {
Expand Down
4 changes: 0 additions & 4 deletions src/main/java/pl/mikigal/config/BukkitConfiguration.java
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ public void set(String path, Object value, Comment comment) {

@Override
public void set(String path, Object value) {
if (value != null && value.getClass().isArray()) {
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
}

if (!(value instanceof Collection) && !(value instanceof Map) && (value == null || TypeUtils.isSimpleType(value))) {
super.set(path, value);

Expand Down
11 changes: 11 additions & 0 deletions src/main/java/pl/mikigal/config/ConfigAPI.java
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,17 @@ public static <T extends Config> T init(Class<T> clazz, NameStyle nameStyle, Com
return configuration;
}

/**
* Initializes instance of Config with default values
* (CAMEL_CASE as NameStyle, ABOVE_CONTENT as CommentStyle, enabled automatic translation of '&' based colors)
* @param clazz Class of your Config interface
* @param plugin Instance of your plugin
* @return Instance of {@param clazz} ready to use methods
*/
public static <T extends Config> T init(Class<T> clazz, JavaPlugin plugin) {
return init(clazz, NameStyle.CAMEL_CASE, CommentStyle.ABOVE_CONTENT, true, plugin);
}

/**
* Allows to get BukkitConfiguration object for config. It allows to access Bukkit's YamlConfiguration raw methods
* @param name Name of your config
Expand Down
16 changes: 0 additions & 16 deletions src/main/java/pl/mikigal/config/ConfigInvocationHandler.java
Original file line number Diff line number Diff line change
Expand Up @@ -84,10 +84,6 @@ private Object executeGetter(Method method) {
return null;
}

if (method.getReturnType().isArray()) {
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
}

String path = this.getConfigPath(method);
Object value = this.configuration.get(path);

Expand Down Expand Up @@ -141,10 +137,6 @@ private void executeSetter(Method method, Object[] args) {
throw new InvalidConfigException("You can't set value to config setter that isn't @ConfigOptional (method: " + method + ")");
}

if (value != null && value.getClass().isArray()) {
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
}

configuration.set(this.getConfigPath(method), value, method.getAnnotation(Comment.class));
this.configuration.save();
}
Expand All @@ -160,10 +152,6 @@ private void prepareMethods() {
continue;
}

if (method.getReturnType().isArray()) {
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
}

if (!method.isDefault()) {
throw new InvalidConfigException("Getter method " + name + " has not default value");
}
Expand All @@ -186,10 +174,6 @@ private void prepareMethods() {
continue;
}

if (method.getReturnType().isArray()) {
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
}

if (method.isDefault()) {
throw new InvalidConfigException("Setter method " + name + " has default value");
}
Expand Down
2 changes: 2 additions & 0 deletions src/main/java/pl/mikigal/config/serializer/Serializers.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.bukkit.potion.PotionEffect;
import pl.mikigal.config.exception.InvalidConfigException;
import pl.mikigal.config.exception.MissingSerializerException;
import pl.mikigal.config.serializer.universal.UniversalArraySerializer;
import pl.mikigal.config.serializer.universal.UniversalCollectionSerializer;
import pl.mikigal.config.serializer.universal.UniversalMapSerializer;

Expand All @@ -30,6 +31,7 @@ public class Serializers {
register(PotionEffect.class, new PotionEffectSerializer());
register(UUID.class, new UUIDSerializer());

register(Object[].class, new UniversalArraySerializer());
register(Collection.class, new UniversalCollectionSerializer());
register(Map.class, new UniversalMapSerializer());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package pl.mikigal.config.serializer.universal;

import org.bukkit.configuration.ConfigurationSection;
import pl.mikigal.config.BukkitConfiguration;
import pl.mikigal.config.exception.InvalidConfigFileException;
import pl.mikigal.config.exception.MissingSerializerException;
import pl.mikigal.config.serializer.Serializer;
import pl.mikigal.config.serializer.Serializers;
import pl.mikigal.config.util.ConversionUtils;
import pl.mikigal.config.util.TypeUtils;

import java.lang.reflect.Array;
import java.util.Collections;
import java.util.Objects;
import java.util.Set;
import java.util.stream.Collectors;

public class UniversalArraySerializer extends Serializer<Object[]> {

@Override
protected void saveObject(String path, Object[] object, BukkitConfiguration configuration) {
if (object.length == 0) {
throw new IllegalStateException("Can't set empty array to config");
}

Class<?> generic = TypeUtils.getArrayGeneric(object);
Serializer<?> serializer = Serializers.of(generic);
if (serializer == null && !TypeUtils.isSimpleType(generic)) {
throw new MissingSerializerException(generic);
}

configuration.set(path + ".type", generic.getName());

int index = 0;
for (Object element : object) {
if (serializer == null) {
configuration.set(path + "." + index, element);
}
else {
serializer.serialize(path + "." + index, element, configuration);
}

index++;
}
}

@Override
public Object[] deserialize(String path, BukkitConfiguration configuration) {
ConfigurationSection section = configuration.getConfigurationSection(path);
Set<String> keys = section.getKeys(false);
keys.stream()
.filter(key -> !key.equals("type"))
.forEach(key -> {
if (ConversionUtils.asInt(key) == Integer.MIN_VALUE) {
throw new InvalidConfigFileException("Invalid index: " + key + " in " + path + " (should be integer)");
}
});

String type = section.getString("type");
Objects.requireNonNull(type, "Serializer type is not defined for " + path);

try {
Serializer<?> serializer = Serializers.of(type);
Class<?> typeClass = Class.forName(type);

if (serializer == null && !TypeUtils.isSimpleType(typeClass)) {
throw new MissingSerializerException(typeClass);
}

int length = Collections.max(keys
.stream()
.filter(key -> !key.equals("type"))
.map(Integer::parseInt)
.collect(Collectors.toList())) + 1;

Object[] array = (Object[]) Array.newInstance(typeClass, length);
for (String key : keys) {
if (key.equals("type")) {
continue;
}

int index = Integer.parseInt(key);
if (serializer == null) {
array[index] = configuration.get(path + "." + index);
continue;
}

array[index] = serializer.deserialize(path + "." + index, configuration);
}

return array;
} catch (ClassNotFoundException e) {
throw new RuntimeException(e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import pl.mikigal.config.exception.MissingSerializerException;
import pl.mikigal.config.serializer.Serializer;
import pl.mikigal.config.serializer.Serializers;
import pl.mikigal.config.util.ConversionUtils;
import pl.mikigal.config.util.TypeUtils;

import java.lang.reflect.Method;
Expand Down Expand Up @@ -59,18 +60,18 @@ public Collection<?> deserialize(String path, BukkitConfiguration configuration)
ConfigurationSection section = configuration.getConfigurationSection(path);

String collectionRaw = section.getString("structure");
String serializerRaw = section.getString("type");
String type = section.getString("type");

Objects.requireNonNull(collectionRaw, "Collection type is not defined for " + path);
Objects.requireNonNull(serializerRaw, "Serializer type is not defined for " + path);
Objects.requireNonNull(type, "Serializer type is not defined for " + path);

try {
Serializer<?> serializer = Serializers.of(serializerRaw);
Serializer<?> serializer = Serializers.of(type);
Class<?> collectionClass = Class.forName(collectionRaw);
Class<?> serializerClass = Class.forName(serializerRaw);
Class<?> typeClass = Class.forName(type);

if (serializer == null && !TypeUtils.isSimpleType(serializerClass)) {
throw new MissingSerializerException(serializerClass);
if (serializer == null && !TypeUtils.isSimpleType(typeClass)) {
throw new MissingSerializerException(typeClass);
}

Collection collection = (Collection) collectionClass.newInstance();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,18 @@ protected void saveObject(String path, Map object, BukkitConfiguration configura
ConfigurationSection section = configuration.getConfigurationSection(path);

String mapRaw = section.getString("structure");
String serializerRaw = section.getString("type");
String type = section.getString("type");

Objects.requireNonNull(mapRaw, "Collection type is not defined for " + path);
Objects.requireNonNull(serializerRaw, "Serializer type is not defined for " + path);
Objects.requireNonNull(type, "Serializer type is not defined for " + path);

try {
Serializer<?> serializer = Serializers.of(serializerRaw);
Serializer<?> serializer = Serializers.of(type);
Class<?> mapClass = Class.forName(mapRaw);
Class<?> serializerClass = Class.forName(serializerRaw);
Class<?> typeClass = Class.forName(type);

if (serializer == null && !TypeUtils.isSimpleType(serializerClass)) {
throw new MissingSerializerException(serializerClass);
if (serializer == null && !TypeUtils.isSimpleType(typeClass)) {
throw new MissingSerializerException(typeClass);
}

Map map = (Map) mapClass.newInstance();
Expand Down
13 changes: 13 additions & 0 deletions src/main/java/pl/mikigal/config/util/ConversionUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,19 @@ public static double round(double value) {
return (double) tmp / factor;
}

/**
* Converts String to int
* @param input number as String
* @return number as int, if input is invalid returns Integer.MIN_VALUE
*/
public static int asInt(String input) {
try {
return Integer.parseInt(input);
} catch (NumberFormatException e) {
return Integer.MIN_VALUE;
}
}

/**
* Translates text colored by '&' to ChatColor based
* @param raw text colored by '&'
Expand Down
26 changes: 26 additions & 0 deletions src/main/java/pl/mikigal/config/util/TypeUtils.java
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ public static boolean isSimpleType(Class<?> type) {
*/
public static Class<?> getCollectionGeneric(Collection<?> collection) {
for (Object element : collection) {
if (element == null) {
continue;
}

return element.getClass();
}

Expand All @@ -102,12 +106,34 @@ public static Class<?> getCollectionGeneric(Collection<?> collection) {
*/
public static Class<?>[] getMapGeneric(Map<?, ?> map) {
for (Map.Entry<?, ?> entry : map.entrySet()) {
if (entry.getKey() == null || entry.getValue() == null) {
continue;
}

return new Class<?>[]{entry.getKey().getClass(), entry.getValue().getClass()};
}

throw new InvalidConfigException("Can't get generic type of empty Map");
}

/**
* Return generic types of given non-empty array instance
* @param array instance of array
* @return generic type of given class
* @throws InvalidConfigException if array is empty
*/
public static Class<?> getArrayGeneric(Object[] array) {
for (Object element : array) {
if (element == null) {
continue;
}

return element.getClass();
}

throw new InvalidConfigException("Can't get generic type of empty Array");
}

/**
* Allows to check it given type primitive or primitive's wrapper
* @param clazz class which you want to check
Expand Down

0 comments on commit 4d5be63

Please sign in to comment.