This is an automated email from the ASF dual-hosted git repository. spmallette pushed a commit to branch TINKERPOP-2862 in repository https://gitbox.apache.org/repos/asf/tinkerpop.git
commit d3650430a17aad69ebf431dad171fd0f5f9afa4a Author: Stephen Mallette <[email protected]> AuthorDate: Wed Jan 31 17:05:49 2024 -0500 TINKERPOP-2862 wip --- .../grammar/DefaultGremlinBaseVisitor.java | 59 +++--- .../grammar/TraversalSourceSelfMethodVisitor.java | 19 +- .../language/grammar/TraversalStrategyVisitor.java | 159 ++++++--------- .../process/traversal/TraversalStrategies.java | 61 ++++++ .../util/GremlinDisabledListDelimiterHandler.java | 46 +++++ .../TraversalSourceSelfMethodVisitorTest.java | 8 + .../grammar/TraversalStrategyVisitorTest.java | 5 + gremlin-language/src/main/antlr4/Gremlin.g4 | 214 +++++++++------------ 8 files changed, 310 insertions(+), 261 deletions(-) diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java index f89c3037d9..e336f041e1 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/DefaultGremlinBaseVisitor.java @@ -1267,26 +1267,6 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override public T visitTraversalStrategyExpr(final GremlinParser.TraversalStrategyExprContext ctx) { notImplemented(ctx); return null; } - /** - * {@inheritDoc} - */ - @Override public T visitTraversalStrategyArgs_PartitionStrategy(final GremlinParser.TraversalStrategyArgs_PartitionStrategyContext ctx) { notImplemented(ctx); return null; } - /** - * {@inheritDoc} - */ - @Override public T visitTraversalStrategyArgs_EdgeLabelVerificationStrategy(final GremlinParser.TraversalStrategyArgs_EdgeLabelVerificationStrategyContext ctx) { notImplemented(ctx); return null; } - /** - * {@inheritDoc} - */ - @Override public T visitTraversalStrategyArgs_ReservedKeysVerificationStrategy(final GremlinParser.TraversalStrategyArgs_ReservedKeysVerificationStrategyContext ctx) { notImplemented(ctx); return null; } - /** - * {@inheritDoc} - */ - @Override public T visitTraversalStrategyArgs_SubgraphStrategy(final GremlinParser.TraversalStrategyArgs_SubgraphStrategyContext ctx) { notImplemented(ctx); return null; } - /** - * {@inheritDoc} - */ - @Override public T visitTraversalStrategyArgs_ProductiveByStrategy(final GremlinParser.TraversalStrategyArgs_ProductiveByStrategyContext ctx) { return null; } /** * {@inheritDoc} */ @@ -1697,35 +1677,48 @@ public class DefaultGremlinBaseVisitor<T> extends AbstractParseTreeVisitor<T> im * {@inheritDoc} */ @Override public T visitTraversalDTArgument(final GremlinParser.TraversalDTArgumentContext ctx) { notImplemented(ctx); return null; } - /** * {@inheritDoc} */ - @Override - public T visitTraversalSackMethodArgument(final GremlinParser.TraversalSackMethodArgumentContext ctx) { notImplemented(ctx); return null; } + @Override public T visitTraversalSackMethodArgument(final GremlinParser.TraversalSackMethodArgumentContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ - @Override - public T visitGenericLiteralVarargs(final GremlinParser.GenericLiteralVarargsContext ctx) { notImplemented(ctx); return null; } + @Override public T visitGenericLiteralVarargs(final GremlinParser.GenericLiteralVarargsContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ - @Override - public T visitGenericLiteralMapArgument(final GremlinParser.GenericLiteralMapArgumentContext ctx) { notImplemented(ctx); return null; } + @Override public T visitGenericLiteralMapArgument(final GremlinParser.GenericLiteralMapArgumentContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ - @Override - public T visitGenericLiteralMapNullableArgument(final GremlinParser.GenericLiteralMapNullableArgumentContext ctx) { notImplemented(ctx); return null; } + @Override public T visitGenericLiteralMapNullableArgument(final GremlinParser.GenericLiteralMapNullableArgumentContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ - @Override - public T visitStringLiteralVarargs(final GremlinParser.StringLiteralVarargsContext ctx) { notImplemented(ctx); return null; } + @Override public T visitStringLiteralVarargs(final GremlinParser.StringLiteralVarargsContext ctx) { notImplemented(ctx); return null; } /** * {@inheritDoc} */ - @Override - public T visitTraversalMethod_option_Merge_Map_Cardinality(final GremlinParser.TraversalMethod_option_Merge_Map_CardinalityContext ctx) { notImplemented(ctx); return null; } + @Override public T visitTraversalMethod_option_Merge_Map_Cardinality(final GremlinParser.TraversalMethod_option_Merge_Map_CardinalityContext ctx) { notImplemented(ctx); return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalSourceSelfMethod_withoutStrategies(final GremlinParser.TraversalSourceSelfMethod_withoutStrategiesContext ctx) { return null; } + /** + * {@inheritDoc} + */ + @Override public T visitClassTypeList(final GremlinParser.ClassTypeListContext ctx) { return null; } + /** + * {@inheritDoc} + */ + @Override public T visitClassTypeExpr(final GremlinParser.ClassTypeExprContext ctx) { return null; } + /** + * {@inheritDoc} + */ + @Override public T visitClassType(final GremlinParser.ClassTypeContext ctx) { return null; } + /** + * {@inheritDoc} + */ + @Override public T visitTraversalStrategyArg(final GremlinParser.TraversalStrategyArgContext ctx) { return null; } } \ No newline at end of file diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java index 1df2b54afb..273f3e233a 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitor.java @@ -19,12 +19,14 @@ package org.apache.tinkerpop.gremlin.language.grammar; import org.apache.tinkerpop.gremlin.process.traversal.Operator; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Optional; import java.util.function.BiFunction; import java.util.function.BinaryOperator; @@ -91,7 +93,6 @@ public class TraversalSourceSelfMethodVisitor extends DefaultGremlinBaseVisitor< @Override public GraphTraversalSource visitTraversalSourceSelfMethod_withStrategies(final GremlinParser.TraversalSourceSelfMethod_withStrategiesContext ctx) { - if (null == traversalStrategyVisitor) traversalStrategyVisitor = new TraversalStrategyVisitor(antlr); @@ -108,6 +109,22 @@ public class TraversalSourceSelfMethodVisitor extends DefaultGremlinBaseVisitor< } } + @Override + public GraphTraversalSource visitTraversalSourceSelfMethod_withoutStrategies(final GremlinParser.TraversalSourceSelfMethod_withoutStrategiesContext ctx) { + final List<GremlinParser.ClassTypeContext> contexts = new ArrayList<>(); + contexts.add(ctx.classType()); + if (ctx.classTypeList() != null) { + contexts.addAll(ctx.classTypeList().classTypeExpr().classType()); + } + + final Class[] strategyClasses = contexts.stream().map(c -> TraversalStrategies.GlobalCache.getRegisteredStrategyClass(c.getText())) + .filter(Optional::isPresent) + .map(Optional::get) + .toArray(Class[]::new); + + return source.withoutStrategies(strategyClasses); + } + /** * {@inheritDoc} */ diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitor.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitor.java index 7fd04974f5..7ae90920e7 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitor.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitor.java @@ -18,6 +18,10 @@ */ package org.apache.tinkerpop.gremlin.language.grammar; +import org.antlr.v4.runtime.tree.ParseTree; +import org.apache.commons.configuration2.BaseConfiguration; +import org.apache.commons.configuration2.Configuration; +import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategies; import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy; @@ -27,10 +31,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.Abst import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReservedKeysVerificationStrategy; +import org.apache.tinkerpop.gremlin.util.GremlinDisabledListDelimiterHandler; +import java.util.ArrayList; import java.util.Arrays; import java.util.HashSet; import java.util.List; +import java.util.Optional; public class TraversalStrategyVisitor extends DefaultGremlinBaseVisitor<TraversalStrategy> { protected final GremlinAntlrToJava antlr; @@ -44,117 +51,67 @@ public class TraversalStrategyVisitor extends DefaultGremlinBaseVisitor<Traversa // child count of one implies init syntax for the singleton constructed strategies. otherwise, it will // fall back to the Builder methods for construction if (ctx.getChildCount() == 1) { - final String strategyName = ctx.getChild(0).getText(); - if (strategyName.equals(ReadOnlyStrategy.class.getSimpleName())) - return ReadOnlyStrategy.instance(); - else if (strategyName.equals(ProductiveByStrategy.class.getSimpleName())) - return ProductiveByStrategy.instance(); - } else if (ctx.getChild(0).getText().equals("new")) { - final String strategyName = ctx.getChild(1).getText(); - if (strategyName.equals(PartitionStrategy.class.getSimpleName())) - return getPartitionStrategy(ctx.traversalStrategyArgs_PartitionStrategy()); - else if (strategyName.equals(ReservedKeysVerificationStrategy.class.getSimpleName())) - return getReservedKeysVerificationStrategy(ctx.traversalStrategyArgs_ReservedKeysVerificationStrategy()); - else if (strategyName.equals(EdgeLabelVerificationStrategy.class.getSimpleName())) - return getEdgeLabelVerificationStrategy(ctx.traversalStrategyArgs_EdgeLabelVerificationStrategy()); - else if (strategyName.equals(SubgraphStrategy.class.getSimpleName())) - return getSubgraphStrategy(ctx.traversalStrategyArgs_SubgraphStrategy()); - else if (strategyName.equals(SeedStrategy.class.getSimpleName())) - return new SeedStrategy(antlr.argumentVisitor.parseNumber(ctx.integerArgument()).longValue()); - else if (strategyName.equals(ProductiveByStrategy.class.getSimpleName())) - return getProductiveByStrategy(ctx.traversalStrategyArgs_ProductiveByStrategy()); + final String strategyName = ctx.classType() != null ? constructClassName(ctx.classType()) : ctx.getChild(0).getText(); + return tryToConstructStrategy(strategyName, getConfiguration(ctx.traversalStrategyArg())); + } else { + // start looking at strategies after the "new" keyword + final int childIndex = ctx.getChild(0).getText().equals("new") ? 1 : 0; + final String strategyName = ctx.getChild(childIndex).getText(); + return tryToConstructStrategy(strategyName, getConfiguration(ctx.traversalStrategyArg())); } - throw new IllegalStateException("Unexpected TraversalStrategy specification - " + ctx.getText()); } - private EdgeLabelVerificationStrategy getEdgeLabelVerificationStrategy(final List<GremlinParser.TraversalStrategyArgs_EdgeLabelVerificationStrategyContext> ctxs) { - if (null == ctxs || ctxs.isEmpty()) - return EdgeLabelVerificationStrategy.build().create(); - - final EdgeLabelVerificationStrategy.Builder builder = EdgeLabelVerificationStrategy.build(); - ctxs.forEach(ctx -> { - switch (ctx.getChild(0).getText()) { - case AbstractWarningVerificationStrategy.LOG_WARNING: - builder.logWarning(antlr.argumentVisitor.parseBoolean(ctx.booleanArgument())); - break; - case AbstractWarningVerificationStrategy.THROW_EXCEPTION: - builder.throwException(antlr.argumentVisitor.parseBoolean(ctx.booleanArgument())); - break; + private Configuration getConfiguration(final List<GremlinParser.TraversalStrategyArgContext> contexts) { + final BaseConfiguration conf = new BaseConfiguration(); + conf.setListDelimiterHandler(GremlinDisabledListDelimiterHandler.instance()); + if (null != contexts) { + for (GremlinParser.TraversalStrategyArgContext ctx : contexts) { + final String key = ctx.getChild(0).getText(); + final Object val = antlr.argumentVisitor.visitGenericLiteralArgument(ctx.genericLiteralArgument()); + conf.setProperty(key, val); } - }); - - return builder.create(); + } + return conf; } - private ReservedKeysVerificationStrategy getReservedKeysVerificationStrategy(final List<GremlinParser.TraversalStrategyArgs_ReservedKeysVerificationStrategyContext> ctxs) { - if (null == ctxs || ctxs.isEmpty()) - return ReservedKeysVerificationStrategy.build().create(); - - final ReservedKeysVerificationStrategy.Builder builder = ReservedKeysVerificationStrategy.build(); - ctxs.forEach(ctx -> { - switch (ctx.getChild(0).getText()) { - case AbstractWarningVerificationStrategy.LOG_WARNING: - builder.logWarning(antlr.argumentVisitor.parseBoolean(ctx.booleanArgument())); - break; - case AbstractWarningVerificationStrategy.THROW_EXCEPTION: - builder.throwException(antlr.argumentVisitor.parseBoolean(ctx.booleanArgument())); - break; - case ReservedKeysVerificationStrategy.KEYS: - builder.reservedKeys(new HashSet<>(Arrays.asList(antlr.genericVisitor.parseStringList(ctx.stringLiteralList())))); - break; - } - }); - - return builder.create(); + private String constructClassName(final GremlinParser.ClassTypeContext ctx) { + final StringBuilder builder = new StringBuilder(); + for (ParseTree child : ctx.children) { + builder.append(child.getText()); + } + return builder.toString(); } - private PartitionStrategy getPartitionStrategy(final List<GremlinParser.TraversalStrategyArgs_PartitionStrategyContext> ctxs) { - final PartitionStrategy.Builder builder = PartitionStrategy.build(); - ctxs.forEach(ctx -> { - switch (ctx.getChild(0).getText()) { - case PartitionStrategy.INCLUDE_META_PROPERTIES: - builder.includeMetaProperties(antlr.argumentVisitor.parseBoolean(ctx.booleanArgument())); - break; - case PartitionStrategy.READ_PARTITIONS: - builder.readPartitions(Arrays.asList(antlr.genericVisitor.parseStringList(ctx.stringLiteralList()))); - break; - case PartitionStrategy.WRITE_PARTITION: - builder.writePartition(antlr.argumentVisitor.parseString(ctx.stringArgument())); - break; - case PartitionStrategy.PARTITION_KEY: - builder.partitionKey(antlr.argumentVisitor.parseString(ctx.stringArgument())); - break; - } - }); + /** + * Try to instantiate the strategy by checking registered {@link TraversalStrategy} implementations that are + * registered globally. + */ + private static TraversalStrategy tryToConstructStrategy(final String strategyName, final Configuration conf) { + // try to grab the strategy class from registered sources first + final Optional<? extends Class<? extends TraversalStrategy>> opt = TraversalStrategies.GlobalCache.getRegisteredStrategyClass(strategyName); - return builder.create(); - } + if (!opt.isPresent()) { + throw new IllegalStateException("Unexpected TraversalStrategy specification - " + strategyName); + } - private SubgraphStrategy getSubgraphStrategy(final List<GremlinParser.TraversalStrategyArgs_SubgraphStrategyContext> ctxs) { - final SubgraphStrategy.Builder builder = SubgraphStrategy.build(); - ctxs.forEach(ctx -> { - switch (ctx.getChild(0).getText()) { - case SubgraphStrategy.VERTICES: - builder.vertices(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); - break; - case SubgraphStrategy.EDGES: - builder.edges(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); - break; - case SubgraphStrategy.VERTEX_PROPERTIES: - builder.vertexProperties(antlr.tvisitor.visitNestedTraversal(ctx.nestedTraversal())); - break; - case SubgraphStrategy.CHECK_ADJACENT_VERTICES: - builder.checkAdjacentVertices(antlr.argumentVisitor.parseBoolean(ctx.booleanArgument())); - break; + final Class clazz = opt.get(); + + try { + if (conf.isEmpty()) { + try { + return (TraversalStrategy) clazz.getMethod("instance").invoke(null); + } catch (Exception ex) { + try { + return (TraversalStrategy) clazz.getConstructor().newInstance(); + } catch (Exception exinner) { + return (TraversalStrategy) clazz.getMethod("create", Configuration.class).invoke(null, conf); + } + } + } else { + return (TraversalStrategy) clazz.getMethod("create", Configuration.class).invoke(null, conf); } - }); - - return builder.create(); - } - - private ProductiveByStrategy getProductiveByStrategy(final GremlinParser.TraversalStrategyArgs_ProductiveByStrategyContext ctx) { - final ProductiveByStrategy.Builder builder = ProductiveByStrategy.build(); - builder.productiveKeys(Arrays.asList(antlr.genericVisitor.parseStringList(ctx.stringLiteralList()))); - return builder.create(); + } catch (Exception ex) { + throw new IllegalStateException("Unexpected TraversalStrategy specification - " + strategyName, ex); + } } } diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java index fc97198ac6..2df79bac46 100644 --- a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/process/traversal/TraversalStrategies.java @@ -23,7 +23,16 @@ import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.finaliza import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.GraphFilterStrategy; import org.apache.tinkerpop.gremlin.process.computer.traversal.strategy.optimization.MessagePassingReductionStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ConnectiveStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.ElementIdStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.EventStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.HaltedTraverserStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.OptionsStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SackStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ProfileStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.finalization.ReferenceElementStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.AdjacentToIncidentStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy; @@ -37,8 +46,13 @@ import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.Matc import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.OrderLimitStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathProcessorStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ComputerVerificationStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.LambdaRestrictionStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReservedKeysVerificationStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.StandardVerificationStrategy; import org.apache.tinkerpop.gremlin.process.traversal.util.DefaultTraversalStrategies; import org.apache.tinkerpop.gremlin.structure.Graph; @@ -214,6 +228,33 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T private static final Map<Class<? extends Graph>, TraversalStrategies> GRAPH_CACHE = new HashMap<>(); private static final Map<Class<? extends GraphComputer>, TraversalStrategies> GRAPH_COMPUTER_CACHE = new HashMap<>(); + /** + * A register of the simple names for all strategies that can be constructed by the user. + */ + private static final Map<String, Class<? extends TraversalStrategy>> GREMLIN_CONSTRUCTED = new HashMap<String, Class<? extends TraversalStrategy>>() {{ + // decorations + put(ConnectiveStrategy.class.getSimpleName(), ConnectiveStrategy.class); + put(ElementIdStrategy.class.getSimpleName(), ElementIdStrategy.class); + put(EventStrategy.class.getSimpleName(), EventStrategy.class); + put(HaltedTraverserStrategy.class.getSimpleName(), HaltedTraverserStrategy.class); + put(OptionsStrategy.class.getSimpleName(), OptionsStrategy.class); + put(PartitionStrategy.class.getSimpleName(), PartitionStrategy.class); + put(SeedStrategy.class.getSimpleName(), SeedStrategy.class); + put(SubgraphStrategy.class.getSimpleName(), SubgraphStrategy.class); + + // finalization + put(ReferenceElementStrategy.class.getSimpleName(), ReferenceElementStrategy.class); + + // optimizations + put(ProductiveByStrategy.class.getSimpleName(), ProductiveByStrategy.class); + + // verification + put(EdgeLabelVerificationStrategy.class.getSimpleName(), EdgeLabelVerificationStrategy.class); + put(LambdaRestrictionStrategy.class.getSimpleName(), LambdaRestrictionStrategy.class); + put(ReadOnlyStrategy.class.getSimpleName(), ReadOnlyStrategy.class); + put(ReservedKeysVerificationStrategy.class.getSimpleName(), ReservedKeysVerificationStrategy.class); + }}; + static { final TraversalStrategies graphStrategies = new DefaultTraversalStrategies(); graphStrategies.addStrategies( @@ -257,6 +298,26 @@ public interface TraversalStrategies extends Serializable, Cloneable, Iterable<T throw new IllegalArgumentException("The TraversalStrategies.GlobalCache only supports Graph and GraphComputer strategy caching: " + graphOrGraphComputerClass.getCanonicalName()); } + /** + * Looks up a strategy by its simple name. + */ + public static Optional<? extends Class<? extends TraversalStrategy>> getRegisteredStrategyClass(final String strategyName) { + if (GREMLIN_CONSTRUCTED.containsKey(strategyName)) + return Optional.of(GREMLIN_CONSTRUCTED.get(strategyName)); + + final Optional<? extends Class<? extends TraversalStrategy>> optFromGraph = GRAPH_CACHE.values().stream().flatMap(strategies -> strategies.toList().stream()). + filter(strategy -> strategy.getClass().getSimpleName().equals(strategyName)).map(strategy -> strategy.getClass()).findAny(); + if (optFromGraph.isPresent()) + return optFromGraph; + + final Optional<? extends Class<? extends TraversalStrategy>> optFromGraphComputer = GRAPH_COMPUTER_CACHE.values().stream().flatMap(strategies -> strategies.toList().stream()). + filter(strategy -> strategy.getClass().getSimpleName().equals(strategyName)).map(strategy -> strategy.getClass()).findAny(); + if (optFromGraphComputer.isPresent()) + return optFromGraphComputer; + + return Optional.empty(); + } + public static TraversalStrategies getStrategies(final Class graphOrGraphComputerClass) { try { // be sure to load the class so that its static{} traversal strategy registration component is loaded. diff --git a/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinDisabledListDelimiterHandler.java b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinDisabledListDelimiterHandler.java new file mode 100644 index 0000000000..239bac3bd0 --- /dev/null +++ b/gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/util/GremlinDisabledListDelimiterHandler.java @@ -0,0 +1,46 @@ +/* + * 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.util; + +import org.apache.commons.configuration2.convert.DisabledListDelimiterHandler; +import org.apache.tinkerpop.gremlin.process.traversal.Traversal; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; + +import java.util.Collections; + +/** + * Special handler that prevents the list delimiter from iterating traversals that are used for configuration, like + * when given to {@link SubgraphStrategy} + */ +public class GremlinDisabledListDelimiterHandler extends DisabledListDelimiterHandler { + + private static final GremlinDisabledListDelimiterHandler INSTANCE = new GremlinDisabledListDelimiterHandler(); + + public static GremlinDisabledListDelimiterHandler instance() { + return INSTANCE; + } + + @Override + public Iterable<?> parse(final Object value) { + if (value instanceof Traversal) + return Collections.singletonList(value); + else + return super.parse(value); + } +} diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitorTest.java index 6c555c6c99..25f1271651 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalSourceSelfMethodVisitorTest.java @@ -23,6 +23,10 @@ import org.antlr.v4.runtime.CommonTokenStream; import org.apache.tinkerpop.gremlin.process.traversal.Operator; import org.apache.tinkerpop.gremlin.process.traversal.P; import org.apache.tinkerpop.gremlin.process.traversal.dsl.graph.GraphTraversalSource; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.EarlyLimitStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.PathRetractionStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.RepeatUnrollStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; import org.apache.tinkerpop.gremlin.structure.util.empty.EmptyGraph; @@ -61,6 +65,10 @@ public class TraversalSourceSelfMethodVisitorTest { {"withStrategies(new EdgeLabelVerificationStrategy(logWarning: true, throwException: true))", g.withStrategies(EdgeLabelVerificationStrategy.build().logWarning(true).throwException(true).create())}, {"withStrategies(ReadOnlyStrategy, new EdgeLabelVerificationStrategy(logWarning: true, throwException: true))", g.withStrategies(ReadOnlyStrategy.instance(), EdgeLabelVerificationStrategy.build().logWarning(true).throwException(true).create())}, {"withStrategies(new EdgeLabelVerificationStrategy(logWarning: true, throwException: true), ReadOnlyStrategy)", g.withStrategies(EdgeLabelVerificationStrategy.build().logWarning(true).throwException(true).create(), ReadOnlyStrategy.instance())}, + {"withoutStrategies(CountStrategy)", g.withoutStrategies(CountStrategy.class)}, + {"withoutStrategies(CountStrategy, EarlyLimitStrategy)", g.withoutStrategies(CountStrategy.class, EarlyLimitStrategy.class)}, + {"withoutStrategies(CountStrategy, EarlyLimitStrategy, PathRetractionStrategy)", g.withoutStrategies(CountStrategy.class, EarlyLimitStrategy.class, PathRetractionStrategy.class)}, + {"withoutStrategies(CountStrategy, EarlyLimitStrategy, PathRetractionStrategy, RepeatUnrollStrategy)", g.withoutStrategies(CountStrategy.class, EarlyLimitStrategy.class, PathRetractionStrategy.class, RepeatUnrollStrategy.class)}, {"with('requestId', '7c55d4d7-809a-4f84-9720-63b48cb2fd14')", g.with("requestId", "7c55d4d7-809a-4f84-9720-63b48cb2fd14")}, {"with('requestId')", g.with("requestId")}, {"withSideEffect('hello', ['one':1])", g.withSideEffect("hello", map)}, diff --git a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitorTest.java b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitorTest.java index 445c6ff9db..6245a21a10 100644 --- a/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitorTest.java +++ b/gremlin-core/src/test/java/org/apache/tinkerpop/gremlin/language/grammar/TraversalStrategyVisitorTest.java @@ -26,6 +26,7 @@ import org.apache.tinkerpop.gremlin.process.traversal.TraversalStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.PartitionStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SeedStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.decoration.SubgraphStrategy; +import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.CountStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.optimization.ProductiveByStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.EdgeLabelVerificationStrategy; import org.apache.tinkerpop.gremlin.process.traversal.strategy.verification.ReadOnlyStrategy; @@ -58,6 +59,7 @@ public class TraversalStrategyVisitorTest { return Arrays.asList(new Object[][]{ {"ReadOnlyStrategy", ReadOnlyStrategy.instance()}, {"new SeedStrategy(seed: 999999)", new SeedStrategy(999999)}, + {"SeedStrategy(seed: 999999)", new SeedStrategy(999999)}, {"new PartitionStrategy(partitionKey: 'k', includeMetaProperties: true)", PartitionStrategy.build().partitionKey("k").includeMetaProperties(true).create()}, {"new PartitionStrategy(partitionKey: 'k', writePartition: 'p', readPartitions: ['p','x','y'])", PartitionStrategy.build().partitionKey("k").writePartition("p").readPartitions("p", "x", "y").create()}, {"ProductiveByStrategy", ProductiveByStrategy.instance()}, @@ -66,10 +68,13 @@ public class TraversalStrategyVisitorTest { {"new EdgeLabelVerificationStrategy(logWarning: true, throwException: true)", EdgeLabelVerificationStrategy.build().logWarning(true).throwException(true).create()}, {"new ReservedKeysVerificationStrategy()", ReservedKeysVerificationStrategy.build().create()}, {"new ReservedKeysVerificationStrategy(logWarning: true, throwException: true)", ReservedKeysVerificationStrategy.build().logWarning(true).throwException(true).create()}, + {"ReservedKeysVerificationStrategy(logWarning: true, throwException: true)", ReservedKeysVerificationStrategy.build().logWarning(true).throwException(true).create()}, {"new ReservedKeysVerificationStrategy(logWarning: true, throwException: false)", ReservedKeysVerificationStrategy.build().logWarning(true).create()}, {"new ReservedKeysVerificationStrategy(keys: ['a','b'])", ReservedKeysVerificationStrategy.build().reservedKeys(new HashSet<>(Arrays.asList("a", "b"))).create()}, {"new SubgraphStrategy(vertices: hasLabel('person'))", SubgraphStrategy.build().vertices(hasLabel("person")).create()}, + {"SubgraphStrategy(vertices: hasLabel('person'))", SubgraphStrategy.build().vertices(hasLabel("person")).create()}, {"new SubgraphStrategy(vertices: hasLabel('person'), edges: hasLabel('knows'), vertexProperties: has('time', between(1234, 4321)), checkAdjacentVertices: true)", SubgraphStrategy.build().vertices(hasLabel("person")).edges(hasLabel("knows")).vertexProperties(has("time", P.between(1234, 4321))).checkAdjacentVertices(true).create()}, + {"CountStrategy", CountStrategy.instance()}, }); } diff --git a/gremlin-language/src/main/antlr4/Gremlin.g4 b/gremlin-language/src/main/antlr4/Gremlin.g4 index 107396547b..c0227a8055 100644 --- a/gremlin-language/src/main/antlr4/Gremlin.g4 +++ b/gremlin-language/src/main/antlr4/Gremlin.g4 @@ -63,6 +63,7 @@ traversalSourceSelfMethod | traversalSourceSelfMethod_withSack | traversalSourceSelfMethod_withSideEffect | traversalSourceSelfMethod_withStrategies + | traversalSourceSelfMethod_withoutStrategies | traversalSourceSelfMethod_with ; @@ -87,6 +88,10 @@ traversalSourceSelfMethod_withStrategies : 'withStrategies' LPAREN traversalStrategy (COMMA traversalStrategyList)? RPAREN ; +traversalSourceSelfMethod_withoutStrategies + : 'withoutStrategies' LPAREN classType (COMMA classTypeList)? RPAREN + ; + traversalSourceSelfMethod_with : 'with' LPAREN stringArgument RPAREN | 'with' LPAREN stringArgument COMMA genericLiteralArgument RPAREN @@ -961,70 +966,15 @@ structureVertex ; traversalStrategy -// : 'ConnectiveStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'ElementIdStrategy' - not supported as the configuration takes a lambda -// | 'EventStrategy' - not supported as there is no way to send events back to the client -// | 'HaltedTraverserStrategy' - not supported as it is not typically relevant to OLTP -// | 'OptionsStrategy' - not supported as it's internal to with() - : NEW 'PartitionStrategy' LPAREN traversalStrategyArgs_PartitionStrategy? (COMMA traversalStrategyArgs_PartitionStrategy)* RPAREN -// | 'RequirementStrategy' - not supported as it's internally relevant only -// | 'SackStrategy' - not supported directly as it's internal to withSack() - | NEW 'SeedStrategy' LPAREN 'seed' COLON integerArgument RPAREN -// | 'SideEffectStrategy' - not supported directly as it's internal to withSideEffect() - | NEW 'SubgraphStrategy' LPAREN traversalStrategyArgs_SubgraphStrategy? (COMMA traversalStrategyArgs_SubgraphStrategy)* RPAREN -// | 'MatchAlgorithmStrategy' - not supported directly as it's internal to match() -// | 'ProfileStrategy' - not supported directly as it's internal to profile() -// | 'ReferenceElementStrategy' - not supported directly as users really can't/shouldn't change this in our context of a remote Gremlin provider -// | 'AdjacentToIncidentStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'ByModulatorOptimizationStrategy' - not supported as it is a default strategy and we don't allow removal at this time - | NEW? 'ProductiveByStrategy' (LPAREN traversalStrategyArgs_ProductiveByStrategy? RPAREN)? -// | 'CountStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'EarlyLimitStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'FilterRankingStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'IdentityRemovalStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'IncidentToAdjacentStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'InlineFilterStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'LazyBarrierStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'MatchPredicateStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'OrderLimitStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'PathProcessorStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'PathRetractionStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'RepeatUnrollStrategy' - not supported as it is a default strategy and we don't allow removal at this time -// | 'ComputerVerificationStrategy' - not supported since it's GraphComputer related - | NEW 'EdgeLabelVerificationStrategy' LPAREN traversalStrategyArgs_EdgeLabelVerificationStrategy? (COMMA traversalStrategyArgs_EdgeLabelVerificationStrategy)* RPAREN -// | 'LambdaRestrictionStrategy' - not supported as we don't support lambdas in any situation - | 'ReadOnlyStrategy' - | NEW 'ReservedKeysVerificationStrategy' LPAREN traversalStrategyArgs_ReservedKeysVerificationStrategy? (COMMA traversalStrategyArgs_ReservedKeysVerificationStrategy)* RPAREN -// | 'StandardVerificationStrategy' - not supported since this is an interal strategy - ; - -traversalStrategyArgs_ProductiveByStrategy - : 'productiveKeys' COLON stringLiteralList - ; - -traversalStrategyArgs_PartitionStrategy - : 'includeMetaProperties' COLON booleanArgument - | 'writePartition' COLON stringArgument - | 'partitionKey' COLON stringArgument - | 'readPartitions' COLON stringLiteralList - ; - -traversalStrategyArgs_SubgraphStrategy - : 'vertices' COLON nestedTraversal - | 'edges' COLON nestedTraversal - | 'vertexProperties' COLON nestedTraversal - | 'checkAdjacentVertices' COLON booleanArgument - ; - -traversalStrategyArgs_EdgeLabelVerificationStrategy - : 'throwException' COLON booleanArgument - | 'logWarning' COLON booleanArgument - ; - -traversalStrategyArgs_ReservedKeysVerificationStrategy - : 'keys' COLON stringLiteralList - | 'throwException' COLON booleanArgument - | 'logWarning' COLON booleanArgument + : NEW? classType (LPAREN (traversalStrategyArg (COMMA traversalStrategyArg)*)? RPAREN)? + ; + +// sorta annoying that certain keywords can end up being used as configuration keys on a strategy. if they aren't +// defined here then the parser doesn't pick them up. what's the right way to fix this?! +traversalStrategyArg + : 'keys' COLON genericLiteralArgument + | 'edges' COLON genericLiteralArgument + | Identifier COLON genericLiteralArgument ; traversalScope @@ -1620,6 +1570,14 @@ traversalStrategyExpr : traversalStrategy (COMMA traversalStrategy)* ; +classTypeList + : classTypeExpr? + ; + +classTypeExpr + : classType (COMMA classType)* + ; + nestedTraversalList : nestedTraversalExpr? ; @@ -1747,6 +1705,10 @@ infLiteral : SignedInfLiteral ; +classType + : Identifier + ; + variable : Identifier ; @@ -1766,185 +1728,185 @@ NEW : 'new'; // Integer Literals IntegerLiteral - : Sign? DecimalIntegerLiteral - | Sign? HexIntegerLiteral - | Sign? OctalIntegerLiteral + : Sign? DecimalIntegerLiteral + | Sign? HexIntegerLiteral + | Sign? OctalIntegerLiteral ; fragment DecimalIntegerLiteral - : DecimalNumeral IntegerTypeSuffix? + : DecimalNumeral IntegerTypeSuffix? ; fragment HexIntegerLiteral - : HexNumeral IntegerTypeSuffix? + : HexNumeral IntegerTypeSuffix? ; fragment OctalIntegerLiteral - : OctalNumeral IntegerTypeSuffix? + : OctalNumeral IntegerTypeSuffix? ; fragment IntegerTypeSuffix - : [bBsSnNiIlL] + : [bBsSnNiIlL] ; fragment DecimalNumeral - : '0' - | NonZeroDigit (Digits? | Underscores Digits) + : '0' + | NonZeroDigit (Digits? | Underscores Digits) ; fragment Digits - : Digit (DigitsAndUnderscores? Digit)? + : Digit (DigitsAndUnderscores? Digit)? ; fragment Digit - : '0' - | NonZeroDigit + : '0' + | NonZeroDigit ; fragment NonZeroDigit - : [1-9] + : [1-9] ; fragment DigitsAndUnderscores - : DigitOrUnderscore+ + : DigitOrUnderscore+ ; fragment DigitOrUnderscore - : Digit - | '_' + : Digit + | '_' ; fragment Underscores - : '_'+ + : '_'+ ; fragment HexNumeral - : '0' [xX] HexDigits + : '0' [xX] HexDigits ; fragment HexDigits - : HexDigit (HexDigitsAndUnderscores? HexDigit)? + : HexDigit (HexDigitsAndUnderscores? HexDigit)? ; fragment HexDigit - : [0-9a-fA-F] + : [0-9a-fA-F] ; fragment HexDigitsAndUnderscores - : HexDigitOrUnderscore+ + : HexDigitOrUnderscore+ ; fragment HexDigitOrUnderscore - : HexDigit - | '_' + : HexDigit + | '_' ; fragment OctalNumeral - : '0' Underscores? OctalDigits + : '0' Underscores? OctalDigits ; fragment OctalDigits - : OctalDigit (OctalDigitsAndUnderscores? OctalDigit)? + : OctalDigit (OctalDigitsAndUnderscores? OctalDigit)? ; fragment OctalDigit - : [0-7] + : [0-7] ; fragment OctalDigitsAndUnderscores - : OctalDigitOrUnderscore+ + : OctalDigitOrUnderscore+ ; fragment OctalDigitOrUnderscore - : OctalDigit - | '_' + : OctalDigit + | '_' ; // Floating-Point Literals FloatingPointLiteral - : Sign? DecimalFloatingPointLiteral + : Sign? DecimalFloatingPointLiteral ; fragment DecimalFloatingPointLiteral - : Digits ('.' Digits ExponentPart? | ExponentPart) FloatTypeSuffix? - | Digits FloatTypeSuffix + : Digits ('.' Digits ExponentPart? | ExponentPart) FloatTypeSuffix? + | Digits FloatTypeSuffix ; fragment ExponentPart - : ExponentIndicator SignedInteger + : ExponentIndicator SignedInteger ; fragment ExponentIndicator - : [eE] + : [eE] ; fragment SignedInteger - : Sign? Digits + : Sign? Digits ; fragment Sign - : [+-] + : [+-] ; fragment FloatTypeSuffix - : [fFdDmM] + : [fFdDmM] ; // Boolean Literals BooleanLiteral - : 'true' - | 'false' + : 'true' + | 'false' ; // Null Literal NullLiteral - : 'null' + : 'null' ; // NaN Literal NaNLiteral - : 'NaN' + : 'NaN' ; // Inf Literal SignedInfLiteral - : Sign? InfLiteral + : Sign? InfLiteral ; InfLiteral - : 'Infinity' + : 'Infinity' ; @@ -1954,38 +1916,38 @@ InfLiteral // literals also. A side effect of this is ANTLR will not be able to parse single character string literals with // single quoted so we instead remove char literal altogether and only have string literal in lexer tokens. NonEmptyStringLiteral - : '"' DoubleQuotedStringCharacters '"' - | '\'' SingleQuotedStringCharacters '\'' + : '"' DoubleQuotedStringCharacters '"' + | '\'' SingleQuotedStringCharacters '\'' ; // We define NonEmptyStringLiteral and EmptyStringLiteral separately so that we can unambiguously handle empty queries EmptyStringLiteral - : '""' - | '\'\'' + : '""' + | '\'\'' ; fragment DoubleQuotedStringCharacters - : DoubleQuotedStringCharacter+ + : DoubleQuotedStringCharacter+ ; fragment DoubleQuotedStringCharacter - : ~('"' | '\\') - | JoinLineEscape - | EscapeSequence + : ~('"' | '\\') + | JoinLineEscape + | EscapeSequence ; fragment SingleQuotedStringCharacters - : SingleQuotedStringCharacter+ + : SingleQuotedStringCharacter+ ; fragment SingleQuotedStringCharacter - : ~('\'' | '\\') - | JoinLineEscape - | EscapeSequence + : ~('\'' | '\\') + | JoinLineEscape + | EscapeSequence ; // Escape Sequences for Character and String Literals @@ -1995,21 +1957,21 @@ fragment JoinLineEscape fragment EscapeSequence - : '\\' [btnfr"'\\] - | OctalEscape - | UnicodeEscape // This is not in the spec but prevents having to preprocess the input + : '\\' [btnfr"'\\] + | OctalEscape + | UnicodeEscape // This is not in the spec but prevents having to preprocess the input ; fragment OctalEscape - : '\\' OctalDigit - | '\\' OctalDigit OctalDigit - | '\\' ZeroToThree OctalDigit OctalDigit + : '\\' OctalDigit + | '\\' OctalDigit OctalDigit + | '\\' ZeroToThree OctalDigit OctalDigit ; fragment ZeroToThree - : [0-3] + : [0-3] ; // This is not in the spec but prevents having to preprocess the input
