text:
package com.github.ferstl.streams;

import java.util.ArrayList;
import java.util.IntSummaryStatistics;
import java.util.OptionalDouble;
import java.util.OptionalInt;
import java.util.PrimitiveIterator;
import java.util.Spliterator;
import java.util.concurrent.ForkJoinPool;
import java.util.concurrent.ForkJoinWorkerThread;
import java.util.concurrent.atomic.AtomicReference;
import java.util.function.BiConsumer;
import java.util.function.IntBinaryOperator;
import java.util.function.IntConsumer;
import java.util.function.IntFunction;
import java.util.function.IntPredicate;
import java.util.function.IntSupplier;
import java.util.function.IntToDoubleFunction;
import java.util.function.IntToLongFunction;
import java.util.function.IntUnaryOperator;
import java.util.function.ObjIntConsumer;
import java.util.function.Supplier;
import java.util.stream.DoubleStream;
import java.util.stream.IntStream;
import java.util.stream.IntStream.Builder;
import java.util.stream.LongStream;
import java.util.stream.Stream;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
import static java.lang.Thread.currentThread;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.hamcrest.Matchers.instanceOf;
import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertSame;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;

@SuppressWarnings("ResultOfMethodCallIgnored")
public class ParallelIntStreamSupportTest extends AbstractParallelStreamSupportTest<Integer, IntStream, ParallelIntStreamSupport> {

  private Stream<?> mappedDelegateMock;
  private IntStream mappedIntDelegateMock;
  private LongStream mappedLongDelegateMock;
  private DoubleStream mappedDoubleDelegateMock;
  private PrimitiveIterator.OfInt iteratorMock;
  private Spliterator.OfInt spliteratorMock;
  private int[] toArrayResult;
  private IntSummaryStatistics summaryStatistics;

  private ParallelIntStreamSupport parallelIntStreamSupport;

  @Override
  protected ParallelIntStreamSupport createParallelStreamSupportMock(ForkJoinPool workerPool) {
    return new ParallelIntStreamSupport(mock(IntStream.class), workerPool);
  }

  @BeforeEach
  @SuppressWarnings({"unchecked", "rawtypes"})
  void init() {
    // Precondition for all tests
    assertFalse(currentThread() instanceof ForkJoinWorkerThread, "This test must not run in a ForkJoinPool");

    this.mappedDelegateMock = mock(Stream.class);
    this.mappedIntDelegateMock = mock(IntStream.class);
    this.mappedLongDelegateMock = mock(LongStream.class);
    this.mappedDoubleDelegateMock = mock(DoubleStream.class);
    this.iteratorMock = mock(PrimitiveIterator.OfInt.class);
    this.spliteratorMock = mock(Spliterator.OfInt.class);
    this.toArrayResult = new int[0];
    this.summaryStatistics = new IntSummaryStatistics();

    when(this.delegateMock.map(any())).thenReturn(this.mappedIntDelegateMock);
    when(this.delegateMock.mapToObj(any())).thenReturn((Stream) this.mappedDelegateMock);
    when(this.delegateMock.mapToLong(any())).thenReturn(this.mappedLongDelegateMock);
    when(this.delegateMock.mapToDouble(any())).thenReturn(this.mappedDoubleDelegateMock);
    when(this.delegateMock.flatMap(any())).thenReturn(this.mappedIntDelegateMock);
    when(this.delegateMock.iterator()).thenReturn(this.iteratorMock);
    when(this.delegateMock.spliterator()).thenReturn(this.spliteratorMock);
    when(this.delegateMock.isParallel()).thenReturn(false);
    when(this.delegateMock.toArray()).thenReturn(this.toArrayResult);
    when(this.delegateMock.reduce(anyInt(), any())).thenReturn(42);
    when(this.delegateMock.reduce(any())).thenReturn(OptionalInt.of(42));
    when(this.delegateMock.collect(any(), any(), any())).thenReturn("collect");
    when(this.delegateMock.sum()).thenReturn(42);
    when(this.delegateMock.min()).thenReturn(OptionalInt.of(42));
    when(this.delegateMock.max()).thenReturn(OptionalInt.of(42));
    when(this.delegateMock.count()).thenReturn(42L);
    when(this.delegateMock.average()).thenReturn(OptionalDouble.of(42.0));
    when(this.delegateMock.summaryStatistics()).thenReturn(this.summaryStatistics);
    when(this.delegateMock.anyMatch(any())).thenReturn(true);
    when(this.delegateMock.allMatch(any())).thenReturn(true);
    when(this.delegateMock.noneMatch(any())).thenReturn(true);
    when(this.delegateMock.findFirst()).thenReturn(OptionalInt.of(42));
    when(this.delegateMock.findAny()).thenReturn(OptionalInt.of(42));
    when(this.delegateMock.asLongStream()).thenReturn(this.mappedLongDelegateMock);
    when(this.delegateMock.asDoubleStream()).thenReturn(this.mappedDoubleDelegateMock);
    when(this.delegateMock.boxed()).thenReturn((Stream) this.mappedDelegateMock);

    IntStream delegate = IntStream.of(1).parallel();
    this.parallelIntStreamSupport = new ParallelIntStreamSupport(delegate, this.workerPool);
  }

  @Test
  void parallelStreamWithArray() {
    IntStream stream = ParallelIntStreamSupport.parallelStream(new int[]{42}, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertEquals(OptionalInt.of(42), stream.findAny());
  }

  @Test
  void parallelStreamWithNullArray() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.parallelStream((int[]) null, this.workerPool));
  }

  @Test
  void parallelStreamSupportWithSpliterator() {
    Spliterator.OfInt spliterator = IntStream.of(42).spliterator();
    IntStream stream = ParallelIntStreamSupport.parallelStream(spliterator, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertEquals(OptionalInt.of(42), stream.findAny());
  }

  @Test
  void parallelStreamSupportWithNullSpliterator() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.parallelStream((Spliterator.OfInt) null, this.workerPool));
  }

  @Test
  void parallelStreamSupportWithSpliteratorSupplier() {
    Supplier<Spliterator.OfInt> supplier = () -> IntStream.of(42).spliterator();
    IntStream stream = ParallelIntStreamSupport.parallelStream(supplier, 0, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertEquals(OptionalInt.of(42), stream.findAny());
  }

  @Test
  void parallelStreamSupportWithNullSpliteratorSupplier() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.parallelStream(null, 0, this.workerPool));
  }

  @Test
  void parallelStreamWithBuilder() {
    Builder builder = IntStream.builder();
    builder.accept(42);
    IntStream stream = ParallelIntStreamSupport.parallelStream(builder, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertEquals(OptionalInt.of(42), stream.findAny());
  }

  @Test
  void parallelStreamWithNullBuilder() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.parallelStream((Builder) null, this.workerPool));
  }

  @Test
  void iterate() {
    IntUnaryOperator operator = a -> a;
    IntStream stream = ParallelIntStreamSupport.iterate(42, operator, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertEquals(OptionalInt.of(42), stream.findAny());
  }

  @Test
  void iterateWithNullOperator() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.iterate(42, null, this.workerPool));
  }

  @Test
  void generate() {
    IntSupplier supplier = () -> 42;
    IntStream stream = ParallelIntStreamSupport.generate(supplier, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertEquals(OptionalInt.of(42), stream.findAny());
  }

  @Test
  void generateWithNullSupplier() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.generate(null, this.workerPool));
  }

  @Test
  void range() {
    IntStream stream = ParallelIntStreamSupport.range(0, 5, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertArrayEquals(stream.toArray(), new int[]{0, 1, 2, 3, 4});
  }

  @Test
  void rangeClosed() {
    IntStream stream = ParallelIntStreamSupport.rangeClosed(0, 5, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertArrayEquals(stream.toArray(), new int[]{0, 1, 2, 3, 4, 5});
  }

  @Test
  void concat() {
    IntStream a = IntStream.of(42);
    IntStream b = IntStream.of(43);
    IntStream stream = ParallelIntStreamSupport.concat(a, b, this.workerPool);

    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertTrue(stream.isParallel());
    assertArrayEquals(stream.toArray(), new int[]{42, 43});
  }

  @Test
  void concatWithNullStreamA() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.concat(null, IntStream.of(42), this.workerPool));
  }

  @Test
  void concatWithNullStreamB() {
    assertThrows(NullPointerException.class, () -> ParallelIntStreamSupport.concat(IntStream.of(42), null, this.workerPool));
  }

  @Test
  void filter() {
    IntPredicate p = i -> true;
    IntStream stream = this.parallelStreamSupportMock.filter(p);

    verify(this.delegateMock).filter(p);
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void map() {
    IntUnaryOperator f = i -> 42;
    IntStream stream = this.parallelStreamSupportMock.map(f);

    verify(this.delegateMock).map(f);
    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertSame(((ParallelIntStreamSupport) stream).delegate, this.mappedIntDelegateMock);
    assertSame(((ParallelIntStreamSupport) stream).workerPool, this.workerPool);
  }

  @Test
  void mapToObj() {
    IntFunction<String> f = i -> "x";
    Stream<String> stream = this.parallelStreamSupportMock.mapToObj(f);

    verify(this.delegateMock).mapToObj(f);
    assertThat(stream, instanceOf(ParallelStreamSupport.class));
    assertSame(((ParallelStreamSupport) stream).delegate, this.mappedDelegateMock);
    assertSame(((ParallelStreamSupport) stream).workerPool, this.workerPool);
  }

  @Test
  void mapToLong() {
    IntToLongFunction f = i -> 1L;
    LongStream stream = this.parallelStreamSupportMock.mapToLong(f);

    verify(this.delegateMock).mapToLong(f);
    assertThat(stream, instanceOf(ParallelLongStreamSupport.class));
    assertSame(((ParallelLongStreamSupport) stream).delegate, this.mappedLongDelegateMock);
    assertSame(((ParallelLongStreamSupport) stream).workerPool, this.workerPool);
  }

  @Test
  void mapToDouble() {
    IntToDoubleFunction f = i -> 1.0;
    DoubleStream stream = this.parallelStreamSupportMock.mapToDouble(f);

    verify(this.delegateMock).mapToDouble(f);
    assertThat(stream, instanceOf(ParallelDoubleStreamSupport.class));
    assertSame(((ParallelDoubleStreamSupport) stream).delegate, this.mappedDoubleDelegateMock);
    assertSame(((ParallelDoubleStreamSupport) stream).workerPool, this.workerPool);
  }

  @Test
  void flatMap() {
    IntFunction<IntStream> f = i -> IntStream.of(1);
    IntStream stream = this.parallelStreamSupportMock.flatMap(f);

    verify(this.delegateMock).flatMap(f);
    assertThat(stream, instanceOf(ParallelIntStreamSupport.class));
    assertSame(((ParallelIntStreamSupport) stream).delegate, this.mappedIntDelegateMock);
  }

  @Test
  void distinct() {
    IntStream stream = this.parallelStreamSupportMock.distinct();

    verify(this.delegateMock).distinct();
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void sorted() {
    IntStream stream = this.parallelStreamSupportMock.sorted();

    verify(this.delegateMock).sorted();
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void peek() {
    IntConsumer c = i -> {
    };
    IntStream stream = this.parallelStreamSupportMock.peek(c);

    verify(this.delegateMock).peek(c);
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void limit() {
    IntStream stream = this.parallelStreamSupportMock.limit(5);

    verify(this.delegateMock).limit(5);
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void skip() {
    IntStream stream = this.parallelStreamSupportMock.skip(5);

    verify(this.delegateMock).skip(5);
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void takeWhile() {
    IntPredicate predicate = x -> true;
    IntStream stream = this.parallelStreamSupportMock.takeWhile(predicate);

    verify(this.delegateMock).takeWhile(predicate);
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void dropWhile() {
    IntPredicate predicate = x -> true;
    IntStream stream = this.parallelStreamSupportMock.dropWhile(predicate);

    verify(this.delegateMock).dropWhile(predicate);
    assertSame(this.parallelStreamSupportMock, stream);
  }

  @Test
  void forEach() {
    IntConsumer c = i -> {
    };
    this.parallelStreamSupportMock.forEach(c);

    verify(this.delegateMock).forEach(c);
  }

  @Test
  void forEachSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport.forEach(i -> threadRef.set(currentThread()));

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void forEachParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport.forEach(i -> threadRef.set(currentThread()));

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void forEachOrdered() {
    IntConsumer c = i -> {
    };
    this.parallelStreamSupportMock.forEachOrdered(c);

    verify(this.delegateMock).forEachOrdered(c);
  }

  @Test
  void forEachOrderedSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport.forEachOrdered(i -> threadRef.set(currentThread()));

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void forEachOrderedParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport.forEachOrdered(i -> threadRef.set(currentThread()));

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void toArray() {
    int[] array = this.parallelStreamSupportMock.toArray();

    verify(this.delegateMock).toArray();
    assertSame(this.toArrayResult, array);
  }

  @Test
  void toArraySequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .toArray();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void toArrayParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .toArray();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void reduceWithIdentityAndAccumulator() {
    IntBinaryOperator accumulator = (a, b) -> b;
    int result = this.parallelStreamSupportMock.reduce(0, accumulator);

    verify(this.delegateMock).reduce(0, accumulator);
    assertEquals(42, result);
  }

  @Test
  void reduceWithIdentityAndAccumulatorSequential() {
    this.parallelIntStreamSupport.sequential();
    IntBinaryOperator accumulator = (a, b) -> b;
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .reduce(0, accumulator);

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void reduceWithIdentityAndAccumulatorParallel() {
    this.parallelIntStreamSupport.parallel();
    IntBinaryOperator accumulator = (a, b) -> b;
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .reduce(0, accumulator);

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void reduceWithAccumulator() {
    IntBinaryOperator accumulator = (a, b) -> b;
    OptionalInt result = this.parallelStreamSupportMock.reduce(accumulator);

    verify(this.delegateMock).reduce(accumulator);
    assertEquals(OptionalInt.of(42), result);
  }

  @Test
  void reduceWithAccumulatorSequential() {
    this.parallelIntStreamSupport.sequential();
    IntBinaryOperator accumulator = (a, b) -> b;
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .reduce(accumulator);

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void reduceWithAccumulatorParallel() {
    this.parallelIntStreamSupport.parallel();
    IntBinaryOperator accumulator = (a, b) -> b;
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .reduce(accumulator);

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void collectWithSupplierAndAccumulatorAndCombiner() {
    Supplier<String> supplier = () -> "x";
    ObjIntConsumer<String> accumulator = (a, b) -> {
    };
    BiConsumer<String, String> combiner = (a, b) -> {
    };

    String result = this.parallelStreamSupportMock.collect(supplier, accumulator, combiner);

    verify(this.delegateMock).collect(supplier, accumulator, combiner);
    assertEquals("collect", result);
  }

  @Test
  void collectWithSupplierAndAccumulatorAndCombinerSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void collectWithSupplierAndAccumulatorAndCombinerParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .collect(ArrayList::new, ArrayList::add, ArrayList::addAll);

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void sum() {
    int result = this.parallelStreamSupportMock.sum();

    verify(this.delegateMock).sum();
    assertEquals(42, result);
  }

  @Test
  void sumSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .sum();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void sumParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .sum();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void min() {
    OptionalInt result = this.parallelStreamSupportMock.min();

    verify(this.delegateMock).min();
    assertEquals(OptionalInt.of(42), result);
  }

  @Test
  void minSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .min();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void minParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .min();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void max() {
    OptionalInt result = this.parallelStreamSupportMock.max();

    verify(this.delegateMock).max();
    assertEquals(OptionalInt.of(42), result);
  }

  @Test
  void maxSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .max();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void maxParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .max();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void count() {
    long count = this.parallelStreamSupportMock.count();

    verify(this.delegateMock).count();
    assertEquals(42L, count);
  }

  @Test
  void countSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .filter(i -> {
          // Don't use peek() in combination with count(). See Javadoc.
          threadRef.set(currentThread());
          return true;
        }).count();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void countParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        // Don't use peek() in combination with count(). See Javadoc.
        .filter(i -> {
          threadRef.set(currentThread());
          return true;
        }).count();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void average() {
    OptionalDouble result = this.parallelStreamSupportMock.average();

    verify(this.delegateMock).average();
    assertEquals(OptionalDouble.of(42.0), result);
  }

  @Test
  void averageSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .average();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void averageParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .average();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void summaryStatistics() {
    IntSummaryStatistics result = this.parallelStreamSupportMock.summaryStatistics();

    verify(this.delegateMock).summaryStatistics();
    assertEquals(this.summaryStatistics, result);
  }

  @Test
  void summaryStatisticsSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .summaryStatistics();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void summaryStatisticsParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .summaryStatistics();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void anyMatch() {
    IntPredicate p = i -> true;

    boolean result = this.parallelStreamSupportMock.anyMatch(p);

    verify(this.delegateMock).anyMatch(p);
    assertTrue(result);
  }

  @Test
  void anyMatchSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .anyMatch(i -> true);

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void anyMatchParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .anyMatch(i -> true);

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void allMatch() {
    IntPredicate p = i -> true;

    boolean result = this.parallelStreamSupportMock.allMatch(p);

    verify(this.delegateMock).allMatch(p);
    assertTrue(result);
  }

  @Test
  void allMatchSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .allMatch(i -> true);

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void allMatchParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .allMatch(i -> true);

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void noneMatch() {
    IntPredicate p = i -> true;

    boolean result = this.parallelStreamSupportMock.noneMatch(p);

    verify(this.delegateMock).noneMatch(p);
    assertTrue(result);
  }

  @Test
  void noneMatchSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .noneMatch(i -> true);

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void noneMatchParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .noneMatch(i -> true);

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void findFirst() {
    OptionalInt result = this.parallelStreamSupportMock.findFirst();

    verify(this.delegateMock).findFirst();
    assertEquals(OptionalInt.of(42), result);
  }

  @Test
  void findFirstSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .findFirst();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void findFirstParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .findFirst();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void findAny() {
    OptionalInt result = this.parallelStreamSupportMock.findAny();

    verify(this.delegateMock).findAny();
    assertEquals(OptionalInt.of(42), result);
  }

  @Test
  void findAnytSequential() {
    this.parallelIntStreamSupport.sequential();
    Thread thisThread = currentThread();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .findAny();

    assertEquals(thisThread, threadRef.get());
  }

  @Test
  void findAnyParallel() {
    this.parallelIntStreamSupport.parallel();
    AtomicReference<Thread> threadRef = new AtomicReference<>();

    this.parallelIntStreamSupport
        .peek(i -> threadRef.set(currentThread()))
        .findAny();

    assertThat(threadRef.get(), instanceOf(ForkJoinWorkerThread.class));
  }

  @Test
  void asLongStream() {
    LongStream stream = this.parallelStreamSupportMock.asLongStream();

    verify(this.delegateMock).asLongStream();
    assertThat(stream, instanceOf(ParallelLongStreamSupport.class));
    assertSame(this.mappedLongDelegateMock, ((ParallelLongStreamSupport) stream).delegate);
    assertSame(this.workerPool, ((ParallelLongStreamSupport) stream).workerPool);
  }

  @Test
  void asDoubleStream() {
    DoubleStream stream = this.parallelStreamSupportMock.asDoubleStream();

    verify(this.delegateMock).asDoubleStream();
    assertThat(stream, instanceOf(ParallelDoubleStreamSupport.class));
    assertSame(this.mappedDoubleDelegateMock, ((ParallelDoubleStreamSupport) stream).delegate);
    assertSame(this.workerPool, ((ParallelDoubleStreamSupport) stream).workerPool);
  }

  @Test
  void boxed() {
    Stream<Integer> stream = this.parallelStreamSupportMock.boxed();

    verify(this.delegateMock).boxed();
    assertThat(stream, instanceOf(ParallelStreamSupport.class));
    assertSame(this.mappedDelegateMock, ((ParallelStreamSupport) stream).delegate);
    assertSame(this.workerPool, ((ParallelStreamSupport) stream).workerPool);
  }

  @Override
  @Test
  void iterator() {
    PrimitiveIterator.OfInt iterator = this.parallelStreamSupportMock.iterator();

    verify(this.delegateMock).iterator();
    assertSame(this.iteratorMock, iterator);
  }

  @Override
  @Test
  void spliterator() {
    Spliterator.OfInt spliterator = this.parallelStreamSupportMock.spliterator();

    verify(this.delegateMock).spliterator();
    assertSame(this.spliteratorMock, spliterator);
  }
}


text:
from os.path import join as pjoin, abspath, dirname, pardir

PROJ_ROOT = abspath(pjoin(dirname(__file__), pardir))
DATA_ROOT = pjoin(PROJ_ROOT, 'data')
THUMBNAIL_PREFIX = 'test/cache/'
THUMBNAIL_DEBUG = True
THUMBNAIL_LOG_HANDLER = {
    'class': 'sorl.thumbnail.log.ThumbnailLogHandler',
    'level': 'ERROR',
}
THUMBNAIL_KVSTORE = 'thumbnail_tests.kvstore.TestKVStore'
THUMBNAIL_STORAGE = 'thumbnail_tests.storage.TestStorage'
DEFAULT_FILE_STORAGE = 'thumbnail_tests.storage.TestStorage'
ADMINS = (
    ('Sorl', 'thumbnail@sorl.net'),
)
DATABASES = {
    'default': {
        'ENGINE': 'django.db.backends.sqlite3',
        'NAME': 'thumbnail_tests',
    }
}
MEDIA_ROOT = pjoin(PROJ_ROOT, 'media')
MEDIA_URL = '/media/'
ROOT_URLCONF = 'thumbnail_tests.urls'
INSTALLED_APPS = (
    'thumbnail',
    'thumbnail_tests',
)
TEMPLATE_CONTEXT_PROCESSORS = (
    "django.core.context_processors.request",
)

redpajama_set_name: RedPajamaGithub


text:
namespace Ui {
    class OknoGry;
}

class KontrolerGry : public QWidget
{
    Q_OBJECT

    using Pozycja = Plansza::Pozycja;
    using StanGry = Warcaby::StatusGry;

public:
    explicit KontrolerGry(Polaczenie *polaczenie, bool host, QWidget *parent = 0);
    ~KontrolerGry() override;

private:

    Ui::OknoGry *ui;

    Polaczenie *_polaczenie = nullptr;

    Warcaby _gra;
    Gracz _gracz = Gracz::pusty;
    Pozycja _obecnaPozycja = {-1, -1};

    void _aktualizujWidok();

signals:
    void graZakonczona();

private slots:
    // Fukcje związane z grą
    void _czekajZaNowaGra();
    void _zacznijNowaGre();
    void _ustawGracza(Gracz gracz);
    void _poddaj();
    void _pytajORemis();
    void _handleRuchPionka(const Pozycja pozycja);
    void _handleWyborPionka(const Pozycja pozycja);
    void _handleKoniecGry(QString stan, QString dodatkowyOpis = "");

    // Funkcje związane z wysyłaniem wiadomości
    void _handleWiadomosc(QString wiadomosc);

    void _wyslijGracz(Gracz gracz);
    void _wyslijRuch(const Pozycja &pozycja);
    void _wyslijWybrany(const Pozycja &pozycja);
    void _wyslijGotowy(QString szczegoly);
    void _wyslijKapitulacje();
    void _wyslijRemis(QString szczegoly);

    void _handleGracz(const QStringList &list);
    void _handleRuch(const QStringList &list);
    void _handleWybrany(const QStringList &list);
    void _handleGotowy(const QStringList &list);
    void _handleKapitulacja(const QStringList &list);
    void _handleRemis(const QStringList &list);
    void _handleClick(const Pozycja &pozycja);

    // Obsługa zamknięcia okna
    void closeEvent(QCloseEvent *event);
};

#endif // KONTROLERGRY_H


text:
<component name="libraryTable">
  <library name="espresso-core-2.2.2">
    <CLASSES>
      <root url="jar://$PROJECT_DIR$/build/intermediates/exploded-aar/com.android.support.test.espresso/espresso-core/2.2.2/jars/classes.jar!/" />
      <root url="file://$PROJECT_DIR$/build/intermediates/exploded-aar/com.android.support.test.espresso/espresso-core/2.2.2/res" />
    </CLASSES>
    <JAVADOC />
    <SOURCES>
      <root url="jar://$USER_HOME$/Downloads/adt-bundle-mac-x86_64-20131030/sdk/extras/android/m2repository/com/android/support/test/espresso/espresso-core/2.2.2/espresso-core-2.2.2-sources.jar!/" />
    </SOURCES>
  </library>
</component>


text:
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading;
using System.Threading.Tasks;

namespace Couchbase.Linq.Extensions
{
    // AnyAsync and AllAsync extensions

    public static partial class QueryExtensions
    {
        /// <summary>
        /// Asynchronously tests if the query returns any results.
        /// </summary>
        /// <typeparam name="T">Type of item to query.</typeparam>
        /// <param name="source">Source <see cref="IQueryable{T}"/></param>.
        /// <returns>True if the query returns any results..</returns>
        public static Task<bool> AnyAsync<T>(this IQueryable<T> source) =>
            source.AnyAsync(default(CancellationToken));

        /// <summary>
        /// Asynchronously tests if the query returns any results.
        /// </summary>
        /// <typeparam name="T">Type of item to query.</typeparam>
        /// <param name="source">Source <see cref="IQueryable{T}"/></param>.
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>True if the query returns any results..</returns>
        public static Task<bool> AnyAsync<T>(this IQueryable<T> source, CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }

            return ExecuteAsync<T, Task<bool>>(QueryExtensionMethods.AnyAsyncNoPredicate, source, null,
                cancellationToken);
        }

        /// <summary>
        /// Asynchronously tests if the query returns any results.
        /// </summary>
        /// <typeparam name="T">Type of item to query.</typeparam>
        /// <param name="source">Source <see cref="IQueryable{T}"/></param>.
        /// <param name="predicate">Predicate to filter query results.</param>
        /// <returns>True if the query returns any results..</returns>
        public static Task<bool> AnyAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate) =>
            source.AnyAsync(predicate, default);

        /// <summary>
        /// Asynchronously tests if the query returns any results.
        /// </summary>
        /// <typeparam name="T">Type of item to query.</typeparam>
        /// <param name="source">Source <see cref="IQueryable{T}"/></param>.
        /// <param name="predicate">Predicate to filter query results.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>True if the query returns any results..</returns>
        public static Task<bool> AnyAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            return ExecuteAsync<T, Task<bool>>(QueryExtensionMethods.AnyAsyncWithPredicate, source, predicate,
                cancellationToken);
        }

        /// <summary>
        /// Asynchronously tests if all items returned by the query match a predicate.
        /// </summary>
        /// <typeparam name="T">Type of item to query.</typeparam>
        /// <param name="source">Source <see cref="IQueryable{T}"/></param>.
        /// <param name="predicate">Predicate to test all query results.</param>
        /// <returns>True if all query results match the predicate.</returns>
        public static Task<bool> AllAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate) =>
            source.AllAsync(predicate, default);

        /// <summary>
        /// Asynchronously tests if all items returned by the query match a predicate.
        /// </summary>
        /// <typeparam name="T">Type of item to query.</typeparam>
        /// <param name="source">Source <see cref="IQueryable{T}"/></param>.
        /// <param name="predicate">Predicate to test all query results.</param>
        /// <param name="cancellationToken">Cancellation token.</param>
        /// <returns>True if all query results match the predicate.</returns>
        public static Task<bool> AllAsync<T>(this IQueryable<T> source, Expression<Func<T, bool>> predicate, CancellationToken cancellationToken)
        {
            if (source == null)
            {
                throw new ArgumentNullException(nameof(source));
            }
            if (predicate == null)
            {
                throw new ArgumentNullException(nameof(predicate));
            }

            return ExecuteAsync<T, Task<bool>>(QueryExtensionMethods.AllAsync, source, predicate,
                cancellationToken);
        }
    }
}


text:
package org.elasticsearch.client.ml;

import org.elasticsearch.client.ml.job.process.ModelSnapshot;
import org.elasticsearch.client.ml.job.process.ModelSnapshotTests;
import org.elasticsearch.common.xcontent.XContentParser;
import org.elasticsearch.test.AbstractXContentTestCase;

import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

public class GetModelSnapshotsResponseTests extends AbstractXContentTestCase<GetModelSnapshotsResponse> {

    @Override
    protected GetModelSnapshotsResponse createTestInstance() {
        int listSize = randomInt(10);
        List<ModelSnapshot.Builder> modelSnapshots = new ArrayList<>(listSize);
        for (int j = 0; j < listSize; j++) {
            modelSnapshots.add(ModelSnapshotTests.createRandomizedBuilder());
        }
        return new GetModelSnapshotsResponse(modelSnapshots, listSize);
    }

    @Override
    protected GetModelSnapshotsResponse doParseInstance(XContentParser parser) throws IOException {
        return GetModelSnapshotsResponse.fromXContent(parser);
    }

    @Override
    protected boolean supportsUnknownFields() {
        return true;
    }
}


text:
<!DOCTYPE html>

<meta charset="UTF-8">

<title>LICENSE - RDoc Documentation</title>

<link href="./css/fonts.css" rel="stylesheet">
<link href="./css/rdoc.css" rel="stylesheet">

    Home

    Pages
    Classes
    Methods

  <form action="#" method="get" accept-charset="utf-8">

      <input id="search-field" role="combobox" aria-label="Search"
             aria-autocomplete="list" aria-controls="search-results"
             type="text" name="search" placeholder="Search" spellcheck="false"
             title="Type to search, Up and Down to navigate, Enter to load">

  </form>

Pages

Gemfile

Gemfile.lock

LICENSE

README

Rakefile

application.js

bjqs.min.js

categories.coffee

home.coffee

let-it-snow.min.js

sessions.coffee

users.coffee

application.css

bjqs.css

categories.scss

home.scss

let-it-snow.css

mixins.scss

sessions.scss

slideshow.scss

users.scss

config.ru

development.log

404.html

422.html

500.html

favicon.ico

robots

The MIT License (MIT)

Copyright © 2015 Sam Allen

Permission is hereby granted, free of charge, to any person obtaining a
copy of this software and associated documentation files (the "Software"),
to deal in the Software without restriction, including without limitation
the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the
Software is furnished to do so, subject to the following conditions:

The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.

THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
DEALINGS IN THE SOFTWARE.

Validate

Generated by RDoc 4.2.0.

Based on Darkfish by Michael Granger.


text:
const mongoose = require('mongoose');

let articleSchema = mongoose.Schema({
    title: {type: String, required: true},
    content: {type: String, required: true},
    author: {type: mongoose.Schema.Types.ObjectId, required: true, ref: 'User'},
    date: {type: Date, default: Date.now()}
});

const Article = mongoose.model('Article', articleSchema);

module.exports = Article;


text:
package org.apache.solr.util.hll;

/**
 * The types of algorithm/data structure that {@link HLL} can utilize. For more
 * information, see the Javadoc for {@link HLL}.
 */
public enum HLLType {
    EMPTY,
    EXPLICIT,
    SPARSE,
    FULL;
}


text:
package entities;

import LRMv2.LRM_static_schema;
import models.CoreModel;
import models.InfrastructureModel;
import models.ScenarioModel;

/**
 * A Technical Service is a infrastructure consisting of hard- and software components and service interfaces. A
 * Technical Service provides services for the ecosystem, which can be accessed by its service interfaces. A technical
 * service consists of infrastructure components, digital objects, processes, and other technical services.
 */
public class TechnicalService extends EcosystemEntity {

    public TechnicalService(ScenarioModel model, String identifier, Template template) {
        super(model, identifier, template);
    }

    public TechnicalService(ScenarioModel model, String identifier) {
        super(model, identifier, CoreModel.technicalService);
    }

    public void accessedVia(ServiceInterface SInterface) {
        addProperty(InfrastructureModel.accessedVia, SInterface);
    }

    public void hasPart(DigitalObject digitalObject) {
        hasPart((EcosystemEntity) digitalObject);
    }

    public void hasPart(Process process) {
        hasPart((EcosystemEntity) process);
    }

    public void hasPart(TechnicalService service) {
        hasPart((EcosystemEntity) service);
    }

    public void hasPart(InfrastructureComponent component) {
        hasPart((EcosystemEntity) component);
    }

    private void hasPart(EcosystemEntity entity) {
        entity.addProperty(LRM_static_schema.partOf, this);
        addProperty(LRM_static_schema.hasPart, entity);
    }

    public void hasPart(AutomaticAgent agent) {
        agent.addProperty(LRM_static_schema.partOf, this);
        addProperty(LRM_static_schema.hasPart, agent);
    }

    public void partOf(TechnicalService service) {
        service.hasPart(this);
    }

    public static class TechnicalServiceTemplate extends Template {
        public TechnicalServiceTemplate(CoreModel coreModel) {
            super(coreModel, "Technical Service", CoreModel.ecosystemEntity);
            addDescription("A Technical Service is a infrastructure consisting of "
                    + "hard- and software components and service interfaces. A "
                    + "Technical Service provides services for the ecosystem, "
                    + "which can be accessed by its service interfaces. A technical "
                    + "service consists of infrastructure components, digital objects, "
                    + "processes, and other technical services.");
            addSuperClass(LRM_static_schema.AggregatedResource);
        }

        public TechnicalServiceTemplate(ScenarioModel scenario, String name, String comment) {
            super(scenario, name, CoreModel.technicalService);
            addDescription(comment);
            addSuperClass(LRM_static_schema.AggregatedResource);
        }

        @Override
        public TechnicalService createEntity(ScenarioModel model, String ID) {
            return new TechnicalService(model, ID);
        }
    }

}


text:
<?php

namespace lulutrip\modules\tour\controllers;

use api\library\Help;
use Curl\Curl;
use api\library\product\Comment;
use api\library\product\Qna;
use lulutrip\library\common\LogRecord;
use lulutrip\models\sale\Activities;
use lulutrip\modules\tour\library\detail\Discount;
use lulutrip\modules\tour\library\detail\GetItinerary;
use Yii;

class DetailController extends BaseController {
    /**
     * @var 用于trackCode
     */
    public $trackCode;

    /**
     * @var 折后价显示标示
     */
    public $discounAfterPirce;

    /**
     * 获取秒杀折扣
     * @author Victor Tang<victor.tang@ipptravel.com>
     * @copyright 2017-08-15
     * @param $tourCode integer 团号
     * @return float|int
     */
    protected function getOffPercent($tourCode) {
        $curl = new Curl();
        $offPercent = 1;

        //团购
        $curl->get(Yii::$app->params['service']['api'] . "/tour/group-buying/". $tourCode);
        if(!empty($curl->response->data)){
            $offPercent = $curl->response->data->off_percent / 10;
        }

        if($offPercent == 1) {
            //折后价
            $post = [
                'pid'       => $tourCode,
                'channel'   => Activities::CHANNEL_1,
                'platform'  => Activities::PLATFORM_LUPC
            ];
            $curl->post(Yii::$app->params['service']['api'] . "/saleactivity/get-max-discount", $post);
            if(!empty($curl->response->data)){
                $offPercent = $curl->response->data->discount / 10;
                $this->discounAfterPirce = 1;
            }
        }

        return $offPercent;

    }

    /**
     * 面包屑
     * @author Xiaopei Dou<xiaopei.dou@ipptravel.com>
     * @copyright 2018-01-18
     * @param $navigations
     * @return array 返回数据
     */
    private function setBreadData($navigations){
        if(empty($navigations)) return [];
        $domain = Yii::$app->params['service']['www'];
        $data = [['name' => '首页', 'url' => '/']];
        $count = count($navigations);
        foreach($navigations as $key => $value) {
            $temp = [];
            if($key+1 < $count){
                $temp['name'] = $value['cnName'] . "旅游";
                $temp['url'] = $domain . '/tour/destination/region-' . $value['luluCode'];
            }else{
                $temp['name'] = $value['cnName'] . "出发";
                $temp['url'] = $count < 2 ? '' : $domain . '/tour/destination/region-' . $navigations[$count-2]['luluCode'] . '_c-' . $value['luluCode'];
            }
            $data[] = $temp;
        }

        return $data;
    }

    /**
     * 旅行团详情页
     * @author Victor Tang<victor.tang@ipptravel.com>
     * @copyright 2017-08-14
     * @param $tourCode integer 团号
     * @return string
     */
    public function actionView($tourCode) {
        $firstTime = Yii::$app->helper->getMicroTime();//当前使用时间
        $firstMem  = memory_get_usage();//当前内存

        $currency = Yii::$app->params['curCurrencies']['code'];

        $curl = new Curl();
        $curl->setHeader('currency', $currency);
        $url = Yii::$app->params['service']['tourapi'] . "/gtravel/lulu/product/{$tourCode}";

        $secondTime = Yii::$app->helper->getMicroTime();//当前使用时间
        $secondMem  = memory_get_usage();//当前内存

        $curl->get($url);
        Yii::info('API-GET: ' . $url . '===HEAD: ' . json_encode(['currency' => $currency]) . '===' . json_encode($curl->response), __METHOD__);
        $tourData = json_decode(json_encode($curl->response), true);

        $threeTime = Yii::$app->helper->getMicroTime();//当前使用时间
        $threeMem  = memory_get_usage();//当前内存

        if($tourData['rs'] == 0){
            if(!isset($_SERVER['HTTP_ISNEW'])){
                return $this->redirect(Yii::$app->params['service']['www'] . '/old/tour/view/tourcode-' . $tourCode);
            }else{
                Yii::$app->helper->set404();
            }
        }

//        if ($tourData['data']['state'] == 2) {
//            http_response_code(404);
//            echo '产品已下架';
//            exit();
//        }

        if(!empty($tourData['data']['navigations'][0])) $region = $tourData['data']['navigations'][0];
        //非洲的产品详情所在的导航定位在欧洲
        if((!empty($tourData['data']['navigations'][1]) && $tourData['data']['navigations'][1]['luluCode'] == 'ARE') || (!empty($region['luluCode']) && $region['luluCode'] == 'AF')){
            $this->regionRoot = 'EU';
        }elseif(!empty($region['luluCode'])){
            $this->regionRoot = $region['luluCode'];
        }else{
            $this->regionRoot = 'NA';
        }

        // TKD
        $this->pageTitle = $tourData['data']['translation']['seoInfo']['stitle'];
        $this->pageKeywords = implode(',', explode(' ', $tourData['data']['translation']['seoInfo']['skeywords']));
        $this->pageDesc = $tourData['data']['translation']['seoInfo']['sdesc'];

        $comment = new Comment;
        $commentCount = $comment->getCommsNum($tourCode, 'tour', 'PC');

        $Qna = new Qna();
        $questionCount = $Qna->getQnasNum($tourCode, 'tour');

        //判断该产品在包团定制库中是否存在对应产品
        $domain = Yii::$app->params['service']['www'];
        $row = Yii::$app->db->createCommand("SELECT packid from packagetours WHERE `tourcode` = " . $tourCode . " and p_active = 'Y'")->queryOne();
        if(!empty($row['packid'])){
            $customLink = $domain . '/privatetour/view-' . $row['packid'];
        }else{
            $customLink = $domain . '/private/tour_book/type-tour';
        }
        if($this->regionRoot == 'NA'){
            $wechatLink = Yii::$app->helper->getFileUrl('/images/private_trips/ptCode_weixin_NA.jpg');
        }elseif ($this->regionRoot == 'EU'){
            $wechatLink = Yii::$app->helper->getFileUrl('/images/private_trips/euCode.jpg');
        }else{
            $wechatLink = Yii::$app->helper->getFileUrl('/images/private_trips/ptCode_weixin_AU.png');
        }

        $offPercent = $this->getOffPercent($tourCode);

        //组装trackcode记录数据
        $navigations = array_column($tourData['data']['navigations'], 'code', 'enName');
        $this->trackCode = [
            'productId' => $tourCode,
            'cityCode' => $navigations[$tourData['data']['basic']['startLocation'][0]['enName']],
            'photo' => empty($tourData['data']['photos'][0]['photo']) ? '' : $tourData['data']['photos'][0]['photo'],
            'url' => Yii::$app->params['service']['www'] . '/tour/view/tourcode-'. $tourCode,
            'price' =>  $tourData['data']['startPrice'],
            'offPercent' => $offPercent,
            'priceRMB'   => $offPercent < 1 ? Help::exchangeCurrency(ceil($tourData['data']['startPrice'] *$offPercent), $tourData['data']['currency'], 'RMB') : Help::exchangeCurrency($tourData['data']['startPrice'], $tourData['data']['currency'], 'RMB')
        ];

        //获取静态配置数据
        $url = Yii::$app->params['service']['api'] . '/get-static-nav';
        $staticNav = Yii::$app->helper->curlGet($url);

        //结伴期小于1天及已过期的结伴不会出现 by Ivyzhang 17.10.10
        $time = date("Y-m-d", strtotime('+1 days', time()));
        //行程部分支持展示景点链接 by Ivyzhang 17.10.10
        $tourData['data']['itinerarys'] = (new GetItinerary())->dealIti($tourData['data']['itinerarys'], $tourCode);
        //自选项目套餐类型id
        if(!empty($tourData['data']['activityGroups'])) $tourData['data']['optionalItemSubType'] = array_column($tourData['data']['activityGroups'], 'subType');

        $data = [
            'currency' => Yii::$app->params['curCurrencies']['sign'],
            'offPercent' => $offPercent,
            'tourCode' => $tourCode,
            'tourData' => $tourData['data'],
            'commentCount' => $commentCount,
            'questionCount' => $questionCount,
            'luluTopicCount' => Yii::$app->db->createCommand('select * from c_topics where tourcode = :tourcode and is_finished = 0 and is_deleted = 0', [':tourcode' => $tourCode])->query()->count(),
            'luluPartner' => Yii::$app->db->createCommand("select c_topics.id as topicId, memberid, screenname, avatar, i_am, subject, content, date(FROM_UNIXTIME(created_at)) as postDate from c_topics left join members using (memberid) where tourcode = :tourcode and is_finished = 0 and is_deleted = 0 and start_date > '$time' order by id desc limit 3", [':tourcode' => $tourCode])->queryAll(),
            'customLink' => $customLink,
            'wechatLink' => $wechatLink,
            'phones'     => $staticNav['data']['IpPhone'],
            'discountInfo' => Discount::getDiscounts($tourCode)
        ];
        //面包屑
        $data['breadData'] = $this->setBreadData($tourData['data']['navigations']);

        LogRecord::recordDebugInfo($firstTime, $secondTime, $threeTime, $firstMem, $secondMem, $threeMem, __METHOD__);

        return $this->render('index.html', $data);
    }

}


text:
package com.bakerbeach.market.cart.service;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;

import org.apache.commons.collections4.CollectionUtils;
import org.apache.commons.collections4.MapUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.exception.ExceptionUtils;
import org.mongodb.morphia.Datastore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import com.bakerbeach.market.cart.api.model.CartRuleContext;
import com.bakerbeach.market.cart.api.model.CartRuleResult;
import com.bakerbeach.market.cart.api.model.CartRuleSet;
import com.bakerbeach.market.cart.api.model.Rule.Intention;
import com.bakerbeach.market.cart.api.model.RuleInstance;
import com.bakerbeach.market.cart.api.model.RuleStore;
import com.bakerbeach.market.cart.api.service.CartRuleAware;
import com.bakerbeach.market.cart.api.service.CartService;
import com.bakerbeach.market.cart.api.service.CartServiceException;
import com.bakerbeach.market.cart.dao.MongoCartDao;
import com.bakerbeach.market.cart.model.TotalImpl;
import com.bakerbeach.market.cart.model.TotalImpl.LineImpl;
import com.bakerbeach.market.cart.rules.CartRuleDao;
import com.bakerbeach.market.cart.rules.SimpleCartRuleContextImpl;
import com.bakerbeach.market.commons.Message;
import com.bakerbeach.market.commons.MessageImpl;
import com.bakerbeach.market.commons.Messages;
import com.bakerbeach.market.commons.MessagesImpl;
import com.bakerbeach.market.core.api.model.Cart;
import com.bakerbeach.market.core.api.model.CartItem;
import com.bakerbeach.market.core.api.model.CartItemQualifier;
import com.bakerbeach.market.core.api.model.Coupon;
import com.bakerbeach.market.core.api.model.Customer;
import com.bakerbeach.market.core.api.model.ShopContext;
import com.bakerbeach.market.core.api.model.TaxCode;
import com.bakerbeach.market.core.api.model.Total;
import com.bakerbeach.market.core.api.model.Total.Line;
import com.bakerbeach.market.tax.api.service.TaxService;
import com.bakerbeach.market.translation.api.service.TranslationService;
import com.bakerbeach.market.xcatalog.service.XCatalogService;

public class XCartServiceImpl implements CartService {
	protected static final Logger log = LoggerFactory.getLogger(XCartServiceImpl.class);
	private static final BigDecimal HUNDRED = new BigDecimal(100);

	protected Map<String, MongoCartDao> mongoCartDaos;

	@Autowired
	private Datastore shopDatastore;

	@Autowired
	private TaxService taxService;

	@Autowired
	protected TranslationService translationService;

	@Autowired
	protected XCatalogService catalogService;

	@Autowired
	public RuleStore ruleStore;

	@Autowired
	private CartRuleDao cartRuleDao;

	@Override
	public Cart getInstance(ShopContext shopContext, Customer customer) throws CartServiceException {
		try {
			if (!(customer.isAnonymousCustomer())) {
				Cart cart = loadActiveCart(shopContext, customer);
				if (cart != null) {
					calculate(shopContext, cart, customer);
					return cart;
				}
			}
		} catch (CartServiceException e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}

		Cart cart = getNewInstance(shopContext, customer);
		calculate(shopContext, cart, customer);
		return cart;
	}

	@Override
	public Cart getNewInstance(ShopContext shopContext, Customer customer) throws CartServiceException {
		Cart cart = getNewCart(shopContext);
		cart.setCustomerId(customer.getId());
		cart.setCreatedAt(new Date());
		cart.setCreatedBy(customer.getId());
		cart.setShopCode(shopContext.getShopCode());
		cart.setStatus("ACTIVE");
		return cart;
	}

	@Override
	public Messages addCartItem(ShopContext shopContext, Cart cart, CartItem cartItem) throws CartServiceException {
		try {
			Messages messages = new MessagesImpl();

			// TODO: optionally check inventory
			// TODO: optionally set reservation
			// TODO: check or create unique id for the entry

			addCartItemPrecheck(shopContext, cart, cartItem);

			// TODO: update for same id
			// cartItem.getId()

			cart.add(cartItem);

			messages.add(
					new MessageImpl("add", Message.TYPE_INFO, "successfully.added.item", Arrays.asList(Message.TAG_BOX),
							Arrays.asList(cartItem.getId(), cartItem.getCode(), cartItem.getQuantity())));
			return messages;
		} catch (CartServiceException e) {
			return e.getMessages();
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));

			Messages messages = new MessagesImpl();
			messages.add(
					new MessageImpl("add", Message.TYPE_ERROR, "cart.error.", Arrays.asList(Message.TAG_BOX), null));
			throw new CartServiceException(messages);
		}
	}

	protected void addCartItemPrecheck(ShopContext shopContext, Cart cart, CartItem newCartItem)
			throws CartServiceException {
	}

	private void setCartItems(List<CartItem> discountItems, ShopContext shopContext, Cart cart, Customer customer) {
		if (CollectionUtils.isNotEmpty(discountItems)) {
			for (CartItem discountItem : discountItems) {
				calculateItem(discountItem, shopContext.getCountryOfDelivery(), customer.getTaxCode());
				if (discountItem.getTotalPrice("std") != null && BigDecimal.ZERO.compareTo(discountItem.getTotalPrice("std")) != 0) {
					cart.set(discountItem);
				}
			}
		}
	}

	@SuppressWarnings("unused")
	@Override
	public final synchronized Messages setQuantity(Cart cart, String id, BigDecimal quantity)
			throws CartServiceException {
		try {
			Messages messages = new MessagesImpl();

			// TODO: optionally check inventory
			// TODO: optionally set reservation
			if (false)
				throw new CartServiceException();

			CartItem item = cart.findItemById(id);

			if (item != null && !item.isImmutable()) {
				if (quantity.compareTo(BigDecimal.ZERO) < 1) {
					cart.remove(item);
				} else if (quantity.compareTo(item.getMinQty()) > -1 && quantity.compareTo(item.getMaxQty()) < 1) {
					item.setQuantity(quantity);
				}

				messages.addGlobalMessage(new MessageImpl("set", Message.TYPE_INFO, "successfully.updated.item",
						Arrays.asList(Message.TAG_BOX),
						Arrays.asList(item.getId(), item.getCode(), item.getQuantity())));
			}

			return messages;
		} catch (CartServiceException e) {
			return e.getMessages();
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));

			Messages messages = new MessagesImpl();
			messages.addGlobalMessage(
					new MessageImpl("set", Message.TYPE_ERROR, "cart.error", Arrays.asList(Message.TAG_BOX), null));
			throw new CartServiceException(messages);
		}
	}

	@Override
	public final synchronized void removeCartItem(Cart cart, CartItem cartItem) {
		cart.remove(cartItem);
	}

	@Override
	public final synchronized void clear(Cart cart) {
		cart.clear();
	}

	@Override
	public final synchronized void calculate(ShopContext shopContext, Cart cart, Customer customer) {
		calculate(shopContext, cart, customer, new MessagesImpl());
	}

	@Override
	public final synchronized void calculate(ShopContext shopContext, Cart cart, Customer customer, Messages messages) {
		Map<String, CartItem> synchronizedMap = Collections.synchronizedMap(cart.getItems());

		Messages cartMessages = new MessagesImpl();

		Map<String, CartRuleResult> ruleResults = new LinkedHashMap<>();

		Date now = new Date();

		// clear rule messages ---
		if (cart instanceof CartRuleAware) {
			((CartRuleAware) cart).getMessages().clear();
		}

		// check cart rules ---
		if (cart instanceof CartRuleAware) {
			checkCartRules(shopContext.getShopCode(), cart, customer, now);
			// messages.add(checkCartRules(cart, customer, now));
		}

		// remove zero quantity and volatile items and clear line discounts ---
		Collection<String> toBeRemoved = new ArrayList<String>();
		synchronizedMap.forEach((k, i) -> {
			if (i.getQuantity().compareTo(BigDecimal.ZERO) < 1 || i.isVolatile()) {
				toBeRemoved.add(k);
			}
		});
		synchronizedMap.keySet().removeAll(toBeRemoved);

		// check min and max quantity ---
		synchronizedMap.forEach((k, i) -> {
			if (i.getMinQty() != null && i.getMaxQty() != null) {
				if (i.getQuantity().compareTo(i.getMaxQty()) > -1) {
					i.setQuantity(i.getMaxQty());
				} else if (i.getQuantity().compareTo(i.getMinQty()) == -1) {
					i.setQuantity(i.getMinQty());
				}
			}
		});

		// reset line discount ---
		synchronizedMap.forEach((k, i) -> {
			i.setDiscount(BigDecimal.ZERO);
		});

		// rules that may change the items/lines ---
		try {
			if (cart instanceof CartRuleAware) {
				CartRuleContext context = new SimpleCartRuleContextImpl();
				context.put("shopCode", shopContext.getShopCode());
				context.put("catalogService", catalogService);
				context.put("shopDatastore", shopDatastore);
				context.put("shippingAddress", shopContext.getShippingAddress());

				Map<String, CartRuleResult> currentRuleResults = applyCartRules(shopContext.getShopCode(), cart, customer, Intention.LINE_CHANGES,
						context, ruleResults);

				for (CartRuleResult ruleResult : currentRuleResults.values()) {
					if (ruleResult.containsKey("newCartItem")) {
						CartItem item = (CartItem) ruleResult.get("newCartItem");

						item.getTitle().put("title1", translationService.getMessage("product.cart.title.1", "text",
								item.getCode(), null, "product.cart.title.1", shopContext.getCurrentLocale()));
						item.getTitle().put("title2", translationService.getMessage("product.cart.title.2", "text",
								item.getCode(), null, "product.cart.title.2", shopContext.getCurrentLocale()));
						item.getTitle().put("title3", translationService.getMessage("product.cart.title.3", "text",
								item.getCode(), null, "product.cart.title.3", shopContext.getCurrentLocale()));

						cart.set(item);
					}
				}

				ruleResults.putAll(currentRuleResults);
				messages.add(getRuleResultsMessages(currentRuleResults));
				cartMessages.add(getRuleResultsMessages(currentRuleResults, Arrays.asList("cart")));
			}
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}

		// zeilen berechnen (alle produkte) ---
		synchronizedMap.forEach((k, item) -> {
			calculateItem(item, shopContext.getCountryOfDelivery(), customer.getTaxCode());
		});

		Total goods = calculateTotal(cart, Arrays.asList(CartItemQualifier.PRODUCT, CartItemQualifier.VPRODUCT));

		// TODO: set payment ---
		cart.setPayment(BigDecimal.ZERO);

		// TODO: line-discount ---
		// ...

		// calculate (again) goods (now including line discounts) ---
		goods = calculateTotal(cart, Arrays.asList(CartItemQualifier.PRODUCT, CartItemQualifier.VPRODUCT));
		cart.setValueOfGoods(goods.getGross());

		// discount-1 (discount on goods, before shipping) ---
		try {
			if (cart instanceof CartRuleAware) {
				CartRuleContext context = new SimpleCartRuleContextImpl();
				context.put("shopCode", shopContext.getShopCode());
				context.put("catalogService", catalogService);
				context.put("shopDatastore", shopDatastore);
				context.put("customer", customer);
				context.put("shippingAddress", shopContext.getShippingAddress());

				Map<String, CartRuleResult> currentRuleResults = applyCartRules(shopContext.getShopCode(), cart, customer,
						Intention.DISCOUNT_ON_GOODS, context, ruleResults);
				BigDecimal discount = getRuleResultsTotal(currentRuleResults);

				List<CartItem> discountItems = getCartDiscountItems(cart, "discount-1", goods, discount, shopContext);
				setCartItems(discountItems, shopContext, cart, customer);

				ruleResults.putAll(currentRuleResults);
				messages.add(getRuleResultsMessages(currentRuleResults));
				cartMessages.add(getRuleResultsMessages(currentRuleResults, Arrays.asList("cart")));
			}
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}

		// shipping ---
		Total shippingGoods = calculateTotal(cart, Arrays.asList(CartItemQualifier.PRODUCT));
		cart.setValueOfShippingGoods(shippingGoods.getGross());

		try {
			if (cart instanceof CartRuleAware) {
				CartRuleContext context = new SimpleCartRuleContextImpl();
				context.put("shopCode", shopContext.getShopCode());
				context.put("catalogService", catalogService);
				context.put("shopDatastore", shopDatastore);
				context.put("cart", cart);
				context.put("customer", customer);
				context.put("shippingAddress", shopContext.getShippingAddress());

				Map<String, CartRuleResult> currentRuleResults = applyCartRules(shopContext.getShopCode(), cart, customer, Intention.SHIPPING,
						context, ruleResults);
				BigDecimal shipping = getRuleResultsTotal(currentRuleResults);

				CartItem shippingItem = createItem(cart, "shipping", CartItemQualifier.SHIPPING, TaxCode.NORMAL,
						BigDecimal.ONE, true, true, true, shipping, shopContext);
				setCartItems(Arrays.asList(shippingItem), shopContext, cart, customer);

				ruleResults.putAll(currentRuleResults);
				messages.add(getRuleResultsMessages(currentRuleResults));
				cartMessages.add(getRuleResultsMessages(currentRuleResults, Arrays.asList("cart")));
			}
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}

		// TODO: shipping discount ---
		try {
			if (cart instanceof CartRuleAware) {
				CartRuleContext context = new SimpleCartRuleContextImpl();
				context.put("shopCode", shopContext.getShopCode());
				context.put("catalogService", catalogService);
				context.put("shopDatastore", shopDatastore);
				context.put("customer", customer);
				context.put("shippingAddress", shopContext.getShippingAddress());

				Map<String, CartRuleResult> currentRuleResults = applyCartRules(shopContext.getShopCode(), cart, customer,
						Intention.DISCOUNT_ON_SHIPPING, context, ruleResults);
				BigDecimal shipping = getRuleResultsTotal(currentRuleResults);

				CartItem shippingDiscountItem = createItem(cart, "discount-shipping", CartItemQualifier.SHIPPING,
						TaxCode.NORMAL, BigDecimal.ONE, true, true, true, shipping, shopContext);
				setCartItems(Arrays.asList(shippingDiscountItem), shopContext, cart, customer);

				ruleResults.putAll(currentRuleResults);
				messages.add(getRuleResultsMessages(currentRuleResults));
				cartMessages.add(getRuleResultsMessages(currentRuleResults, Arrays.asList("cart")));
			}
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}

		// shipping gross price ---
		Total shipping = calculateTotal(cart, Arrays.asList(CartItemQualifier.SHIPPING));
		cart.setShipping(shipping.getGross());

		// waren und services inkl. (line)discount (als basis zur berechnung der
		// resource produkte) ---
		Total goodsAndServices = calculateTotal(cart, Arrays.asList(CartItemQualifier.PRODUCT,
				CartItemQualifier.VPRODUCT, CartItemQualifier.SERVICE, CartItemQualifier.SHIPPING));

		// global cart discount including shipping and services ---
		try {
			if (cart instanceof CartRuleAware) {
				CartRuleContext context = new SimpleCartRuleContextImpl();
				context.put("shopCode", shopContext.getShopCode());
				context.put("catalogService", catalogService);
				context.put("shopDatastore", shopDatastore);
				context.put("customer", customer);
				context.put("shippingAddress", shopContext.getShippingAddress());

				Map<String, CartRuleResult> currentRuleResults = applyCartRules(shopContext.getShopCode(), cart, customer,
						Intention.DISCOUNT_ON_GOODS_AND_SERVICES, context, ruleResults);

				List<CartItem> discountItems = getDiscountItems(currentRuleResults, cart, goodsAndServices, shopContext);
				setCartItems(discountItems, shopContext, cart, customer);

				ruleResults.putAll(currentRuleResults);
				messages.add(getRuleResultsMessages(currentRuleResults));
				cartMessages.add(getRuleResultsMessages(currentRuleResults, Arrays.asList("cart")));
			}
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}

		// summe alle discounts ---
		Total discount = calculateTotal(cart, Arrays.asList(CartItemQualifier.DISCOUNT));
		cart.setDiscount(discount);

		// summen ---
		Total total = calculateTotal(cart,
				Arrays.asList(CartItemQualifier.PRODUCT, CartItemQualifier.VPRODUCT, CartItemQualifier.SERVICE,
						CartItemQualifier.DISCOUNT, CartItemQualifier.RESOURCE, CartItemQualifier.SHIPPING));
		cart.setTotal(total);

		// removed failed coupon rules ---
		if (cart instanceof CartRuleAware) {
			CartRuleSet ruleSet = getCartRuleSet(shopContext.getShopCode(), cart);
			if (ruleSet != null) {
				Set<String> tbr = new HashSet<>();
				for (Entry<String, RuleInstance> entry : ruleSet.getCodeRules().entrySet()) {
					if (entry.getValue().getStatus().equals(RuleInstance.Status.DISABLED)) {
						tbr.add(entry.getKey());
					}
				}

				for (String key : tbr) {
					ruleSet.getCodeRules().remove(key);
				}
			}
		}

		// clear rule messages ---
		if (cart instanceof CartRuleAware) {
			((CartRuleAware) cart).getMessages().add(cartMessages);
		}

	}

	private Messages getRuleResultsMessages(Map<String, CartRuleResult> ruleResults, List<String> tags) {
		Messages messages = new MessagesImpl();
		if (MapUtils.isNotEmpty(ruleResults)) {
			for (CartRuleResult result : ruleResults.values()) {
				for (Message message : result.getMessages().getAll()) {
					if (message != null) {
						if (CollectionUtils.isNotEmpty(tags)) {
							if (message.getTags() != null && CollectionUtils.containsAny(message.getTags(), tags)) {
								messages.add(message);
							}
						} else {
							messages.add(message);
						}
					}
				}
			}
		}

		return messages;
	}

	private Messages getRuleResultsMessages(Map<String, CartRuleResult> ruleResults) {
		return getRuleResultsMessages(ruleResults, null);
	}

	private BigDecimal getRuleResultsTotal(Map<String, CartRuleResult> ruleResults) {
		BigDecimal total = BigDecimal.ZERO;
		if (MapUtils.isNotEmpty(ruleResults)) {
			for (CartRuleResult result : ruleResults.values()) {
				if (result.getValues().containsKey("total")) {
					total = total.add(result.getValues().get("total"));
				}
			}
		}

		return total;
	}

	private void calculateItem(CartItem item, String countryOfDelivery, TaxCode customerTaxCode) {
		BigDecimal totalPrice = item.getUnitPrice("std").multiply(item.getQuantity()).setScale(2,
				BigDecimal.ROUND_HALF_UP);
		item.getTotalPrices().put("std", totalPrice);

		BigDecimal taxRate = taxService.getTaxRate(item.getTaxCode(), customerTaxCode, countryOfDelivery);
		item.setTaxPercent(taxRate.multiply(HUNDRED));
	}

	private Total calculateTotal(Cart cart, List<String> qualifiers) {
		TotalImpl total = new TotalImpl();

		Map<TaxCode, Line> lines = total.getLines();
		for (CartItem item : cart.getItems().values()) {
			if (qualifiers.contains(item.getQualifier())) {
				TaxCode taxCode = item.getTaxCode();
				BigDecimal taxPercent = item.getTaxPercent();
				BigDecimal gross = item.getTotalPrice("std").add(item.getDiscount());
				BigDecimal quantity = item.getQuantity();

				total.addGross(gross);
				total.addQuantity(quantity);

				Line line = lines.get(taxCode);
				if (line == null) {
					line = new LineImpl(taxCode, taxPercent);
					lines.put(taxCode, line);
				}
				line.addGross(gross);
				line.addQuantity(quantity);
			}
		}

		BigDecimal cent = new BigDecimal(100);
		for (Line line : lines.values()) {
			line.setNet(
					line.getGross().divide(line.getTaxPercent().add(cent).divide(cent), 2, BigDecimal.ROUND_HALF_UP));
			line.setTax(line.getGross().subtract(line.getNet()));
		}

		return total;
	}

	@Override
	public Messages check(ShopContext shopContext, Cart cart, Customer customer, boolean timing) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public void setCurrency(ShopContext shopContext, Cart cart, Customer customer) {
		// TODO Auto-generated method stub

	}

	@Override
	public void saveCart(Customer customer, Cart cart) throws CartServiceException {
		if (StringUtils.isNotBlank(cart.getShopCode())) {
			mongoCartDaos.get(cart.getShopCode()).saveCart(cart);
		} else {
			throw new CartServiceException("empty shop code!");
		}
	}

	@Override
	public Cart loadActiveCart(ShopContext context, Customer customer) throws CartServiceException {
		List<Cart> carts = mongoCartDaos.get(context.getShopCode()).loadCart(customer,
				Arrays.asList(context.getShopCode()), Arrays.asList("ACTIVE"));
		if (CollectionUtils.isNotEmpty(carts)) {
			return carts.get(0);
		} else {
			return null;
		}
	}

	@Override
	public Cart loadCart(ShopContext context, String id) throws CartServiceException {
		return mongoCartDaos.get(context.getShopCode()).loadCart(id);
	}

	@Override
	public List<Cart> loadCart(ShopContext context, Customer customer, List<String> codes, List<String> status)
			throws CartServiceException {
		return mongoCartDaos.get(context.getShopCode()).loadCart(customer, codes, status);
	}

	@Override
	public void deleteCart(Cart cart) throws CartServiceException {
		mongoCartDaos.get(cart.getShopCode()).deleteCart(cart);
	}

	@Override
	public void merge(Cart storedCart, Cart cart) {
		// TODO Auto-generated method stub

	}

	@Override
	public Cart getNewCart(ShopContext shopContext) throws CartServiceException {
		return mongoCartDaos.get(shopContext.getShopCode()).getNewCart();
	}

	private List<CartItem> getDiscountItems(Map<String, CartRuleResult> ruleResults, Cart cart,
			Total total, ShopContext shopContext) {

		Map<String, BigDecimal> values = new HashMap<>();
		if (MapUtils.isNotEmpty(ruleResults)) {
			for (CartRuleResult result : ruleResults.values()) {
				if (result.getValues().containsKey("total")) {
					String code = result.getCode();

					if (!values.containsKey(code)) {
						values.put(code, BigDecimal.ZERO);
					}
					BigDecimal value = values.get(code).add(result.getValues().get("total"));
					values.put(code, value);
				}
			}
		}

		List<CartItem> items = new ArrayList<CartItem>();
		if (MapUtils.isNotEmpty(values)) {
			for (String code : values.keySet()) {
				BigDecimal value = values.get(code);
				items.addAll(getCartDiscountItems(cart, code, total, value, shopContext));
			}
		}

		return items;
	}

	protected List<CartItem> getCartDiscountItems(Cart cart, String _id, Total total, BigDecimal discount,
			ShopContext shopContext) {
		BigDecimal maxDiscount = total.getGross();
		BigDecimal discountRest = discount.min(maxDiscount).setScale(2, BigDecimal.ROUND_HALF_UP);

		List<CartItem> items = new ArrayList<CartItem>();
		Collection<Line> lines = total.getLines().values();
		for (Iterator<Line> i = lines.iterator(); i.hasNext();) {
			Line line = (Line) i.next();

			BigDecimal resourceGross;
			if (i.hasNext()) {
				BigDecimal q = line.getGross().divide(maxDiscount, 5, BigDecimal.ROUND_HALF_UP);
				resourceGross = q.multiply(discountRest).setScale(2, BigDecimal.ROUND_HALF_UP);
				discountRest = discountRest.subtract(resourceGross);
			} else {
				resourceGross = discountRest;
			}

			String id = _id + "-" + line.getTaxCode().name().toLowerCase();
			CartItem item = cart.getNewItem(id, BigDecimal.ONE);
			item.setId(id);
			item.setQualifier(CartItemQualifier.DISCOUNT);
			item.setIsVisible(true);
			item.setIsVolatile(true);
			item.setIsImmutable(true);
			item.setTaxCode(line.getTaxCode());
			item.setUnitPrice("std", resourceGross);

			item.getTitle().put("title1", translationService.getMessage("product.cart.title1", "text", "discount", null,
					"product.cart.title1", shopContext.getCurrentLocale()));
			item.getTitle().put("title2", translationService.getMessage("product.cart.title2", "text", "discount", null,
					"product.cart.title2", shopContext.getCurrentLocale()));
			item.getTitle().put("title3", translationService.getMessage("product.cart.title3", "text", "discount", null,
					"product.cart.title3", shopContext.getCurrentLocale()));

			items.add(item);
		}

		return items;
	}

	protected CartItem createItem(Cart cart, String id, String qualifier, TaxCode taxCode, BigDecimal quantity,
			Boolean isVisible, Boolean isVolatile, Boolean isImmutable, BigDecimal stdUnitPrice,
			ShopContext shopContext) {
		try {
			CartItem item = cart.getNewItem(id, quantity);
			item.setId(id);
			item.setQualifier(qualifier);
			item.setIsVisible(isVisible);
			item.setIsVolatile(isVolatile);
			item.setIsImmutable(isImmutable);
			item.setTaxCode(taxCode);
			item.setUnitPrice("std", stdUnitPrice);

			item.getTitle().put("title1", translationService.getMessage("product.cart.title1", "text", "shipping", null,
					"product.cart.title1", shopContext.getCurrentLocale()));
			item.getTitle().put("title2", translationService.getMessage("product.cart.title2", "text", "shipping", null,
					"product.cart.title2", shopContext.getCurrentLocale()));
			item.getTitle().put("title3", translationService.getMessage("product.cart.title3", "text", "shipping", null,
					"product.cart.title3", shopContext.getCurrentLocale()));

			return item;
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
			return null;
		}
	}

	protected CartItem getShippingItem(Cart cart, BigDecimal value, ShopContext shopContext) {
		if (value != null && value.compareTo(BigDecimal.ZERO) == 1) {
			CartItem item = cart.getNewItem("shipping", BigDecimal.ONE);
			item.setId("shipping");
			item.setQualifier(CartItemQualifier.SHIPPING);
			item.setIsVisible(true);
			item.setIsVolatile(true);
			item.setIsImmutable(true);
			// TODO: tax code from
			item.setTaxCode(TaxCode.NORMAL);
			item.setUnitPrice("std", value);

			item.getTitle().put("title1", translationService.getMessage("product.cart.title1", "text", "shipping", null,
					"product.cart.title1", shopContext.getCurrentLocale()));
			item.getTitle().put("title2", translationService.getMessage("product.cart.title2", "text", "shipping", null,
					"product.cart.title2", shopContext.getCurrentLocale()));
			item.getTitle().put("title3", translationService.getMessage("product.cart.title3", "text", "shipping", null,
					"product.cart.title3", shopContext.getCurrentLocale()));

			return item;
		} else {
			return null;
		}
	}

	@Override
	public void addCodeRule(String shopCode, Cart cart, String key, RuleInstance rule) {
		CartRuleSet ruleSet = getCartRuleSet(shopCode, cart);
		if (ruleSet != null) {
			ruleSet.addCodeRule(key, rule);
		}
	}

	@Override
	@Deprecated
	public void addCodeRule(Cart cart, String key, RuleInstance rule) {
		CartRuleSet ruleSet = getCartRuleSet(null, cart);
		if (ruleSet != null) {
			ruleSet.addCodeRule(key, rule);
		}
	}

	@Override
	public void clearCodeRules(String shopCode, Cart cart) {
		CartRuleSet ruleSet = getCartRuleSet(shopCode, cart);
		if (ruleSet != null) {
			ruleSet.clearCodeRules();
		}
	}

	@Override
	@Deprecated
	public void clearCodeRules(Cart cart) {
		CartRuleSet ruleSet = getCartRuleSet(null, cart);
		if (ruleSet != null) {
			ruleSet.clearCodeRules();
		}
	}

	@Override
	public Map<String, RuleInstance> getCodeRules(String shopCode, Cart cart) {
		CartRuleSet ruleSet = getCartRuleSet(shopCode, cart);
		if (ruleSet != null) {
			return ruleSet.getCodeRules();
		}

		return null;
	}

	@Override
	@Deprecated
	public Map<String, RuleInstance> getCodeRules(Cart cart) {
		CartRuleSet ruleSet = getCartRuleSet(null, cart);
		if (ruleSet != null) {
			return ruleSet.getCodeRules();
		}

		return null;
	}

	@Override
	public void setRuleUse(ShopContext context, Cart cart, Customer customer, String qualifier)
			throws CartServiceException {
		try {
			if (cart instanceof CartRuleAware) {
				CartRuleSet ruleSet = ((CartRuleAware) cart).getCartRuleSet();
				for (Entry<String, RuleInstance> entry : ruleSet.entrySet()) {
					String key = entry.getKey();
					RuleInstance rule = entry.getValue();

					if (RuleInstance.Status.PASSED.equals(rule.getStatus())) {
						if (rule.getMaxIndividualUse() != null) {
							cartRuleDao.setUse(key, customer.getId(), 1, rule.getMaxIndividualUse(), qualifier,
									new Date());
							rule.setIsUsed(true);
						}
					}
				}
			}
		} catch (Exception e) {
			log.info(ExceptionUtils.getMessage(e));
			throw new CartServiceException();
		}
	}

	@Override
	public void unsetRuleUse(ShopContext context, Cart cart, Customer customer, String qualifier) {
		try {
			if (cart instanceof CartRuleAware) {
				CartRuleSet ruleSet = ((CartRuleAware) cart).getCartRuleSet();
				for (Entry<String, RuleInstance> entry : ruleSet.entrySet()) {
					String key = entry.getKey();
					RuleInstance rule = entry.getValue();

					if (rule.getMaxIndividualUse() != null && rule.getIsUsed()) {
						cartRuleDao.unsetUse(key, customer.getId(), 1, qualifier, new Date());
						rule.setIsUsed(false);
					}
				}
			}
		} catch (Exception e) {
			log.error(ExceptionUtils.getStackTrace(e));
		}
	}

	private Map<String, CartRuleResult> applyCartRules(String shopCode, Cart cart, Customer customer, Intention intention,
			CartRuleContext context, Map<String, CartRuleResult> results) {
		Map<String, CartRuleResult> currentResults = new LinkedHashMap<>();

		CartRuleSet ruleSet = getCartRuleSet(shopCode, cart);
		if (ruleSet != null) {
			for (Entry<String, RuleInstance> entry : ruleSet.entrySet()) {
				RuleInstance rule = entry.getValue();

				if (rule.getIntentions().contains(intention)) {
					if (!RuleInstance.Status.DISABLED.equals(rule.getStatus())) {
						CartRuleResult result = rule.apply(cart, intention, context, results);
						currentResults.put(result.getId(), result);
					}
				}
			}
		}

		return currentResults;
	}

	@Override
	public RuleInstance getCodeRuleInstance(String shopCode, String code) {
		return ruleStore.instanceByCode(shopCode, code);
	}

	@Override
	@Deprecated
	public RuleInstance getCodeRuleInstance(String code) {
		return ruleStore.instanceByCode(null, code);
	}

	@Override
	public Messages checkCartRules(String shopCode, Cart cart, Customer customer, Date date) {
		Messages messages = new MessagesImpl();

		CartRuleSet ruleSet = getCartRuleSet(shopCode, cart);
		if (ruleSet != null) {
			for (Entry<String, RuleInstance> entry : ruleSet.entrySet()) {
				String key = entry.getKey();
				RuleInstance rule = entry.getValue();
				messages.add(checkCartRule(key, rule, date, customer));
			}
		}

		return messages;
	}

	@Override
	@Deprecated
	public Messages checkCartRules(Cart cart, Customer customer, Date date) {
		Messages messages = new MessagesImpl();

		CartRuleSet ruleSet = getCartRuleSet(null, cart);
		if (ruleSet != null) {
			for (Entry<String, RuleInstance> entry : ruleSet.entrySet()) {
				String key = entry.getKey();
				RuleInstance rule = entry.getValue();
				messages.add(checkCartRule(key, rule, date, customer));
			}
		}

		return messages;
	}

	@Override
	public Message checkCartRule(String key, RuleInstance rule, Date date, Customer customer) {
		if (!date.before(rule.getEnd())) {
			rule.setStatus(RuleInstance.Status.DISABLED);
			return rule.getDisabledMessage();
		}

		if (date.before(rule.getStart())) {
			rule.setStatus(RuleInstance.Status.FAILED);
			return rule.getFailedMessage();
		}

		if (rule.getMaxIndividualUse() != null) {
			if (customer != null) {
				if (!customer.getId().equals(rule.getCustomerId()) || rule.getUseCount() == null) {
					rule.setUseCount(cartRuleDao.getUseCount(key, customer.getId()));
					rule.setCustomerId(customer.getId());
				}
				if (rule.getUseCount() >= rule.getMaxIndividualUse()) {
					rule.setStatus(RuleInstance.Status.DISABLED);
					return rule.getDisabledMessage();
				}
			}
		}

		return null;
	}

	public Message __checkCartRule(String key, RuleInstance rule, Date date, Customer customer) {
		if (!date.before(rule.getStart()) && date.before(rule.getEnd())) {
			if (rule.getMaxIndividualUse() != null) {
				if (customer != null) {
					if (!customer.getId().equals(rule.getCustomerId()) || rule.getUseCount() == null) {
						rule.setUseCount(cartRuleDao.getUseCount(key, customer.getId()));
						rule.setCustomerId(customer.getId());
					}
				}

				if (rule.getMaxIndividualUse() > rule.getUseCount()) {
					rule.setStatus(RuleInstance.Status.ENABLED);
					// TODO: get code,tags and args from rule implementation
					return new MessageImpl(key, Message.TYPE_INFO, rule.getId(), Arrays.asList(Message.TAG_BOX),
							Arrays.asList(RuleInstance.Status.ENABLED));
				} else {
					rule.setStatus(RuleInstance.Status.DISABLED);
					// TODO: get code,tags and args from rule implementation
					return new MessageImpl(key, Message.TYPE_INFO, rule.getId(), Arrays.asList(Message.TAG_BOX),
							Arrays.asList(RuleInstance.Status.DISABLED));
				}
			} else {
				rule.setStatus(RuleInstance.Status.ENABLED);
				// TODO: get code,tags and args from rule implementation
				return new MessageImpl(key, Message.TYPE_INFO, rule.getId(), Arrays.asList(Message.TAG_BOX),
						Arrays.asList(RuleInstance.Status.ENABLED));
			}
		} else {
			rule.setStatus(RuleInstance.Status.DISABLED);
			// TODO: get code,tags and args from rule implementation
			return new MessageImpl(key, Message.TYPE_INFO, rule.getId(), Arrays.asList(Message.TAG_BOX),
					Arrays.asList(RuleInstance.Status.DISABLED));
		}
	}

	public void setMongoCartDaos(Map<String, MongoCartDao> mongoCartDaos) {
		this.mongoCartDaos = mongoCartDaos;
	}

	protected CartRuleSet getCartRuleSet(String shopCode, Cart cart) {
		if (cart instanceof CartRuleAware) {
			CartRuleSet cartRuleSet = ((CartRuleAware) cart).getCartRuleSet();

			long offset = 1000 * 60 * 30;
			long last = cartRuleSet.getUpdatedAt().getTime();
			long now = new Date().getTime();

			if (now > last + offset) {
				for (RuleInstance inst : ruleStore.commonInstances(shopCode)) {
					cartRuleSet.addCommonRule(inst.getId(), inst);
					cartRuleSet.setUpdatedAt(new Date());
				}
			}

			return cartRuleSet;
		} else {
			return null;
		}
	}

	@Override
	public void setIndividualUse(Coupon coupon, String customerId, String orderId, Cart cart, String shopCode)
			throws CartServiceException {
		// TODO Auto-generated method stub
		throw new RuntimeException("not implemented");
	}

	@Override
	public void setStatus(Customer customer, Cart cart, String status) throws CartServiceException {
		cart.setStatus(status);
		saveCart(customer, cart);
	}

}