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/master Commit: 3d4de7e112d5ba040171d380e1c041372556769d Parents: 9b5e190 Author: Thiago H. de Paula Figueiredo <thiag...@apache.org> Authored: Sat Dec 6 15:16:02 2014 -0200 Committer: Thiago H. de Paula Figueiredo <thiag...@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); + } +}