Skip to content

Commit 4d5be63

Browse files
committed
Implemented support for arrays
1 parent a39b07d commit 4d5be63

11 files changed

+164
-35
lines changed

README.MD

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ maven {
2020
url = 'https://repo.mikigal.pl/releases'
2121
}
2222
23-
compile group: 'pl.mikigal', name: 'ConfigAPI', version: '1.1.4'
23+
compile group: 'pl.mikigal', name: 'ConfigAPI', version: '1.1.5'
2424
```
2525

2626
#### Maven
@@ -33,7 +33,7 @@ compile group: 'pl.mikigal', name: 'ConfigAPI', version: '1.1.4'
3333
<dependency>
3434
<groupId>pl.mikigal</groupId>
3535
<artifactId>ConfigAPI</artifactId>
36-
<version>1.1.4</version>
36+
<version>1.1.5</version>
3737
<scope>compile</scope>
3838
</dependency>
3939
```

build.gradle

+1-1
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ plugins {
44
}
55

66
group 'pl.mikigal'
7-
version '1.1.4'
7+
version '1.1.5'
88

99
publishing {
1010
repositories {

src/main/java/pl/mikigal/config/BukkitConfiguration.java

-4
Original file line numberDiff line numberDiff line change
@@ -71,10 +71,6 @@ public void set(String path, Object value, Comment comment) {
7171

7272
@Override
7373
public void set(String path, Object value) {
74-
if (value != null && value.getClass().isArray()) {
75-
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
76-
}
77-
7874
if (!(value instanceof Collection) && !(value instanceof Map) && (value == null || TypeUtils.isSimpleType(value))) {
7975
super.set(path, value);
8076

src/main/java/pl/mikigal/config/ConfigAPI.java

+11
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,17 @@ public static <T extends Config> T init(Class<T> clazz, NameStyle nameStyle, Com
7272
return configuration;
7373
}
7474

75+
/**
76+
* Initializes instance of Config with default values
77+
* (CAMEL_CASE as NameStyle, ABOVE_CONTENT as CommentStyle, enabled automatic translation of '&' based colors)
78+
* @param clazz Class of your Config interface
79+
* @param plugin Instance of your plugin
80+
* @return Instance of {@param clazz} ready to use methods
81+
*/
82+
public static <T extends Config> T init(Class<T> clazz, JavaPlugin plugin) {
83+
return init(clazz, NameStyle.CAMEL_CASE, CommentStyle.ABOVE_CONTENT, true, plugin);
84+
}
85+
7586
/**
7687
* Allows to get BukkitConfiguration object for config. It allows to access Bukkit's YamlConfiguration raw methods
7788
* @param name Name of your config

src/main/java/pl/mikigal/config/ConfigInvocationHandler.java

-16
Original file line numberDiff line numberDiff line change
@@ -84,10 +84,6 @@ private Object executeGetter(Method method) {
8484
return null;
8585
}
8686

87-
if (method.getReturnType().isArray()) {
88-
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
89-
}
90-
9187
String path = this.getConfigPath(method);
9288
Object value = this.configuration.get(path);
9389

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

144-
if (value != null && value.getClass().isArray()) {
145-
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
146-
}
147-
148140
configuration.set(this.getConfigPath(method), value, method.getAnnotation(Comment.class));
149141
this.configuration.save();
150142
}
@@ -160,10 +152,6 @@ private void prepareMethods() {
160152
continue;
161153
}
162154

163-
if (method.getReturnType().isArray()) {
164-
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
165-
}
166-
167155
if (!method.isDefault()) {
168156
throw new InvalidConfigException("Getter method " + name + " has not default value");
169157
}
@@ -186,10 +174,6 @@ private void prepareMethods() {
186174
continue;
187175
}
188176

189-
if (method.getReturnType().isArray()) {
190-
throw new InvalidConfigException("Arrays are not supported, use Collection instead");
191-
}
192-
193177
if (method.isDefault()) {
194178
throw new InvalidConfigException("Setter method " + name + " has default value");
195179
}

src/main/java/pl/mikigal/config/serializer/Serializers.java

+2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import org.bukkit.potion.PotionEffect;
77
import pl.mikigal.config.exception.InvalidConfigException;
88
import pl.mikigal.config.exception.MissingSerializerException;
9+
import pl.mikigal.config.serializer.universal.UniversalArraySerializer;
910
import pl.mikigal.config.serializer.universal.UniversalCollectionSerializer;
1011
import pl.mikigal.config.serializer.universal.UniversalMapSerializer;
1112

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

34+
register(Object[].class, new UniversalArraySerializer());
3335
register(Collection.class, new UniversalCollectionSerializer());
3436
register(Map.class, new UniversalMapSerializer());
3537
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,96 @@
1+
package pl.mikigal.config.serializer.universal;
2+
3+
import org.bukkit.configuration.ConfigurationSection;
4+
import pl.mikigal.config.BukkitConfiguration;
5+
import pl.mikigal.config.exception.InvalidConfigFileException;
6+
import pl.mikigal.config.exception.MissingSerializerException;
7+
import pl.mikigal.config.serializer.Serializer;
8+
import pl.mikigal.config.serializer.Serializers;
9+
import pl.mikigal.config.util.ConversionUtils;
10+
import pl.mikigal.config.util.TypeUtils;
11+
12+
import java.lang.reflect.Array;
13+
import java.util.Collections;
14+
import java.util.Objects;
15+
import java.util.Set;
16+
import java.util.stream.Collectors;
17+
18+
public class UniversalArraySerializer extends Serializer<Object[]> {
19+
20+
@Override
21+
protected void saveObject(String path, Object[] object, BukkitConfiguration configuration) {
22+
if (object.length == 0) {
23+
throw new IllegalStateException("Can't set empty array to config");
24+
}
25+
26+
Class<?> generic = TypeUtils.getArrayGeneric(object);
27+
Serializer<?> serializer = Serializers.of(generic);
28+
if (serializer == null && !TypeUtils.isSimpleType(generic)) {
29+
throw new MissingSerializerException(generic);
30+
}
31+
32+
configuration.set(path + ".type", generic.getName());
33+
34+
int index = 0;
35+
for (Object element : object) {
36+
if (serializer == null) {
37+
configuration.set(path + "." + index, element);
38+
}
39+
else {
40+
serializer.serialize(path + "." + index, element, configuration);
41+
}
42+
43+
index++;
44+
}
45+
}
46+
47+
@Override
48+
public Object[] deserialize(String path, BukkitConfiguration configuration) {
49+
ConfigurationSection section = configuration.getConfigurationSection(path);
50+
Set<String> keys = section.getKeys(false);
51+
keys.stream()
52+
.filter(key -> !key.equals("type"))
53+
.forEach(key -> {
54+
if (ConversionUtils.asInt(key) == Integer.MIN_VALUE) {
55+
throw new InvalidConfigFileException("Invalid index: " + key + " in " + path + " (should be integer)");
56+
}
57+
});
58+
59+
String type = section.getString("type");
60+
Objects.requireNonNull(type, "Serializer type is not defined for " + path);
61+
62+
try {
63+
Serializer<?> serializer = Serializers.of(type);
64+
Class<?> typeClass = Class.forName(type);
65+
66+
if (serializer == null && !TypeUtils.isSimpleType(typeClass)) {
67+
throw new MissingSerializerException(typeClass);
68+
}
69+
70+
int length = Collections.max(keys
71+
.stream()
72+
.filter(key -> !key.equals("type"))
73+
.map(Integer::parseInt)
74+
.collect(Collectors.toList())) + 1;
75+
76+
Object[] array = (Object[]) Array.newInstance(typeClass, length);
77+
for (String key : keys) {
78+
if (key.equals("type")) {
79+
continue;
80+
}
81+
82+
int index = Integer.parseInt(key);
83+
if (serializer == null) {
84+
array[index] = configuration.get(path + "." + index);
85+
continue;
86+
}
87+
88+
array[index] = serializer.deserialize(path + "." + index, configuration);
89+
}
90+
91+
return array;
92+
} catch (ClassNotFoundException e) {
93+
throw new RuntimeException(e);
94+
}
95+
}
96+
}

src/main/java/pl/mikigal/config/serializer/universal/UniversalCollectionSerializer.java

+7-6
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import pl.mikigal.config.exception.MissingSerializerException;
77
import pl.mikigal.config.serializer.Serializer;
88
import pl.mikigal.config.serializer.Serializers;
9+
import pl.mikigal.config.util.ConversionUtils;
910
import pl.mikigal.config.util.TypeUtils;
1011

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

6162
String collectionRaw = section.getString("structure");
62-
String serializerRaw = section.getString("type");
63+
String type = section.getString("type");
6364

6465
Objects.requireNonNull(collectionRaw, "Collection type is not defined for " + path);
65-
Objects.requireNonNull(serializerRaw, "Serializer type is not defined for " + path);
66+
Objects.requireNonNull(type, "Serializer type is not defined for " + path);
6667

6768
try {
68-
Serializer<?> serializer = Serializers.of(serializerRaw);
69+
Serializer<?> serializer = Serializers.of(type);
6970
Class<?> collectionClass = Class.forName(collectionRaw);
70-
Class<?> serializerClass = Class.forName(serializerRaw);
71+
Class<?> typeClass = Class.forName(type);
7172

72-
if (serializer == null && !TypeUtils.isSimpleType(serializerClass)) {
73-
throw new MissingSerializerException(serializerClass);
73+
if (serializer == null && !TypeUtils.isSimpleType(typeClass)) {
74+
throw new MissingSerializerException(typeClass);
7475
}
7576

7677
Collection collection = (Collection) collectionClass.newInstance();

src/main/java/pl/mikigal/config/serializer/universal/UniversalMapSerializer.java

+6-6
Original file line numberDiff line numberDiff line change
@@ -55,18 +55,18 @@ protected void saveObject(String path, Map object, BukkitConfiguration configura
5555
ConfigurationSection section = configuration.getConfigurationSection(path);
5656

5757
String mapRaw = section.getString("structure");
58-
String serializerRaw = section.getString("type");
58+
String type = section.getString("type");
5959

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

6363
try {
64-
Serializer<?> serializer = Serializers.of(serializerRaw);
64+
Serializer<?> serializer = Serializers.of(type);
6565
Class<?> mapClass = Class.forName(mapRaw);
66-
Class<?> serializerClass = Class.forName(serializerRaw);
66+
Class<?> typeClass = Class.forName(type);
6767

68-
if (serializer == null && !TypeUtils.isSimpleType(serializerClass)) {
69-
throw new MissingSerializerException(serializerClass);
68+
if (serializer == null && !TypeUtils.isSimpleType(typeClass)) {
69+
throw new MissingSerializerException(typeClass);
7070
}
7171

7272
Map map = (Map) mapClass.newInstance();

src/main/java/pl/mikigal/config/util/ConversionUtils.java

+13
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,19 @@ public static double round(double value) {
2424
return (double) tmp / factor;
2525
}
2626

27+
/**
28+
* Converts String to int
29+
* @param input number as String
30+
* @return number as int, if input is invalid returns Integer.MIN_VALUE
31+
*/
32+
public static int asInt(String input) {
33+
try {
34+
return Integer.parseInt(input);
35+
} catch (NumberFormatException e) {
36+
return Integer.MIN_VALUE;
37+
}
38+
}
39+
2740
/**
2841
* Translates text colored by '&' to ChatColor based
2942
* @param raw text colored by '&'

src/main/java/pl/mikigal/config/util/TypeUtils.java

+26
Original file line numberDiff line numberDiff line change
@@ -88,6 +88,10 @@ public static boolean isSimpleType(Class<?> type) {
8888
*/
8989
public static Class<?> getCollectionGeneric(Collection<?> collection) {
9090
for (Object element : collection) {
91+
if (element == null) {
92+
continue;
93+
}
94+
9195
return element.getClass();
9296
}
9397

@@ -102,12 +106,34 @@ public static Class<?> getCollectionGeneric(Collection<?> collection) {
102106
*/
103107
public static Class<?>[] getMapGeneric(Map<?, ?> map) {
104108
for (Map.Entry<?, ?> entry : map.entrySet()) {
109+
if (entry.getKey() == null || entry.getValue() == null) {
110+
continue;
111+
}
112+
105113
return new Class<?>[]{entry.getKey().getClass(), entry.getValue().getClass()};
106114
}
107115

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

119+
/**
120+
* Return generic types of given non-empty array instance
121+
* @param array instance of array
122+
* @return generic type of given class
123+
* @throws InvalidConfigException if array is empty
124+
*/
125+
public static Class<?> getArrayGeneric(Object[] array) {
126+
for (Object element : array) {
127+
if (element == null) {
128+
continue;
129+
}
130+
131+
return element.getClass();
132+
}
133+
134+
throw new InvalidConfigException("Can't get generic type of empty Array");
135+
}
136+
111137
/**
112138
* Allows to check it given type primitive or primitive's wrapper
113139
* @param clazz class which you want to check

0 commit comments

Comments
 (0)