AbstractGremlinSource.java

// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.

package com.azure.spring.data.gremlin.conversion.source;

import com.azure.spring.data.gremlin.annotation.GeneratedValue;
import com.azure.spring.data.gremlin.common.Constants;
import com.azure.spring.data.gremlin.conversion.MappingGremlinConverter;
import com.azure.spring.data.gremlin.conversion.result.GremlinResultsReader;
import com.azure.spring.data.gremlin.conversion.script.GremlinScriptLiteral;
import com.azure.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException;
import org.apache.tinkerpop.gremlin.driver.Result;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;

import java.lang.reflect.Field;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;

public abstract class AbstractGremlinSource<T> implements GremlinSource<T> {

    private Object id;

    private String label;

    private Field idField;

    private Class<T> domainClass;

    private Map<String, Object> properties;

    private GremlinScriptLiteral scriptLiteral;

    private GremlinSourceWriter<T> sourceWriter;

    private GremlinSourceReader<T> sourceReader;

    private GremlinResultsReader resultReader;

    protected AbstractGremlinSource() {
        this.properties = new HashMap<>();
    }

    protected AbstractGremlinSource(Class<T> domainClass) {
        this.domainClass = domainClass;
        this.properties = new HashMap<>();

        setProperty(Constants.GREMLIN_PROPERTY_CLASSNAME, domainClass.getName());
    }

    @Override
    public Optional<Object> getId() {
        return Optional.ofNullable(this.id);
    }

    /**
     * The type of Id keep the consistency with the result from gremlin server, for generate query correctly. So if the
     * id is ${@link GeneratedValue}, which may have different type against entity id field.
     *
     * @param id the given id from query.
     */
    @Override
    public void setId(Object id) {
        final Field idField = getIdField();

        if (idField == null) {
            throw new GremlinInvalidEntityIdFieldException("Id Field of GremlinSource cannot be null");
        }

        if (idField.isAnnotationPresent(GeneratedValue.class) && id instanceof String) {
            try {
                this.id = Long.valueOf((String) id); // Gremlin server default id type is Long.
            } catch (NumberFormatException ignore) {
                this.id = id;
            }
        } else {
            this.id = id;
        }
    }

    @Override
    public void setGremlinScriptStrategy(@NonNull GremlinScriptLiteral script) {
        this.scriptLiteral = script;
    }

    @Override
    public void setGremlinSourceWriter(@NonNull GremlinSourceWriter<T> writer) {
        this.sourceWriter = writer;
    }

    @Override
    public void setGremlinSourceReader(@NonNull GremlinSourceReader<T> reader) {
        this.sourceReader = reader;
    }

    @Override
    public void setGremlinResultReader(@NonNull GremlinResultsReader reader) {
        this.resultReader = reader;
    }

    @Override
    public GremlinScriptLiteral getGremlinScriptLiteral() {
        return this.scriptLiteral;
    }

    @Override
    public void doGremlinSourceWrite(@NonNull Object domain, @NonNull MappingGremlinConverter converter) {
        Assert.notNull(this.sourceWriter, "the sourceWriter must be set before do writing");

        this.sourceWriter.write(domain, converter, this);
    }

    @Override
    public T doGremlinSourceRead(@NonNull Class<T> domainClass, @NonNull MappingGremlinConverter converter) {
        Assert.notNull(this.sourceReader, "the sourceReader must be set before do reading");

        return this.sourceReader.read(domainClass, converter, this);
    }

    @Override
    public void doGremlinResultRead(@NonNull List<Result> results) {
        Assert.notNull(this.resultReader, "the resultReader must be set before do reading");

        this.resultReader.read(results, this);
    }

    private boolean hasProperty(String key) {
        return this.properties.get(key) != null;
    }

    @Override
    public void setProperty(String key, Object value) {
        if (this.hasProperty(key) && value == null) {
            this.properties.remove(key);
        } else {
            this.properties.put(key, value);
        }
    }

    public void setIdField(Field idField) {
        this.idField = idField;
    }

    @Override
    public String getLabel() {
        return label;
    }

    @Override
    public void setLabel(String label) {
        this.label = label;
    }

    @Override
    public Field getIdField() {
        return idField;
    }

    @Override
    public Class<T> getDomainClass() {
        return domainClass;
    }

    @Override
    public Map<String, Object> getProperties() {
        return properties;
    }

    public void setProperties(Map<String, Object> properties) {
        this.properties = properties;
    }
}