Author: hlship
Date: Thu Jun  3 23:10:46 2010
New Revision: 951199

URL: http://svn.apache.org/viewvc?rev=951199&view=rev
Log:
Be more careful about deferring computation (of the first value in a lazy flow) 
to ensure that Mappers are not invoked unless truly needed

Added:
    
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMappedValue.java
      - copied, changed from r951076, 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
    
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyValue.java
      - copied, changed from r951076, 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
    
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/StaticValue.java
      - copied, changed from r951076, 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
    
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
   (with props)
Modified:
    
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyContinuation.java
    
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyFlow.java
    
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
    
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java

Modified: 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyContinuation.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyContinuation.java?rev=951199&r1=951198&r2=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyContinuation.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyContinuation.java
 Thu Jun  3 23:10:46 2010
@@ -21,18 +21,29 @@ package org.apache.tapestry5.func;
  */
 public class LazyContinuation<T>
 {
-    private final T nextValue;
+    private final LazyValue<T> nextValue;
 
     private final LazyFunction<T> nextFunction;
 
-    public LazyContinuation(T next, LazyFunction<T> nextFunction)
+    public LazyContinuation(T nextValue, LazyFunction<T> nextFunction)
     {
-        this.nextValue = next;
+        this(new StaticValue<T>(nextValue), nextFunction);
+    }
+
+    public LazyContinuation(LazyValue<T> nextValue, LazyFunction<T> 
nextFunction)
+    {
+        assert nextValue != null;
+        assert nextFunction != null;
+
+        this.nextValue = nextValue;
         this.nextFunction = nextFunction;
     }
 
-    /** Returns the next value computed by the lazy function. */
-    public T nextValue()
+    /**
+     * Returns, indirectly, the next value computed by the lazy function. The 
LazyValue represents
+     * a deferred computation.
+     */
+    public LazyValue<T> nextValue()
     {
         return nextValue;
     }

Modified: 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyFlow.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyFlow.java?rev=951199&r1=951198&r2=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyFlow.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyFlow.java
 Thu Jun  3 23:10:46 2010
@@ -18,15 +18,21 @@ class LazyFlow<T> extends AbstractFlow<T
 {
     // All instance variables guarded by this
 
-    private boolean resolved;
+    // Retained up until resolve() is called
+    
+    private LazyFunction<T> lazyFunction;
 
+    // Set inside resolve()
     private boolean empty;
 
-    private T first;
+    // Set inside resolve(), used and discarded inside first()
+    private LazyValue<T> lazyFirst;
 
+    // Set inside resolve()
     private Flow<T> rest;
 
-    private LazyFunction<T> lazyFunction;
+    // Set inside first()
+    private T first;
 
     public LazyFlow(LazyFunction<T> lazyFunction)
     {
@@ -37,6 +43,16 @@ class LazyFlow<T> extends AbstractFlow<T
     {
         resolve();
 
+        // Immediately after resolving, all we have is the function to call to 
get
+        // the first object. And once we get that object, we don't need (or 
want) the
+        // function.
+
+        if (lazyFirst != null)
+        {
+            first = lazyFirst.get();
+            lazyFirst = null;
+        }
+
         return first;
     }
 
@@ -56,7 +72,7 @@ class LazyFlow<T> extends AbstractFlow<T
 
     private synchronized void resolve()
     {
-        if (resolved)
+        if (lazyFunction == null)
             return;
 
         LazyContinuation<T> continuation = lazyFunction.next();
@@ -68,13 +84,11 @@ class LazyFlow<T> extends AbstractFlow<T
         }
         else
         {
-            first = continuation.nextValue();
+            lazyFirst = continuation.nextValue();
 
             rest = new LazyFlow<T>(continuation.nextFunction());
         }
 
-        resolved = true;
-        
         lazyFunction = null;
     }
 }

Copied: 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMappedValue.java
 (from r951076, 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java)
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMappedValue.java?p2=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMappedValue.java&p1=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java&r1=951076&r2=951199&rev=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMappedValue.java
 Thu Jun  3 23:10:46 2010
@@ -14,26 +14,20 @@
 
 package org.apache.tapestry5.func;
 
-class LazyMapper<T, X> implements LazyFunction<X>
+class LazyMappedValue<T, X> implements LazyValue<X>
 {
-    private final Mapper<T, X> mapper;
+    private final T input;
 
-    private final Flow<T> flow;
+    private final Mapper<T, X> mapper;
 
-    public LazyMapper(Mapper<T, X> mapper, Flow<T> flow)
+    public LazyMappedValue(T input, Mapper<T, X> mapper)
     {
+        this.input = input;
         this.mapper = mapper;
-        this.flow = flow;
     }
 
-    public LazyContinuation<X> next()
+    public X get()
     {
-        if (flow.isEmpty())
-            return null;
-
-        X mapped = mapper.map(flow.first());
-
-        return new LazyContinuation<X>(mapped, new LazyMapper<T, X>(mapper, 
flow.rest()));
+        return mapper.map(input);
     }
-
 }

Modified: 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java?rev=951199&r1=951198&r2=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
 Thu Jun  3 23:10:46 2010
@@ -31,9 +31,8 @@ class LazyMapper<T, X> implements LazyFu
         if (flow.isEmpty())
             return null;
 
-        X mapped = mapper.map(flow.first());
-
-        return new LazyContinuation<X>(mapped, new LazyMapper<T, X>(mapper, 
flow.rest()));
+        return new LazyContinuation<X>(new LazyMappedValue<T, X>(flow.first(), 
mapper), new LazyMapper<T, X>(mapper,
+                flow.rest()));
     }
 
 }

Copied: 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyValue.java
 (from r951076, 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java)
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyValue.java?p2=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyValue.java&p1=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java&r1=951076&r2=951199&rev=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyValue.java
 Thu Jun  3 23:10:46 2010
@@ -14,26 +14,11 @@
 
 package org.apache.tapestry5.func;
 
-class LazyMapper<T, X> implements LazyFunction<X>
+/**
+ * A function that returns a value, allowing the computation of that value to 
be deferred as late as possible.
+ */
+public interface LazyValue<T>
 {
-    private final Mapper<T, X> mapper;
-
-    private final Flow<T> flow;
-
-    public LazyMapper(Mapper<T, X> mapper, Flow<T> flow)
-    {
-        this.mapper = mapper;
-        this.flow = flow;
-    }
-
-    public LazyContinuation<X> next()
-    {
-        if (flow.isEmpty())
-            return null;
-
-        X mapped = mapper.map(flow.first());
-
-        return new LazyContinuation<X>(mapped, new LazyMapper<T, X>(mapper, 
flow.rest()));
-    }
-
+    /** Compute and return the value. */
+    T get();
 }

Copied: 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/StaticValue.java
 (from r951076, 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java)
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/StaticValue.java?p2=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/StaticValue.java&p1=tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java&r1=951076&r2=951199&rev=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/LazyMapper.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/main/java/org/apache/tapestry5/func/StaticValue.java
 Thu Jun  3 23:10:46 2010
@@ -4,7 +4,7 @@
 // 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
+//     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,
@@ -14,26 +14,17 @@
 
 package org.apache.tapestry5.func;
 
-class LazyMapper<T, X> implements LazyFunction<X>
+class StaticValue<T> implements LazyValue<T>
 {
-    private final Mapper<T, X> mapper;
+    private final T nextValue;
 
-    private final Flow<T> flow;
-
-    public LazyMapper(Mapper<T, X> mapper, Flow<T> flow)
+    StaticValue(T nextValue)
     {
-        this.mapper = mapper;
-        this.flow = flow;
+        this.nextValue = nextValue;
     }
 
-    public LazyContinuation<X> next()
+    public T get()
     {
-        if (flow.isEmpty())
-            return null;
-
-        X mapped = mapper.map(flow.first());
-
-        return new LazyContinuation<X>(mapped, new LazyMapper<T, X>(mapper, 
flow.rest()));
+        return nextValue;
     }
-
-}
+}
\ No newline at end of file

Modified: 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java?rev=951199&r1=951198&r2=951199&view=diff
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java
 (original)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/FuncTest.java
 Thu Jun  3 23:10:46 2010
@@ -25,35 +25,7 @@ import org.testng.annotations.Test;
 
 public class FuncTest extends BaseFuncTest
 {
-    protected Mapper<Integer, Flow<Integer>> sequencer = new Mapper<Integer, 
Flow<Integer>>()
-    {
-
-        public Flow<Integer> map(Integer value)
-        {
-            Flow<Integer> flow = F.flow();
-
-            for (int i = 0; i < value; i++)
-                flow = flow.append(value);
-
-            return flow;
-        }
-    };
-
-    @Test
-    public void map()
-    {
-        List<String> source = Arrays.asList("Mary", "had", "a", "little", 
"lamb");
-
-        List<Integer> lengths = F.flow(source).map(stringToLength).toList();
-
-        assertListsEquals(lengths, 4, 3, 1, 6, 4);
-    }
-
-    @Test
-    public void flow_map()
-    {
-        assertFlowValues(F.flow("Mary", "had", "a", "little", 
"lamb").map(stringToLength), 4, 3, 1, 6, 4);
-    }
+   
 
     @Test
     public void flow_reverse()
@@ -518,20 +490,7 @@ public class FuncTest extends BaseFuncTe
         assertListsEquals(result, "a", "had", "Mary", "lamb");
     }
 
-    @Test
-    public void map_of_filtered_empty_is_empty()
-    {
-        assertTrue(filteredEmpty.map(new Mapper<Integer, Integer>()
-        {
-            public Integer map(Integer value)
-            {
-                unreachable();
-
-                return value;
-            }
-        }).isEmpty());
-    }
-
+ 
     @Test
     public void each_on_empty_flow()
     {
@@ -571,23 +530,6 @@ public class FuncTest extends BaseFuncTe
         }, initial), initial);
     }
 
-    @Test
-    public void mapcat_on_empty_flow_is_empty()
-    {
-        Flow<Integer> flow = F.flow();
-
-        assertSame(flow.mapcat(sequencer), flow);
-
-        assertTrue(filteredEmpty.mapcat(sequencer).isEmpty());
-    }
-
-    @Test
-    public void mapcat()
-    {
-        Flow<Integer> flow = F.flow(3, 1, 2);
-
-        assertFlowValues(flow.mapcat(sequencer), 3, 3, 3, 1, 2, 2);
-    }
 
     @Test
     public void count_of_the_empty_flow_is_zero()
@@ -610,23 +552,7 @@ public class FuncTest extends BaseFuncTe
         assertEquals(flow.filter(F.notNull()).count(), 5);
     }
 
-    @Test
-    public void count_of_a_mapped_filtered_empty_flow()
-    {
-        Flow<Integer> flow = F.flow("Mary", "had", 
"etc.").filter(F.isNull()).map(stringToLength);
-
-        assertTrue(flow.isEmpty());
-        assertEquals(flow.count(), 0);
-    }
-
-    @Test
-    public void toString_mapper()
-    {
-        Flow<Integer> flow = F.flow(1, 2, 3);
-
-        assertFlowValues(flow.map(F.<Integer> stringValueOf()), "1", "2", "3");
-    }
-
+ 
     @Test
     public void concat_empty_list()
     {

Added: 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
URL: 
http://svn.apache.org/viewvc/tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java?rev=951199&view=auto
==============================================================================
--- 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
 (added)
+++ 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
 Thu Jun  3 23:10:46 2010
@@ -0,0 +1,132 @@
+// 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.Arrays;
+import java.util.List;
+import java.util.Map;
+import java.util.concurrent.atomic.AtomicInteger;
+
+import org.testng.annotations.Test;
+
+public class MapperTest extends BaseFuncTest
+{
+    protected Mapper<Integer, Flow<Integer>> sequencer = new Mapper<Integer, 
Flow<Integer>>()
+    {
+
+        public Flow<Integer> map(Integer value)
+        {
+            Flow<Integer> flow = F.flow();
+
+            for (int i = 0; i < value; i++)
+                flow = flow.append(value);
+
+            return flow;
+        }
+    };
+
+    @Test
+    public void map()
+    {
+        List<String> source = Arrays.asList("Mary", "had", "a", "little", 
"lamb");
+
+        List<Integer> lengths = F.flow(source).map(stringToLength).toList();
+
+        assertListsEquals(lengths, 4, 3, 1, 6, 4);
+    }
+
+    @Test
+    public void flow_map()
+    {
+        assertFlowValues(F.flow("Mary", "had", "a", "little", 
"lamb").map(stringToLength), 4, 3, 1, 6, 4);
+    }
+
+    @Test
+    public void map_of_filtered_empty_is_empty()
+    {
+        assertTrue(filteredEmpty.map(new Mapper<Integer, Integer>()
+        {
+            public Integer map(Integer value)
+            {
+                unreachable();
+
+                return value;
+            }
+        }).isEmpty());
+    }
+
+    @Test
+    public void mapcat_on_empty_flow_is_empty()
+    {
+        Flow<Integer> flow = F.flow();
+
+        assertSame(flow.mapcat(sequencer), flow);
+
+        assertTrue(filteredEmpty.mapcat(sequencer).isEmpty());
+    }
+
+    @Test
+    public void mapcat()
+    {
+        Flow<Integer> flow = F.flow(3, 1, 2);
+
+        assertFlowValues(flow.mapcat(sequencer), 3, 3, 3, 1, 2, 2);
+    }
+
+    @Test
+    public void count_of_a_mapped_filtered_empty_flow()
+    {
+        Flow<Integer> flow = F.flow("Mary", "had", 
"etc.").filter(F.isNull()).map(stringToLength);
+
+        assertTrue(flow.isEmpty());
+        assertEquals(flow.count(), 0);
+    }
+
+    @Test
+    public void toString_mapper()
+    {
+        Flow<Integer> flow = F.flow(1, 2, 3);
+
+        assertFlowValues(flow.map(F.<Integer> stringValueOf()), "1", "2", "3");
+    }
+
+    @Test
+    public void no_excess_mapping()
+    {
+        final AtomicInteger count = new AtomicInteger();
+
+        Mapper<Integer, Integer> doubler = new Mapper<Integer, Integer>()
+        {
+            public Integer map(Integer value)
+            {
+                count.incrementAndGet();
+
+                return value * 2;
+            }
+        };
+
+        assertFlowValues(F.range(1, 
100).filter(F.gt(10)).map(doubler).take(3), 22, 24, 26);
+
+        assertEquals(count.get(), 3);
+
+        count.set(0);
+
+        // Because of laziness, its possible to count all the values in some 
mapped lists, without
+        // ever actually running the mapper to determine the final value.
+        
+        assertEquals(F.range(1, 100).map(doubler).count(), 99);
+        assertEquals(count.get(), 0);
+    }
+}

Propchange: 
tapestry/tapestry5/trunk/tapestry-func/src/test/java/org/apache/tapestry5/func/MapperTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to