Skip to content

Commit c349333

Browse files
authored
Merge pull request #5 from ZorTik/development
Development
2 parents 9ad42d9 + 0fd2861 commit c349333

14 files changed

Lines changed: 486 additions & 31 deletions

.idea/jarRepositories.xml

Lines changed: 5 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,12 +18,17 @@ repositories {
1818
name = 'sonatype'
1919
url = "https://oss.sonatype.org/content/repositories/snapshots/"
2020
}
21+
maven {
22+
name = 'jitpack'
23+
url = 'https://jitpack.io'
24+
}
2125
}
2226

2327
dependencies {
2428
implementation group: 'org.jetbrains', name: 'annotations', version: '20.1.0'
2529
compileOnly 'org.spigotmc:spigot-api:1.8.8-R0.1-SNAPSHOT'
2630
compileOnly 'org.projectlombok:lombok:1.18.24'
31+
compileOnly 'com.github.ZorTik:ContainrGUI:0.4.1'
2732
annotationProcessor 'org.projectlombok:lombok:1.18.24'
2833
}
2934

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package me.zort.configurationlib;
2+
3+
import me.zort.configurationlib.configuration.bukkit.BukkitFileConfigurationNode;
4+
import me.zort.configurationlib.configuration.virtual.VirtualSectionNode;
5+
import org.bukkit.plugin.Plugin;
6+
7+
import java.io.File;
8+
9+
public final class ConfigurationLib {
10+
11+
private ConfigurationLib() {
12+
}
13+
14+
public static BukkitFileConfigurationNode bukkit(File file) {
15+
return ConfigurationLibBukkit.of(ConfigurationLibBukkit.prepareDataFile(file));
16+
}
17+
18+
public static BukkitFileConfigurationNode bukkit(Plugin plugin, String name) {
19+
return ConfigurationLibBukkit.of(ConfigurationLibBukkit.prepareDataFile(plugin, name));
20+
}
21+
22+
public static VirtualSectionNode virtual() {
23+
return new VirtualSectionNode();
24+
}
25+
26+
}

src/main/java/me/zort/configurationlib/NodeDeserializer.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import me.zort.configurationlib.util.Placeholders;
44
import org.jetbrains.annotations.NotNull;
5+
import org.jetbrains.annotations.Nullable;
56

67
/**
78
* Node deserializer class.
@@ -11,6 +12,11 @@
1112
*/
1213
public interface NodeDeserializer<T, L> extends NodeAdapter<T, L> {
1314

15+
@Nullable
16+
default T preBuildInstance(Class<T> deserializeInto, NodeContext<Node<L>, L> context, Placeholders placeholders) {
17+
return null;
18+
}
19+
1420
/**
1521
* Deserializes the given context to deserializeInto object.
1622
* Return value of this function should return deserializeInto

src/main/java/me/zort/configurationlib/SectionNode.java

Lines changed: 95 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010
import me.zort.configurationlib.annotation.ThisNodeId;
1111
import me.zort.configurationlib.util.NodeTypeToken;
1212
import me.zort.configurationlib.util.Placeholders;
13+
import me.zort.configurationlib.util.ReflectionHelper;
1314
import me.zort.configurationlib.util.Validator;
1415
import org.apache.commons.lang.ArrayUtils;
1516
import org.jetbrains.annotations.ApiStatus;
@@ -35,7 +36,8 @@
3536
public abstract class SectionNode<L> implements Node<L> {
3637

3738
private final Map<Class<?>, NodeAdapter<?, L>> adapters = new ConcurrentHashMap<>();
38-
@Nullable
39+
40+
@Getter(onMethod_ = {@Nullable})
3941
private final SectionNode<L> parent;
4042
private LogAdapter logAdapter;
4143
@Setter
@@ -55,11 +57,10 @@ public SectionNode(@Nullable SectionNode<L> parent, LogAdapter logAdapter) {
5557
}
5658

5759
@ApiStatus.Internal
58-
public abstract Node<L> createNode(String key, Object value, NodeTypeToken<?> type);
60+
public abstract Node<L> createNode(String key, @Nullable Object value, NodeTypeToken<?> type);
5961
// Key is always definitive.
6062
public abstract void deleteNode(String key);
6163
public abstract void set(String key, Node<L> node);
62-
public abstract Node<L> get(String path);
6364
public abstract Collection<Node<L>> getNodes();
6465

6566
public void clear() {
@@ -100,6 +101,15 @@ public void unregisterAdapter(Class<?> type) {
100101
adapters.remove(type);
101102
}
102103

104+
/**
105+
* Creates and sets new section to the source.
106+
*
107+
* @param key The key/path of the section.
108+
*/
109+
public void createSection(String key) {
110+
set(key, createNode(key, null, NodeTypes.SECTION));
111+
}
112+
103113
/**
104114
* Updates this node's values from the provided mapped
105115
* object.
@@ -129,7 +139,7 @@ public void set(Object from) {
129139
content.forEach(this::set);
130140
}
131141

132-
public void set(String key, Object value) {
142+
public void set(String key, @Nullable Object value) {
133143
if(isContextDebug() && isHighestLevel()) {
134144
doLog("Highest level inspection:");
135145
doLog("Before update:");
@@ -187,17 +197,40 @@ public <T> T map(Class<T> typeClass) {
187197
return map(typeClass, new Placeholders());
188198
}
189199

200+
@SuppressWarnings("rawtypes, unchecked")
190201
public <T> T map(Class<T> typeClass, Placeholders placeholders) {
191202
try {
192203
if(Primitives.isWrapperType(Primitives.wrap(typeClass))) {
193204
// We cannot map sections to primitive types since
194205
// sections are not leaf nodes.
195206
return null;
196207
}
197-
Constructor<T> declaredConstructor = typeClass.getDeclaredConstructor();
198-
declaredConstructor.setAccessible(true);
199-
return map(declaredConstructor.newInstance(), placeholders);
200-
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
208+
try {
209+
Constructor<T> declaredConstructor = typeClass.getDeclaredConstructor();
210+
declaredConstructor.setAccessible(true);
211+
return map(declaredConstructor.newInstance(), placeholders);
212+
} catch(NoSuchMethodException e1) {
213+
T instance = null;
214+
215+
try {
216+
// Use custom pre-build strategy first to assure correct
217+
// object creation.
218+
NodeDeserializer deserializer = obtainAdapter(typeClass, NodeDeserializer.class);
219+
Object tempPreBuiltInstance;
220+
if (deserializer != null && (tempPreBuiltInstance = deserializer.preBuildInstance(typeClass, getContext(), placeholders)) != null) {
221+
instance = (T) tempPreBuiltInstance;
222+
}
223+
} catch(Exception e2) {
224+
debug(e2.getMessage());
225+
if (isContextDebug()) e2.printStackTrace();
226+
}
227+
228+
if (instance == null)
229+
throw new RuntimeException("Cannot create instance of " + typeClass.getName() + "!");
230+
231+
return map(instance, placeholders);
232+
}
233+
} catch (InstantiationException | IllegalAccessException | InvocationTargetException e) {
201234
throw new RuntimeException(e);
202235
}
203236
}
@@ -222,18 +255,20 @@ public <T> T map(T obj, Placeholders placeholders) {
222255
public <T> T map(T obj, Placeholders placeholders, boolean useCustomAdapters) {
223256
Class<?> typeClass = obj.getClass();
224257
if(Primitives.isWrapperType(Primitives.wrap(typeClass))) {
258+
debug(String.format("Cannot map section to primitive type for %s", typeClass.getName()));
225259
return null;
226260
}
227261

228262
NodeDeserializer nodeDeserializer = useCustomAdapters ? obtainAdapter(obj, NodeDeserializer.class) : null;
229-
if(nodeDeserializer != null) {
263+
if(nodeDeserializer != null) {
230264
NodeContext<Node<L>, L> context = getContext();
231265
Object deserialized = nodeDeserializer.deserialize(obj, context, placeholders);
232266
if(!obj.getClass().isAssignableFrom(deserialized.getClass())) {
233267
throw new RuntimeException(String.format("Deserialized object is not assignable to the provided type! (%s -> %s)",
234268
obj.getClass().getName(),
235269
deserialized.getClass().getName()));
236270
}
271+
debug(String.format("Deserialized object %s using adapter %s", deserialized, nodeDeserializer.getClass().getName()));
237272
return (T) deserialized;
238273
}
239274

@@ -243,13 +278,16 @@ public <T> T map(T obj, Placeholders placeholders, boolean useCustomAdapters) {
243278
for(Field field : typeClass.getDeclaredFields()) {
244279
if(Modifier.isTransient(field.getModifiers())) {
245280
// Transient fields are skipped.
281+
debug(String.format("Skipping transient field %s", field.getName()));
246282
continue;
247283
}
248284
field.setAccessible(true);
249285
Object value = Defaults.defaultValue(field.getType());
250286
if(String.class.equals(field.getType()) && field.isAnnotationPresent(ThisNodeId.class)) {
251287
value = getName();
288+
debug(String.format("Field %s is mapped to this node id %s", field.getName(), value));
252289
} else if(nodeCandidates.containsKey(field.getName())) {
290+
debug("Found node for field " + field.getName());
253291
Object builtValue = buildValue(field, nodeCandidates.get(field.getName()), placeholders);
254292
if(builtValue != null) {
255293
value = builtValue;
@@ -286,25 +324,58 @@ public <T> T map(T obj, Placeholders placeholders, boolean useCustomAdapters) {
286324
public Object buildValue(Field field, Node<L> node, Placeholders placeholders) {
287325
Object value = null;
288326

327+
debug("Building value for field " + field.getName());
328+
289329
if(node instanceof SimpleNode && isPrimitive(field.getType())) {
290330
value = ((SimpleNode<L>) node).get();
331+
debug(String.format("Field %s is mapped to simple node %s", field.getName(), value));
291332
} else if(node instanceof SectionNode) {
292333
Class<?> contentType;
293334
if(List.class.isAssignableFrom(field.getType())
294335
&& !isPrimitive(contentType = (Class<?>) ((ParameterizedType) field.getGenericType()).getActualTypeArguments()[0])) {
295336
List list = new ArrayList();
296337
((SectionNode<Object>) node).getNodes(NodeTypes.SECTION)
297338
.forEach(sn -> {
298-
list.add(sn.map(contentType));
339+
if (SectionNode.class.isAssignableFrom(contentType)) {
340+
list.add(sn);
341+
} else {
342+
list.add(sn.map(contentType));
343+
}
299344
});
345+
debug(String.format("Field %s is mapped to list of sections %s", field.getName(), list));
300346
return list;
347+
} else if (SectionNode.class.isAssignableFrom(field.getType())) {
348+
value = node;
349+
debug(String.format("Field %s is mapped to section node %s", field.getName(), value));
301350
} else {
302351
value = ((SectionNode<L>) node).map(field.getType());
352+
debug(String.format("Field %s is mapped to section node %s", field.getName(), value));
303353
}
304354
}
305355
return value;
306356
}
307357

358+
@Nullable
359+
public Node<L> get(String path) {
360+
Map<String, Node<L>> children = new HashMap<>();
361+
362+
for (Node<L> node : getNodes()) {
363+
children.put(node.getName(), node);
364+
}
365+
366+
Node<L> current = this;
367+
for(String key : path.split("\\.")) {
368+
if(!(current instanceof SectionNode)) {
369+
// Path points nowhere.
370+
return null;
371+
}
372+
current = current == this
373+
? children.get(key)
374+
: ((SectionNode<L>) current).get(key);
375+
}
376+
return current;
377+
}
378+
308379
public SimpleNode<L> getSimple(String path) {
309380
return (SimpleNode<L>) get(path);
310381
}
@@ -322,6 +393,10 @@ public <T extends Node<?>> List<T> getNodes(NodeTypeToken<T> type) {
322393
.collect(Collectors.toList());
323394
}
324395

396+
public boolean has(String path) {
397+
return get(path) != null;
398+
}
399+
325400
public NodeContext<Node<L>, L> getContext() {
326401
NodeContext<Node<L>, L> context = new NodeContext<>(this);
327402
getNodes().forEach(node -> context.set(node.getName(), node));
@@ -361,15 +436,20 @@ private boolean isPrimitive(Class<?> clazz) {
361436
return Primitives.isWrapperType(Primitives.wrap(clazz)) || String.class.equals(clazz);
362437
}
363438

364-
@SuppressWarnings("rawtypes, unchecked")
439+
@SuppressWarnings("rawtypes")
365440
private <T extends NodeAdapter> T obtainAdapter(Object toBeSerialized, Class<T> adapterTypeClass) {
441+
return obtainAdapter(toBeSerialized.getClass(), adapterTypeClass);
442+
}
443+
444+
@SuppressWarnings("rawtypes, unchecked")
445+
private <T extends NodeAdapter> T obtainAdapter(Class<?> toBeSerialized, Class<T> adapterTypeClass) {
366446
Validator.requireAnyType(adapterTypeClass, NodeSerializer.class, NodeDeserializer.class);
367447

368-
// I allow users tto define their own serializers.
448+
// I allow users to define their own serializers.
369449
// @see NodeSerializer
370450
for (Class<?> aClass : adapters.keySet()) {
371451
NodeAdapter<?, L> nodeAdapter = adapters.get(aClass);
372-
if(adapterTypeClass.isAssignableFrom(nodeAdapter.getClass()) && aClass.isAssignableFrom(toBeSerialized.getClass())) {
452+
if(adapterTypeClass.isAssignableFrom(nodeAdapter.getClass()) && aClass.isAssignableFrom(toBeSerialized)) {
373453
return (T) nodeAdapter;
374454
}
375455
}
@@ -394,7 +474,7 @@ public boolean isHighestLevel() {
394474
return parent == null;
395475
}
396476

397-
private void debug(String message) {
477+
public void debug(String message) {
398478
if (isContextDebug()) {
399479
getContextLogAdapter().log(Level.INFO, message);
400480
}
@@ -414,7 +494,7 @@ private LogAdapter getContextLogAdapter() {
414494
private boolean makeContextCheck(Predicate<SectionNode<L>> test) {
415495
if(test.test(this))
416496
return true;
417-
return parent != null && test.test(parent);
497+
return parent != null && parent.makeContextCheck(test);
418498
}
419499

420500
public static class DefaultNodeSerializer<L> implements NodeSerializer<Object, L> {
@@ -437,10 +517,6 @@ public void serialize(NodeContext context, Object from) {
437517
} else {
438518
name = field.getName();
439519
}
440-
/*getNodes().stream()
441-
.filter(n -> n.getName().equals(name))
442-
.findFirst()
443-
.ifPresent(node -> node.set(value));*/
444520
context.set(name, value);
445521
} catch (IllegalAccessException e) {
446522
e.printStackTrace();

src/main/java/me/zort/configurationlib/SimpleNode.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ default Object orElse(Object other) {
1313
}
1414

1515
default String getAsString() throws ConfigurationException {
16+
if (get() == null) return null;
17+
1618
if(!isString()) throw new ConfigurationException(this, "Node " + getName() + " is not string!");
1719
return (String) get();
1820
}

0 commit comments

Comments
 (0)