package com.tinkerpop.blueprints;

import com.tinkerpop.blueprints.strategy.GraphStrategy;

import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.UnaryOperator;

/**
 * @author Stephen Mallette (http://stephen.genoprime.com)
 */
public interface Strategy {
    public void set(final Optional<? extends GraphStrategy> strategy);
    public Optional<? extends GraphStrategy> getGraphStrategy();

    /**
     * If a {@link Strategy} is present, then return a {@link GraphStrategy} function that takes the function of the
     * Blueprints implementation denoted by {@code T} as an argument and returns back a function with {@code T}. If
     * no {@link Strategy} is present then it simply returns the {@code impl} as the default.
     *
     * @param f a function to execute if a {@link GraphStrategy} is present.
     * @param impl the base implementation of an operation that does something in a Blueprints implementation.
     * @return a function that will be applied in the Blueprints implementation
     */
    public default <T> T compose(final Function<GraphStrategy, UnaryOperator<T>> f, final T impl) {
        return getGraphStrategy().isPresent() ? f.apply(getGraphStrategy().get()).apply(impl) : impl;
    }

    public static class Context<T> {
        private final Graph g;
        private final Map<String,Object> environment;
        private final T current;

        public Context(final Graph g, final T current) {
            this(g, current, Optional.empty());
        }

        public Context(final Graph g, final T current, final Optional<Map<String,Object>> environment) {
            if (null == g)
                throw new IllegalArgumentException("g");

            this.g = g;
            this.current = current;
            this.environment = environment.orElse(new HashMap<>());
        }

        public T getCurrent() {
            return current;
        }

        public Graph getGraph() {
            return g;
        }

        public Map<String, Object> getEnvironment() {
            return Collections.unmodifiableMap(environment);
        }
    }

    public static class Simple implements Strategy {
        private Optional<? extends GraphStrategy> strategy;

        @Override
        public void set(final Optional<? extends GraphStrategy> strategy) {
            this.strategy = strategy;
        }

        @Override
        public Optional<? extends GraphStrategy> getGraphStrategy() {
            return strategy;
        }
    }

    public static class None implements Strategy {
        @Override
        public void set(final Optional<? extends GraphStrategy> strategy) {
            throw new UnsupportedOperationException("Strategy is not supported by this implementation");
        }

        @Override
        public Optional<GraphStrategy> getGraphStrategy() {
            return Optional.empty();
        }
    }

    public static class Local implements Strategy {
        private ThreadLocal<Optional<? extends GraphStrategy>> strategy = new ThreadLocal<Optional<? extends GraphStrategy>>(){
            @Override
            protected Optional<GraphStrategy> initialValue() {
                return Optional.empty();
            }
        };

        @Override
        public void set(final Optional<? extends GraphStrategy> strategy) {
            this.strategy.set(strategy);
        }

        @Override
        public Optional<? extends GraphStrategy> getGraphStrategy() {
            return this.strategy.get();
        }
    }
}

@@ -159,24 +159,50 @@
 a.util.Map;%0A
+import java.util.Objects;%0A
 import java.
@@ -298,16 +298,100 @@
 r;%0A%0A/**%0A
+ * A holder of a %7B@link GraphStrategy%7D object owned by a %7B@link Graph%7D instance.%0A *%0A
  * @auth
@@ -477,78 +477,324 @@
 y %7B%0A
-    public void set(final Optional%3C? extends GraphStrategy%3E strategy);
+%0A    /**%0A     * Set the %7B@link GraphStrategy%7D to utilized in the various Blueprints methods that it supports.  Set to%0A     * %7B@link Optional#EMPTY%7D by default.%0A     */%0A    public void set(final Optional%3C? extends GraphStrategy%3E strategy);%0A%0A    /**%0A     * Gets the %7B@link GraphStrategy%7D for the %7B@link Graph%7D.%0A     */
 %0A   
@@ -1675,32 +1675,349 @@
  : impl;%0A    %7D%0A%0A
+    /**%0A     * The %7B@link Context%7D object is provided to the methods of %7B@link GraphStrategy%7D so that the strategy functions%0A     * it constructs have some knowledge of the environment.%0A     *%0A     * @param %3CT%3E represents the object that is calling the strategy (i.e. the vertex on which addEdge was called).%0A     */%0A
     public stati
@@ -2393,103 +2393,42 @@
     
-if (null == g)%0A                throw new IllegalArgumentException(%22g%22);%0A%0A            this.g = g
+this.g = Objects.requireNonNull(g)
 ;%0A  
@@ -2452,23 +2452,47 @@
 rrent = 
+Objects.requireNonNull(
 current
+)
 ;%0A      
@@ -2516,16 +2516,39 @@
 nment = 
+Objects.requireNonNull(
 environm
@@ -2550,16 +2550,17 @@
 ironment
+)
 .orElse(
@@ -2850,32 +2850,144 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * Basic %7B@link Strategy%7D implementation where the %7B@link GraphStrategy%7D can be get or set.%0A     */%0A
     public stati
@@ -3170,32 +3170,78 @@
 gy%3E strategy) %7B%0A
+            Objects.requireNonNull(strategy);%0A
             this
@@ -3399,32 +3399,191 @@
        %7D%0A    %7D%0A%0A
+    /**%0A     * A %7B@link Strategy%7D implementation that does not allow the %7B@link GraphStrategy%7D to be set and returns an%0A     * %7B@link Optional#EMPTY%7D.%0A     */%0A
     public stati
@@ -3955,24 +3955,155 @@
    %7D%0A    %7D%0A%0A
+    /**%0A     * A %7B@link Strategy%7D implementation where the %7B@link GraphStrategy%7D can be get or set as %7B@link ThreadLocal%7D.%0A     */%0A
     public s
@@ -4508,32 +4508,78 @@
 gy%3E strategy) %7B%0A
+            Objects.requireNonNull(strategy);%0A
             this

Add javadoc on Strategy.