// Path: driver/src/main/java/org/apache/tinkerpop/gremlin/orientdb/executor/OGremlinResult.java
// public class OGremlinResult {
// 
//   private OrientGraph graph;
//   OResult inner;
// 
//   public OGremlinResult(OrientGraph graph, OResult inner) {
//     this.graph = graph;
//     this.inner = inner;
//   }
// 
//   public <T> T getProperty(String name) {
//     Object value = inner.getProperty(name);
//     if (value instanceof Iterable) {
//       Spliterator spliterator = ((Iterable) value).spliterator();
//       value =
//           StreamSupport.stream(spliterator, false)
//               .map(
//                   (e) -> {
//                     if (e instanceof OResult) {
//                       return new OGremlinResult(this.graph, (OResult) e);
//                     } else {
//                       return e;
//                     }
//                   })
//               .collect(Collectors.toList());
//     }
//     return (T) value;
//   }
// 
//   public Optional<OrientVertex> getVertex() {
//     return inner.getVertex().map((v) -> graph.elementFactory().wrapVertex(v));
//   }
// 
//   public Optional<OrientEdge> getEdge() {
//     return inner.getEdge().map((v) -> graph.elementFactory().wrapEdge(v));
//   }
// 
//   public boolean isElement() {
//     return inner.isElement();
//   }
// 
//   public boolean isVertex() {
//     return inner.isVertex();
//   }
// 
//   public boolean isEdge() {
//     return inner.isEdge();
//   }
// 
//   public boolean isBlob() {
//     return inner.isBlob();
//   }
// 
//   public OResult getRawResult() {
//     return inner;
//   }
// }


import com.google.common.annotations.VisibleForTesting;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.index.OIndexManager;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import org.apache.tinkerpop.gremlin.orientdb.*;
import org.apache.tinkerpop.gremlin.orientdb.executor.OGremlinResult;
import org.apache.tinkerpop.gremlin.process.traversal.Compare;
import org.apache.tinkerpop.gremlin.process.traversal.Contains;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.DefaultCloseableIterator;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.function.TriFunction;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;
package org.apache.tinkerpop.gremlin.orientdb.traversal.step.sideeffect;


public class OrientGraphStep<S, E extends Element> extends GraphStep<S, E>
    implements HasContainerHolder {

  private static final long serialVersionUID = 8141248670294067626L;

  private final List<HasContainer> hasContainers = new ArrayList<>();

  public OrientGraphStep(final GraphStep<S, E> originalGraphStep) {
    super(
        originalGraphStep.getTraversal(),
        originalGraphStep.getReturnClass(),
        originalGraphStep.isStartStep(),
        originalGraphStep.getIds());
    originalGraphStep.getLabels().forEach(this::addLabel);
    this.setIteratorSupplier(() -> (Iterator<E>) (isVertexStep() ? this.vertices() : this.edges()));
  }

  public boolean isVertexStep() {
    return Vertex.class.isAssignableFrom(this.returnClass);
  }

  private Iterator<? extends Vertex> vertices() {
    return elements(
        OGraph::vertices, OGraph::getIndexedVertices, OGraph::vertices, v -> v.getVertex().get());
  }

  private Iterator<? extends Edge> edges() {
    return elements(
        OGraph::edges, OGraph::getIndexedEdges, OGraph::edges, (e) -> e.getEdge().get());
  }

  /**
   * Gets an iterator over those vertices/edges that have the specific IDs wanted, or those that
   * have indexed properties with the wanted values, or failing that by just getting all of the
   * vertices or edges.
   *
   * @param getElementsByIds Function that will return an iterator over all the vertices/edges in
   *     the graph that have the specific IDs
   * @param getElementsByIndex Function that returns a stream of all the vertices/edges in the graph
   *     that have an indexed property with a specific value
   * @param getAllElements Function that returns an iterator of all the vertices or all the edges
   *     (i.e. full scan)
   * @return An iterator for all the vertices/edges for this step
   */
  private <ElementType extends Element> Iterator<? extends ElementType> elements(
      BiFunction<OGraph, Object[], Iterator<ElementType>> getElementsByIds,
      TriFunction<OGraph, OIndex, Iterator<Object>, Stream<? extends ElementType>>
          getElementsByIndex,
      Function<OGraph, Iterator<ElementType>> getAllElements,
// Path: driver/src/main/java/org/apache/tinkerpop/gremlin/orientdb/executor/OGremlinResult.java
// public class OGremlinResult {
// 
//   private OrientGraph graph;
//   OResult inner;
// 
//   public OGremlinResult(OrientGraph graph, OResult inner) {
//     this.graph = graph;
//     this.inner = inner;
//   }
// 
//   public <T> T getProperty(String name) {
//     Object value = inner.getProperty(name);
//     if (value instanceof Iterable) {
//       Spliterator spliterator = ((Iterable) value).spliterator();
//       value =
//           StreamSupport.stream(spliterator, false)
//               .map(
//                   (e) -> {
//                     if (e instanceof OResult) {
//                       return new OGremlinResult(this.graph, (OResult) e);
//                     } else {
//                       return e;
//                     }
//                   })
//               .collect(Collectors.toList());
//     }
//     return (T) value;
//   }
// 
//   public Optional<OrientVertex> getVertex() {
//     return inner.getVertex().map((v) -> graph.elementFactory().wrapVertex(v));
//   }
// 
//   public Optional<OrientEdge> getEdge() {
//     return inner.getEdge().map((v) -> graph.elementFactory().wrapEdge(v));
//   }
// 
//   public boolean isElement() {
//     return inner.isElement();
//   }
// 
//   public boolean isVertex() {
//     return inner.isVertex();
//   }
// 
//   public boolean isEdge() {
//     return inner.isEdge();
//   }
// 
//   public boolean isBlob() {
//     return inner.isBlob();
//   }
// 
//   public OResult getRawResult() {
//     return inner;
//   }
// }


// Path: driver/src/main/java/org/apache/tinkerpop/gremlin/orientdb/traversal/step/sideeffect/OrientGraphStep.java
import com.google.common.annotations.VisibleForTesting;
import com.orientechnologies.common.log.OLogManager;
import com.orientechnologies.orient.core.index.OIndex;
import com.orientechnologies.orient.core.index.OIndexManager;
import com.orientechnologies.orient.core.metadata.schema.OClass;
import java.util.*;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.stream.Stream;
import org.apache.tinkerpop.gremlin.orientdb.*;
import org.apache.tinkerpop.gremlin.orientdb.executor.OGremlinResult;
import org.apache.tinkerpop.gremlin.process.traversal.Compare;
import org.apache.tinkerpop.gremlin.process.traversal.Contains;
import org.apache.tinkerpop.gremlin.process.traversal.P;
import org.apache.tinkerpop.gremlin.process.traversal.step.HasContainerHolder;
import org.apache.tinkerpop.gremlin.process.traversal.step.map.GraphStep;
import org.apache.tinkerpop.gremlin.process.traversal.step.util.HasContainer;
import org.apache.tinkerpop.gremlin.structure.Edge;
import org.apache.tinkerpop.gremlin.structure.Element;
import org.apache.tinkerpop.gremlin.structure.T;
import org.apache.tinkerpop.gremlin.structure.Vertex;
import org.apache.tinkerpop.gremlin.structure.util.DefaultCloseableIterator;
import org.apache.tinkerpop.gremlin.structure.util.StringFactory;
import org.apache.tinkerpop.gremlin.util.function.TriFunction;
import org.apache.tinkerpop.gremlin.util.iterator.IteratorUtils;

package org.apache.tinkerpop.gremlin.orientdb.traversal.step.sideeffect;


public class OrientGraphStep<S, E extends Element> extends GraphStep<S, E>
    implements HasContainerHolder {

  private static final long serialVersionUID = 8141248670294067626L;

  private final List<HasContainer> hasContainers = new ArrayList<>();

  public OrientGraphStep(final GraphStep<S, E> originalGraphStep) {
    super(
        originalGraphStep.getTraversal(),
        originalGraphStep.getReturnClass(),
        originalGraphStep.isStartStep(),
        originalGraphStep.getIds());
    originalGraphStep.getLabels().forEach(this::addLabel);
    this.setIteratorSupplier(() -> (Iterator<E>) (isVertexStep() ? this.vertices() : this.edges()));
  }

  public boolean isVertexStep() {
    return Vertex.class.isAssignableFrom(this.returnClass);
  }

  private Iterator<? extends Vertex> vertices() {
    return elements(
        OGraph::vertices, OGraph::getIndexedVertices, OGraph::vertices, v -> v.getVertex().get());
  }

  private Iterator<? extends Edge> edges() {
    return elements(
        OGraph::edges, OGraph::getIndexedEdges, OGraph::edges, (e) -> e.getEdge().get());
  }

  /**
   * Gets an iterator over those vertices/edges that have the specific IDs wanted, or those that
   * have indexed properties with the wanted values, or failing that by just getting all of the
   * vertices or edges.
   *
   * @param getElementsByIds Function that will return an iterator over all the vertices/edges in
   *     the graph that have the specific IDs
   * @param getElementsByIndex Function that returns a stream of all the vertices/edges in the graph
   *     that have an indexed property with a specific value
   * @param getAllElements Function that returns an iterator of all the vertices or all the edges
   *     (i.e. full scan)
   * @return An iterator for all the vertices/edges for this step
   */
  private <ElementType extends Element> Iterator<? extends ElementType> elements(
      BiFunction<OGraph, Object[], Iterator<ElementType>> getElementsByIds,
      TriFunction<OGraph, OIndex, Iterator<Object>, Stream<? extends ElementType>>
          getElementsByIndex,
      Function<OGraph, Iterator<ElementType>> getAllElements,
      Function<OGremlinResult, ElementType> getElement) {