| #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.Diagnostics.CodeAnalysis; |
| using System.Linq; |
| using Gremlin.Net.Process.Traversal; |
| |
| namespace Gremlin.Net.Structure |
| { |
| /// <summary> |
| /// A Path denotes a particular walk through a graph as defined by a <see cref="ITraversal" />. |
| /// </summary> |
| /// <remarks> |
| /// In abstraction, any Path implementation maintains two lists: a list of sets of labels and a list of objects. |
| /// The list of labels are the labels of the steps traversed. The list of objects are the objects traversed. |
| /// </remarks> |
| public class Path : IReadOnlyList<object?>, IEquatable<Path> |
| { |
| /// <summary> |
| /// Initializes a new instance of the <see cref="Path" /> class. |
| /// </summary> |
| /// <param name="labels">The labels associated with the path</param> |
| /// <param name="objects">The objects in the <see cref="Path" />.</param> |
| public Path(IList<ISet<string>> labels, IList<object?> objects) |
| { |
| Labels = labels ?? throw new ArgumentNullException(nameof(labels)); |
| Objects = objects ?? throw new ArgumentNullException(nameof(objects)); |
| } |
| |
| /// <summary> |
| /// Gets an ordered list of the labels associated with the <see cref="Path" />. |
| /// </summary> |
| public IList<ISet<string>> Labels { get; } |
| |
| /// <summary> |
| /// Gets an ordered list of the objects in the <see cref="Path" />. |
| /// </summary> |
| public IList<object?> Objects { get; } |
| |
| /// <summary> |
| /// Gets the object associated with the particular label of the path. |
| /// </summary> |
| /// <remarks>If the path has multiple labels of the type, then get a collection of those objects.</remarks> |
| /// <param name="label">The label of the path</param> |
| /// <returns>The object associated with the label of the path</returns> |
| /// <exception cref="KeyNotFoundException">Thrown if the path does not contain the label.</exception> |
| public object this[string label] |
| { |
| get |
| { |
| var objFound = TryGetValue(label, out var obj); |
| if (!objFound) |
| throw new KeyNotFoundException($"The step with label {label} does not exist"); |
| return obj!; |
| } |
| } |
| |
| /// <inheritdoc /> |
| public bool Equals(Path? other) |
| { |
| if (ReferenceEquals(null, other)) return false; |
| if (ReferenceEquals(this, other)) return true; |
| return ObjectsEqual(other.Objects) && LabelsEqual(other.Labels); |
| } |
| |
| /// <summary> |
| /// Get the object associated with the specified index into the path. |
| /// </summary> |
| /// <param name="index">The index of the path</param> |
| /// <returns>The object associated with the index of the path</returns> |
| public dynamic? this[int index] => Objects[index]; |
| |
| /// <summary> |
| /// Gets the number of steps in the path. |
| /// </summary> |
| public int Count => Objects.Count; |
| |
| /// <inheritdoc /> |
| public IEnumerator<object> GetEnumerator() |
| { |
| return ((IReadOnlyList<object>) Objects).GetEnumerator(); |
| } |
| |
| IEnumerator IEnumerable.GetEnumerator() |
| { |
| return ((IReadOnlyList<object>) Objects).GetEnumerator(); |
| } |
| |
| /// <inheritdoc /> |
| public override string ToString() |
| { |
| return $"path[{string.Join(", ", Objects)}]"; |
| } |
| |
| /// <summary> |
| /// Returns true if the path has the specified label, else return false. |
| /// </summary> |
| /// <param name="key">The label to search for.</param> |
| /// <returns>True if the label exists in the path.</returns> |
| public bool ContainsKey(string key) |
| { |
| return Labels.Any(objLabels => objLabels.Contains(key)); |
| } |
| |
| /// <summary> |
| /// Tries to get the object associated with the particular label of the path. |
| /// </summary> |
| /// <remarks>If the path has multiple labels of the type, then get a collection of those objects.</remarks> |
| /// <param name="label">The label of the path.</param> |
| /// <param name="value">The object associated with the label of the path.</param> |
| /// <returns>True, if an object was found for the label.</returns> |
| public bool TryGetValue(string label, [NotNullWhen(true)] out object? value) |
| { |
| value = null; |
| for (var i = 0; i < Labels.Count; i++) |
| { |
| if (!Labels[i].Contains(label)) continue; |
| if (value == null) |
| value = Objects[i]; |
| else if (value.GetType() == typeof(List<object?>)) |
| ((List<object?>) value).Add(Objects[i]); |
| else |
| value = new List<object?> {value, Objects[i]}; |
| } |
| return value != null; |
| } |
| |
| private bool ObjectsEqual(ICollection<object?> otherObjects) |
| { |
| return Objects.SequenceEqual(otherObjects); |
| } |
| |
| private bool LabelsEqual(ICollection<ISet<string>> otherLabels) |
| { |
| if (Labels.Count != otherLabels.Count) |
| return false; |
| using var enumOther = otherLabels.GetEnumerator(); |
| using var enumThis = Labels.GetEnumerator(); |
| while (enumOther.MoveNext() && enumThis.MoveNext()) |
| { |
| if (!enumOther.Current.SequenceEqual(enumThis.Current)) |
| { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| /// <inheritdoc /> |
| 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((Path) obj); |
| } |
| |
| /// <inheritdoc /> |
| public override int GetHashCode() |
| { |
| unchecked |
| { |
| var hashCode = 19; |
| hashCode = Labels |
| .Aggregate(hashCode, |
| (current1, objLabels) => objLabels.Aggregate(current1, |
| (current, label) => current * 31 + label.GetHashCode())); |
| hashCode = Objects.Aggregate(hashCode, |
| (current, obj) => current * 31 + (obj == null ? 0 : obj.GetHashCode())); |
| return hashCode; |
| } |
| } |
| } |
| } |