-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: add an encoder that will create objects with a constructor with…
… a single string parameter. #122
- Loading branch information
1 parent
b76d114
commit fd0f650
Showing
9 changed files
with
274 additions
and
4 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
73 changes: 73 additions & 0 deletions
73
gestalt-core/src/main/java/org/github/gestalt/config/decoder/StringConstructorDecoder.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,73 @@ | ||
package org.github.gestalt.config.decoder; | ||
|
||
import org.github.gestalt.config.entity.ValidationError; | ||
import org.github.gestalt.config.node.ConfigNode; | ||
import org.github.gestalt.config.node.LeafNode; | ||
import org.github.gestalt.config.reflect.TypeCapture; | ||
import org.github.gestalt.config.tag.Tags; | ||
import org.github.gestalt.config.utils.ValidateOf; | ||
|
||
import java.lang.reflect.Constructor; | ||
import java.lang.reflect.InvocationTargetException; | ||
import java.util.Arrays; | ||
|
||
/** | ||
* Decode a String. | ||
* | ||
* @author <a href="mailto:colin.redmond@outlook.com"> Colin Redmond </a> (c) 2023. | ||
*/ | ||
public final class StringConstructorDecoder implements Decoder<Object> { | ||
|
||
public StringConstructorDecoder() { | ||
} | ||
|
||
@Override | ||
public Priority priority() { | ||
return Priority.LOW; | ||
} | ||
|
||
@Override | ||
public String name() { | ||
return "StringConstructor"; | ||
} | ||
|
||
@Override | ||
public boolean canDecode(String path, Tags tags, ConfigNode node, TypeCapture<?> type) { | ||
Class<?> klass = type.getRawType(); | ||
Constructor<?>[] stringConstructor = klass.getConstructors(); | ||
|
||
if (!(node instanceof LeafNode)) { | ||
return false; | ||
} | ||
|
||
return Arrays.stream(stringConstructor) | ||
.anyMatch(it -> it.getParameterCount() == 1 && it.getParameters()[0].getType().equals(String.class)); | ||
} | ||
|
||
/** | ||
* Decode the current node. If the current node is a class or list we may need to decode sub nodes. | ||
* | ||
* @param path the current path | ||
* @param tags the tags for the current request | ||
* @param node the current node we are decoding. | ||
* @param type the type of object we are decoding. | ||
* @param decoderContext The context of the current decoder. | ||
* @return ValidateOf the current node with details of either success or failures. | ||
*/ | ||
public ValidateOf<Object> decode(String path, Tags tags, ConfigNode node, TypeCapture<?> type, DecoderContext decoderContext) { | ||
|
||
LeafNode leafNode = (LeafNode) node; | ||
if (leafNode.getValue().isEmpty()) { | ||
return ValidateOf.inValid(new ValidationError.LeafNodesIsNullDecoding(path, type)); | ||
} | ||
|
||
Class<?> klass = type.getRawType(); | ||
|
||
try { | ||
Constructor<?> stringConstructor = klass.getConstructor(String.class); | ||
return ValidateOf.valid(stringConstructor.newInstance(node.getValue().get())); | ||
} catch (InvocationTargetException | NoSuchMethodException | InstantiationException | IllegalAccessException e) { | ||
return ValidateOf.inValid(new ValidationError.StringConstructorNotFound(path, type)); | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
117 changes: 117 additions & 0 deletions
117
...lt-core/src/test/java/org/github/gestalt/config/decoder/StringConstructorDecoderTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,117 @@ | ||
package org.github.gestalt.config.decoder; | ||
|
||
import org.github.gestalt.config.node.LeafNode; | ||
import org.github.gestalt.config.node.MapNode; | ||
import org.github.gestalt.config.reflect.TypeCapture; | ||
import org.github.gestalt.config.tag.Tags; | ||
import org.junit.jupiter.api.Assertions; | ||
import org.junit.jupiter.api.Test; | ||
|
||
import java.util.Map; | ||
|
||
import static org.github.gestalt.config.entity.ValidationLevel.ERROR; | ||
import static org.github.gestalt.config.entity.ValidationLevel.MISSING_VALUE; | ||
|
||
class StringConstructorDecoderTest { | ||
|
||
@Test | ||
void priority() { | ||
StringConstructorDecoder decoder = new StringConstructorDecoder(); | ||
|
||
Assertions.assertEquals(Priority.LOW, decoder.priority()); | ||
} | ||
|
||
@Test | ||
void name() { | ||
StringConstructorDecoder decoder = new StringConstructorDecoder(); | ||
|
||
Assertions.assertEquals("StringConstructor", decoder.name()); | ||
} | ||
|
||
@Test | ||
void canDecode() { | ||
StringConstructorDecoder decoder = new StringConstructorDecoder(); | ||
|
||
Assertions.assertFalse(decoder.canDecode("", Tags.of(), new LeafNode(""), TypeCapture.of(MyClass.class))); | ||
Assertions.assertFalse(decoder.canDecode("", Tags.of(), new LeafNode(""), new TypeCapture<MyClass>() { | ||
})); | ||
Assertions.assertTrue(decoder.canDecode("", Tags.of(), new LeafNode(""), new TypeCapture<MyStringClass>() { | ||
})); | ||
Assertions.assertTrue(decoder.canDecode("", Tags.of(), new LeafNode(""), new TypeCapture<MyStringClass>() { | ||
})); | ||
|
||
Assertions.assertFalse(decoder.canDecode("", Tags.of(), new MapNode(Map.of()), new TypeCapture<MyStringClass>() { | ||
})); | ||
|
||
} | ||
|
||
@Test | ||
void decode() { | ||
|
||
StringConstructorDecoder decoder = new StringConstructorDecoder(); | ||
var results = decoder.decode("hello", Tags.of(), new LeafNode("test"), TypeCapture.of(MyStringClass.class), null); | ||
|
||
Assertions.assertTrue(results.hasResults()); | ||
Assertions.assertFalse(results.hasErrors()); | ||
|
||
MyStringClass decoded = (MyStringClass) results.results(); | ||
|
||
Assertions.assertEquals("test", decoded.myData); | ||
} | ||
|
||
@Test | ||
void decodeEmptyLeaf() { | ||
|
||
StringConstructorDecoder decoder = new StringConstructorDecoder(); | ||
var results = decoder.decode("hello", Tags.of(), new LeafNode(null), TypeCapture.of(MyStringClass.class), null); | ||
|
||
Assertions.assertFalse(results.hasResults()); | ||
Assertions.assertTrue(results.hasErrors()); | ||
|
||
Assertions.assertEquals(1, results.getErrors().size()); | ||
|
||
Assertions.assertEquals(MISSING_VALUE, results.getErrors().get(0).level()); | ||
Assertions.assertEquals("Leaf nodes is null on path: hello decoding type MyStringClass", | ||
results.getErrors().get(0).description()); | ||
} | ||
|
||
@Test | ||
void decodeEmptyWrongType() { | ||
|
||
StringConstructorDecoder decoder = new StringConstructorDecoder(); | ||
var results = decoder.decode("hello", Tags.of(), new LeafNode("test"), TypeCapture.of(MyClass.class), null); | ||
|
||
Assertions.assertFalse(results.hasResults()); | ||
Assertions.assertTrue(results.hasErrors()); | ||
|
||
Assertions.assertEquals(1, results.getErrors().size()); | ||
|
||
Assertions.assertEquals(ERROR, results.getErrors().get(0).level()); | ||
Assertions.assertEquals("String Constructor for: MyClass is not found on Path: hello", | ||
results.getErrors().get(0).description()); | ||
} | ||
|
||
private static class MyClass { | ||
Integer myData; | ||
|
||
public MyClass(Integer myData) { | ||
this.myData = myData; | ||
} | ||
|
||
public MyClass() { | ||
|
||
} | ||
} | ||
|
||
private static class MyStringClass { | ||
String myData; | ||
|
||
public MyStringClass(String myData) { | ||
this.myData = myData; | ||
} | ||
|
||
public MyStringClass() { | ||
|
||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters