You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by th...@apache.org on 2014/12/07 03:29:41 UTC

[43/45] tapestry-5 git commit: First pass creating the BeanModel and Commons packages. Lots of stuff moved around, but no actual code changes so far

First pass creating the BeanModel and Commons packages. Lots of stuff moved around, but no actual code changes so far


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

Branch: refs/heads/beanmodel-split
Commit: 3d4de7e112d5ba040171d380e1c041372556769d
Parents: 9b5e190
Author: Thiago H. de Paula Figueiredo <th...@apache.org>
Authored: Sat Dec 6 15:16:02 2014 -0200
Committer: Thiago H. de Paula Figueiredo <th...@apache.org>
Committed: Sat Dec 6 20:32:39 2014 -0200

----------------------------------------------------------------------
 beanmodel/build.gradle                          |   27 +
 .../org/apache/tapestry5/PropertyConduit.java   |   45 +
 .../org/apache/tapestry5/PropertyConduit2.java  |   40 +
 .../apache/tapestry5/beaneditor/BeanModel.java  |  169 ++
 .../tapestry5/beaneditor/PropertyModel.java     |   97 ++
 .../internal/InternalPropertyConduit.java       |   37 +
 .../internal/beaneditor/BeanModelImpl.java      |  289 ++++
 .../internal/beaneditor/PropertyModelImpl.java  |  139 ++
 .../CoercingPropertyConduitWrapper.java         |   67 +
 .../services/PropertyConduitDelegate.java       |   53 +
 .../services/PropertyConduitSourceImpl.java     | 1563 ++++++++++++++++++
 .../tapestry5/services/BeanModelSource.java     |   70 +
 .../services/PropertyConduitSource.java         |   41 +
 commons/build.gradle                            |   18 +
 .../tapestry5/internal/util/IntegerRange.java   |  125 ++
 .../tapestry5/internal/util/MultiKey.java       |   86 +
 .../tapestry5/ioc/AnnotationProvider.java       |   33 +
 .../org/apache/tapestry5/ioc/Locatable.java     |   27 +
 .../java/org/apache/tapestry5/ioc/Location.java |   38 +
 .../apache/tapestry5/ioc/MessageFormatter.java  |   32 +
 .../java/org/apache/tapestry5/ioc/Messages.java |   61 +
 .../org/apache/tapestry5/ioc/ObjectLocator.java |  143 ++
 .../java/org/apache/tapestry5/ioc/Resource.java |  108 ++
 .../ioc/internal/NullAnnotationProvider.java    |   35 +
 .../ioc/internal/util/CollectionFactory.java    |  139 ++
 .../ioc/internal/util/GenericsUtils.java        |  615 +++++++
 .../ioc/internal/util/TapestryException.java    |   75 +
 .../ioc/services/ClassPropertyAdapter.java      |   79 +
 .../apache/tapestry5/ioc/services/Coercion.java |   31 +
 .../tapestry5/ioc/services/CoercionTuple.java   |  145 ++
 .../tapestry5/ioc/services/PropertyAccess.java  |   77 +
 .../tapestry5/ioc/services/PropertyAdapter.java |  121 ++
 .../tapestry5/ioc/services/TypeCoercer.java     |   88 +
 .../tapestry5/ioc/util/AvailableValues.java     |   87 +
 .../tapestry5/ioc/util/CaseInsensitiveMap.java  |  499 ++++++
 .../tapestry5/ioc/util/ExceptionUtils.java      |  115 ++
 .../ioc/util/UnknownValueException.java         |   47 +
 .../services/InvalidationEventHub.java          |   60 +
 .../services/InvalidationListener.java          |   33 +
 settings.gradle                                 |    2 +-
 tapestry-core/build.gradle                      |    6 +-
 .../org/apache/tapestry5/PropertyConduit.java   |   45 -
 .../org/apache/tapestry5/PropertyConduit2.java  |   40 -
 .../apache/tapestry5/beaneditor/BeanModel.java  |  169 --
 .../tapestry5/beaneditor/PropertyModel.java     |   97 --
 .../internal/InternalPropertyConduit.java       |   37 -
 .../internal/beaneditor/BeanModelImpl.java      |  289 ----
 .../internal/beaneditor/PropertyModelImpl.java  |  139 --
 .../CoercingPropertyConduitWrapper.java         |   67 -
 .../services/PropertyConduitDelegate.java       |   53 -
 .../services/PropertyConduitSourceImpl.java     | 1563 ------------------
 .../tapestry5/internal/util/IntegerRange.java   |  125 --
 .../tapestry5/internal/util/MultiKey.java       |   86 -
 .../tapestry5/services/BeanModelSource.java     |   70 -
 .../tapestry5/services/ComponentClasses.java    |   35 -
 .../tapestry5/services/ComponentLayer.java      |   37 -
 .../services/InvalidationEventHub.java          |   60 -
 .../services/InvalidationListener.java          |   33 -
 .../services/PropertyConduitSource.java         |   41 -
 tapestry-ioc/build.gradle                       |    1 +
 .../tapestry5/ioc/AnnotationProvider.java       |   33 -
 .../org/apache/tapestry5/ioc/Locatable.java     |   27 -
 .../java/org/apache/tapestry5/ioc/Location.java |   38 -
 .../apache/tapestry5/ioc/MessageFormatter.java  |   32 -
 .../java/org/apache/tapestry5/ioc/Messages.java |   61 -
 .../org/apache/tapestry5/ioc/ObjectLocator.java |  143 --
 .../java/org/apache/tapestry5/ioc/Resource.java |  108 --
 .../ioc/annotations/UsesConfiguration.java      |   34 -
 .../ioc/internal/NullAnnotationProvider.java    |   35 -
 .../ioc/internal/util/CollectionFactory.java    |  139 --
 .../ioc/internal/util/GenericsUtils.java        |  615 -------
 .../ioc/internal/util/TapestryException.java    |   75 -
 .../ioc/services/ClassPropertyAdapter.java      |   79 -
 .../apache/tapestry5/ioc/services/Coercion.java |   31 -
 .../tapestry5/ioc/services/CoercionTuple.java   |  145 --
 .../tapestry5/ioc/services/PropertyAccess.java  |   77 -
 .../tapestry5/ioc/services/PropertyAdapter.java |  121 --
 .../tapestry5/ioc/services/TypeCoercer.java     |   88 -
 .../tapestry5/ioc/util/AvailableValues.java     |   87 -
 .../tapestry5/ioc/util/CaseInsensitiveMap.java  |  499 ------
 .../tapestry5/ioc/util/ExceptionUtils.java      |  115 --
 .../ioc/util/UnknownValueException.java         |   47 -
 tapestry5-annotations/build.gradle              |    2 +-
 .../ioc/annotations/UsesConfiguration.java      |   34 +
 .../tapestry5/services/ComponentClasses.java    |   35 +
 .../tapestry5/services/ComponentLayer.java      |   37 +
 86 files changed, 5664 insertions(+), 5622 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/build.gradle
----------------------------------------------------------------------
diff --git a/beanmodel/build.gradle b/beanmodel/build.gradle
new file mode 100644
index 0000000..ccdeb37
--- /dev/null
+++ b/beanmodel/build.gradle
@@ -0,0 +1,27 @@
+import org.gradle.plugins.ide.idea.model.*
+import t5build.*
+
+description = "Fast class property discovery, reading and writing library based on bytecode generation. Extracted from Apache Tapestry, but not dependent on the Web framework (tapestry-core) not the IoC one (tapestry-ioc)."
+
+//apply plugin: JavaPlugin
+
+buildDir = 'target/gradle-build'
+       
+project.ext.libraryVersions = [
+	jcache: '1.0.0',
+]
+
+dependencies {
+	compile project(":plastic")
+	compile project(":tapestry5-annotations")
+	compile project(":commons")
+	// Transitive will bring in the unwanted string template library as well
+	compile "org.antlr:antlr-runtime:3.5.2", {
+		exclude group: "org.antlr", module: "stringtemplate"
+	}
+}
+
+jar {	
+	manifest {	
+	}
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit.java b/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit.java
new file mode 100644
index 0000000..3dbb0c0
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit.java
@@ -0,0 +1,45 @@
+// Copyright 2007 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;
+
+import org.apache.tapestry5.ioc.AnnotationProvider;
+
+/**
+ * Used to read or update the value associated with a property. A PropertyConduit provides access to the annotations on
+ * the underlying getter and/or setter methods.
+ */
+public interface PropertyConduit extends AnnotationProvider
+{
+    /**
+     * Reads the property from the instance.
+     *
+     * @param instance object containing the property
+     * @return the current value of the property
+     */
+    Object get(Object instance);
+
+    /**
+     * Changes the current value of the property.
+     *
+     * @param instance object containing the property
+     * @param value    to change the property to
+     */
+    void set(Object instance, Object value);
+
+    /**
+     * Returns the type of the property read or updated by the conduit.
+     */
+    Class getPropertyType();
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit2.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit2.java b/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit2.java
new file mode 100644
index 0000000..839d70f
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/PropertyConduit2.java
@@ -0,0 +1,40 @@
+// Copyright 2007 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;
+
+import java.lang.reflect.Method;
+import java.lang.reflect.Type;
+
+import org.apache.tapestry5.services.PropertyConduitSource;
+
+
+/**
+ * Extension to {@link PropertyConduit} that adds a method to access the generic property type.
+ * {@link PropertyConduitSource} instances should ideally return PropertyConduit2 objects, not PropertyConduit.
+ * This is only primarily of interest to {@link Binding2}.
+ * 
+ * @since 5.4
+ */
+public interface PropertyConduit2 extends PropertyConduit
+{
+    /**
+     * Returns the generic type of the property
+     * 
+     * @see Method#getGenericReturnType()
+     * @see java.lang.reflect.Field#getGenericType()
+     * 
+     */
+	Type getPropertyGenericType();
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModel.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModel.java b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModel.java
new file mode 100644
index 0000000..0a60fd7
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModel.java
@@ -0,0 +1,169 @@
+// Copyright 2007, 2008, 2009, 2010, 2011 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.beaneditor;
+
+import org.apache.tapestry5.PropertyConduit;
+
+import java.util.List;
+
+/**
+ * Provides the information necessary to build a user interface to view, create or edit an instance of a particular
+ * type.
+ * <p/>
+ * BeanModels are not thread-safe, they are also not serializable.
+ * <p/>
+ * Here, and in {@link org.apache.tapestry5.beaneditor.PropertyModel}, the term "propertyName" is used for simplicitly.
+ * However, a full {@linkplain org.apache.tapestry5.services.PropertyConduitSource#create(Class, String) property
+ * expression} may be utilized when {@linkplain #add(String) adding new properties to an existing BeanModel}.
+ *
+ * @see org.apache.tapestry5.services.BeanModelSource
+ */
+public interface BeanModel<T>
+{
+    /**
+     * Returns the type of bean for which this model was initially created.
+     */
+    Class<T> getBeanType();
+
+
+    /**
+     * Creates a new bean instance.  This is based on {@link org.apache.tapestry5.ioc.ObjectLocator#autobuild(Class)},
+     * so a public constructor will be used, and dependencies injected.
+     *
+     * @return new instance of the bean
+     */
+    T newInstance();
+
+    /**
+     * Returns a list of the editable properties of the bean, in <em>presentation</em> order.
+     */
+    List<String> getPropertyNames();
+
+    /**
+     * Returns the named model.
+     *
+     * @param propertyName name of property to retrieve model for (case is ignored)
+     * @return the model for the property
+     * @throws RuntimeException if the bean editor model does not have a property model for the provided name
+     */
+    PropertyModel get(String propertyName);
+
+    /**
+     * Returns the identified model.  Property ids are a stripped version of the property name. Case is ignored.
+     *
+     * @param propertyId matched caselessly against {@link org.apache.tapestry5.beaneditor.PropertyModel#getId()}
+     * @throws RuntimeException if the bean editor model does not have a property model with the indicated id
+     */
+    PropertyModel getById(String propertyId);
+
+    /**
+     * Adds a new property to the model, returning its mutable model for further refinement. The property is added to
+     * the <em>end</em> of the list of properties. The property must be real (but may have been excluded if there was no
+     * {@linkplain org.apache.tapestry5.beaneditor.DataType datatype} associated with the property). To add a synthetic
+     * property, use {@link #add(String, org.apache.tapestry5.PropertyConduit)}
+     *
+     * @param propertyName name of property to add
+     * @return the new property model (for further configuration)
+     * @throws RuntimeException if the property already exists
+     */
+    PropertyModel add(String propertyName);
+
+
+    /**
+     * Adds a new synthetic property to the model, returning its mutable model for further refinement. The property is added to
+     * the <em>end</em> of the list of properties.
+     *
+     * @param propertyName name of property to add
+     * @param expression   expression for the property
+     * @return the new property model (for further configuration)
+     * @throws RuntimeException if the property already exists
+     * @since 5.3
+     */
+    PropertyModel addExpression(String propertyName, String expression);
+
+    /**
+     * Adds an empty property (one with no property conduit).
+     *
+     * @param propertyName name of property to add
+     * @return the new property model (for further configuration)
+     * @throws RuntimeException if the property already exists
+     * @since 5.3
+     */
+    PropertyModel addEmpty(String propertyName);
+
+    /**
+     * Adds a new property to the model (as with {@link #add(String)}), ordered before or after an existing property.
+     *
+     * @param position             controls whether the new property is ordered before or after the existing property
+     * @param existingPropertyName the name of an existing property (this must exist)
+     * @param propertyName         the new property to add
+     * @return the new property model (for further configuration)
+     * @throws RuntimeException if the existing property does not exist, or if the new property already does exist
+     */
+    PropertyModel add(RelativePosition position, String existingPropertyName, String propertyName);
+
+    /**
+     * Adds a new property to the model, ordered before or after an existing property.
+     *
+     * @param position             controls whether the new property is ordered before or after the existing property
+     * @param existingPropertyName the name of an existing property (this must exist)
+     * @param propertyName         the new property to add
+     * @param conduit              conduit used to read or update the property; this may be null for a synthetic or
+     *                             placeholder property
+     * @return the new property model (for further configuration)
+     * @throws RuntimeException if the existing property does not exist, or if the new property already does exist
+     */
+    PropertyModel add(RelativePosition position, String existingPropertyName, String propertyName,
+                      PropertyConduit conduit);
+
+    /**
+     * Adds a new, synthetic property to the model, returning its mutable model for further refinement.
+     *
+     * @param propertyName name of property to add
+     * @param conduit      the conduit used to read or update the property; this may be null for a synthetic or
+     *                     placeholder property.  Instead of passing null, please invoke {@link #addEmpty(String)}.
+     * @return the model for the property
+     * @throws RuntimeException if the property already exists
+     * @see #addExpression(String, String)
+     */
+    PropertyModel add(String propertyName, PropertyConduit conduit);
+
+    /**
+     * Removes the named properties from the model, if present. It is not considered an error to remove a property that
+     * does not exist.
+     *
+     * @param propertyNames the names of properties to be removed (case insensitive)
+     * @return the model for further modifications
+     */
+    BeanModel<T> exclude(String... propertyNames);
+
+    /**
+     * Re-orders the properties of the model into the specified order. Existing properties that are not indicated are
+     * retained, but ordered to the end of the list.
+     *
+     * @param propertyNames property names in order they should be displayed (case insensitive)
+     * @return the model for further modifications
+     */
+    BeanModel<T> reorder(String... propertyNames);
+
+    /**
+     * Re-orders the properties of the model into the specified order. Existing properties that are not indicated are
+     * <<removed>>.
+     *
+     * @param propertyNames the names of properties to be retained
+     * @return the model for further modifications
+     */
+    BeanModel<T> include(String... propertyNames);
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/PropertyModel.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/PropertyModel.java b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/PropertyModel.java
new file mode 100644
index 0000000..6095fb9
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/PropertyModel.java
@@ -0,0 +1,97 @@
+// Copyright 2007, 2008 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.beaneditor;
+
+import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.ioc.AnnotationProvider;
+
+/**
+ * Part of a {@link org.apache.tapestry5.beaneditor.BeanModel} that defines the attributes of a single property of a
+ * bean.
+ * <p/>
+ * <p/>
+ * A PropertyModel is also an {@link AnnotationProvider}, as long as the {@link org.apache.tapestry5.PropertyConduit} is
+ * non-null.  When there is no property conduit, then {@link org.apache.tapestry5.ioc.AnnotationProvider#getAnnotation(Class)}
+ * will return null.
+ */
+public interface PropertyModel extends AnnotationProvider
+{
+    /**
+     * Returns the name of the property (which may, in fact, be a property expression).
+     */
+    String getPropertyName();
+
+    /**
+     * Returns the id used to access other resources (this is based on the property name, but with any excess
+     * punctuation stripped out).
+     */
+    String getId();
+
+    /**
+     * Returns a user-presentable label for the property.
+     */
+    String getLabel();
+
+    /**
+     * Returns the type of the property.
+     */
+    Class getPropertyType();
+
+    /**
+     * Returns a logical name for the type of UI needed to view or edit the property. This is initially determined from
+     * the property type.
+     */
+    String getDataType();
+
+    /**
+     * Changes the data type for the property.
+     *
+     * @param dataType
+     * @return the property model, for further changes
+     */
+    PropertyModel dataType(String dataType);
+
+    /**
+     * Returns an object used to read or update the property. For virtual properties (properties that do not actually
+     * exist on the bean), the conduit may be null.
+     */
+    PropertyConduit getConduit();
+
+    /**
+     * Changes the label for the property to the provided value.
+     *
+     * @param label new label for property
+     * @return the property model, for further changes
+     */
+    PropertyModel label(String label);
+
+    /**
+     * Returns the containing model, often used for "fluent" construction of the model.
+     */
+    BeanModel model();
+
+    /**
+     * Returns true if the property can be used for sorting. By default, this is true only if the property type
+     * implements Comparable.
+     */
+    boolean isSortable();
+
+    /**
+     * Updates sortable and returns the model for further changes.
+     *
+     * @return the property model, for further changes
+     */
+    PropertyModel sortable(boolean sortable);
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalPropertyConduit.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalPropertyConduit.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalPropertyConduit.java
new file mode 100644
index 0000000..315b372
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalPropertyConduit.java
@@ -0,0 +1,37 @@
+// 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.internal;
+
+import org.apache.tapestry5.PropertyConduit2;
+
+
+/**
+ * Extension to {@link org.apache.tapestry5.PropertyConduit2} that adds a method to determine the name of the property.
+ * 
+ * @since 5.2.0
+ *
+ */
+public interface InternalPropertyConduit extends PropertyConduit2
+{
+    /**
+     * Returns the name of the property read or updated by the conduit or null. 
+     * If the expression points to a property on a bean (e.g. user.name) this method returns the last property in the chain. 
+     * Otherwise this method returns {@code null}.
+     * 
+     * @return property name or {@code null}
+     * 
+     */
+    String getPropertyName();
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelImpl.java
new file mode 100644
index 0000000..26eb309
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelImpl.java
@@ -0,0 +1,289 @@
+// Copyright 2007, 2008, 2010, 2011 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.beaneditor;
+
+import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.beaneditor.BeanModel;
+import org.apache.tapestry5.beaneditor.PropertyModel;
+import org.apache.tapestry5.beaneditor.RelativePosition;
+import org.apache.tapestry5.internal.services.CoercingPropertyConduitWrapper;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.ObjectLocator;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.ioc.util.AvailableValues;
+import org.apache.tapestry5.ioc.util.UnknownValueException;
+import org.apache.tapestry5.plastic.PlasticUtils;
+import org.apache.tapestry5.services.PropertyConduitSource;
+
+import java.util.List;
+import java.util.Map;
+
+public class BeanModelImpl<T> implements BeanModel<T>
+{
+    private final Class<T> beanType;
+
+    private final PropertyConduitSource propertyConduitSource;
+
+    private final TypeCoercer typeCoercer;
+
+    private final Messages messages;
+
+    private final ObjectLocator locator;
+
+    private final Map<String, PropertyModel> properties = CollectionFactory.newCaseInsensitiveMap();
+
+    // The list of property names, in desired order (generally not alphabetical order).
+
+    private final List<String> propertyNames = CollectionFactory.newList();
+
+    private static PropertyConduit NULL_PROPERTY_CONDUIT = null;
+
+    public BeanModelImpl(Class<T> beanType, PropertyConduitSource propertyConduitSource, TypeCoercer typeCoercer,
+                         Messages messages, ObjectLocator locator)
+
+    {
+        this.beanType = beanType;
+        this.propertyConduitSource = propertyConduitSource;
+        this.typeCoercer = typeCoercer;
+        this.messages = messages;
+        this.locator = locator;
+    }
+
+    public Class<T> getBeanType()
+    {
+        return beanType;
+    }
+
+    public T newInstance()
+    {
+        return locator.autobuild("Instantiating new instance of " + beanType.getName(), beanType);
+    }
+
+    public PropertyModel add(String propertyName)
+    {
+        return addExpression(propertyName, propertyName);
+    }
+
+    public PropertyModel addEmpty(String propertyName)
+    {
+        return add(propertyName, NULL_PROPERTY_CONDUIT);
+    }
+
+    public PropertyModel addExpression(String propertyName, String expression)
+    {
+        PropertyConduit conduit = createConduit(expression);
+
+        return add(propertyName, conduit);
+
+    }
+
+    private void validateNewPropertyName(String propertyName)
+    {
+        assert InternalUtils.isNonBlank(propertyName);
+        if (properties.containsKey(propertyName))
+            throw new RuntimeException(String.format(
+                    "Bean editor model for %s already contains a property model for property '%s'.",
+                    beanType.getName(), propertyName));
+    }
+
+    public PropertyModel add(RelativePosition position, String existingPropertyName, String propertyName,
+                             PropertyConduit conduit)
+    {
+        assert position != null;
+        validateNewPropertyName(propertyName);
+
+        // Locate the existing one.
+
+        PropertyModel existing = get(existingPropertyName);
+
+        // Use the case normalized property name.
+
+        int pos = propertyNames.indexOf(existing.getPropertyName());
+
+        PropertyModel newModel = new PropertyModelImpl(this, propertyName, conduit, messages);
+
+        properties.put(propertyName, newModel);
+
+        int offset = position == RelativePosition.AFTER ? 1 : 0;
+
+        propertyNames.add(pos + offset, propertyName);
+
+        return newModel;
+    }
+
+    public PropertyModel add(RelativePosition position, String existingPropertyName, String propertyName)
+    {
+        PropertyConduit conduit = createConduit(propertyName);
+
+        return add(position, existingPropertyName, propertyName, conduit);
+    }
+
+    public PropertyModel add(String propertyName, PropertyConduit conduit)
+    {
+        validateNewPropertyName(propertyName);
+
+        PropertyModel propertyModel = new PropertyModelImpl(this, propertyName, conduit, messages);
+
+        properties.put(propertyName, propertyModel);
+
+        // Remember the order in which the properties were added.
+
+        propertyNames.add(propertyName);
+
+        return propertyModel;
+    }
+
+    private CoercingPropertyConduitWrapper createConduit(String propertyName)
+    {
+        return new CoercingPropertyConduitWrapper(propertyConduitSource.create(beanType, propertyName), typeCoercer);
+    }
+
+    public PropertyModel get(String propertyName)
+    {
+        PropertyModel propertyModel = properties.get(propertyName);
+
+        if (propertyModel == null)
+            throw new UnknownValueException(String.format(
+                    "Bean editor model for %s does not contain a property named '%s'.", beanType.getName(),
+                    propertyName), new AvailableValues("Defined properties", propertyNames));
+
+        return propertyModel;
+    }
+
+    public PropertyModel getById(String propertyId)
+    {
+        for (PropertyModel model : properties.values())
+        {
+            if (model.getId().equalsIgnoreCase(propertyId))
+                return model;
+        }
+
+        // Not found, so we throw an exception. A bit of work to set
+        // up the exception however.
+
+        List<String> ids = CollectionFactory.newList();
+
+        for (PropertyModel model : properties.values())
+        {
+            ids.add(model.getId());
+        }
+
+        throw new UnknownValueException(String.format(
+                "Bean editor model for %s does not contain a property with id '%s'.", beanType.getName(), propertyId),
+                new AvailableValues("Defined property ids", ids));
+    }
+
+    public List<String> getPropertyNames()
+    {
+        return CollectionFactory.newList(propertyNames);
+    }
+
+    public BeanModel<T> exclude(String... propertyNames)
+    {
+        for (String propertyName : propertyNames)
+        {
+            PropertyModel model = properties.get(propertyName);
+
+            if (model == null)
+                continue;
+
+            // De-referencing from the model is needed because the name provided may not be a
+            // case-exact match, so we get the normalized or canonical name from the model because
+            // that's the one in propertyNames.
+
+            this.propertyNames.remove(model.getPropertyName());
+
+            properties.remove(propertyName);
+        }
+
+        return this;
+    }
+
+    public BeanModel<T> reorder(String... propertyNames)
+    {
+        List<String> remainingPropertyNames = CollectionFactory.newList(this.propertyNames);
+        List<String> reorderedPropertyNames = CollectionFactory.newList();
+
+        for (String name : propertyNames)
+        {
+            PropertyModel model = get(name);
+
+            // Get the canonical form (which may differ from name in terms of case)
+            String canonical = model.getPropertyName();
+
+            reorderedPropertyNames.add(canonical);
+
+            remainingPropertyNames.remove(canonical);
+        }
+
+        this.propertyNames.clear();
+        this.propertyNames.addAll(reorderedPropertyNames);
+
+        // Any unspecified names are ordered to the end. Don't want them? Remove them instead.
+        this.propertyNames.addAll(remainingPropertyNames);
+
+        return this;
+    }
+
+    public BeanModel<T> include(String... propertyNames)
+    {
+        List<String> reorderedPropertyNames = CollectionFactory.newList();
+        Map<String, PropertyModel> reduced = CollectionFactory.newCaseInsensitiveMap();
+
+        for (String name : propertyNames)
+        {
+
+            PropertyModel model = get(name);
+
+            String canonical = model.getPropertyName();
+
+            reorderedPropertyNames.add(canonical);
+            reduced.put(canonical, model);
+
+        }
+
+        this.propertyNames.clear();
+        this.propertyNames.addAll(reorderedPropertyNames);
+
+        properties.clear();
+        properties.putAll(reduced);
+
+        return this;
+    }
+
+    @Override
+    public String toString()
+    {
+        StringBuilder builder = new StringBuilder("BeanModel[");
+        builder.append(PlasticUtils.toTypeName(beanType));
+
+        builder.append(" properties:");
+        String sep = "";
+
+        for (String name : propertyNames)
+        {
+            builder.append(sep);
+            builder.append(name);
+
+            sep = ", ";
+        }
+
+        builder.append("]");
+
+        return builder.toString();
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/PropertyModelImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/PropertyModelImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/PropertyModelImpl.java
new file mode 100644
index 0000000..703ce44
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/PropertyModelImpl.java
@@ -0,0 +1,139 @@
+// Copyright 2007, 2008, 2010, 2011, 2014 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.beaneditor;
+
+import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.beaneditor.BeanModel;
+import org.apache.tapestry5.beaneditor.PropertyModel;
+import org.apache.tapestry5.beaneditor.Sortable;
+import org.apache.tapestry5.internal.TapestryInternalUtils;
+import org.apache.tapestry5.ioc.Messages;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.plastic.PlasticUtils;
+
+import java.lang.annotation.Annotation;
+
+@SuppressWarnings("all")
+public class PropertyModelImpl implements PropertyModel
+{
+    private final BeanModel model;
+
+    private final String name;
+
+    private final PropertyConduit conduit;
+
+    private final String id;
+
+    private String label;
+
+    private String dataType;
+
+    private boolean sortable;
+
+    public PropertyModelImpl(BeanModel model, String name, PropertyConduit conduit, Messages messages)
+    {
+        this.model = model;
+        this.name = name;
+        this.conduit = conduit;
+
+        id = TapestryInternalUtils.extractIdFromPropertyExpression(name);
+
+        label = TapestryInternalUtils.defaultLabel(id, messages, name);
+
+        // TAP5-2305
+        if (conduit != null)
+        {
+            Sortable sortableAnnotation = conduit.getAnnotation(Sortable.class);
+            if (sortableAnnotation != null)
+            {
+                sortable = sortableAnnotation.value();
+            }
+            else
+            {
+                // Primitive types need to be converted to wrapper types before checking to see
+                // if they are sortable.
+                Class wrapperType = PlasticUtils.toWrapperType(getPropertyType());
+                sortable = Comparable.class.isAssignableFrom(wrapperType);
+            }
+        }
+    }
+
+    public String getId()
+    {
+        return id;
+    }
+
+    public Class getPropertyType()
+    {
+        return conduit == null ? Object.class : conduit.getPropertyType();
+    }
+
+    public PropertyConduit getConduit()
+    {
+        return conduit;
+    }
+
+    public PropertyModel label(String label)
+    {
+        assert InternalUtils.isNonBlank(label);
+        this.label = label;
+
+        return this;
+    }
+
+    public String getLabel()
+    {
+        return label;
+    }
+
+    public String getPropertyName()
+    {
+        return name;
+    }
+
+    public BeanModel model()
+    {
+        return model;
+    }
+
+    public PropertyModel dataType(String dataType)
+    {
+        this.dataType = dataType;
+
+        return this;
+    }
+
+    public String getDataType()
+    {
+        return dataType;
+    }
+
+    public boolean isSortable()
+    {
+        return sortable;
+    }
+
+    public PropertyModel sortable(boolean sortable)
+    {
+        this.sortable = sortable;
+
+        return this;
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+    {
+        return conduit == null ? null : conduit.getAnnotation(annotationClass);
+    }
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
new file mode 100644
index 0000000..4dbfb2d
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/services/CoercingPropertyConduitWrapper.java
@@ -0,0 +1,67 @@
+// Copyright 2007 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 java.lang.annotation.Annotation;
+import java.lang.reflect.Type;
+
+import org.apache.tapestry5.PropertyConduit;
+import org.apache.tapestry5.PropertyConduit2;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+
+public class CoercingPropertyConduitWrapper implements PropertyConduit2
+{
+    private final PropertyConduit conduit;
+
+    private final TypeCoercer coercer;
+
+    public CoercingPropertyConduitWrapper(final PropertyConduit conduit, final TypeCoercer coercer)
+    {
+        this.conduit = conduit;
+        this.coercer = coercer;
+    }
+
+    public Object get(Object instance)
+    {
+        return conduit.get(instance);
+    }
+
+    public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+    {
+        return conduit.getAnnotation(annotationClass);
+    }
+
+    public Class getPropertyType()
+    {
+        return conduit.getPropertyType();
+    }
+    
+    public Type getPropertyGenericType()
+    {
+    	if (conduit instanceof PropertyConduit2) {
+    		return ((PropertyConduit2) conduit).getPropertyGenericType();
+    	}
+    	return conduit.getPropertyType();
+    }
+
+    @SuppressWarnings("unchecked")
+    public void set(Object instance, Object value)
+    {
+        Object coerced = coercer.coerce(value, getPropertyType());
+
+        conduit.set(instance, coerced);
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/3d4de7e1/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitDelegate.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitDelegate.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitDelegate.java
new file mode 100644
index 0000000..1242031
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitDelegate.java
@@ -0,0 +1,53 @@
+// Copyright 2007, 2008, 2009, 2010, 2011 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 java.lang.annotation.Annotation;
+
+import org.apache.tapestry5.internal.InternalPropertyConduit;
+import org.apache.tapestry5.internal.util.IntegerRange;
+import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.internal.util.InternalUtils;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+
+/**
+ * Companion class for {@link org.apache.tapestry5.PropertyConduit} instances created by the
+ * {@link org.apache.tapestry5.services.PropertyConduitSource}.
+ */
+@SuppressWarnings("all")
+public class PropertyConduitDelegate
+{
+    private final TypeCoercer typeCoercer;
+
+    public PropertyConduitDelegate(TypeCoercer typeCoercer)
+    {
+        this.typeCoercer = typeCoercer;
+    }
+
+    public final IntegerRange range(int from, int to)
+    {
+        return new IntegerRange(from, to);
+    }
+
+    public final <T> T coerce(Object value, Class<T> type)
+    {
+        return typeCoercer.coerce(value, type);
+    }
+
+    public final boolean invert(Object value)
+    {
+        return coerce(value, Boolean.class).equals(Boolean.FALSE);
+    }
+}