-
-
Notifications
You must be signed in to change notification settings - Fork 4
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
feat: Add support for Snake Yaml {,Engine}
With this commit the yaml config module now requires to have one of the three dependencies so it can properly read the yaml configuration. More libraries should come but those three are arguably the most popular ones for now so it makes sense to allow for something that the users might be already using.
- Loading branch information
Showing
12 changed files
with
614 additions
and
186 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
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 |
---|---|---|
@@ -1,16 +1,19 @@ | ||
import penna.config.yaml.JacksonYamlConfigProvider; | ||
import penna.config.yaml.YamlConfigManager; | ||
import penna.config.yaml.YamlConfigProvider; | ||
|
||
module penna.config.yaml { | ||
requires org.slf4j; | ||
requires transitive penna.api; | ||
requires penna.core; | ||
|
||
// (Optional) support for Jackson | ||
requires static com.fasterxml.jackson.databind; | ||
requires static com.fasterxml.jackson.dataformat.yaml; | ||
requires penna.core; | ||
|
||
provides penna.api.config.ConfigManager with YamlConfigManager; | ||
provides penna.api.configv2.Provider with JacksonYamlConfigProvider; | ||
// (Optional) support for Snakeyaml | ||
requires static org.yaml.snakeyaml; | ||
requires static org.snakeyaml.engine.v2; | ||
|
||
exports penna.config.yaml; | ||
provides penna.api.config.ConfigManager with YamlConfigManager; | ||
provides penna.api.configv2.Provider with YamlConfigProvider; | ||
} |
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
103 changes: 103 additions & 0 deletions
103
penna-yaml-config/src/main/java/penna/config/yaml/parser/JacksonParser.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,103 @@ | ||
package penna.config.yaml.parser; | ||
|
||
import com.fasterxml.jackson.databind.JsonNode; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.fasterxml.jackson.dataformat.yaml.YAMLMapper; | ||
import org.jetbrains.annotations.VisibleForTesting; | ||
import penna.api.config.ExceptionHandling; | ||
import penna.config.yaml.models.ConfigMap; | ||
import penna.config.yaml.models.ConfigNode; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Files; | ||
import java.nio.file.Path; | ||
import java.util.*; | ||
import java.util.stream.StreamSupport; | ||
|
||
public final class JacksonParser implements Parser { | ||
@VisibleForTesting | ||
transient final ObjectMapper mapper; | ||
|
||
public JacksonParser() {this.mapper = new YAMLMapper();} | ||
|
||
private String level(JsonNode node) { | ||
return node.get("level").asText(); | ||
} | ||
|
||
private List<String> fields(JsonNode node) { | ||
var iterator = node.get("fields").iterator(); | ||
if (iterator.hasNext()) { | ||
return StreamSupport.stream(Spliterators.spliteratorUnknownSize(iterator, Spliterator.NONNULL), false).map(JsonNode::asText).toList(); | ||
} else { | ||
return List.of(); | ||
} | ||
} | ||
|
||
private ExceptionHandling exceptions(JsonNode node) throws IOException { | ||
var next = node.get("exception"); | ||
|
||
var base = ExceptionHandling.getDefault(); | ||
if (next.has("deduplication")) { | ||
base = base.replaceDeduplication(next.get("deduplication").asBoolean()); | ||
} | ||
|
||
if (next.has("maxDepth")) { | ||
base = base.replaceMaxDepth(next.get("maxDepth").asInt()); | ||
} | ||
|
||
if (next.has("traverseDepth")) { | ||
base = base.replaceTraverseDepth(next.get("traverseDepth").asInt()); | ||
} | ||
|
||
return base; | ||
} | ||
|
||
public ConfigNode deserialize(JsonNode node) throws IOException { | ||
// TODO recurse into the object, produce multiple objects | ||
var hasLevel = node.hasNonNull("level"); | ||
var hasFields = node.hasNonNull("fields"); | ||
var hasException = node.hasNonNull("exception"); | ||
|
||
|
||
if (hasLevel && hasFields && hasException) { | ||
return new ConfigNode.CompleteConfig(level(node), fields(node), exceptions(node)); | ||
} else if (hasLevel && hasFields) { | ||
return new ConfigNode.LevelAndFields(level(node), fields(node)); | ||
} else if (hasLevel && hasException) { | ||
return new ConfigNode.LevelAndExceptions(level(node), exceptions(node)); | ||
} else if (hasFields && hasException) { | ||
return new ConfigNode.FieldsAndException(fields(node), exceptions(node)); | ||
} else if (hasLevel) { | ||
return new ConfigNode.OnlyLevel(level(node)); | ||
} else if (hasFields) { | ||
return new ConfigNode.OnlyFields(fields(node)); | ||
} else if (hasException) { | ||
return new ConfigNode.OnlyExceptions(exceptions(node)); | ||
} else { | ||
return null; | ||
} | ||
} | ||
|
||
@VisibleForTesting | ||
ConfigMap readConfig(JsonNode root) { | ||
var configNodes = new HashMap<String, ConfigNode>(); | ||
var cfg = root.get("config"); | ||
var iterator = cfg.fields(); | ||
while (iterator.hasNext()) { | ||
try { | ||
var entry = iterator.next(); | ||
configNodes.put(entry.getKey(), deserialize(entry.getValue())); | ||
} catch (IOException e) { | ||
// TODO Handle malformed configuration | ||
continue; | ||
} | ||
} | ||
return new ConfigMap(Map.copyOf(configNodes)); | ||
} | ||
|
||
@Override | ||
public ConfigMap readAndParse(Path path) throws IOException { | ||
var tree = mapper.readTree(Files.newBufferedReader(path)); | ||
return readConfig(tree); | ||
} | ||
} |
45 changes: 45 additions & 0 deletions
45
penna-yaml-config/src/main/java/penna/config/yaml/parser/Parser.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,45 @@ | ||
package penna.config.yaml.parser; | ||
|
||
import penna.config.yaml.models.ConfigMap; | ||
|
||
import java.io.IOException; | ||
import java.nio.file.Path; | ||
|
||
public interface Parser { | ||
ConfigMap readAndParse(Path file) throws IOException; | ||
|
||
class Factory { | ||
private static Parser tryJackson() throws ClassNotFoundException { | ||
Class.forName("com.fasterxml.jackson.dataformat.yaml.YAMLMapper"); | ||
return new JacksonParser(); | ||
} | ||
|
||
private static Parser trySnakeyamlEngine() throws ClassNotFoundException { | ||
Class.forName("org.snakeyaml.engine.v2.api.Load"); | ||
return new SnakeyamlEngineParser(); | ||
} | ||
|
||
private static Parser trySnakeyaml() throws ClassNotFoundException { | ||
Class.forName("org.yaml.snakeyaml.Yaml"); | ||
return new SnakeyamlParser(); | ||
} | ||
|
||
public static Parser getParser() { | ||
try { | ||
return tryJackson(); | ||
} catch (ClassNotFoundException ignored) {} | ||
|
||
try { | ||
return trySnakeyamlEngine(); | ||
} catch (ClassNotFoundException ignored) {} | ||
|
||
try { | ||
return trySnakeyaml(); | ||
} catch (ClassNotFoundException ignored) {} | ||
|
||
return null; | ||
} | ||
|
||
} | ||
|
||
} |
Oops, something went wrong.