@Data
public class Modern {

  private final Graph graph;

  public Person marko, vadas, josh, peter;
  public Software lop, ripple, gremlin;

  public static Modern of(Graph graph) {
    Modern modern = new Modern(graph);
    modern.generateModern();
    return modern;
  }

  private void generateModern() {
    graph
        .addVertex(Person.of("marko", 29)).as(this::setMarko)
        .addVertex(Person.of("vadas", 27)).as(this::setVadas)
        .addVertex(Software.of("lop")).as("lop")
        .addVertex(Software.of("ripple")).as("ripple")
        .addVertex(Person.of("josh", 32)).as("josh")
        .addEdge(Created.of(1.0d), "ripple")
        .addEdge(Created.of(0.4d), "lop")
        .addVertex(Person.of("peter", 35)).as("peter")
        .addEdge(Created.of(0.2d), "lop");
    graph
        .addEdge(Knows.of(0.5d), marko, vadas)
        .addEdge(Knows.of(1.0d), marko, "josh")
        .addEdge(Created.of(0.4d), marko, "lop");

    josh = graph.get("josh", Person.class);
    peter = graph.get("peter", Person.class);

    lop = graph.get("lop", Software.class);
    ripple = graph.get("ripple", Software.class);
  }
} @Data
public class TheCrew {

  private final Graph graph;

  public Person marko, stephen, matthias, daniel;
  public Software tinkergraph, gremlin;

  public Develops markoDevelopsGremlin;

  public static TheCrew of(Graph graph) {
    TheCrew crew = new TheCrew(graph);
    crew.generateTheCrew();
    return crew;
  }

  public void generateTheCrew() {
    graph
        .addVertex(Software.of("tinkergraph")).as("tinkergraph")
        .addVertex(Software.of("gremlin")).as("gremlin")
        .addEdge(Traverses.of(), "tinkergraph");
    graph
        .addVertex(
            Person.of("marko",
                Location.of("san diego", 1997, 2001),
                Location.of("santa cruz", 2001, 2004),
                Location.of("brussels", 2004, 2005),
                Location.of("santa fe", 2005))).as("marko")
        .addEdge(Develops.of(2009), "gremlin").as(this::setMarkoDevelopsGremlin)
        .addEdge(Develops.of(2010), "tinkergraph")
        .addEdge(Uses.of(Proficient), "gremlin")
        .addEdge(Uses.of(Expert), "tinkergraph")
        .addVertex(
            Person.of("stephen",
                Location.of("centreville", 1990, 2000),
                Location.of("dulles", 2000, 2006),
                Location.of("purcellville", 2006))).as("stephen")
        .addEdge(Develops.of(2010), "gremlin")
        .addEdge(Develops.of(2011), "tinkergraph")
        .addEdge(Uses.of(Expert), "gremlin")
        .addEdge(Uses.of(Proficient), "tinkergraph")
        .addVertex(
            Person.of("matthias",
                Location.of("bremen", 2004, 2007),
                Location.of("baltimore", 2007, 2011),
                Location.of("oakland", 2011, 2014),
                Location.of("seattle", 2014))).as("matthias")
        .addEdge(Develops.of(2012), "gremlin")
        .addEdge(Uses.of(Competent), "gremlin")
        .addEdge(Uses.of(Competent), "tinkergraph")
        .addVertex(
            Person.of("daniel",
                Location.of("spremberg", 1982, 2005),
                Location.of("kaiserslautern", 2005, 2009),
                Location.of("aachen", 2009))).as("daniel")
        .addEdge(Uses.of(Expert), "gremlin")
        .addEdge(Uses.of(Competent), "tinkergraph");

    marko = graph.get("marko", Person.class);
    stephen = graph.get("stephen", Person.class);
    matthias = graph.get("matthias", Person.class);
    daniel = graph.get("daniel", Person.class);

    gremlin = graph.get("gremlin", Software.class);
    tinkergraph = graph.get("tinkergraph", Software.class);
  }

} @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode(of = {}, callSuper = true)
public class Develops extends Edge {

  /**
   * Go to the {@link Person} who develop the currently selected {@link Software}s.
   */
  public final static ToVertex Developers = traversal -> traversal
      .in(Label.of(Develops.class))
      .hasLabel(Label.of(Person.class));
  public final static ToVertex Softwares = traversal -> traversal
      .out(Label.of(Develops.class))
      .hasLabel(Label.of(Software.class));
  private Instant since;

  public static Develops of(Instant since) {
    return Develops.builder().since(since).build();
  }

  public static Develops of(int year) {
    return of(Location.year(year));
  }

  @Override
  protected List<Connection> connections() {
    return Connection.list(Person.class, Software.class);
  }
} @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode(of = {}, callSuper = true)
public class Knows extends Edge {

  /**
   * Go to the {@link Person}s you know, assuming you're currently selected.
   */
  public final static ToVertex KnowsWho = traversal -> traversal
      .outE(Label.of(Knows.class))
      .toV(Direction.IN);
  /**
   * Go to  to the {@link Person}s who know of you, assuming you're currently selected.
   */
  public final static ToVertex KnownBy = traversal -> traversal
      .inE(Label.of(Knows.class))
      .toV(Direction.OUT);

  private Double weight;

  private Instant since;

  public static Knows of(double weight) {
    return of(weight, null);
  }

  public static Knows of(Instant since) {
    return of(null, since);
  }

  public static Knows of(Double weight, Instant since) {
    return Knows.builder().weight(weight).since(since).build();
  }

  @Override
  protected List<Connection> connections() {
    return Connection.list(Person.class, Person.class);
  }

} @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Alias(label = "location")
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode(of = {}, callSuper = true)
public class Location extends Element {

  @OrderingKey
  @PropertyValue
  private String name;
  @OrderingKey
  private Instant startTime;
  private Instant endTime;

  public static Location of(String name, int startYear) {
    return of(name, year(startYear));
  }

  public static Location of(String name, Instant startTime) {
    return of(name, startTime, null);
  }

  public static Location of(String name, int startYear, int endYear) {
    return of(name, year(startYear), year(endYear));
  }

  public static Location of(String name, Instant startTime, Instant endTime) {
    return Location.builder().name(name).startTime(startTime).endTime(endTime).build();
  }

  private static Map<Integer, Instant> years = new HashMap<>();

  public static Instant year(int value) {
    Instant instant = years.get(value);
    if (instant != null) {
      return instant;
    }
    instant = LocalDate.parse(String.format("%s-01-01", value)).atStartOfDay()
        .toInstant(ZoneOffset.UTC);
    years.put(value, instant);
    return instant;
  }
} @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Alias(label = "person")
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode(of = {}, callSuper = true)
public class Person extends Vertex {

  public static ToVertex KnowsPeople = traversal -> traversal
      .out(Label.of(Knows.class))
      .hasLabel(Label.of(Person.class));

  @PrimaryKey
  private String name;

  @OrderingKey
  private int age;

  /**
   * Since the lambda object below is instance-specific, it needs to either have it's name start
   * with a {@code $} symbol, or be marked as {@code transient}. If you have a lot of such
   * instance-specific lambda fields, then you could ignore them all for the purposes of {@link
   * #equals(Object)} and {@link #hashCode()}, by including {@code of={}} in the {@link
   * EqualsAndHashCode} annotation above.
   */
  public final AnyTraversal $sameAge = g -> g.V()
      .hasLabel(Label.of(Knows.class))
      .has("age", age);

  /**
   * This is a multi-property of primitive types.
   */
  private Set<String> titles;

  /**
   * This is a multi-property of a vertex property, each of which has meta-properties of its own.
   */
  private List<Location> locations;

  public static Person of(String name, Location... locations) {
    return of(name, 0, locations);
  }

  public static Person of(String name, int age) {
    return of(name, age, (Location[]) null);
  }

  public static Person of(String name, int age, Location... locations) {
    return of(name, age, null, locations);
  }

  public static Person of(String name, String title, Location... locations) {
    return of(name, 0, title, locations);
  }

  public static Person of(String name, int age, String title, Location... locations) {
    Set<String> titleSet = null;
    if (title != null) {
      titleSet = new HashSet<>();
      Collections.addAll(titleSet, title);
    }
    List<Location> locationList = null;
    if (locations != null && locations.length > 0) {
      locationList = Arrays.asList(locations);
    }
    return of(name, age, titleSet, locationList);
  }

  public static Person of(String name, int age, Set<String> titles, List<Location> locations) {
    Person.PersonBuilder builder = Person.builder().name(name).age(age);
    if (locations != null) {
      builder.locations(locations);
    }
    if (titles != null) {
      builder.titles(titles);
    }
    return builder.build();
  }
} @Data
@Builder
@NoArgsConstructor
@AllArgsConstructor
@Accessors(fluent = true, chain = true)
@EqualsAndHashCode(of = {}, callSuper = true)
public class Uses extends Edge {

  /**
   * Go to the {@link Software}s used by the currently selected {@link Person}s.
   */
  public final static ToVertex Softwares = traversal -> traversal
      .outE(Label.of(Uses.class))
      .toV(Direction.IN);
  /**
   * Go to the {@link Person}s using the currently selected {@link Software}s.
   */
  public final static ToVertex People = traversal -> traversal
      .inE(Label.of(Uses.class))
      .toV(Direction.OUT);

  /**
   * An enum field whose name is used as the value of the "skill" property.
   */
  private Skill skill;

  public static Uses of(Skill skill) {
    return Uses.builder().skill(skill).build();
  }

  @Override
  protected List<Connection> connections() {
    return Connection.list(Person.class, Software.class);
  }

  public enum Skill {
    Novice, Beginner, Competent, Proficient, Expert,
  }

} public interface  Graph {

  /**
   * Add an given {@link Vertex} to the graph.
   */
  <V extends Vertex> Graph addVertex(V vertex);

  /**
   * Remove the given {@link Vertex} from the graph.
   */
  <V extends Vertex> Graph removeVertex(V vertex);

  /**
   * Add an {@link Edge} from the current vertex to the vertex referenced by the given alias.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, String toVertexAlias);

  /**
   * Add an {@link Edge} from the current vertex to the given {@link Vertex}
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, V toVertex);

  /**
   * Add an {@link Edge} from the given from and to vertices specified by their aliases.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, String fromVertexAlias, String toVertexAlias);

  /**
   * Add an {@link Edge} between the given vertices.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, V fromVertex, V toVertex);

  /**
   * Add an {@link Edge} form the aliased vertex to the given vertex.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, String fromVertexAlias, V toVertex);

  /**
   * Add an {@link Edge} from the given vertex to the aliased vertex.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, Vertex fromVertex, String toVertexAlias);

  /**
   * Add an {@link Edge} from the given vertex to the vertices returned by the {@link
   * AnyTraversal}.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, Vertex fromVertex, AnyTraversal toAnyTraversal);

  /**
   * Add an {@link Edge} from the given vertex to the vertices returned by the {@link
   * SubTraversal}s.
   */
  <E extends Edge,
      V extends Vertex> Graph addEdge(E edge, Vertex fromVertex, SubTraversal... toVertexQueries);

  /**
   * Add an {@link Edge} to the vertices returned by the given {@link AnyTraversal}.
   */
  <E extends Edge> Graph addEdge(E edge, AnyTraversal toAnyTraversal);

  /**
   * Add an {@link Edge} to the vertices returned by the given {@link SubTraversal}s.
   */
  @SuppressWarnings("rawtypes")
  <E extends Edge> Graph addEdge(E edge, SubTraversal... toVertexQueries);

  /**
   * Remove the given edge.
   */
  <E extends Edge> Graph removeEdge(E edge);

  /**
   * Get the {@link Element} tied to the given alias.
   *
   * This is a materialization of the element provided to this graph, and may not reflect what's
   * actually in the graph. To see the actual element, use the {@link Query} interface.
   */
  @SuppressWarnings({"PMD.ShortMethodName"})
  Graph as(String alias);

  /**
   * Supply the {@link Element} tied to the given consumer.
   *
   * This is a materialization of the element provided to this graph, and may not reflect what's
   * actually in the graph. To see the actual element, use the {@link Query} interface.
   */
  @SuppressWarnings({"PMD.ShortMethodName"})
  <E extends Element> Graph as(Consumer<E> consumer);

  /**
   * Get the last added {@link Element} in the form of the given type.
   *
   * This is a materialization of the element provided to this graph, and may not reflect what's
   * actually in the graph. To see the actual element, use the {@link Query} interface.
   */
  @SuppressWarnings({"PMD.ShortMethodName"})
  <E extends Element> E as(Class<E> elementType);

  /**
   * How {@link Should} the {@link Graph} try to add vertices and edges?
   */
  Graph should(Should should);

  /**
   * What is the {@link Should} mode currently set to?
   */
  Should should();

  /**
   * Should primitive required keys be allowed to have their primitive default values?
   */
  Graph defaultKeys(boolean allow);

  /**
   * Verify if the graph has the given feature.
   */
  boolean verify(HasFeature hasFeature);

  /**
   * Get the element of the given type stored against the given alias.
   */
  <E extends Element> E get(String alias, Class<E> elementType);

  /**
   * Return the associated {@link Query} instance.
   */
  Query query();

  /**
   * Drop the elements in the graph.
   */
  void drop();

  /**
   * Release the resources in the graph.
   */
  void close();

  /**
   * Reset the state of the graph, including any aliases, and element mappings.
   */
  void reset();

  /**
   * How should the graph add elements, considering that an element with that id or key might
   * already exist?
   */
  enum Should {
    /**
     * The default, which is to merge properties of the new and existing element, if any.
     */
    MERGE,
    /**
     * If it exists, replace all its (mutable) properties. Otherwise {@link #INSERT}.
     */
    REPLACE,
    /**
     * If the graph supports user ids, then {@link #INSERT}. Otherwise, {@link #MERGE}.
     */
    CREATE,
    /**
     * Add the element if and only if there is no existing element.
     */
    IGNORE,
    /**
     * Add the element, raising an exception if it already exists, and a key violation occurs.
     */
    INSERT;

    public String label() {
      return name().toLowerCase();
    }
  }

  /**
   * An interface that defines graph update events.
   */
  interface Observer {

    <V extends Vertex> void vertexAdded(V given, V stored);

    <V extends Vertex> void vertexRemoved(V given);

    <E extends Edge> void edgeAdded(E given, E stored);

    <E extends Edge> void edgeRemoved(E given);
  }

  /**
   * {@link Observer}s can register interest in the graph through this class.
   */
  @SuppressWarnings("PMD.ShortClassName")
  class O extends Observable<Observer> {

    private static final O INSTANCE = new O();

    public static O get() {
      return INSTANCE;
    }
  }
} @Data
@Slf4j
@RunWith(Parameterized.class)
public abstract class GraphTest {

  protected final Graph graph;
  @Parameterized.Parameter
  public Graph.Should should;

  protected GraphTest(Graph graph) {
    this.graph = graph;
  }

  @Parameterized.Parameters(name = "should({0})")
  public static Collection<Object[]> data() {
    return Arrays.asList(new Object[][] {
        {CREATE}, {MERGE}, {REPLACE}, {IGNORE}, {INSERT}
    });
  }

  @Before
  public void setUp() {
    this.graph.should(should);
    graph.drop();
  }

  @After
  public void tearDown() {
    graph.reset();
    graph.drop();
  }
} @SuppressWarnings({"rawtypes", "PMD.AvoidDuplicateLiterals"})
public interface Query {

  /**
   * Specify a traversal as an {@link AnyTraversal} deemed to be complete.
   *
   * @return this {@link Query}
   */
  @SuppressWarnings({"PMD.ShortMethodName"})
  Query by(AnyTraversal traversal);

  /**
   * Specify a chain of {@link SubTraversal}s that will be applied one in order.
   *
   * @return this {@link Query}
   */
  @SuppressWarnings({"PMD.ShortMethodName"})
  Query by(SubTraversal... subTraversals);

  /**
   * Define an alias for the object referenced in a {@link GraphTraversal#select} step.
   *
   * @return this {@link Query}
   */
  @SuppressWarnings({"PMD.ShortMethodName"})
  Query as(String alias, Class elementType);

  /**
   * Set the source of the query to either the vertex ({@link Source#V} or edge ({@link Source#E}}
   * graph.
   *
   * @return this {@link Query}
   */
  Query source(Source source);

  /**
   * Complete the query by retrieving the result as an object of the given element type.
   *
   * Typically, the @{link #type} represents an object element. However, it may also denote the type
   * of a property value.
   *
   * @return an object of the given type
   */
  <T> T one(Class<T> type);


  /**
   * Complete the query by retrieving the result as an optional object of the given element type.
   *
   * Typically, the @{link #type} represents an object element. However, it may also denote the type
   * of a property value.
   *
   * @return an optional object of the given type
   */
  <T> Optional<T> optional(Class<T> type);

  /**
   * Complete the query by retrieving the result as a list of objects of the given element type.
   *
   * Typically, the @{link #type} represents an object element. However, it may also denote the type
   * of a property value.
   *
   * @return the list of objects of the given type
   */
  <T> List<T> list(Class<T> type);

  /**
   * Complete the traversal specified by the query. This comes in handy when drop steps are
   * involved.
   */
  void none();

  /**
   * Complete the traversal by selecting the aliases specified by {@link Query#as} into their
   * corresponding types of object.
   */
  Selections select();

  /**
   * Release the resources held by the underlying graph system.
   */
  void close();

  /**
   * Disable the specified types of steps.
   */
  void disable(StepType... stepTypes);

  /**
   * Enforce the given mode, by disabling the steps disallowed by it.
   */
  default void enforce(Mode mode) {
    disable(mode.get());
  }

  /**
   * {@link Source} lets you specify whether the starting point of the traversal.
   */
  enum Source implements Supplier<Function<GraphTraversalSource, GraphTraversal>> {
    /**
     * Specifies a traversal of the vertex graph.
     */
    V {
      @Override
      public Function<GraphTraversalSource, GraphTraversal> get() {
        return g -> g.V();
      }
    },
    /**
     * Specifies a traversal of the edge graph.
     */
    E {
      @Override
      public Function<GraphTraversalSource, GraphTraversal> get() {
        return g -> g.E();
      }
    };
  }

  /**
   * {@link Mode} dictates which steps are permitted (or not) by the {@link Query}.
   */
  enum Mode implements Supplier<StepType[]> {
    /**
     * Block steps that can change the graph in this mode.
     */
    READ_ONLY {
      @Override
      public StepType[] get() {
        return new StepType[] {StepType.DROP, StepType.ADDV, StepType.ADDE, StepType.PROPERTY};
      }
    },
    /**
     * Allow both read and write queries in this mode.
     */
    READ_WRITE {
      @Override
      public StepType[] get() {
        return new StepType[] {};
      }
    };
  }

  /**
   * {@link StepType} outlines the steps that the {@link Query} may have to filter for.
   */
  enum StepType {
    DROP, ADDV, ADDE, PROPERTY
  }

  /**
   * Exceptions that might come up during the lifecycle of a query.
   */
  class Exceptions {

    private Exceptions() {}

    public static ClassCastException invalidObjectType(Object element, Class objectClass) {
      return new ClassCastException(
          String.format("The resulting object %s is not of the desired %s type",
              element, name(objectClass)));
    }

    public static IllegalArgumentException unexpectedMultipleResults(Query query) {
      return new IllegalArgumentException(
          String.format("Must specify a traversal that returns no more than one element: %s", query));
    }

    public static IllegalArgumentException unexpectedNoResults(Query query) {
      return new IllegalArgumentException(
          String.format("Must specify a traversal that returns at least one element: %s", query));
    }

    public static IllegalArgumentException noTraversalSpecified(Query query) {
      return new IllegalArgumentException(
          String.format("Must specify a traversal before completing it: %s", query));
    }

    public static IllegalArgumentException disabledStepQueried(List<StepType> stepTypes) {
      return new IllegalArgumentException(
          String.format("Must specify a traversal without these disabled steps: %s", stepTypes));
    }
  }

} @RequiredArgsConstructor(staticName = "of")
@EqualsAndHashCode(callSuper = true, exclude = {"classes"})
@SuppressWarnings({"PMD.ShortMethodName", "rawtypes"})
public class Selections extends ArrayList<Selections.Selection> {

  public static final long serialVersionUID = 1L;
  private Map<String, Class> classes = new HashMap<>();

  public static Selections of(Selection... selectionList) {
    Selections selections = Selections.of();
    selections.addAll(Arrays.asList(selectionList));
    return selections;
  }

  public void as(String alias, Class clazz) {
    classes.put(alias, clazz);
  }

  public Class as(String alias) {
    return classes.get(alias);
  }

  @RequiredArgsConstructor(staticName = "of")
  public static class Selection extends HashMap<String, Object> {

    public static final long serialVersionUID = 1L;


    public Selection add(String alias, Object object) {
      this.put(alias, object);
      return this;
    }

    @SuppressWarnings("unchecked")
    public <T> T as(String alias, Class<T> elementType) {
      return (T) this.get(alias);
    }
  }
} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
@SuppressWarnings({"unchecked", "rawtypes", "PMD.TooManyStaticImports"})
public class AddV implements AnyTraversal {

  private final org.apache.tinkerpop.gremlin.object.structure.Element element;

  @Override
  @SneakyThrows
  public GraphTraversal<Element, Element> apply(GraphTraversalSource g) {
    GraphTraversal traversal = g.addV(element.label());
    if (element.id() != null && HasFeature.Verifier.of(g)
        .verify(supportsUserSuppliedIds(element))) {
      traversal.property(T.id, element.id());
    }
    for (Field field : keyFields(element)) {
      String key = propertyKey(field);
      Object value = propertyValue(field, element);
      if (isMissing(value)) {
        throw org.apache.tinkerpop.gremlin.object.structure.Element.Exceptions.requiredKeysMissing(
            element.getClass(), key);
      }
      traversal.property(key, value);
    }
    return traversal;
  }
} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
public class Count implements ElementTo.Long {

  @Override
  public GraphTraversal<Element, Long> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.count();
  }
} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
public class HasId implements ElementTo.Element {

  private final org.apache.tinkerpop.gremlin.object.structure.Element element;

  @Override
  public GraphTraversal<Element, Element> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.hasId(Keys.id(element));
  }

} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
public class HasLabel implements ElementTo.Element {

  private final String label;

  @SuppressWarnings("PMD.ShortMethodName")
  public static HasLabel of(org.apache.tinkerpop.gremlin.object.structure.Element element) {
    return of(element.getClass());
  }

  @SuppressWarnings("PMD.ShortMethodName")
  public static HasLabel of(
      Class<? extends org.apache.tinkerpop.gremlin.object.structure.Element> elementType) {
    return of(org.apache.tinkerpop.gremlin.object.reflect.Label.of(elementType));
  }

  @Override
  public GraphTraversal<Element, Element> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.hasLabel(label);
  }

} @Data
@Builder
@AllArgsConstructor
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
@SuppressWarnings("PMD.TooManyStaticImports")
public class HasKeys implements ElementTo.Element {

  private final org.apache.tinkerpop.gremlin.object.structure.Element element;

  private Class<? extends Annotation> keyType;

  @SuppressWarnings("PMD.ShortMethodName")
  public static HasKeys of(org.apache.tinkerpop.gremlin.object.structure.Element element,
      Class<? extends Annotation> keyType) {
    return HasKeys.builder()
        .element(element)
        .keyType(keyType)
        .build();
  }

  @Override
  @SneakyThrows
  public GraphTraversal<Element, Element> apply(GraphTraversal<Element, Element> traversal) {
    traversal.hasLabel(element.label());
    List<Field> fields;
    if (keyType != null) {
      if (is(keyType, PrimaryKey.class)) {
        fields = primaryKeyFields(element);
      } else if (is(keyType, OrderingKey.class)) {
        fields = orderingKeyFields(element);
      } else {
        throw org.apache.tinkerpop.gremlin.object.structure.Element.Exceptions
            .invalidAnnotationType(keyType);
      }
    } else {
      fields = keyFields(element);
    }
    for (Field field : fields) {
      String key = propertyKey(field);
      Object value = propertyValue(field, element);
      if (isMissing(value)) {
        throw org.apache.tinkerpop.gremlin.object.structure.Element.Exceptions.requiredKeysMissing(
            element.getClass(), key);
      }
      traversal.has(key, value);
    }
    return traversal;
  }
} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "by")
public class Order implements ElementTo.Element {

  private final String property;

  @Override
  public GraphTraversal<Element, Element> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.order().by(property);
  }

} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
@SuppressWarnings({"PMD.ShortClassName"})
public class Out implements SubTraversal<Element, Vertex> {

  private final String label;

  @SuppressWarnings("PMD.ShortMethodName")
  public static Out of(org.apache.tinkerpop.gremlin.object.structure.Element element) {
    return of(org.apache.tinkerpop.gremlin.object.reflect.Label.of(element));
  }

  @SuppressWarnings("PMD.ShortMethodName")
  public static Out of(
      Class<? extends org.apache.tinkerpop.gremlin.object.structure.Element> elementType) {
    return of(org.apache.tinkerpop.gremlin.object.reflect.Label.of(elementType));
  }

  @Override
  public GraphTraversal<Element, Vertex> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.out(label);
  }

} @Data
@Accessors(fluent = true, chain = true)
@RequiredArgsConstructor(staticName = "of")
public class Range implements ElementTo.Element {

  private final int low;
  private final int high;

  @Override
  public GraphTraversal<Element, Element> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.range(low, high);
  }
} @Data
@Accessors(fluent = true, chain = true)
public class Values implements ElementTo.Any {

  private final String[] propertyKeys;

  @SuppressWarnings("PMD.ShortMethodName")
  public static Values of(org.apache.tinkerpop.gremlin.object.structure.Element element) {
    return of(Properties.names(element));
  }

  @SuppressWarnings("PMD.ShortMethodName")
  public static Values of(List<String> propertyKeys) {
    return of(propertyKeys.toArray(new String[] {}));
  }

  @SuppressWarnings("PMD.ShortMethodName")
  public static Values of(String... propertyKeys) {
    return new Values(propertyKeys);
  }

  @Override
  public GraphTraversal<Element, Object> apply(GraphTraversal<Element, Element> traversal) {
    return traversal.values(propertyKeys).dedup();
  }

} public static Instant year(int value) {
  Instant instant = years.get(value);
  if (instant != null) {
    return instant;
  }
  instant = LocalDate.parse(String.format("%s-01-01", value)).atStartOfDay()
      .toInstant(ZoneOffset.UTC);
  years.put(value, instant);
  return instant;
} static HasFeature supportsGraphAddEdge() {
  return features -> features.edge().supportsAddEdges();
} static HasFeature supportsUserSuppliedIds(Element element) {
  return features -> (isVertex(element) ? features.vertex() : features.edge())
      .supportsUserSuppliedIds();
} @RequiredArgsConstructor(staticName = "of")
public static class Selection extends HashMap<String, Object> {

  public static final long serialVersionUID = 1L;


  public Selection add(String alias, Object object) {
    this.put(alias, object);
    return this;
  }

  @SuppressWarnings("unchecked")
  public <T> T as(String alias, Class<T> elementType) {
    return (T) this.get(alias);
  }
}
import org.apache.tinkerpop.gremlin.object.graphs.Modern;
import org.apache.tinkerpop.gremlin.object.graphs.TheCrew;
import org.apache.tinkerpop.gremlin.object.edges.Develops;
import org.apache.tinkerpop.gremlin.object.edges.Knows;
import org.apache.tinkerpop.gremlin.object.vertices.Location;
import org.apache.tinkerpop.gremlin.object.vertices.Person;
import org.apache.tinkerpop.gremlin.object.edges.Uses;
import org.apache.tinkerpop.gremlin.object.structure.Graph;
import org.apache.tinkerpop.gremlin.object.structure.GraphTest;
import org.apache.tinkerpop.gremlin.object.traversal.Query;
import org.apache.tinkerpop.gremlin.object.traversal.Selections;
import org.apache.tinkerpop.gremlin.object.traversal.library.AddV;
import org.apache.tinkerpop.gremlin.object.traversal.library.Count;
import org.apache.tinkerpop.gremlin.object.traversal.library.HasId;
import org.apache.tinkerpop.gremlin.object.traversal.library.HasLabel;
import org.apache.tinkerpop.gremlin.object.traversal.library.HasKeys;
import org.apache.tinkerpop.gremlin.object.traversal.library.Order;
import org.apache.tinkerpop.gremlin.object.traversal.library.Out;
import org.apache.tinkerpop.gremlin.object.traversal.library.Range;
import org.apache.tinkerpop.gremlin.object.traversal.library.Values;
import org.apache.tinkerpop.gremlin.structure.T;
import org.junit.Test;
import java.time.Instant;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.extern.slf4j.Slf4j;
import static org.apache.tinkerpop.gremlin.object.vertices.Location.year;
import static org.apache.tinkerpop.gremlin.object.edges.Uses.Skill.Competent;
import static org.apache.tinkerpop.gremlin.object.edges.Uses.Skill.Expert;
import static org.apache.tinkerpop.gremlin.object.edges.Uses.Skill.Proficient;
import static org.apache.tinkerpop.gremlin.object.structure.HasFeature.supportsGraphAddEdge;
import static org.apache.tinkerpop.gremlin.object.structure.HasFeature.supportsUserSuppliedIds;
import static org.apache.tinkerpop.gremlin.object.traversal.Selections.Selection;
import static org.apache.tinkerpop.gremlin.process.traversal.Order.incr;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.junit.Assert.assertTrue;
import static org.junit.Assert.fail;
/*
 * 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.
 */
package org.apache.tinkerpop.gremlin.object;





/**
 * This acts as the base of all provider specific implementations of the {@link Graph} and {@link
 * Query} interfaces. It uses the {@link Modern} and {@link TheCrew} factories to create sample
 * graphs, and runs a gauntlet of queries against them.
 *
 * <p>
 * The providers of the object graph are expected to extend this class, and pass the
 * implementation-defined instances of the {@link Graph} and {@link Query} to this class.
 *
 * @author Karthick Sankarachary (http://github.com/karthicks)
 */
@Data
@Slf4j
@EqualsAndHashCode(callSuper = false)
public abstract class ObjectGraphTest extends GraphTest {

  protected Query query;

  protected ObjectGraphTest(Graph graph, Query query) {
    super(graph);
    this.query = query;
  }

  @Test
  public void testGetFirstNames() {
    Modern modern = Modern.of(graph);
    List<String> names = query
        .by(g -> g.V().values("name"))
        .list(String.class);
    assertNotNull(names);
    assertTrue(names.contains(modern.vadas.name()));
    assertTrue(names.contains(modern.marko.name()));
  }

  @Test
  public void testGetKnownPeopleNames() {
    Modern modern = Modern.of(graph);
    List<String> names = query
        .by(Person.KnowsPeople, Values.of("name"))
        .list(String.class);
    assertNotNull(names);
    Collections.sort(names);
    assertEquals(Arrays.asList(modern.josh.name(), modern.vadas.name()), names);
  }

  @Test
  public void testDoesNotKnowPeople() {
    Modern modern = Modern.of(graph);
    List<Person> friends = query
        .by(modern.vadas.s("name"), Person.KnowsPeople)
        .list(Person.class);
    assertNotNull(friends);
    assertTrue(friends.isEmpty());
  }

  @Test
  public void testKnowsSomePeople() {
    Modern modern = Modern.of(graph);
    List<Person> friends = query
        .by(HasKeys.of(modern.marko), Person.KnowsPeople)
        .list(Person.class);
    assertNotNull(friends);
    Collections.sort(friends);
    assertEquals(Arrays.asList(modern.josh, modern.vadas), friends);
  }

  @Test
  public void testGetRangeOfPeople() {
    Modern modern = Modern.of(graph);
    List<Person> friends = query
        .by(HasLabel.of(Person.class), Range.of(0, 5))
        .list(Person.class);
    assertNotNull(friends);
    Collections.sort(friends);
    assertEquals(Arrays.asList(modern.josh, modern.marko, modern.peter, modern.vadas), friends);
  }

  @Test
  public void testFindGremlinDevelopers() {
    TheCrew crew = TheCrew.of(graph);
    List<Person> friends = query
        .by(HasKeys.of(crew.gremlin), Develops.Developers, Order.by("name"))