blob: f7f773e72bd30bd7fc819903b8eab8dd90142232 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you under the Apache License, Version 2.0 (the
* "License"); you may not use this file except in compliance
* with the License. You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the License is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
* KIND, either express or implied. See the License for the
* specific language governing permissions and limitations
* under the License.
*/
package org.apache.tinkerpop.gremlin.groovy.jsr223;
import groovy.json.StringEscapeUtils;
import org.apache.commons.configuration2.ConfigurationConverter;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.SackFunctions;
import org.apache.tinkerpop.gremlin.process.traversal.Script;
import org.apache.tinkerpop.gremlin.process.traversal.TextP;
import org.apache.tinkerpop.gremlin.process.traversal.Translator;
import org.apache.tinkerpop.gremlin.process.traversal.Traversal;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalSource;
import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.step.TraversalOptionParent;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.TraversalStrategyProxy;
import org.apache.tinkerpop.gremlin.process.traversal.util.ConnectiveP;
import org.apache.tinkerpop.gremlin.process.traversal.util.OrP;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.VertexProperty;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.function.Lambda;
import java.sql.Timestamp;
import java.util.Date;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.stream.Collectors;
import java.util.function.BinaryOperator;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
/**
* Converts bytecode to a Groovy string of Gremlin.
*
* @author Marko A. Rodriguez (http://markorodriguez.com)
* @author Stephen Mallette (http://stephen.genoprime.com)
* @author Stark Arya (sandszhou.zj@alibaba-inc.com)
* @deprecated As of release 3.4.9, moved to {@link org.apache.tinkerpop.gremlin.process.traversal.translator.GroovyTranslator} in {@code gremlin-core}.
*/
@Deprecated
public final class GroovyTranslator implements Translator.ScriptTranslator {
private final String traversalSource;
private final TypeTranslator typeTranslator;
private GroovyTranslator(final String traversalSource, final TypeTranslator typeTranslator) {
this.traversalSource = traversalSource;
this.typeTranslator = typeTranslator;
}
/**
* Creates the translator with a {@code false} argument to {@code withParameters} using
* {@link #of(String, boolean)}.
*/
public static final GroovyTranslator of(final String traversalSource) {
return of(traversalSource, false);
}
/**
* Creates the translator with the {@link DefaultTypeTranslator} passing the {@code withParameters} option to it
* which will handle type translation in a fashion that should typically increase cache hits and reduce
* compilation times if enabled at the sacrifice to rewriting of the script that could reduce readability.
*/
public static final GroovyTranslator of(final String traversalSource, final boolean withParameters) {
return of(traversalSource, new DefaultTypeTranslator(withParameters));
}
public static final GroovyTranslator of(final String traversalSource, final TypeTranslator typeTranslator) {
return new GroovyTranslator(traversalSource, typeTranslator);
}
///////
@Override
public Script translate(final Bytecode bytecode) {
return typeTranslator.apply(traversalSource, bytecode);
}
@Override
public String getTargetLanguage() {
return "gremlin-groovy";
}
@Override
public String toString() {
return StringFactory.translatorString(this);
}
@Override
public String getTraversalSource() {
return this.traversalSource;
}
/**
* Performs standard type translation for the TinkerPop types to Groovy.
*/
public static class DefaultTypeTranslator implements TypeTranslator {
protected final boolean withParameters;
protected final Script script;
public DefaultTypeTranslator(final boolean withParameters) {
this.withParameters = withParameters;
this.script = new Script();
}
@Override
public Script apply(final String traversalSource, final Object o) {
this.script.init();
if (o instanceof Bytecode) {
return internalTranslate(traversalSource, (Bytecode) o);
} else {
return convertToScript(o);
}
}
/**
* For each operator argument, if withParameters set true, try parametrization as follows:
*
* -----------------------------------------------
* if unpack, why ? ObjectType
* -----------------------------------------------
* (Yes) Bytecode.Binding
* (Recursion, No) Bytecode
* (Recursion, No) Traversal
* (Yes) String
* (Recursion, No) Set
* (Recursion, No) List
* (Recursion, No) Map
* (Yes) Long
* (Yes) Double
* (Yes) Float
* (Yes) Integer
* (Yes) Timestamp
* (Yes) Date
* (Yes) Uuid
* (Recursion, No) P
* (Enumeration, No) SackFunctions.Barrier
* (Enumeration, No) VertexProperty.Cardinality
* (Enumeration, No) TraversalOptionParent.Pick
* (Enumeration, No) Enum
* (Recursion, No) Vertex
* (Recursion, No) Edge
* (Recursion, No) VertexProperty
* (Yes) Lambda
* (Recursion, No) TraversalStrategyProxy
* (Enumeration, No) TraversalStrategy
* (Yes) Other
* -------------------------------------------------
*
* @param object
* @return String Repres
*/
protected Script convertToScript(final Object object) {
if (object instanceof Bytecode.Binding) {
return script.getBoundKeyOrAssign(withParameters, ((Bytecode.Binding) object).variable());
} else if (object instanceof Bytecode) {
return internalTranslate("__", (Bytecode) object);
} else if (object instanceof Traversal) {
return convertToScript(((Traversal) object).asAdmin().getBytecode());
} else if (object instanceof String) {
final String wrapper = (((String) object).contains("\"") ? "\"\"\"" + StringEscapeUtils.escapeJava((String) object) + "\"\"\"" : "\"" + StringEscapeUtils.escapeJava((String) object) + "\"")
.replace("$", "\\$");
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : wrapper);
} else if (object instanceof Set) {
convertToScript(((Set)object).stream().collect(Collectors.toList()));
return script.append(" as Set");
} else if (object instanceof List) {
Iterator<?> iterator = ((List)object).iterator();
if (! iterator.hasNext()) {
return script.append("[]");
} else {
script.append("[");
for (;;) {
Object e = iterator.next();
convertToScript(e);
if (! iterator.hasNext()) {
return script.append("]");
} else {
script.append(",").append(" ");
}
}
}
} else if (object instanceof Map) {
script.append("[");
for (final Map.Entry<?, ?> entry : ((Map<?, ?>) object).entrySet()) {
script.append("(");
convertToScript(entry.getKey());
script.append("):(");
convertToScript(entry.getValue());
script.append("),");
}
// only need to remove this last bit if entries were added
if (!((Map<?, ?>) object).isEmpty()) {
return script.setCharAtEnd(']');
} else {
return script.append("]");
}
} else if (object instanceof Long) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : object + "L");
} else if (object instanceof Double) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : object + "d");
} else if (object instanceof Float) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : object + "f");
} else if (object instanceof Integer) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : "(int) " + object);
} else if (object instanceof Class) {
return script.append(((Class) object).getCanonicalName());
} else if (object instanceof Timestamp) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : "new java.sql.Timestamp(" + ((Timestamp) object).getTime() + ")");
} else if (object instanceof Date) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : "new java.util.Date(" + ((Date) object).getTime() + ")");
} else if (object instanceof UUID) {
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : "java.util.UUID.fromString('" + object.toString() + "')");
} else if (object instanceof P) {
return convertPToScript((P) object);
} else if (object instanceof SackFunctions.Barrier) {
return script.append("SackFunctions.Barrier." + object.toString());
} else if (object instanceof VertexProperty.Cardinality) {
return script.append("VertexProperty.Cardinality." + object.toString());
} else if (object instanceof TraversalOptionParent.Pick) {
return script.append("TraversalOptionParent.Pick." + object.toString());
} else if (object instanceof Enum) {
return script.append(((Enum) object).getDeclaringClass().getSimpleName() + "." + object.toString());
} else if (object instanceof Element) {
if (object instanceof Vertex) {
final Vertex vertex = (Vertex) object;
script.append("new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertex(");
convertToScript(vertex.id());
script.append(",");
convertToScript(vertex.label());
return script.append(", Collections.emptyMap())");
} else if (object instanceof Edge) {
final Edge edge = (Edge) object;
script.append("new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedEdge(");
convertToScript(edge.id());
script.append(",");
convertToScript(edge.label());
script.append(",");
script.append("Collections.emptyMap(),");
convertToScript(edge.outVertex().id());
script.append(",");
convertToScript(edge.outVertex().label());
script.append(",");
convertToScript(edge.inVertex().id());
script.append(",");
convertToScript(edge.inVertex().label());
return script.append(")");
} else {// VertexProperty
final VertexProperty vertexProperty = (VertexProperty) object;
script.append("new org.apache.tinkerpop.gremlin.structure.util.detached.DetachedVertexProperty(");
convertToScript(vertexProperty.id());
script.append(",");
convertToScript(vertexProperty.label());
script.append(",");
convertToScript(vertexProperty.value());
script.append(",");
script.append("Collections.emptyMap(),");
convertToScript(vertexProperty.element());
return script.append(")");
}
} else if (object instanceof Lambda) {
final String lambdaString = ((Lambda) object).getLambdaScript().trim();
final String wrapper = lambdaString.startsWith("{") ? lambdaString : "{" + lambdaString + "}";
return script.getBoundKeyOrAssign(withParameters, withParameters ? object : wrapper);
} else if (object instanceof TraversalStrategyProxy) {
final TraversalStrategyProxy proxy = (TraversalStrategyProxy) object;
if (proxy.getConfiguration().isEmpty()) {
return script.append(proxy.getStrategyClass().getCanonicalName() + ".instance()");
} else {
script.append(proxy.getStrategyClass().getCanonicalName() + ".create(new org.apache.commons.configuration2.MapConfiguration(");
convertToScript(ConfigurationConverter.getMap(proxy.getConfiguration()));
return script.append("))");
}
} else if (object instanceof TraversalStrategy) {
return convertToScript(new TraversalStrategyProxy(((TraversalStrategy) object)));
} else {
return null == object ? script.append("null") : script.getBoundKeyOrAssign(withParameters, object);
}
}
protected Script internalTranslate(final String start, final Bytecode bytecode) {
script.append(start);
for (final Bytecode.Instruction instruction : bytecode.getInstructions()) {
final String methodName = instruction.getOperator();
if (0 == instruction.getArguments().length) {
script.append(".").append(methodName).append("()");
} else {
script.append(".").append(methodName).append("(");
// have to special case withSack() for Groovy because UnaryOperator and BinaryOperator signatures
// make it impossible for the interpreter to figure out which function to call. specifically we need
// to discern between:
// withSack(A initialValue, UnaryOperator<A> splitOperator)
// withSack(A initialValue, BinaryOperator<A> splitOperator)
// and:
// withSack(Supplier<A> initialValue, UnaryOperator<A> mergeOperator)
// withSack(Supplier<A> initialValue, BinaryOperator<A> mergeOperator)
if (methodName.equals(TraversalSource.Symbols.withSack) &&
instruction.getArguments().length == 2 && instruction.getArguments()[1] instanceof Lambda) {
final String castFirstArgTo = instruction.getArguments()[0] instanceof Lambda ?
Supplier.class.getName() : "";
final Lambda secondArg = (Lambda) instruction.getArguments()[1];
final String castSecondArgTo = secondArg.getLambdaArguments() == 1 ? UnaryOperator.class.getName() :
BinaryOperator.class.getName();
if (!castFirstArgTo.isEmpty())
script.append(String.format("(%s) ", castFirstArgTo));
convertToScript(instruction.getArguments()[0]);
script.append(", (").append(castSecondArgTo).append(") ");
convertToScript(instruction.getArguments()[1]);
script.append(",");
} else {
for (final Object object : instruction.getArguments()) {
convertToScript(object);
script.append(",");
}
}
script.setCharAtEnd(')');
}
}
return script;
}
protected Script convertPToScript(final P p) {
if (p instanceof TextP) {
return convertTextPToScript((TextP) p);
}
if (p instanceof ConnectiveP) {
final List<P<?>> list = ((ConnectiveP) p).getPredicates();
for (int i = 0; i < list.size(); i++) {
convertPToScript(list.get(i));
if (i < list.size() - 1) {
script.append(p instanceof OrP ? ".or(" : ".and(");
}
}
script.append(")");
} else {
script.append("P.").append(p.getBiPredicate().toString()).append("(");
convertToScript(p.getValue());
script.append(")");
}
return script;
}
protected Script convertTextPToScript(final TextP p) {
script.append("TextP.").append(p.getBiPredicate().toString()).append("(");
convertToScript(p.getValue());
script.append(")");
return script;
}
}
}