All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.embulk.config.DataSourceImpl Maven / Gradle / Ivy

package org.embulk.config;

import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.node.ArrayNode;
import com.fasterxml.jackson.databind.node.JsonNodeFactory;
import com.fasterxml.jackson.databind.node.ObjectNode;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

public class DataSourceImpl implements ConfigSource, TaskSource, TaskReport, ConfigDiff {
    protected final ObjectNode data;

    @Deprecated  // https://github.com/embulk/embulk/issues/1304
    protected final ModelManager model;

    @SuppressWarnings("deprecation")  // https://github.com/embulk/embulk/issues/1304
    public DataSourceImpl(ModelManager model) {
        this(model, new ObjectNode(JsonNodeFactory.instance));
    }

    // visible for DataSourceSerDe, ConfigSourceLoader and TaskInvocationHandler.dump
    @SuppressWarnings("deprecation")  // https://github.com/embulk/embulk/issues/1304
    public DataSourceImpl(ModelManager model, ObjectNode data) {
        this.data = data;
        this.model = model;
    }

    @SuppressWarnings("deprecation")  // https://github.com/embulk/embulk/issues/1304
    protected DataSourceImpl newInstance(ModelManager model, ObjectNode data) {
        return new DataSourceImpl(model, (ObjectNode) data);
    }

    // It was overridden from DataSource, but getObjectNode is removed from DataSource.
    // It can be package-private soon. DataSourceSerDe.DataSourceSerializer is the only user in embulk-core.
    @Deprecated
    public ObjectNode getObjectNode() {
        return data;
    }

    @Override
    public List getAttributeNames() {
        final ArrayList copy = new ArrayList<>();
        data.fieldNames().forEachRemaining(copy::add);
        return Collections.unmodifiableList(copy);
    }

    // It was overridden from DataSource, but getAttributes is removed from DataSource.
    @Deprecated
    public Iterable> getAttributes() {
        return new Iterable>() {
            public Iterator> iterator() {
                return data.fields();
            }
        };
    }

    @Override
    public boolean isEmpty() {
        return !data.fieldNames().hasNext();
    }

    @Override
    public boolean has(String attrName) {
        return data.has(attrName);
    }

    @Override
    public  E get(Class type, String attrName) {
        JsonNode json = data.get(attrName);
        if (json == null) {
            throw new ConfigException("Attribute " + attrName + " is required but not set");
        }
        return model.readObject(type, json.traverse());
    }

    @Override
    public  E get(Class type, String attrName, E defaultValue) {
        JsonNode json = data.get(attrName);
        if (json == null) {
            return defaultValue;
        }
        return model.readObject(type, json.traverse());
    }

    @Override
    public DataSourceImpl getNested(String attrName) {
        JsonNode json = data.get(attrName);
        if (json == null) {
            throw new ConfigException("Attribute " + attrName + " is required but not set");
        }
        if (!json.isObject()) {
            throw new ConfigException("Attribute " + attrName + " must be an object");
        }
        return newInstance(model, (ObjectNode) json);
    }

    @Override
    public DataSourceImpl getNestedOrSetEmpty(String attrName) {
        JsonNode json = data.get(attrName);
        if (json == null) {
            json = data.objectNode();
            data.set(attrName, json);
        } else if (!json.isObject()) {
            throw new ConfigException("Attribute " + attrName + " must be an object");
        }
        return newInstance(model, (ObjectNode) json);
    }

    @Override
    public DataSourceImpl getNestedOrGetEmpty(String attrName) {
        JsonNode json = data.get(attrName);
        if (json == null) {
            json = data.objectNode();
        } else if (!json.isObject()) {
            throw new ConfigException("Attribute " + attrName + " must be an object");
        }
        return newInstance(model, (ObjectNode) json);
    }

    @Override
    public DataSourceImpl set(String attrName, Object v) {
        if (v == null) {
            remove(attrName);
        } else {
            data.set(attrName, model.writeObjectAsJsonNode(v));
        }
        return this;
    }

    @Override
    public DataSourceImpl setNested(String attrName, DataSource v) {
        if (v == null) {
            this.data.set(attrName, null);
        } else {
            final String vJsonStringified = v.toJson();
            if (vJsonStringified == null) {
                throw new ConfigException(new NullPointerException("DataSource#setNested accepts only valid DataSource"));
            }
            final JsonNode vJsonNode = this.model.readObject(JsonNode.class, vJsonStringified);
            if (!vJsonNode.isObject()) {
                throw new ConfigException(new ClassCastException("DataSource#setNested accepts only valid JSON object"));
            }
            this.data.set(attrName, (ObjectNode) vJsonNode);
        }
        return this;
    }

    @Override
    public DataSourceImpl setAll(DataSource other) {
        if (other == null) {
            throw new ConfigException(new NullPointerException("DataSource#setAll accepts only non-null value"));
        }
        final String otherJsonStringified = other.toJson();
        if (otherJsonStringified == null) {
            throw new ConfigException(new NullPointerException("DataSource#setAll accepts only valid DataSource"));
        }
        final JsonNode otherJsonNode = this.model.readObject(JsonNode.class, otherJsonStringified);
        if (!otherJsonNode.isObject()) {
            throw new ConfigException(new ClassCastException("DataSource#setAll accepts only valid JSON object"));
        }
        final ObjectNode otherObjectNode = (ObjectNode) otherJsonNode;
        for (Map.Entry field : (Iterable>) () -> otherObjectNode.fields()) {
            this.data.set(field.getKey(), field.getValue());
        }
        return this;
    }

    @Override
    public DataSourceImpl remove(String attrName) {
        data.remove(attrName);
        return this;
    }

    @Override
    public DataSourceImpl deepCopy() {
        return newInstance(model, data.deepCopy());
    }

    @Override
    public DataSourceImpl merge(DataSource other) {
        if (other == null) {
            throw new ConfigException(new NullPointerException("DataSource#merge accepts only non-null value"));
        }
        final String otherJsonStringified = other.toJson();
        if (otherJsonStringified == null) {
            throw new ConfigException(new NullPointerException("DataSource#merge accepts only valid DataSource"));
        }
        final JsonNode otherJsonNode = this.model.readObject(JsonNode.class, otherJsonStringified);
        if (!otherJsonNode.isObject()) {
            throw new ConfigException(new ClassCastException("DataSource#setAll accepts only valid JSON object"));
        }
        mergeJsonObject(data, (ObjectNode) otherJsonNode);
        return this;
    }

    @Override
    public String toJson() {
        return this.model.writeObject(this.data);
    }

    private static void mergeJsonObject(ObjectNode src, ObjectNode other) {
        Iterator> ite = other.fields();
        while (ite.hasNext()) {
            Map.Entry pair = ite.next();
            JsonNode s = src.get(pair.getKey());
            JsonNode v = pair.getValue();
            if (v.isObject() && s != null && s.isObject()) {
                mergeJsonObject((ObjectNode) s, (ObjectNode) v);
            } else if (v.isArray() && s != null && s.isArray()) {
                mergeJsonArray((ArrayNode) s, (ArrayNode) v);
            } else {
                src.replace(pair.getKey(), v);
            }
        }
    }

    private static void mergeJsonArray(ArrayNode src, ArrayNode other) {
        for (int i = 0; i < other.size(); i++) {
            JsonNode s = src.get(i);
            JsonNode v = other.get(i);
            if (s == null) {
                src.add(v);
            } else if (v.isObject() && s.isObject()) {
                mergeJsonObject((ObjectNode) s, (ObjectNode) v);
            } else if (v.isArray() && s.isArray()) {
                mergeJsonArray((ArrayNode) s, (ArrayNode) v);
            } else {
                src.remove(i);
                src.insert(i, v);
            }
        }
    }

    @Override
    @Deprecated
    public  T loadTask(Class taskType) {
        return model.readObject(taskType, data.traverse());
    }

    @Override
    @Deprecated
    public  T loadConfig(Class taskType) {
        return model.readObjectWithConfigSerDe(taskType, data.traverse());
    }

    @Override
    public String toString() {
        return data.toString();
    }

    @Override
    public boolean equals(Object other) {
        if (other == null) {
            return false;
        }
        if (!(other instanceof DataSource)) {
            return false;
        }
        final DataSource otherDataSource = (DataSource) other;
        final String otherJsonStringified = otherDataSource.toJson();
        if (otherJsonStringified == null) {
            return false;
        }
        final JsonNode otherJsonNode = this.model.readObject(JsonNode.class, otherJsonStringified);
        if (!otherJsonNode.isObject()) {
            return false;
        }
        return data.equals((ObjectNode) otherJsonNode);
    }

    @Override
    public int hashCode() {
        return data.hashCode();
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy