StrategyApplier.java

/*
 * @copyright defined in LICENSE.txt
 */

package hera.strategy;

import static hera.api.function.Functions.identify;
import static org.slf4j.LoggerFactory.getLogger;

import hera.Context;
import hera.Strategy;
import hera.api.function.Function0;
import hera.api.function.Function1;
import hera.api.function.Function2;
import hera.api.function.Function3;
import hera.api.function.Function4;
import hera.api.function.Function5;
import hera.api.function.FunctionDecorator;
import hera.api.function.WithIdentity;
import java.util.Collection;
import java.util.Comparator;
import java.util.Map;
import java.util.PriorityQueue;
import lombok.EqualsAndHashCode;
import lombok.ToString;
import org.slf4j.Logger;

@ToString
@EqualsAndHashCode
public class StrategyApplier implements FunctionDecorator {

  protected static final int INITIAL_QUEUE_SIZE = 3;

  /**
   * Make {@code StrategyChain} with a strategy in a context.
   *
   * @param context a context
   * @param priorityConfig priority config
   *
   * @return {@code StrategyChain}
   */
  public static StrategyApplier of(final Context context, final PriorityConfig priorityConfig) {
    return new StrategyApplier(context, priorityConfig);
  }

  protected final Logger logger = getLogger(getClass());

  protected final Collection<FunctionDecorator> chain;

  /**
   * Make {@code StrategyChain} based on {@code context}.
   *
   * @param context a context
   * @param priorityConfig priority config
   */
  public StrategyApplier(final Context context, final PriorityConfig priorityConfig) {
    this.chain = new PriorityQueue<FunctionDecorator>(INITIAL_QUEUE_SIZE,
        new Comparator<FunctionDecorator>() {

          protected final Map<Class<? extends Strategy>, Integer> inner =
              priorityConfig.getStrategy2Priority();

          @Override
          public int compare(final FunctionDecorator left, final FunctionDecorator right) {
            final Integer leftPriority = inner.get(left.getClass());
            final Integer rightPriority = inner.get(right.getClass());

            if (null == leftPriority && null == rightPriority) {
              return 0;
            } else if (null == leftPriority) {
              return -1;
            } else if (null == rightPriority) {
              return 1;
            } else {
              return (leftPriority < rightPriority) ? -1
                  : ((leftPriority == rightPriority) ? 0 : 1);
            }
          }
        });

    for (final Strategy strategy : context.getStrategies()) {
      if (strategy instanceof FunctionDecorator) {
        chain.add((FunctionDecorator) strategy);
      }
    }
    logger.debug("Build strategy chain in order: {}", chain);
  }

  @Override
  public <R> Function0<R> apply(Function0<R> f) {
    Function0<R> ret = f;
    for (final FunctionDecorator next : chain) {
      logger.debug("Apply strategy [{}] to a function", next);
      ret = next.apply(ret);
      if (f instanceof WithIdentity) {
        ret = identify(ret, ((WithIdentity) f).getIdentity());
      }
    }
    return ret;
  }

  @Override
  public <T, R> Function1<T, R> apply(Function1<T, R> f) {
    Function1<T, R> ret = f;
    for (final FunctionDecorator next : chain) {
      logger.debug("Apply strategy [{}] to a function", next);
      ret = next.apply(ret);
      if (f instanceof WithIdentity) {
        ret = identify(ret, ((WithIdentity) f).getIdentity());
      }
    }
    return ret;
  }

  @Override
  public <T1, T2, R> Function2<T1, T2, R> apply(Function2<T1, T2, R> f) {
    Function2<T1, T2, R> ret = f;
    for (final FunctionDecorator next : chain) {
      logger.debug("Apply strategy [{}] to a function", next);
      ret = next.apply(ret);
      if (f instanceof WithIdentity) {
        ret = identify(ret, ((WithIdentity) f).getIdentity());
      }
    }
    return ret;
  }

  @Override
  public <T1, T2, T3, R> Function3<T1, T2, T3, R> apply(Function3<T1, T2, T3, R> f) {
    Function3<T1, T2, T3, R> ret = f;
    for (final FunctionDecorator next : chain) {
      logger.debug("Apply strategy [{}] to a function", next);
      ret = next.apply(ret);
      if (f instanceof WithIdentity) {
        ret = identify(ret, ((WithIdentity) f).getIdentity());
      }
    }
    return ret;
  }

  @Override
  public <T1, T2, T3, T4, R> Function4<T1, T2, T3, T4, R> apply(Function4<T1, T2, T3, T4, R> f) {
    Function4<T1, T2, T3, T4, R> ret = f;
    for (final FunctionDecorator next : chain) {
      logger.debug("Apply strategy [{}] to a function", next);
      ret = next.apply(ret);
      if (f instanceof WithIdentity) {
        ret = identify(ret, ((WithIdentity) f).getIdentity());
      }
    }
    return ret;
  }

  @Override
  public <T1, T2, T3, T4, T5, R> Function5<T1, T2, T3, T4, T5, R> apply(
      Function5<T1, T2, T3, T4, T5, R> f) {
    Function5<T1, T2, T3, T4, T5, R> ret = f;
    for (final FunctionDecorator next : chain) {
      logger.debug("Apply strategy [{}] to a function", next);
      ret = next.apply(ret);
      if (f instanceof WithIdentity) {
        ret = identify(ret, ((WithIdentity) f).getIdentity());
      }
    }
    return ret;
  }

}