blob: 77ae87821488cf1b12b4084763f0c5ea3a1b4a25 [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.process.traversal.strategy.decoration;
import org.apache.tinkerpop.gremlin.jsr223.GremlinScriptEngine;
import org.apache.tinkerpop.gremlin.jsr223.SingleGremlinScriptEngineManager;
import org.apache.tinkerpop.gremlin.process.computer.traversal.step.map.ProgramVertexProgramStep;
import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.decoration.VertexProgramStrategy;
import org.apache.tinkerpop.gremlin.process.remote.traversal.strategy.decoration.RemoteStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.Bytecode;
import org.apache.tinkerpop.gremlin.process.traversal.Step;
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.util.EmptyStep;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.AbstractTraversalStrategy;
import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.VerificationException;
import org.apache.tinkerpop.gremlin.process.traversal.util.TraversalHelper;
import javax.script.Bindings;
import javax.script.ScriptContext;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Set;
import static org.junit.Assert.assertEquals;
/**
* @author Marko A. Rodriguez (http://markorodriguez.com)
*/
public final class TranslationStrategy extends AbstractTraversalStrategy<TraversalStrategy.DecorationStrategy> implements TraversalStrategy.DecorationStrategy {
private final TraversalSource traversalSource;
private final Translator translator;
private final boolean assertBytecode;
private static final Set<Class<? extends DecorationStrategy>> POSTS = new HashSet<>(Arrays.asList(
ConnectiveStrategy.class,
ElementIdStrategy.class,
EventStrategy.class,
HaltedTraverserStrategy.class,
PartitionStrategy.class,
RequirementsStrategy.class,
SackStrategy.class,
SideEffectStrategy.class,
SubgraphStrategy.class,
RemoteStrategy.class,
VertexProgramStrategy.class));
public TranslationStrategy(final TraversalSource traversalSource, final Translator translator, final boolean assertBytecode) {
this.traversalSource = traversalSource;
this.translator = translator;
this.assertBytecode = assertBytecode;
}
@Override
public void apply(final Traversal.Admin<?, ?> traversal) {
if (!(traversal.getParent() instanceof EmptyStep))
return;
final Traversal.Admin<?, ?> translatedTraversal;
final Bytecode bytecode = removeTranslationStrategy(insertBindingsForTesting(traversal.getBytecode()));
////////////////
if (this.translator instanceof Translator.StepTranslator) {
// reflection based translation
translatedTraversal = (Traversal.Admin<?, ?>) this.translator.translate(bytecode);
} else if (this.translator instanceof Translator.ScriptTranslator) {
try {
// script based translation
final GremlinScriptEngine scriptEngine = SingleGremlinScriptEngineManager.get(this.translator.getTargetLanguage());
final Bindings bindings = scriptEngine.createBindings();
bindings.putAll(scriptEngine.getContext().getBindings(ScriptContext.ENGINE_SCOPE));
bindings.put(this.translator.getTraversalSource().toString(), this.traversalSource);
translatedTraversal = (Traversal.Admin<?, ?>) scriptEngine.eval(bytecode, bindings);
} catch (final Exception e) {
throw new IllegalArgumentException(e.getMessage(), e);
}
} else {
throw new IllegalArgumentException("TranslationStrategy does not know how to process the provided translator type: " + this.translator.getClass().getSimpleName());
}
////////////////
assert !translatedTraversal.isLocked();
assert !traversal.isLocked();
traversal.setSideEffects(translatedTraversal.getSideEffects());
TraversalHelper.removeAllSteps(traversal);
TraversalHelper.removeToTraversal((Step) translatedTraversal.getStartStep(), EmptyStep.instance(), traversal);
////////////////
// this tests to ensure that the bytecode being translated is the same as the bytecode of the generated
// traversal. we might not do this sometimes in testing if lambdas are present but still want to use
// TranslationStrategy
if (assertBytecode)
assertEquals(removeTranslationStrategy(traversal.getBytecode()), translatedTraversal.getBytecode());
}
@Override
public Set<Class<? extends DecorationStrategy>> applyPost() {
return POSTS;
}
private static final Bytecode removeTranslationStrategy(final Bytecode bytecode) {
if (bytecode.getSourceInstructions().size() > 0)
bytecode.getSourceInstructions().remove(0);
return bytecode;
}
private static final Bytecode insertBindingsForTesting(final Bytecode bytecode) {
final Bytecode newBytecode = new Bytecode();
for (final Bytecode.Instruction instruction : bytecode.getSourceInstructions()) {
newBytecode.addSource(instruction.getOperator(), instruction.getArguments());
}
for (final Bytecode.Instruction instruction : bytecode.getStepInstructions()) {
final Object[] args = instruction.getArguments();
final Object[] newArgs = new Object[args.length];
for (int i = 0; i < args.length; i++) {
if (args[i].equals("knows"))
newArgs[i] = new Bytecode.Binding<>("a", "knows");
else if (args[i].equals("created"))
newArgs[i] = new Bytecode.Binding<>("b", "created");
else if (args[i].equals(10))
newArgs[i] = new Bytecode.Binding<>("c", 10);
else
newArgs[i] = args[i];
}
newBytecode.addStep(instruction.getOperator(), newArgs);
}
return newBytecode;
}
}