// Copyright 2010 The Apache Software Foundation
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package org.apache.tapestry5.func;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
import java.util.Iterator;
import java.util.List;

import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.Defense;

class FlowImpl<T> implements Flow<T>
{
    private final List<T> values;

    FlowImpl(Collection<T> values)
    {
        this(new ArrayList<T>(values));
    }

    FlowImpl(List<T> values)
    {
        this.values = values;
    }

    public Flow<T> each(Worker<? super T> worker)
    {
        for (T t : values)
        {
            worker.work(t);
        }

        return this;
    }

    public Flow<T> filter(Predicate<? super T> predicate)
    {
        Defense.notNull(predicate, "predicate");

        if (isEmpty())
            return this;

        List<T> result = new ArrayList<T>(values.size());

        for (T item : values)
        {
            if (predicate.accept(item))
                result.add(item);
        }

        return new FlowImpl<T>(result);
    }

    public Flow<T> remove(Predicate<? super T> predicate)
    {
        Defense.notNull(predicate, "predicate");

        return filter(predicate.invert());
    }

    public <X> Flow<X> map(Mapper<T, X> mapper)
    {
        Defense.notNull(mapper, "mapper");

        if (values.isEmpty())
        {
            List<X> empty = Collections.emptyList();
            return new FlowImpl<X>(empty);
        }

        List<X> newValues = new ArrayList<X>(values.size());

        for (T value : values)
        {
            newValues.add(mapper.map(value));
        }

        return new FlowImpl<X>(newValues);
    }

    public <A> A reduce(Reducer<A, T> reducer, A initial)
    {
        Defense.notNull(reducer, "reducer");

        A accumulator = initial;

        for (T value : values)
        {
            accumulator = reducer.reduce(accumulator, value);
        }

        return accumulator;
    }

    /** Returns the values in the flow as an unmodifiable List. */
    public List<T> toList()
    {
        if (values.isEmpty())
            return Collections.emptyList();

        return Collections.unmodifiableList(values);
    }

    public Flow<T> reverse()
    {
        if (values.size() < 2)
            return this;

        List<T> newValues = CollectionFactory.newList(values);

        Collections.reverse(newValues);

        return new FlowImpl<T>(newValues);
    }

    public boolean isEmpty()
    {
        return values.isEmpty();
    }

    public Flow<T> concat(Flow<? extends T> other)
    {
        Defense.notNull(other, "other");

        if (other.isEmpty())
            return this;

        List<T> newValues = copy();
        newValues.addAll(other.toList());

        return new FlowImpl<T>(newValues);
    }

    private List<T> copy()
    {
        return new ArrayList<T>(values);
    }

    public Flow<T> concat(List<? extends T> list)
    {
        return concat(F.flow(list));
    }

    public <V extends T> Flow<T> append(V... values)
    {
        return concat(F.flow(values));
    }

    @SuppressWarnings("unchecked")
    public Flow<T> sort()
    {
        if (values.size() < 2)
            return this;

        List<Comparable> newValues = (List<Comparable>) copy();

        Collections.sort(newValues);

        return new FlowImpl<T>((List<T>) newValues);
    }

    public Flow<T> sort(Comparator<? super T> comparator)
    {
        Defense.notNull(comparator, "comparator");

        if (values.size() < 2)
            return this;

        List<T> newValues = copy();

        Collections.sort(newValues, comparator);

        return new FlowImpl<T>(newValues);
    }

    public Iterator<T> iterator()
    {
        // Kind of inefficient but it works.

        return toList().iterator();
    }

}

@@ -661,16 +661,41 @@
 ayList;%0A
+import java.util.Arrays;%0A
 import j
@@ -1003,23 +1003,19 @@
 e final 
-List%3CT%3E
+T%5B%5D
  values;
@@ -1024,91 +1024,38 @@
     
-FlowImpl(Collection%3CT%3E values)%0A    %7B%0A        this(new ArrayList%3CT%3E(values));%0A    %7D%0A
+@SuppressWarnings(%22unchecked%22)
 %0A   
@@ -1064,20 +1064,26 @@
 lowImpl(
-List
+Collection
 %3CT%3E valu
@@ -1114,22 +1114,38 @@
 alues = 
-values
+(T%5B%5D) values.toArray()
 ;%0A    %7D%0A
@@ -1509,38 +1509,38 @@
 yList%3CT%3E(values.
-size()
+length
 );%0A%0A        for 
@@ -1965,39 +1965,32 @@
 );%0A%0A        if (
-values.
 isEmpty())%0A     
@@ -2153,22 +2153,22 @@
 (values.
-size()
+length
 );%0A%0A    
@@ -2717,23 +2717,16 @@
     if (
-values.
 isEmpty(
@@ -2792,32 +2792,17 @@
 urn 
-Collections.unmodifiable
+Arrays.as
 List
@@ -2864,38 +2864,38 @@
      if (values.
-size()
+length
  %3C 2)%0A          
@@ -3122,25 +3122,27 @@
  values.
-isEmpty()
+length == 0
 ;%0A    %7D%0A
@@ -3490,23 +3490,38 @@
 List%3CT%3E(
+Arrays.asList(
 values)
+)
 ;%0A    %7D%0A
@@ -3804,38 +3804,38 @@
      if (values.
-size()
+length
  %3C 2)%0A          
@@ -4152,14 +4152,14 @@
 ues.
-size()
+length
  %3C 2

Change FlowImpl to use an object array, not a List, to store the values in the Flow