GremlinScriptLiteralHelper.java

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

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

import com.azure.spring.data.gremlin.annotation.GeneratedValue;
import com.azure.spring.data.gremlin.common.Constants;
import com.azure.spring.data.gremlin.common.GremlinEntityType;
import com.azure.spring.data.gremlin.common.GremlinUtils;
import com.azure.spring.data.gremlin.exception.GremlinInvalidEntityIdFieldException;
import com.azure.spring.data.gremlin.exception.GremlinUnexpectedEntityTypeException;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.apache.tinkerpop.shaded.jackson.core.JsonProcessingException;
import org.springframework.lang.NonNull;
import org.springframework.util.Assert;

public class GremlinScriptLiteralHelper {

    static String generateEntityWithRequiredId(@NonNull Object id, GremlinEntityType type) {
        Assert.notNull(id, "id should not be null");
        Assert.isTrue(type == GremlinEntityType.EDGE || type == GremlinEntityType.VERTEX, "should be edge/vertex type");

        final String prefix = (type == GremlinEntityType.VERTEX) ? "V" : "E";

        if (id instanceof String) {
            return prefix + String.format("('%s')", id);
        } else if (id instanceof Integer) {
            return prefix + String.format("(%d)", id);
        } else if (id instanceof Long) {
            return prefix + String.format("(%d)", id);
        }

        throw new GremlinInvalidEntityIdFieldException("Only String/Integer/Long of id is supported");
    }

    static String generatePropertyWithRequiredId(@NonNull Object id) {
        if (id instanceof String) {
            return String.format("property(id, '%s')", id);
        } else if (id instanceof Integer) {
            return String.format("property(id, %d)", id);
        } else if (id instanceof Long) {
            return String.format("property(id, %d)", id);
        }

        throw new GremlinInvalidEntityIdFieldException("Only String/Integer/Long of id is supported");
    }

    static String generateAsWithAlias(@NonNull String alias) {
        return String.format("as('%s')", alias);
    }

    static String generateAddEntityWithLabel(@NonNull String label, GremlinEntityType type) {
        Assert.isTrue(type == GremlinEntityType.EDGE || type == GremlinEntityType.VERTEX, "should be edge/vertex type");

        final String prefix = (type == GremlinEntityType.VERTEX) ? "addV" : "addE";

        return prefix + String.format("('%s')", label);
    }

    static List<String> completeScript(@NonNull List<String> scriptList) {
        return Collections.singletonList(String.join(Constants.GREMLIN_PRIMITIVE_INVOKE, scriptList));
    }

    public static String generateHasLabel(@NonNull String label) {
        return String.format("has(label, '%s')", label);
    }

    /**
     * Generate the hasId statement.
     *
     * @param id The id object.
     * @return The hasId statement.
     * @throws GremlinInvalidEntityIdFieldException If the id type is not supported.
     */
    public static String generateHasId(@NonNull Object id) {
        if (id instanceof String) {
            return String.format("hasId('%s')", id);
        } else if (id instanceof Integer) {
            return String.format("hasId(%d)", id);
        } else if (id instanceof Long) {
            return String.format("hasId(%d)", id);
        } else {
            throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long");
        }
    }

    /**
     * Generate the hasId statement.
     *
     * @param id The id object.
     * @param idFiled The id field.
     * @return The hasId statement.
     * @throws GremlinInvalidEntityIdFieldException If the id type is not supported.
     */
    public static String generateHasId(@NonNull Object id, @NonNull Field idFiled) {
        if (!idFiled.isAnnotationPresent(GeneratedValue.class)) {
            return generateHasId(id);
        } else if (id instanceof String) {
            return String.format("hasId('%s')", id);
        } else if (id instanceof Integer) {
            return String.format("hasId(%dL)", id);
        } else if (id instanceof Long) {
            return String.format("hasId(%dL)", id);
        } else {
            throw new GremlinInvalidEntityIdFieldException("the type of @Id/id field should be String/Integer/Long");
        }
    }

    private static String generateProperty(@NonNull String name, @NonNull String value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_STRING, name, value);
    }

    private static String generateProperty(@NonNull String name, @NonNull Integer value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_NUMBER, name, value);
    }

    private static String generateProperty(@NonNull String name, @NonNull Boolean value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_BOOLEAN, name, value);
    }

    private static String generateProperty(@NonNull String name, @NonNull Long value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_PROPERTY_NUMBER, name, value);
    }

    private static String generateProperty(@NonNull String name, @NonNull Object value) {
        if (value instanceof Integer) {
            return generateProperty(name, (Integer) value);
        } else if (value instanceof Boolean) {
            return generateProperty(name, (Boolean) value);
        } else if (value instanceof String) {
            return generateProperty(name, (String) value);
        } else if (value instanceof Date) {
            return generateProperty(name, GremlinUtils.timeToMilliSeconds(value));
        } else {
            final String propertyScript;

            try {
                propertyScript = generateProperty(name, GremlinUtils.getObjectMapper().writeValueAsString(value));
            } catch (JsonProcessingException e) {
                throw new GremlinUnexpectedEntityTypeException("Failed to write object to String", e);
            }

            return propertyScript;
        }
    }

    protected static List<String> generateProperties(@NonNull final Map<String, Object> properties) {
        final List<String> scripts = new ArrayList<>();

        properties.entrySet().stream().filter(e -> e.getValue() != null)
            .forEach(e -> scripts.add(generateProperty(e.getKey(), e.getValue())));

        return scripts;
    }

    private static String generateHas(@NonNull String name, @NonNull Integer value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_HAS_NUMBER, name, value);
    }

    private static String generateHas(@NonNull String name, @NonNull Boolean value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_HAS_BOOLEAN, name, value);
    }

    private static String generateHas(@NonNull String name, @NonNull String value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_HAS_STRING, name, value);
    }

    private static String generateHas(@NonNull String name, @NonNull Long value) {
        return String.format(Constants.GREMLIN_PRIMITIVE_HAS_NUMBER, name, value);
    }

    // TODO: should move to query method part.

    /**
     * Generate the has statement.
     *
     * @param name The subject part of the has statement.
     * @param value The object part of the has statement.
     * @return The generated has statement.
     * @throws GremlinUnexpectedEntityTypeException If the value could not be written to a string.
     */
    public static String generateHas(@NonNull String name, @NonNull Object value) {

        if (value instanceof Integer) {
            return generateHas(name, (Integer) value);
        } else if (value instanceof Boolean) {
            return generateHas(name, (Boolean) value);
        } else if (value instanceof String) {
            return generateHas(name, (String) value);
        } else if (value instanceof Date) {
            return generateHas(name, GremlinUtils.timeToMilliSeconds(value));
        } else {
            final String hasScript;

            try {
                hasScript = generateHas(name, GremlinUtils.getObjectMapper().writeValueAsString(value));
            } catch (JsonProcessingException e) {
                throw new GremlinUnexpectedEntityTypeException("Failed to write object to String", e);
            }

            return hasScript;
        }
    }
}