| /* |
| * 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.Vertex; |
| |
| import java.io.Serializable; |
| import java.util.Optional; |
| import java.util.Set; |
| import java.util.function.BiConsumer; |
| import java.util.function.BiFunction; |
| import java.util.function.BinaryOperator; |
| import java.util.function.Supplier; |
| import java.util.function.UnaryOperator; |
| |
| /** |
| * A {@link Traversal} can maintain global sideEffects. |
| * Unlike {@link Traverser} "sacks" which are local sideEffects, TraversalSideEffects are accessible by all {@link Traverser} instances within the {@link Traversal}. |
| * |
| * @author Marko A. Rodriguez (http://markorodriguez.com) |
| */ |
| public interface TraversalSideEffects extends Cloneable, Serializable { |
| |
| public static final String SIDE_EFFECTS = "gremlin.traversal.sideEffects"; |
| |
| /** |
| * Set the specified key to the specified value. |
| * If a {@link java.util.function.Supplier} is provided, it is NOT assumed to be a supplier as set by registerSupplier(). |
| * |
| * @param key the key |
| * @param value the value |
| */ |
| public void set(final String key, final Object value); |
| |
| /** |
| * Get the sideEffect associated with the provided key. |
| * If the sideEffect contains an object for the key, return it in an {@link Optional}. |
| * Else if the sideEffect has a registered {@link java.util.function.Supplier} for that key, generate the object, store the object in the sideEffects, and return it. |
| * |
| * @param key the key to get the value for |
| * @param <V> the type of the value to retrieve |
| * @return the value associated with key |
| * @throws IllegalArgumentException if the key does not reference an object or a registered supplier. |
| */ |
| public <V> Optional<V> get(final String key) throws IllegalArgumentException; |
| |
| /** |
| * Remove both the value and registered {@link java.util.function.Supplier} associated with provided key. |
| * |
| * @param key the key of the value and registered supplier to remove |
| */ |
| public void remove(final String key); |
| |
| /** |
| * The keys of the sideEffect which includes registered {@link java.util.function.Supplier} keys. |
| * In essence, that which is possible to get(). |
| * |
| * @return the keys of the sideEffect |
| */ |
| public Set<String> keys(); |
| |
| //////////// |
| |
| /** |
| * Register a {@link java.util.function.Supplier} with the provided key. |
| * When sideEffects get() are called, if no object exists and there exists a registered supplier for the key, the object is generated. |
| * Registered suppliers are used for the lazy generation of sideEffect data. |
| * |
| * @param key the key to register the supplier with |
| * @param supplier the supplier that will generate an object when get() is called if it hasn't already been created |
| */ |
| public void registerSupplier(final String key, final Supplier supplier); |
| |
| /** |
| * Get the registered {@link java.util.function.Supplier} associated with the specified key. |
| * |
| * @param key the key associated with the supplier |
| * @param <V> The object type of the supplier |
| * @return A non-empty optional if the supplier exists |
| */ |
| public <V> Optional<Supplier<V>> getRegisteredSupplier(final String key); |
| |
| /** |
| * A helper method to register a {@link java.util.function.Supplier} if it has not already been registered. |
| * |
| * @param key the key of the supplier to register |
| * @param supplier the supplier to register if the key has not already been registered |
| */ |
| public default void registerSupplierIfAbsent(final String key, final Supplier supplier) { |
| if (!this.getRegisteredSupplier(key).isPresent()) |
| this.registerSupplier(key, supplier); |
| } |
| |
| /** |
| * Set the initial value of each {@link Traverser} "sack" along with the operators for splitting and merging sacks. |
| * If no split operator is provided, then a direct memory copy is assumed (this is typically good for primitive types and strings). |
| * If no merge operator is provided, then traversers with sacks will not be merged. |
| * |
| * @param initialValue the initial value supplier of the traverser sack |
| * @param splitOperator the split operator for splitting traverser sacks |
| * @param mergeOperator the merge operator for merging traverser sacks |
| * @param <S> the sack type |
| */ |
| public <S> void setSack(final Supplier<S> initialValue, final UnaryOperator<S> splitOperator, final BinaryOperator<S> mergeOperator); |
| |
| /** |
| * If sacks are enabled, get the initial value of the {@link Traverser} sack. |
| * If its not enabled, then <code>null</code> is returned. |
| * |
| * @param <S> the sack type |
| * @return the supplier of the initial value of the traverser sack |
| */ |
| public <S> Supplier<S> getSackInitialValue(); |
| |
| /** |
| * If sacks are enabled and a split operator has been specified, then get it (else get <code>null</code>). |
| * The split operator is used to split a sack when a bifurcation in a {@link Traverser} happens. |
| * |
| * @param <S> the sack type |
| * @return the operator for splitting a traverser sack |
| */ |
| public <S> UnaryOperator<S> getSackSplitter(); |
| |
| /** |
| * If sacks are enabled and a merge function has been specified, then get it (else get <code>null</code>). |
| * The merge function is used to merge two sacks when two {@link Traverser}s converge. |
| * |
| * @param <S> the sack type |
| * @return the operator for merging two traverser sacks |
| */ |
| public <S> BinaryOperator<S> getSackMerger(); |
| |
| /** |
| * If the sideEffect contains an object associated with the key, return it. |
| * Else if a "with" supplier exists for the key, generate the object, store it in the sideEffects and return the object. |
| * Else use the provided supplier to generate the object, store it in the sideEffects and return the object. |
| * Note that if the orCreate supplier is used, it is NOT registered as a {@link java.util.function.Supplier}. |
| * |
| * @param key the key of the object to get |
| * @param orCreate if the object doesn't exist as an object or suppliable object, then generate it with the specified supplier |
| * @param <V> the return type of the object |
| * @return the object that is either retrieved, generated, or supplier via orCreate |
| */ |
| public default <V> V getOrCreate(final String key, final Supplier<V> orCreate) { |
| final Optional<V> optional = this.get(key); |
| if (optional.isPresent()) |
| return optional.get(); |
| final Optional<Supplier<V>> with = this.getRegisteredSupplier(key); |
| if (with.isPresent()) { |
| final V v = with.get().get(); |
| this.set(key, v); |
| return v; |
| } else { |
| final V v = orCreate.get(); |
| this.set(key, v); |
| return v; |
| } |
| } |
| |
| //////////// |
| |
| public default <V> void forEach(final BiConsumer<String, V> biConsumer) { |
| this.keys().forEach(key -> biConsumer.accept(key, this.<V>get(key).get())); |
| } |
| |
| /** |
| * In a distributed {@link org.apache.tinkerpop.gremlin.process.computer.GraphComputer} traversal, the sideEffects of the traversal are not a single object within a single JVM. |
| * Instead, the sideEffects are distributed across the graph and the pieces are stored on the computing vertices. |
| * This method is necessary to call when the {@link Traversal} is processing the {@link Traverser}s at a particular {@link org.apache.tinkerpop.gremlin.structure.Vertex}. |
| * |
| * @param vertex the vertex where the traversal is currently executing. |
| */ |
| public void setLocalVertex(final Vertex vertex); |
| |
| /** |
| * Cloning is used to duplicate the sideEffects typically in OLAP environments. |
| * |
| * @return The cloned sideEffects |
| */ |
| @SuppressWarnings("CloneDoesntDeclareCloneNotSupportedException") |
| public TraversalSideEffects clone(); |
| |
| /** |
| * Add the current {@link TraversalSideEffects} data and suppliers to the provided {@link TraversalSideEffects}. |
| * |
| * @param sideEffects the sideEffects to add this traversal's sideEffect data to. |
| */ |
| public void mergeInto(final TraversalSideEffects sideEffects); |
| |
| public static class Exceptions { |
| |
| public static IllegalArgumentException sideEffectKeyCanNotBeEmpty() { |
| return new IllegalArgumentException("Side effect key can not be the empty string"); |
| } |
| |
| public static IllegalArgumentException sideEffectKeyCanNotBeNull() { |
| return new IllegalArgumentException("Side effect key can not be null"); |
| } |
| |
| public static IllegalArgumentException sideEffectValueCanNotBeNull() { |
| return new IllegalArgumentException("Side effect value can not be null"); |
| } |
| |
| public static UnsupportedOperationException dataTypeOfSideEffectValueNotSupported(final Object val) { |
| return new UnsupportedOperationException(String.format("Side effect value [%s] is of type %s is not supported", val, val.getClass())); |
| } |
| } |
| } |