You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by hl...@apache.org on 2013/08/17 02:49:11 UTC

git commit: TAP5-1446: Coercion from List to SelectModel should use the SelectModelFactory service

Updated Branches:
  refs/heads/master e16e5fb50 -> cc5c53df4


TAP5-1446: Coercion from List to SelectModel should use the SelectModelFactory service


Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/cc5c53df
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/cc5c53df
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/cc5c53df

Branch: refs/heads/master
Commit: cc5c53df446417924a2ce79151c62192c9645453
Parents: e16e5fb
Author: Howard M. Lewis Ship <hl...@apache.org>
Authored: Fri Aug 16 17:49:05 2013 -0700
Committer: Howard M. Lewis Ship <hl...@apache.org>
Committed: Fri Aug 16 17:49:05 2013 -0700

----------------------------------------------------------------------
 .../internal/DefaultValueLabelProvider.java     | 39 +++++++++++++
 .../services/EnumValueLabelProvider.java        | 41 ++++++++++++++
 .../services/PropertyValueLabelProvider.java    | 58 ++++++++++++++++++++
 .../services/SelectModelFactoryImpl.java        | 47 +++++++++-------
 .../tapestry5/modules/TapestryModule.java       | 38 +++++++++++--
 .../tapestry5/services/SelectModelFactory.java  | 21 +++++--
 .../tapestry5/services/ValueLabelProvider.java  | 33 +++++++++++
 .../src/test/app1/SelectModelCoercionDemo.tml   | 20 +++++++
 .../test/app1/SelectModelFromObjectsDemo.tml    | 20 +++++++
 tapestry-core/src/test/app1/css/app.css         |  3 -
 .../tapestry5/integration/app1/FormTests.java   | 24 ++++++++
 .../tapestry5/integration/app1/pages/Index.java |  6 ++
 .../app1/pages/SelectModelCoercionDemo.java     | 25 +++++++++
 .../app1/pages/SelectModelFromObjectsDemo.java  | 32 +++++++++++
 .../integration/app1/services/AppModule.java    | 12 +++-
 .../integration/app1/components/Border.tml      |  2 +-
 16 files changed, 385 insertions(+), 36 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValueLabelProvider.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValueLabelProvider.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValueLabelProvider.java
new file mode 100644
index 0000000..12309ac
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/DefaultValueLabelProvider.java
@@ -0,0 +1,39 @@
+// Copyright 2013 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.internal;
+
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.services.ValueLabelProvider;
+
+/**
+ * Uses the {@link TypeCoercer} to convert an arbitrary value to a string.
+ *
+ * @since 5.4
+ */
+public class DefaultValueLabelProvider implements ValueLabelProvider<Object>
+{
+    private final TypeCoercer coercer;
+
+    public DefaultValueLabelProvider(TypeCoercer coercer)
+    {
+        this.coercer = coercer;
+    }
+
+    public String getLabel(Object value)
+    {
+        return coercer.coerce(value, String.class);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EnumValueLabelProvider.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EnumValueLabelProvider.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EnumValueLabelProvider.java
new file mode 100644
index 0000000..ff64ae6
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/EnumValueLabelProvider.java
@@ -0,0 +1,41 @@
+// Copyright 2013 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.internal.services;
+
+import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.services.ValueLabelProvider;
+
+/**
+ * Provides a label from enum.
+ * 
+ * @since 5.4
+ */
+public class EnumValueLabelProvider<T extends Enum> implements ValueLabelProvider<T>
+{
+    private final Messages messages;
+
+    public EnumValueLabelProvider(Messages messages)
+    {
+        this.messages = messages;
+    }
+
+    public String getLabel(T value)
+    {
+        return TapestryInternalUtils.getLabelForEnum(messages, value.getDeclaringClass()
+                .getSimpleName(), value);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyValueLabelProvider.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyValueLabelProvider.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyValueLabelProvider.java
new file mode 100644
index 0000000..f296d25
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/PropertyValueLabelProvider.java
@@ -0,0 +1,58 @@
+// Copyright 2013  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.internal.services;
+
+import org.apache.tapestry5.ValueEncoder;
+import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
+import org.apache.tapestry5.ioc.services.PropertyAccess;
+import org.apache.tapestry5.ioc.services.PropertyAdapter;
+import org.apache.tapestry5.services.ValueEncoderSource;
+import org.apache.tapestry5.services.ValueLabelProvider;
+
+/**
+ * Provides a label from a property of the passed object.
+ * 
+ * @since 5.4
+ */
+public class PropertyValueLabelProvider implements ValueLabelProvider<Object>
+{
+    private final PropertyAccess propertyAccess;
+    private final ValueEncoderSource valueEncoderSource;
+    private final String labelProperty;
+
+    public PropertyValueLabelProvider(ValueEncoderSource valueEncoderSource,
+            PropertyAccess propertyAccess, String labelProperty)
+    {
+        this.valueEncoderSource = valueEncoderSource;
+        this.propertyAccess = propertyAccess;
+        this.labelProperty = labelProperty;
+    }
+
+    @SuppressWarnings({ "unchecked", "rawtypes" })
+    public String getLabel(Object object)
+    {
+        final ClassPropertyAdapter classPropertyAdapter = this.propertyAccess.getAdapter(object);
+
+        final PropertyAdapter propertyAdapter = classPropertyAdapter
+                .getPropertyAdapter(labelProperty);
+
+        final ValueEncoder encoder = this.valueEncoderSource.getValueEncoder(propertyAdapter
+                .getType());
+
+        final Object label = propertyAdapter.get(object);
+
+        return encoder.toClient(label);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SelectModelFactoryImpl.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SelectModelFactoryImpl.java b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SelectModelFactoryImpl.java
index 4cee478..c5c1aee 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SelectModelFactoryImpl.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/internal/services/SelectModelFactoryImpl.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010-2013 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.
@@ -13,51 +13,56 @@
 // limitations under the License.
 package org.apache.tapestry5.internal.services;
 
-import java.util.List;
-
 import org.apache.tapestry5.OptionModel;
 import org.apache.tapestry5.SelectModel;
-import org.apache.tapestry5.ValueEncoder;
 import org.apache.tapestry5.internal.OptionModelImpl;
 import org.apache.tapestry5.internal.SelectModelImpl;
 import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
-import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
 import org.apache.tapestry5.ioc.services.PropertyAccess;
-import org.apache.tapestry5.ioc.services.PropertyAdapter;
 import org.apache.tapestry5.services.SelectModelFactory;
 import org.apache.tapestry5.services.ValueEncoderSource;
+import org.apache.tapestry5.services.ValueLabelProvider;
+
+import java.util.List;
 
 public class SelectModelFactoryImpl implements SelectModelFactory
 {
     private final PropertyAccess propertyAccess;
+
     private final ValueEncoderSource valueEncoderSource;
 
-    public SelectModelFactoryImpl(final PropertyAccess propertyAccess,
-            final ValueEncoderSource valueEncoderSource)
+    private final ValueLabelProvider<Object> valueLabelProvider;
+
+    public SelectModelFactoryImpl(PropertyAccess propertyAccess, ValueEncoderSource valueEncoderSource, ValueLabelProvider<Object> valueLabelProvider)
     {
-        super();
         this.propertyAccess = propertyAccess;
         this.valueEncoderSource = valueEncoderSource;
+        this.valueLabelProvider = valueLabelProvider;
     }
 
-    @SuppressWarnings("unchecked")
-    public SelectModel create(final List<?> objects, final String labelProperty)
-    {
-        final List<OptionModel> options = CollectionFactory.newList();
 
-        for (final Object object : objects)
-        {
-            final ClassPropertyAdapter classPropertyAdapter = this.propertyAccess
-                    .getAdapter(object);
+    public SelectModel create(List<?> objects, String labelProperty)
+    {
+        PropertyValueLabelProvider propertyValueLabelProvider = new PropertyValueLabelProvider(
+                valueEncoderSource, propertyAccess, labelProperty);
 
-            final PropertyAdapter propertyAdapter = classPropertyAdapter.getPropertyAdapter(labelProperty);
+        return createSelectModel(objects, propertyValueLabelProvider);
+    }
 
-            final ValueEncoder encoder = this.valueEncoderSource.getValueEncoder(propertyAdapter.getType());
+    public SelectModel create(List<?> objects)
+    {
+        return createSelectModel(objects, valueLabelProvider);
+    }
 
-            final Object label = propertyAdapter.get(object);
+    private SelectModel createSelectModel(List<?> objects, ValueLabelProvider<Object> labelProvider)
+    {
+        final List<OptionModel> options = CollectionFactory.newList();
 
-            options.add(new OptionModelImpl(encoder.toClient(label), object));
+        for (Object object : objects)
+        {
+            String label = labelProvider.getLabel(object);
 
+            options.add(new OptionModelImpl(label, object));
         }
 
         return new SelectModelImpl(null, options);

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
index 17ddbeb..8b7b969 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/modules/TapestryModule.java
@@ -921,8 +921,7 @@ public final class TapestryModule
      */
     public static void contributeTypeCoercer(Configuration<CoercionTuple> configuration,
 
-                                             @Builtin
-                                             TypeCoercer coercer,
+                                             final ObjectLocator objectLocator,
 
                                              @Builtin
                                              final ThreadLocale threadLocale,
@@ -980,10 +979,19 @@ public final class TapestryModule
 
         configuration.add(CoercionTuple.create(List.class, SelectModel.class, new Coercion<List, SelectModel>()
         {
+            private SelectModelFactory selectModelFactory;
+
             @SuppressWarnings("unchecked")
             public SelectModel coerce(List input)
             {
-                return TapestryInternalUtils.toSelectModel(input);
+                // This doesn't look thread safe, but it is because its a one-time transition from null
+                // to another value, and a race condition is harmless.
+                if (selectModelFactory == null)
+                {
+                    selectModelFactory = objectLocator.getService(SelectModelFactory.class);
+                }
+
+                return selectModelFactory.create(input);
             }
         }));
 
@@ -2086,8 +2094,8 @@ public final class TapestryModule
 
         configuration.add(SymbolConstants.SESSION_LOCKING_ENABLED, true);
 
-		// TAP5-2070 keep the old behavior, defaults to false
-		configuration.add(MetaDataConstants.UNKNOWN_ACTIVATION_CONTEXT_CHECK, false);
+        // TAP5-2070 keep the old behavior, defaults to false
+        configuration.add(MetaDataConstants.UNKNOWN_ACTIVATION_CONTEXT_CHECK, false);
 
     }
 
@@ -2469,7 +2477,7 @@ public final class TapestryModule
         configuration.add(Secure.class, new FixedExtractor(MetaDataConstants.SECURE_PAGE));
         configuration.addInstance(ContentType.class, ContentTypeExtractor.class);
         configuration.add(WhitelistAccessOnly.class, new FixedExtractor(MetaDataConstants.WHITELIST_ONLY_PAGE));
-		configuration.addInstance(UnknownActivationContextCheck.class, UnknownActivationContextExtractor.class);
+        configuration.addInstance(UnknownActivationContextCheck.class, UnknownActivationContextExtractor.class);
     }
 
     /**
@@ -2593,4 +2601,22 @@ public final class TapestryModule
             }
         });
     }
+
+    /**
+     * @since 5.4
+     */
+    @Contribute(ValueLabelProvider.class)
+    public void defaultValueLabelProviders(MappedConfiguration<Class, ValueLabelProvider> configuration)
+    {
+        configuration.addInstance(Object.class, DefaultValueLabelProvider.class);
+        configuration.addInstance(Enum.class, EnumValueLabelProvider.class);
+    }
+
+    /**
+     * @since 5.4
+     */
+    public ValueLabelProvider<?> buildValueLabelProvider(Map<Class, ValueLabelProvider> configuration)
+    {
+        return strategyBuilder.build(ValueLabelProvider.class, configuration);
+    }
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/services/SelectModelFactory.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/SelectModelFactory.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/SelectModelFactory.java
index 8307b3a..c324e87 100644
--- a/tapestry-core/src/main/java/org/apache/tapestry5/services/SelectModelFactory.java
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/SelectModelFactory.java
@@ -1,4 +1,4 @@
-// Copyright 2010 The Apache Software Foundation
+// Copyright 2010-2013  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.
@@ -13,17 +13,17 @@
 // limitations under the License.
 package org.apache.tapestry5.services;
 
-import java.util.List;
-
 import org.apache.tapestry5.SelectModel;
 
+import java.util.List;
+
 /**
  * Used to create an {@link org.apache.tapestry5.SelectModel}.
  * 
  * @since 5.2.0
  */
 public interface SelectModelFactory
-{   
+{
     /**
      * Creates a {@link org.apache.tapestry5.SelectModel} from a list of objects of the same type and a label property name.
      * The returned model creates for every object in the list a selectable option and relies on existing 
@@ -34,4 +34,17 @@ public interface SelectModelFactory
      * @return the model
      */
     public SelectModel create(List<?> objects, String labelProperty);
+
+    /**
+     * Creates a {@link org.apache.tapestry5.SelectModel} from a list of objects of the same type.
+     * 
+     * The returned model creates for every object in the list a selectable option and relies on existing 
+     * {@link org.apache.tapestry5.ValueEncoder} for the object type.
+     * 
+     * @param objects objects to create model from
+     * @return the model
+     * 
+     * @since 5.4
+     */
+    public SelectModel create(List<?> objects);
 }

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/main/java/org/apache/tapestry5/services/ValueLabelProvider.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/main/java/org/apache/tapestry5/services/ValueLabelProvider.java b/tapestry-core/src/main/java/org/apache/tapestry5/services/ValueLabelProvider.java
new file mode 100644
index 0000000..4f96bb3
--- /dev/null
+++ b/tapestry-core/src/main/java/org/apache/tapestry5/services/ValueLabelProvider.java
@@ -0,0 +1,33 @@
+// Copyright 2013 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.services;
+
+/**
+ * An object capable of providing a user-presentable label from a value. A special case exists for
+ * {@linkplain org.apache.tapestry5.internal.services.EnumValueLabelProvider handling enum types}.
+ * 
+ * @since 5.4
+ */
+public interface ValueLabelProvider<V>
+{
+
+    /**
+     * Gets label from the value. The label is used as user-presentable label for a value.
+     * @param value the value to be label provided from
+     * @return
+     */
+    public String getLabel(V value);
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/app1/SelectModelCoercionDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/SelectModelCoercionDemo.tml b/tapestry-core/src/test/app1/SelectModelCoercionDemo.tml
new file mode 100644
index 0000000..39a864d
--- /dev/null
+++ b/tapestry-core/src/test/app1/SelectModelCoercionDemo.tml
@@ -0,0 +1,20 @@
+<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+	<t:form clientValidation="NONE">
+		<p>
+			<t:errors />
+		</p>
+		<p>
+			<t:select t:id="track" model="tracks" />
+
+		</p>
+
+		<p>
+			<t:submit value="literal:Submit" />
+		</p>
+	</t:form>
+	
+	<t:if test="track">
+		<p> Selected track: ${track.title}, ${track.album}</p>
+	</t:if>
+
+</t:border>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/app1/SelectModelFromObjectsDemo.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/SelectModelFromObjectsDemo.tml b/tapestry-core/src/test/app1/SelectModelFromObjectsDemo.tml
new file mode 100644
index 0000000..9694067
--- /dev/null
+++ b/tapestry-core/src/test/app1/SelectModelFromObjectsDemo.tml
@@ -0,0 +1,20 @@
+<t:border xmlns:t="http://tapestry.apache.org/schema/tapestry_5_3.xsd">
+	<t:form clientValidation="NONE">
+		<p>
+			<t:errors />
+		</p>
+		<p>
+			<t:select t:id="track" model="model" />
+
+		</p>
+
+		<p>
+			<t:submit value="literal:Submit" />
+		</p>
+	</t:form>
+	
+	<t:if test="track">
+		<p> Selected track: ${track.title}, ${track.album}</p>
+	</t:if>
+
+</t:border>

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/app1/css/app.css
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/app1/css/app.css b/tapestry-core/src/test/app1/css/app.css
index 84fdd9f..af23567 100644
--- a/tapestry-core/src/test/app1/css/app.css
+++ b/tapestry-core/src/test/app1/css/app.css
@@ -1,6 +1,3 @@
-BODY {
-    padding-top: 60px;
-}
 
 SPAN.inherit {
     font-style: italic;

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
index 24558d2..6c4bd07 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/FormTests.java
@@ -1048,6 +1048,30 @@ public class FormTests extends TapestryCoreTestCase
     }
 
     @Test
+    public void create_select_model_from_objects() throws Exception
+    {
+    	openLinks("SelectModel from objects");
+    	
+    	select("track", "label=The Calling");
+    	
+    	clickAndWait(SUBMIT);
+    	
+    	assertTextPresent("Selected track: The Calling, Synaesthetic");
+    }
+    
+    @Test
+    public void create_select_model_coercion() throws Exception
+    {
+    	openLinks("SelectModel coercion");
+    	
+    	select("track", "label=The Calling");
+    	
+    	clickAndWait(SUBMIT);
+    	
+    	assertTextPresent("Selected track: The Calling, Synaesthetic");
+    }
+    
+    @Test
     public void validation_macro() throws Exception
     {
         openLinks("Validator Macro Demo");

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
index d5cd730..af510b6 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/Index.java
@@ -489,6 +489,12 @@ public class Index
 
                     new Item("SelectModelFromObjectsAndPropertyNameDemo", "SelectModel from objects and property name",
                             "Creating a SelectModel from a list of objects and a label property name"),
+                            
+                    new Item("SelectModelFromObjectsDemo", "SelectModel from objects",
+                            "Creating a SelectModel from a list of objects"),
+                            
+                    new Item("SelectModelCoercionDemo", "SelectModel coercion",
+                            "Creating a SelectModel from a list of objects using coercion"),
 
                     new Item("DecoratePageRenderLinkDemo", "Decorate Page Render Link Demo",
                             "Decorating page render links"),

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelCoercionDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelCoercionDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelCoercionDemo.java
new file mode 100644
index 0000000..131b186
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelCoercionDemo.java
@@ -0,0 +1,25 @@
+package org.apache.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.integration.app1.data.Track;
+import org.apache.tapestry5.integration.app1.services.MusicLibrary;
+import org.apache.tapestry5.ioc.annotations.Inject;
+
+import java.util.List;
+
+public class SelectModelCoercionDemo
+{
+    @Inject
+    private MusicLibrary library;
+    
+    @Property
+    @Persist
+    private Track track;
+    
+    public List<Track> getTracks(){
+    	return library.getTracks();
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelFromObjectsDemo.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelFromObjectsDemo.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelFromObjectsDemo.java
new file mode 100644
index 0000000..ba64455
--- /dev/null
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/pages/SelectModelFromObjectsDemo.java
@@ -0,0 +1,32 @@
+package org.apache.tapestry5.integration.app1.pages;
+
+import org.apache.tapestry5.SelectModel;
+import org.apache.tapestry5.annotations.Persist;
+import org.apache.tapestry5.annotations.Property;
+import org.apache.tapestry5.integration.app1.data.Track;
+import org.apache.tapestry5.integration.app1.services.MusicLibrary;
+import org.apache.tapestry5.ioc.annotations.Inject;
+import org.apache.tapestry5.services.SelectModelFactory;
+
+public class SelectModelFromObjectsDemo
+{
+    @Inject
+    private MusicLibrary library;
+    
+    @Inject
+    private SelectModelFactory modelFactory;
+    
+    @Property
+    private SelectModel model;
+    
+    @Property
+    @Persist
+    private Track track;
+    
+    void onPrepare()
+    {
+        model = modelFactory.create(library.getTracks());
+    }
+    
+    
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
index 7a2c812..f095270 100644
--- a/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
+++ b/tapestry-core/src/test/java/org/apache/tapestry5/integration/app1/services/AppModule.java
@@ -1,4 +1,4 @@
-// Copyright 2006, 2007, 2008, 2009, 2010, 2011 The Apache Software Foundation
+// Copyright 2006-2013 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.
@@ -287,6 +287,16 @@ public class AppModule
     {
         configuration.add("properties");
     }
+    
+    public void contributeValueLabelProvider(MappedConfiguration<Class, ValueLabelProvider> configuration)
+    {
+        configuration.add(Track.class, new ValueLabelProvider<Track>() {
+
+			public String getLabel(Track value) {
+				return value.getTitle();
+			}
+		}); 
+    }
 
     @Contribute(ComponentClassResolver.class)
     public static void setupAlphaLibrary(Configuration<LibraryMapping> configuration)

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/cc5c53df/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Border.tml
----------------------------------------------------------------------
diff --git a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Border.tml b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Border.tml
index af75055..9e8d76c 100644
--- a/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Border.tml
+++ b/tapestry-core/src/test/resources/org/apache/tapestry5/integration/app1/components/Border.tml
@@ -6,7 +6,7 @@
 <body data-floating-console="true">
 
 
-<div class="navbar navbar-inverse navbar-fixed-top">
+<div class="navbar navbar-inverse navbar-static-top">
     <div class="navbar-inner">
         <div class="container">