| #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.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; |
| Objects = 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 object 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, 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) |
| { |
| if (Objects == null) |
| return otherObjects == null; |
| return Objects.SequenceEqual(otherObjects); |
| } |
| |
| private bool LabelsEqual(ICollection<ISet<string>> otherLabels) |
| { |
| if (Labels == null) |
| return otherLabels == null; |
| 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; |
| if (Labels != null) |
| hashCode = Labels.Where(objLabels => objLabels != null) |
| .Aggregate(hashCode, |
| (current1, objLabels) => objLabels.Aggregate(current1, |
| (current, label) => current * 31 + label.GetHashCode())); |
| if (Objects != null) |
| hashCode = Objects.Aggregate(hashCode, (current, obj) => current * 31 + obj.GetHashCode()); |
| return hashCode; |
| } |
| } |
| } |
| } |