#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; | |
using System.Collections.Generic; | |
using System.Globalization; | |
using System.Linq; | |
using System.Text; | |
using Gremlin.Net.Process.Traversal.Step.Util; | |
using Gremlin.Net.Process.Traversal.Strategy; | |
using Gremlin.Net.Process.Traversal.Strategy.Decoration; | |
namespace Gremlin.Net.Process.Traversal.Translator | |
{ | |
/// <summary> | |
/// Converts bytecode to a Groovy string of Gremlin. | |
/// </summary> | |
public class GroovyTranslator | |
{ | |
private GroovyTranslator(string traversalSource) | |
{ | |
TraversalSource = traversalSource; | |
} | |
/// <summary> | |
/// Creates the translator. | |
/// </summary> | |
/// <param name="traversalSource">The traversal source for the traversal to be translated.</param> | |
/// <returns>The created translator instance.</returns> | |
public static GroovyTranslator Of(string traversalSource) | |
{ | |
return new GroovyTranslator(traversalSource); | |
} | |
/// <summary> | |
/// Get the language that the translator is converting the traversal byte code to. | |
/// </summary> | |
public string TargetLanguage => "gremlin-groovy"; | |
/// <summary> | |
/// Gets the <see cref="TraversalSource"/> representation rooting this translator. | |
/// This is typically a "g". | |
/// </summary> | |
public string TraversalSource { get; } | |
/// <summary> | |
/// Translate <see cref="ITraversal"/> into gremlin-groovy. | |
/// </summary> | |
/// <param name="traversal">The traversal to translate.</param> | |
/// <param name="isChildTraversal">Whether this is an anonymous traversal (started via '__').</param> | |
/// <returns>The translated gremlin-groovy traversal.</returns> | |
public string Translate(ITraversal traversal, bool isChildTraversal = false) | |
{ | |
return Translate(traversal.Bytecode, isChildTraversal); | |
} | |
/// <summary> | |
/// Translate <see cref="Bytecode"/> into gremlin-groovy. | |
/// </summary> | |
/// <param name="bytecode">The bytecode representing traversal source and traversal manipulations.</param> | |
/// <param name="isChildTraversal">Whether this is an anonymous traversal (started via '__').</param> | |
/// <returns>The translated gremlin-groovy traversal.</returns> | |
public string Translate(Bytecode bytecode, bool isChildTraversal = false) | |
{ | |
var sb = new StringBuilder(isChildTraversal ? "__" : TraversalSource); | |
foreach (var step in bytecode.SourceInstructions) | |
{ | |
sb.Append(TranslateStep(step)); | |
} | |
foreach (var step in bytecode.StepInstructions) | |
{ | |
sb.Append(TranslateStep(step)); | |
} | |
return sb.ToString(); | |
} | |
private string TranslateStep(Instruction step) | |
{ | |
if (step.OperatorName == "with") | |
{ | |
return $".{step.OperatorName}({TranslateWithArguments(step.Arguments)})"; | |
} | |
return $".{step.OperatorName}({TranslateArguments(step.Arguments)})"; | |
} | |
private string TranslateWithArguments(dynamic[] arguments) | |
{ | |
if (arguments[0] == WithOptions.Tokens) | |
{ | |
return string.Join(", ", arguments.Select(a => PropertyMapOptionsTranslation[a])); | |
} | |
if (arguments[0] == WithOptions.Indexer) | |
{ | |
return string.Join(", ", arguments.Select(a => IndexOptionsTranslation[a])); | |
} | |
return string.Join(", ", arguments.Select(TranslateArgument)); | |
} | |
private static readonly Dictionary<object, string> PropertyMapOptionsTranslation = new Dictionary<object, string> | |
{ | |
{ WithOptions.Tokens, "WithOptions.tokens" }, | |
{ WithOptions.None, "WithOptions.none" }, | |
{ WithOptions.Ids, "WithOptions.ids" }, | |
{ WithOptions.Labels, "WithOptions.labels" }, | |
{ WithOptions.Keys, "WithOptions.keys" }, | |
{ WithOptions.Values, "WithOptions.values" }, | |
{ WithOptions.All, "WithOptions.all" } | |
}; | |
private static readonly Dictionary<object, string> IndexOptionsTranslation = new Dictionary<object, string> | |
{ | |
{ WithOptions.Indexer, "WithOptions.indexer" }, | |
{ WithOptions.List, "WithOptions.list" }, | |
{ WithOptions.Map, "WithOptions.map" } | |
}; | |
private string TranslateArguments(IEnumerable<object> arguments) => | |
string.Join(", ", arguments.Select(TranslateArgument)); | |
private string TranslateArgument(object argument) | |
{ | |
return argument switch | |
{ | |
null => "null", | |
string str => $"'{str}'", | |
char c => $"'{c}'", | |
bool b => b ? "true" : "false", | |
DateTimeOffset dto => TranslateDateTimeOffset(dto), | |
DateTime dt => TranslateDateTimeOffset(dt), | |
Guid guid => $"UUID.fromString('{guid}')", | |
P p => TranslateP(p), | |
IDictionary dict => TranslateDictionary(dict), | |
IEnumerable e => TranslateCollection(e), | |
ITraversal t => TranslateTraversal(t), | |
AbstractTraversalStrategy strategy => TranslateStrategy(strategy), | |
_ => Convert.ToString(argument, CultureInfo.InvariantCulture) | |
}; | |
} | |
private static string TranslateDateTimeOffset(DateTimeOffset dto) | |
{ | |
var year = dto.Year - 1900; | |
var month = dto.Month - 1; | |
var dayOfMonth = dto.Day; | |
var hour = dto.Hour; | |
var minute = dto.Minute; | |
var second = dto.Second; | |
return $"new Date({year}, {month}, {dayOfMonth}, {hour}, {minute}, {second})"; | |
} | |
private string TranslateP(P p) | |
{ | |
return p.Other == null | |
? $"P.{p.OperatorName}({TranslateArgument(p.Value)})" | |
: $"P.{p.OperatorName}({TranslateArgument(p.Value)}, {TranslateArgument(p.Other)})"; | |
} | |
private string TranslateDictionary(IDictionary dict) | |
{ | |
var kvStrings = new List<string>(dict.Count); | |
foreach (DictionaryEntry kv in dict) | |
{ | |
kvStrings.Add($"{TranslateArgument(kv.Key)}: {TranslateArgument(kv.Value)}"); | |
} | |
return $"[{string.Join(", ", kvStrings)}]"; | |
} | |
private string TranslateCollection(IEnumerable enumerable) => | |
$"[{TranslateArguments(enumerable.Cast<object>().ToArray())}]"; | |
private string TranslateTraversal(ITraversal traversal) => Translate(traversal.Bytecode, true); | |
private string TranslateStrategy(AbstractTraversalStrategy strategy) | |
{ | |
var config = string.Join(", ", | |
strategy.Configuration.Select(opt => | |
$"{(strategy.StrategyName == nameof(OptionsStrategy) ? $"'{opt.Key}'" : opt.Key)}: {TranslateArgument(opt.Value)}")); | |
return $"new {strategy.StrategyName}({config})"; | |
} | |
} | |
} |