/* * 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.process.traversal; import org.apache.tinkerpop.gremlin.structure.util.Attachable; import java.io.Serializable; import java.util.Set; import java.util.function.Function; /** * A {@code Traverser} represents the current state of an object flowing through a {@link Traversal}. * A traverser maintains a reference to the current object, a traverser-local "sack", a traversal-global sideEffect, * a bulk count, and a path history. * <p/> * Different types of traversers can exist depending on the semantics of the traversal and the desire for * space/time optimizations of the developer. * * @author Marko A. Rodriguez (http://markorodriguez.com) */ public interface Traverser<T> extends Serializable, Comparable<Traverser<T>>, Cloneable { /** * Get the object that the traverser is current at. * * @return The current object of the traverser */ public T get(); /** * Get the sack local sack object of this traverser. * * @param <S> the type of the sack object * @return the sack object */ public <S> S sack(); /** * Set the traversers sack object to the provided value ("sack the value"). * * @param object the new value of the traverser's sack * @param <S> the type of the object */ public <S> void sack(final S object); /** * Get the current path of the traverser. * * @return The path of the traverser */ public Path path(); /** * Get the object associated with the specified step-label in the traverser's path history. * * @param stepLabel the step-label in the path to access * @param <A> the type of the object * @return the object associated with that path label (if more than one object occurs at that step, a list is returned) */ public default <A> A path(final String stepLabel) { return this.path().get(stepLabel); } public default <A> A path(final Pop pop, final String stepLabel) { return this.path().get(pop, stepLabel); } /** * Return the number of times the traverser has gone through a looping section of a traversal. * * @return The number of times the traverser has gone through a loop */ public int loops(); /** * A traverser may represent a grouping of traversers to allow for more efficient data propagation. * * @return the number of traversers represented in this traverser. */ public long bulk(); /** * Get a particular value from the side-effects of the traverser (thus, traversal). * * @param sideEffectKey the key of the value to get from the sideEffects * @param <A> the type of the returned object * @return the object in the sideEffects of the respective key */ public default <A> A sideEffects(final String sideEffectKey) throws IllegalArgumentException { return this.asAdmin().getSideEffects().<A>get(sideEffectKey); } /** * Add a particular value to the respective side-effect of the traverser (thus, traversal). * * @param sideEffectKey the key of the value to set int the sideEffects * @param sideEffectValue the value to set for the sideEffect key */ public default void sideEffects(final String sideEffectKey, final Object sideEffectValue) throws IllegalArgumentException { this.asAdmin().getSideEffects().add(sideEffectKey, sideEffectValue); } /** * If the underlying object of the traverser is comparable, compare it with the other traverser. * * @param other the other traverser that presumably has a comparable internal object * @return the comparison of the two objects of the traversers * @throws ClassCastException if the object of the traverser is not comparable */ @Override public default int compareTo(final Traverser<T> other) throws ClassCastException { return ((Comparable) this.get()).compareTo(other.get()); } /** * Typecast the traverser to a "system traverser" so {@link Traverser.Admin} methods can be accessed. * This is used as a helper method to avoid the awkwardness of <code>((Traverser.Administrative)traverser)</code>. * The default implementation simply returns "this" type casted to {@link Traverser.Admin}. * * @return The type-casted traverser */ public default Admin<T> asAdmin() { return (Admin<T>) this; } /** * Traverser cloning is important when splitting a traverser at a bifurcation point in a traversal. */ @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException") public Traverser<T> clone(); /** * The methods in System.Traverser are useful to underlying Step and Traversal implementations. * They should not be accessed by the user during lambda-based manipulations. */ public interface Admin<T> extends Traverser<T>, Attachable<T> { public static final String HALT = "halt"; /** * When two traversers are have equality with each other, then they can be merged. * This method is used to merge the traversers into a single traverser. * This is used for optimization where instead of enumerating all traversers, they can be counted. * * @param other the other traverser to merge into this traverser. Once merged, the other can be garbage collected. */ public void merge(final Admin<?> other); /** * Generate a child traverser of the current traverser for current as step and new object location. * The child has the path history, future, and loop information of the parent. * The child extends that path history with the current as and provided R-object. * * @param r The current object of the child * @param step The step yielding the split * @param <R> The current object type of the child * @return The split traverser */ public <R> Admin<R> split(final R r, final Step<T, R> step); /** * Generate a sibling traverser of the current traverser with a full copy of all state within the sibling. * * @return The split traverser */ public Admin<T> split(); public void addLabels(final Set<String> labels); /** * Drop all path information not associated with specified labels. * This is an optimization method that allows a traverser to save memory and increase the likelihood of bulking. * * @param labels the labels to keep path information for. */ public void keepLabels(final Set<String> labels); /** * Drop all path information associated with specified labels. * This is an optimization method that allows a traverser to save memory and increase the likelihood of bulking. * * @param labels the labels to drop path information for. */ public void dropLabels(final Set<String> labels); /** * Drop the path of the traverser. * This is an optimization method that allows a traverser to save memory and increase the likelihood of bulking. */ public void dropPath(); /** * Set the current object location of the traverser. * * @param t The current object of the traverser */ public void set(final T t); /** * Increment the number of times the traverser has gone through a looping section of traversal. * The step label is important to create a stack of loop counters when within a nested context. * If the provided label is not the same as the current label on the stack, add a new loop counter. * If the provided label is the same as the current label on the stack, increment the loop counter. * * @param stepLabel the label of the step that is doing the incrementing */ public void incrLoops(final String stepLabel); /** * Set the number of times the traverser has gone through a loop back to 0. * When a traverser exits a looping construct, this method should be called. * In a nested loop context, the highest stack loop counter should be removed. */ public void resetLoops(); /** * Get the step id of where the traverser is located. * This is typically used in multi-machine systems that require the movement of * traversers between different traversal instances. * * @return The future step for the traverser */ public String getStepId(); /** * Set the step id of where the traverser is located. * If the future is {@link Traverser.Admin#HALT}, then {@link Traverser.Admin#isHalted()} is true. * * @param stepId The future step of the traverser */ public void setStepId(final String stepId); /** * If the traverser has "no future" then it is done with its lifecycle. * This does not mean that the traverser is "dead," only that it has successfully passed through a * {@link Traversal}. * * @return Whether the traverser is done executing or not */ public default boolean isHalted() { return getStepId().equals(HALT); } /** * Set the number of traversers represented by this traverser. * * @param count the number of traversers */ public void setBulk(final long count); /** * Prepare the traverser for migration across a JVM boundary. * * @return The deflated traverser */ public Admin<T> detach(); /** * Regenerate the detached traverser given its location at a particular vertex. * * @param method The method by which to attach a {@code Traverser} to an vertex. * @return The inflated traverser */ @Override public T attach(final Function<Attachable<T>, T> method); /** * Set the sideEffects of the {@link Traversal}. Given that traversers can move between machines, * it may be important to re-set this when the traverser crosses machine boundaries. * * @param sideEffects the sideEffects of the traversal. */ public void setSideEffects(final TraversalSideEffects sideEffects); /** * Get the sideEffects associated with the traversal of the traverser. * * @return the traversal sideEffects of the traverser */ public TraversalSideEffects getSideEffects(); /** * Get the tags associated with the traverser. * Tags are used to categorize historic behavior of a traverser. * The returned set is mutable. * * @return the set of tags associated with the traverser. */ public Set<String> getTags(); } }