package org.embulk.config; import java.io.File; import java.io.FileInputStream; import java.io.InputStream; import java.io.IOException; import java.util.Map; import java.util.Properties; import com.google.common.collect.ImmutableMap; import com.google.inject.Inject; import com.fasterxml.jackson.databind.ObjectMapper; import com.fasterxml.jackson.core.JsonParser; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.RuntimeJsonMappingException; import com.fasterxml.jackson.databind.node.ObjectNode; import com.fasterxml.jackson.databind.node.JsonNodeFactory; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.DumperOptions; import org.yaml.snakeyaml.representer.Representer; import org.yaml.snakeyaml.constructor.SafeConstructor; public class ConfigLoader { private final ModelManager model; @Inject public ConfigLoader(ModelManager model) { this.model = model; } public ConfigSource newConfigSource() { return new DataSourceImpl(model); } public ConfigSource fromJsonString(String string) { JsonNode node; try { node = new ObjectMapper().readTree(string); } catch (IOException ex) { throw new RuntimeException(ex); } validateJsonNode(node); return new DataSourceImpl(model, (ObjectNode) node); } public ConfigSource fromJsonFile(File file) throws IOException { try (FileInputStream is = new FileInputStream(file)) { return fromJson(is); } } public ConfigSource fromJson(InputStream stream) throws IOException { JsonNode node = new ObjectMapper().readTree(stream); validateJsonNode(node); return new DataSourceImpl(model, (ObjectNode) node); } public ConfigSource fromYamlString(String string) { JsonNode node = objectToJson(newYaml().load(string)); validateJsonNode(node); return new DataSourceImpl(model, (ObjectNode) node); } public ConfigSource fromYamlFile(File file) throws IOException { try (FileInputStream stream = new FileInputStream(file)) { return fromYaml(stream); } } public ConfigSource fromYaml(InputStream stream) throws IOException { JsonNode node = objectToJson(newYaml().load(stream)); validateJsonNode(node); return new DataSourceImpl(model, (ObjectNode) node); } private static void validateJsonNode(JsonNode node) { if (!node.isObject()) { throw new RuntimeJsonMappingException("Expected object to load ConfigSource but got "+node); } } @Deprecated public ConfigSource fromJson(JsonParser parser) throws IOException { // TODO check parsed.isObject() ObjectNode source = (ObjectNode) new ObjectMapper().readTree(parser); return new DataSourceImpl(model, source); } public ConfigSource fromPropertiesYamlLiteral(Properties props, String keyPrefix) { ImmutableMap.Builder<String, String> builder = ImmutableMap.builder(); for (String propName : props.stringPropertyNames()) { builder.put(propName, props.getProperty(propName)); } return fromPropertiesYamlLiteral(builder.build(), keyPrefix); } public ConfigSource fromPropertiesYamlLiteral(Map<String, String> props, String keyPrefix) { ObjectNode source = new ObjectNode(JsonNodeFactory.instance); DataSource ds = new DataSourceImpl(model, source); Yaml yaml = newYaml(); for (Map.Entry<String, String> pair : props.entrySet()) { if (!pair.getKey().startsWith(keyPrefix)) { continue; } String keyName = pair.getKey().substring(keyPrefix.length()); Object parsedValue = yaml.load(pair.getValue()); // TODO exception handling JsonNode node = objectToJson(parsedValue); // handle "." as a map acccessor. for example: // in.parser.type=csv => {"in": {"parser": {"type": "csv"}}} // TODO handle "[]" as array index String[] fragments = keyName.split("\\."); DataSource key = ds; for (int i=0; i < fragments.length - 1; i++) { key = key.getNestedOrSetEmpty(fragments[i]); // TODO exception handling } key.set(fragments[fragments.length - 1], node); } return new DataSourceImpl(model, source); } private JsonNode objectToJson(Object object) { ObjectMapper objectMapper = new ObjectMapper(); try { return objectMapper.readTree(objectMapper.writeValueAsString(object)); } catch (IOException ex) { throw new RuntimeException(ex); } } private Yaml newYaml() { return new Yaml(new SafeConstructor(), new Representer(), new DumperOptions(), new YamlTagResolver()); } }