From a44173b439af4a8daf707f0603b96540ecc3ab95 Mon Sep 17 00:00:00 2001 From: Dai MIKURUBE Date: Fri, 27 Oct 2023 18:04:42 +0900 Subject: [PATCH] Implement DataSource's new methods: hasList, hasNested, getListOf, and toMap Some methods have been added in Embulk SPI's DataSource: https://github.com/embulk/embulk/pull/1534 It implements them in embulk-util-config's DataSourceImpl. --- .../embulk/util/config/DataSourceImpl.java | 95 ++++++++++++++++++- 1 file changed, 92 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/embulk/util/config/DataSourceImpl.java b/src/main/java/org/embulk/util/config/DataSourceImpl.java index 273a102..2c15cae 100644 --- a/src/main/java/org/embulk/util/config/DataSourceImpl.java +++ b/src/main/java/org/embulk/util/config/DataSourceImpl.java @@ -25,6 +25,7 @@ import java.util.ArrayList; import java.util.Collections; import java.util.Iterator; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import org.embulk.config.ConfigDiff; @@ -63,6 +64,22 @@ public boolean has(final String attrName) { return this.data.has(attrName); } + @Override + public boolean hasList(final String attrName) { + if (!this.data.has(attrName)) { + return false; + } + return this.data.get(attrName).isArray(); + } + + @Override + public boolean hasNested(final String attrName) { + if (!this.data.has(attrName)) { + return false; + } + return this.data.get(attrName).isObject(); + } + @Override public E get(final Class type, final String attrName) { final JsonNode json = this.data.get(attrName); @@ -89,6 +106,28 @@ public E get(final Class type, final String attrName, final E defaultValu } } + @Override + public List getListOf(final Class type, final String attrName) { + final JsonNode json = this.data.get(attrName); + if (json == null) { + throw new ConfigException("Attribute " + attrName + " is required but not set."); + } + if (!json.isArray()) { + throw new ConfigException("Attribute " + attrName + " must be an array."); + } + + final ArrayList list = new ArrayList<>(); + for (final JsonNode element : json) { // |json| should be ArrayNode. + try { + list.add(this.objectMapper.readValue(element.traverse(), type)); + } catch (final IOException ex) { + throw new ConfigException(ex); + } + } + + return Collections.unmodifiableList(list); + } + @Override public DataSourceImpl getNested(final String attrName) { final JsonNode json = this.data.get(attrName); @@ -237,10 +276,9 @@ public DataSourceImpl merge(final DataSource other) { /** * Implements {@code DataSource#toJson} for Embulk v0.10.3+. * - *

Embulk v0.10.2 or earlier does not have {@code DataSource#toJson} -- it should not {@code @Override} for the time being. + *

Embulk v0.10.2 or earlier does not have {@code DataSource#toJson}. */ - // TODO: Enable @Override after most plugins start using embulk-util-config. - // @Override + @Override public String toJson() { try { return this.objectMapper.writeValueAsString(this.data); @@ -249,6 +287,16 @@ public String toJson() { } } + /** + * Implements {@code DataSource#toMap} for Embulk v0.10.41+. + * + *

Embulk v0.10.40 or earlier does not have {@code DataSource#toMap}. + */ + @Override + public Map toMap() { + return nodeToMap(this.data); + } + /** * Implemented for compatibility with older {@code ConfigSource#loadConfig}. */ @@ -406,6 +454,47 @@ private static void mergeJsonArray(final ArrayNode src, final ArrayNode other) { } } + private static Map nodeToMap(final ObjectNode object) { + final LinkedHashMap map = new LinkedHashMap<>(); + for (final Map.Entry field : (Iterable>) () -> object.fields()) { + map.put(field.getKey(), nodeToJavaObject(field.getValue())); + } + return Collections.unmodifiableMap(map); + } + + private static List nodeToList(final ArrayNode array) { + final ArrayList list = new ArrayList<>(); + for (final JsonNode element : (Iterable) () -> array.elements()) { + list.add(nodeToJavaObject(element)); + } + return Collections.unmodifiableList(list); + } + + private static Object nodeToJavaObject(final JsonNode json) { + switch (json.getNodeType()) { + case ARRAY: + return nodeToList((ArrayNode) json); + case BINARY: + return json.asText(); + case BOOLEAN: + return json.booleanValue(); + case MISSING: + throw new ConfigException("Unexpected JSON node type MISSING in DataSouce."); + case NULL: + return null; // LinkedHashMap accepts null as a value. + case NUMBER: + return json.numberValue(); + case OBJECT: + return nodeToMap((ObjectNode) json); + case POJO: + throw new ConfigException("Unexpected JSON node type POJO in DataSouce."); + case STRING: + return json.asText(); + default: + throw new ConfigException("Unknown JSON node type in DataSouce."); + } + } + private final ObjectNode data; private final ObjectMapper objectMapper; }