Invoke predicates and enums
Project: http://git-wip-us.apache.org/repos/asf/tinkerpop/repo Commit: http://git-wip-us.apache.org/repos/asf/tinkerpop/commit/ee330162 Tree: http://git-wip-us.apache.org/repos/asf/tinkerpop/tree/ee330162 Diff: http://git-wip-us.apache.org/repos/asf/tinkerpop/diff/ee330162 Branch: refs/heads/master Commit: ee33016266b69b2cf771019997b81782acbb9c4e Parents: a805845 Author: Jorge Bay Gondra <jorgebaygon...@gmail.com> Authored: Mon Oct 30 15:14:38 2017 +0100 Committer: Jorge Bay Gondra <jorgebaygon...@gmail.com> Committed: Thu Nov 30 10:00:08 2017 +0100 ---------------------------------------------------------------------- .../StaticTraversalParameter.cs | 15 +-- .../TraversalEnumParameter.cs | 98 ++++++++++++++++++++ .../TraversalEvaluationTests.cs | 16 +++- .../TraversalEvaluation/TraversalParser.cs | 79 ++++++++++------ .../TraversalPredicateParameter.cs | 26 ++++-- .../TraversalTokenParameter.cs | 71 -------------- 6 files changed, 188 insertions(+), 117 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee330162/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs index 8e0fbf9..dca691b 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/StaticTraversalParameter.cs @@ -30,9 +30,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { internal class StaticTraversalParameter : ITokenParameter, IEquatable<StaticTraversalParameter> { + private readonly string _traversalText; + public bool Equals(StaticTraversalParameter other) { - return Parts.SequenceEqual(other.Parts); + return Tokens.SequenceEqual(other.Tokens); } public override bool Equals(object obj) @@ -45,12 +47,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation public override int GetHashCode() { - return Parts != null ? Parts.GetHashCode() : 0; + return Tokens != null ? Tokens.GetHashCode() : 0; } public object GetValue() { - throw new NotImplementedException(); + return TraversalParser.GetTraversalFromTokens(Tokens, null, _traversalText); } public Type GetParameterType() @@ -58,11 +60,12 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return typeof(ITraversal); } - public IList<Token> Parts { get; } + public IList<Token> Tokens { get; } - public StaticTraversalParameter(IList<Token> parts) + public StaticTraversalParameter(IList<Token> tokens, string traversalText) { - Parts = parts; + _traversalText = traversalText; + Tokens = tokens; } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee330162/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs new file mode 100644 index 0000000..663928a --- /dev/null +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEnumParameter.cs @@ -0,0 +1,98 @@ +#region License + +/* + * 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. + */ + +#endregion + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Reflection; +using Gremlin.Net.Process.Traversal; +using TEnum = Gremlin.Net.Process.Traversal.T; + +namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation +{ + /// <summary> + /// Represents a parameter for a traversal token (ie: T.label) + /// </summary> + internal class TraversalEnumParameter : ITokenParameter, IEquatable<TraversalEnumParameter> + { + private readonly string _text; + + private static readonly IDictionary<string, Type> EnumTypesByName = typeof(Scope).GetTypeInfo().Assembly + .GetTypes().Where(t => t.GetTypeInfo().IsEnum && t.GetTypeInfo().IsPublic) + .ToDictionary(e => e.Name, e => e); + + private readonly object _value; + private readonly Type _type; + + public TraversalEnumParameter(string text) + { + _text = text; + var separatorIndex = text.IndexOf('.'); + var enumTypeName = text.Substring(0, separatorIndex); + if (!EnumTypesByName.TryGetValue(enumTypeName, out var type)) + { + throw new KeyNotFoundException($"Enum with name {enumTypeName} not found"); + } + _type = type; + var valueName = text.Substring(separatorIndex + 1); + _value = Enum.Parse(type, GetCsharpName(valueName)); + } + + private string GetCsharpName(string valueText) + { + if (_type == typeof(Direction)) + { + valueText = valueText.ToLower(); + } + return TraversalParser.GetCsharpName(valueText); + } + + public bool Equals(TraversalEnumParameter other) + { + return _text == other._text; + } + + public override bool Equals(object obj) + { + if (ReferenceEquals(null, obj)) return false; + if (ReferenceEquals(this, obj)) return true; + if (obj.GetType() != GetType()) return false; + return Equals((TraversalEnumParameter) obj); + } + + public override int GetHashCode() + { + return _text.GetHashCode(); + } + + public object GetValue() + { + return _value; + } + + public Type GetParameterType() + { + return _type; + } + } +} \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee330162/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs index 4e3ec42..9cbceb9 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalEvaluationTests.cs @@ -54,11 +54,13 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation new[] {new Token("has", new[] {new StringParameter("lang"), new StringParameter("java")})}), Tuple.Create("g.V().where(__.in(\"knows\"))", new[] {new Token("where", new[] {new StaticTraversalParameter( - new[] {new Token("__"), new Token("in", new StringParameter("knows"))})})}), + new[] {new Token("__"), new Token("in", new StringParameter("knows"))}, "__.in(\"knows\")")})}), Tuple.Create("g.V().has(\"age\", P.gt(27)", new[] {new Token("has", new ITokenParameter[] { new StringParameter("age"), new TraversalPredicateParameter( - new[] { new Token("P"), new Token("gt", NumericParameter.Create(27)) }) })}) + new[] { new Token("P"), new Token("gt", NumericParameter.Create(27)) }) })}), + Tuple.Create("g.V().count(Scope.local)", + new[] { new Token("count", new TraversalEnumParameter("Scope.local"))}) }; foreach (var item in items) { @@ -80,16 +82,20 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation var traversalTexts = new [] { "g.V().count()", - "g.V().constant(123)", + //"g.V().constant(123L)", Can be parsed using the new type-safe API "g.V().has(\"no\").count()", "g.V().values(\"age\")", "g.V().valueMap(\"name\", \"age\")", - "g.V().repeat(__.both()).times(5)" + "g.V().where(__.in(\"created\").count().is(1)).values(\"name\")", + "g.V().count(Scope.local)", + "g.V().values(\"age\").is(P.lte(30))" }; var g = new Graph().Traversal(); foreach (var text in traversalTexts) { - Assert.NotNull(TraversalParser.GetTraversal(text, g)); + var traversal = TraversalParser.GetTraversal(text, g); + Assert.NotNull(traversal); + Assert.True(traversal.Bytecode.StepInstructions.Count > 0); } } } http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee330162/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs index 11283c1..9880a64 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalParser.cs @@ -42,49 +42,63 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation private static readonly Regex RegexFloat = new Regex(@"\d+\.\d+f", RegexOptions.Compiled | RegexOptions.IgnoreCase); private static readonly Regex RegexLong = new Regex(@"\d+l", RegexOptions.Compiled | RegexOptions.IgnoreCase); + private static readonly Regex RegexEnum = new Regex(@"\w+\.\w+", RegexOptions.Compiled); internal static ITraversal GetTraversal(string traversalText, GraphTraversalSource g) { - Func<GraphTraversalSource, ITraversal> traversalBuilder; - if (!FixedTranslations.TryGetValue(traversalText, out traversalBuilder)) + if (!FixedTranslations.TryGetValue(traversalText, out var traversalBuilder)) { - return BuildFromMethods(traversalText, g); + var tokens = ParseTraversal(traversalText); + return GetTraversalFromTokens(tokens, g, traversalText); } return traversalBuilder(g); } - private static ITraversal BuildFromMethods(string traversalText, GraphTraversalSource g) + internal static ITraversal GetTraversalFromTokens(IList<Token> tokens, GraphTraversalSource g, + string traversalText) { - var parts = ParseTraversal(traversalText); - if (parts[0].Name != "g") + ITraversal traversal; + Type traversalType; + var initialIndex = 2; + if (tokens[0].Name == "g") { - throw BuildException(traversalText); + switch (tokens[1].Name) + { + case "V": + //TODO: support V() parameters + traversal = g.V(); + break; + case "E": + traversal = g.E(); + break; + default: + throw BuildException(traversalText); + } + traversalType = traversal.GetType(); } - ITraversal traversal; - switch (parts[1].Name) + else if (tokens[0].Name == "__") { - case "V": - //TODO: support V() parameters - traversal = g.V(); - break; - case "E": - traversal = g.E(); - break; - default: - throw BuildException(traversalText); + traversal = null; + traversalType = typeof(__); + initialIndex = 1; } - for (var i = 2; i < parts.Count; i++) + else { - var token = parts[i]; + throw BuildException(traversalText); + } + for (var i = initialIndex; i < tokens.Count; i++) + { + var token = tokens[i]; var name = GetCsharpName(token.Name); - var method = traversal.GetType().GetMethod(name); + var method = traversalType.GetMethod(name); if (method == null) { - throw new InvalidOperationException($"Traversal method '{parts[i]}' not found for testing"); + throw new InvalidOperationException($"Traversal method '{tokens[i]}' not found for testing"); } var parameterValues = BuildParameters(method, token, out var genericParameters); method = BuildGenericMethod(method, genericParameters, parameterValues); traversal = (ITraversal) method.Invoke(traversal, parameterValues); + traversalType = traversal.GetType(); } return traversal; } @@ -155,7 +169,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return type.GetTypeInfo().IsValueType ? Activator.CreateInstance(type) : null; } - private static string GetCsharpName(string part) + internal static string GetCsharpName(string part) { // Transform to PascalCasing and remove the parenthesis return char.ToUpper(part[0]) + part.Substring(1); @@ -268,17 +282,24 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation } if (text.Substring(i, 3).StartsWith("__.")) { - return new StaticTraversalParameter(ParseTokens(text, ref i)); - } - if (text.Substring(i, 2).StartsWith("T.")) - { - return new TraversalTokenParameter(ParseTokens(text, ref i)); + var startIndex = i; + var tokens = ParseTokens(text, ref i); + return new StaticTraversalParameter(tokens, text.Substring(startIndex, i - startIndex)); } if (text.Substring(i, 2).StartsWith("P.")) { return new TraversalPredicateParameter(ParseTokens(text, ref i)); } - return null; + var parameterText = text.Substring(i, text.IndexOf(')', i) - i); + if (string.IsNullOrWhiteSpace(parameterText)) + { + return null; + } + if (RegexEnum.IsMatch(parameterText)) + { + return new TraversalEnumParameter(parameterText); + } + throw new NotSupportedException($"Parameter {parameterText} not supported"); } private static ITokenParameter ParseNumber(string text, ref int i) http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee330162/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs index 8c040b9..0d0dc49 100644 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs +++ b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalPredicateParameter.cs @@ -24,6 +24,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Reflection; using Gremlin.Net.Process.Traversal; namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation @@ -35,7 +36,7 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation { public bool Equals(TraversalPredicateParameter other) { - return Parts.SequenceEqual(other.Parts); + return Tokens.SequenceEqual(other.Tokens); } public override bool Equals(object obj) @@ -48,12 +49,25 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation public override int GetHashCode() { - return Parts != null ? Parts.GetHashCode() : 0; + return Tokens != null ? Tokens.GetHashCode() : 0; } public object GetValue() { - throw new NotImplementedException(); + var type = typeof(P); + object instance = null; + for (var i = 1; i < Tokens.Count; i++) + { + var token = Tokens[i]; + var method = type.GetMethod(TraversalParser.GetCsharpName(token.Name), + BindingFlags.Static | BindingFlags.Public); + if (method == null) + { + throw new InvalidOperationException($"Predicate (P) method '{token}' not found for testing"); + } + instance = method.Invoke(instance, new object[] {token.Parameters.Select(p => p.GetValue()).ToArray()}); + } + return instance; } public Type GetParameterType() @@ -61,11 +75,11 @@ namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation return typeof(TraversalPredicate); } - public IList<Token> Parts { get; } + public IList<Token> Tokens { get; } - public TraversalPredicateParameter(IList<Token> parts) + public TraversalPredicateParameter(IList<Token> tokens) { - Parts = parts; + Tokens = tokens; } } } \ No newline at end of file http://git-wip-us.apache.org/repos/asf/tinkerpop/blob/ee330162/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs ---------------------------------------------------------------------- diff --git a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs b/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs deleted file mode 100644 index 9291cd6..0000000 --- a/gremlin-dotnet/test/Gremlin.Net.IntegrationTest/Gherkin/TraversalEvaluation/TraversalTokenParameter.cs +++ /dev/null @@ -1,71 +0,0 @@ -#region License - -/* - * 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. - */ - -#endregion - -using System; -using System.Collections.Generic; -using System.Linq; -using TEnum = Gremlin.Net.Process.Traversal.T; - -namespace Gremlin.Net.IntegrationTest.Gherkin.TraversalEvaluation -{ - /// <summary> - /// Represents a parameter for a traversal token (ie: T.label) - /// </summary> - internal class TraversalTokenParameter : ITokenParameter, IEquatable<TraversalTokenParameter> - { - public bool Equals(TraversalTokenParameter other) - { - return Parts.SequenceEqual(other.Parts); - } - - public override bool Equals(object obj) - { - if (ReferenceEquals(null, obj)) return false; - if (ReferenceEquals(this, obj)) return true; - if (obj.GetType() != GetType()) return false; - return Equals((TraversalTokenParameter) obj); - } - - public override int GetHashCode() - { - return Parts != null ? Parts.GetHashCode() : 0; - } - - public object GetValue() - { - throw new NotImplementedException(); - } - - public Type GetParameterType() - { - return typeof(TEnum); - } - - public IList<Token> Parts { get; } - - public TraversalTokenParameter(IList<Token> parts) - { - Parts = parts; - } - } -} \ No newline at end of file