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/21 20:56:46 UTC
[20/35] tapestry-5 git commit: Fourth pass creating the BeanModel and
Commons packages.
Fourth pass creating the BeanModel and Commons packages.
Project: http://git-wip-us.apache.org/repos/asf/tapestry-5/repo
Commit: http://git-wip-us.apache.org/repos/asf/tapestry-5/commit/c7bf35ce
Tree: http://git-wip-us.apache.org/repos/asf/tapestry-5/tree/c7bf35ce
Diff: http://git-wip-us.apache.org/repos/asf/tapestry-5/diff/c7bf35ce
Branch: refs/heads/master
Commit: c7bf35ce7840d1131b657da7ec77eb2d9a494eb6
Parents: 9f9e569
Author: Thiago H. de Paula Figueiredo <th...@apache.org>
Authored: Sat Dec 6 19:45:14 2014 -0200
Committer: Thiago H. de Paula Figueiredo <th...@apache.org>
Committed: Sat Dec 6 20:32:39 2014 -0200
----------------------------------------------------------------------
beanmodel/build.gradle | 1 +
.../beaneditor/BeanModelSourceBuilder.java | 121 +++++
.../internal/InternalBeanModelUtils.java | 145 ------
.../internal/beaneditor/BeanModelImpl.java | 4 +-
.../internal/beaneditor/BeanModelUtils.java | 4 +-
.../internal/beaneditor/PropertyModelImpl.java | 10 +-
.../services/PropertyConduitSourceImpl.java | 10 +-
.../services/ClassPropertyAdapterImpl.java | 249 +++++++++
.../services/PlasticClassListenerLogger.java | 47 ++
.../services/PlasticProxyFactoryImpl.java | 285 +++++++++++
.../internal/services/PropertyAccessImpl.java | 217 ++++++++
.../internal/services/PropertyAdapterImpl.java | 273 ++++++++++
commons/build.gradle | 1 +
.../internal/services/StringInternerImpl.java | 54 ++
.../org/apache/tapestry5/ioc/Configuration.java | 53 ++
.../tapestry5/ioc/MappedConfiguration.java | 81 +++
.../tapestry5/ioc/OrderedConfiguration.java | 84 +++
.../ioc/internal/BasicTypeCoercions.java | 342 +++++++++++++
.../AccessableObjectAnnotationProvider.java | 46 ++
.../services/AnnotationProviderChain.java | 59 +++
.../ioc/internal/services/CompoundCoercion.java | 54 ++
.../ioc/internal/services/ServiceMessages.java | 68 +++
.../ioc/internal/services/StringLocation.java | 65 +++
.../ioc/internal/services/TypeCoercerImpl.java | 508 +++++++++++++++++++
.../ioc/internal/util/InheritanceSearch.java | 159 ++++++
.../ioc/internal/util/InternalCommonsUtils.java | 388 ++++++++++++++
.../ioc/internal/util/InternalStringUtils.java | 203 --------
.../ioc/internal/util/LockSupport.java | 89 ++++
.../ioc/internal/util/MessageFormatterImpl.java | 65 +++
.../ioc/internal/util/MessagesImpl.java | 74 +++
.../ioc/internal/util/TapestryException.java | 23 +-
.../tapestry5/ioc/util/AbstractMessages.java | 94 ++++
.../tapestry5/ioc/util/AvailableValues.java | 4 +-
.../apache/tapestry5/ioc/util/TimeInterval.java | 195 +++++++
.../tapestry5/util/StringToEnumCoercion.java | 91 ++++
.../internal/TapestryInternalUtils.java | 8 +-
.../internal/services/StringInternerImpl.java | 53 --
.../org/apache/tapestry5/ioc/Configuration.java | 53 --
.../tapestry5/ioc/MappedConfiguration.java | 81 ---
.../tapestry5/ioc/OrderedConfiguration.java | 84 ---
.../AccessableObjectAnnotationProvider.java | 46 --
.../services/AnnotationProviderChain.java | 59 ---
.../services/ClassPropertyAdapterImpl.java | 250 ---------
.../ioc/internal/services/CompoundCoercion.java | 54 --
.../services/PlasticClassListenerLogger.java | 47 --
.../services/PlasticProxyFactoryImpl.java | 286 -----------
.../internal/services/PropertyAccessImpl.java | 217 --------
.../internal/services/PropertyAdapterImpl.java | 273 ----------
.../ioc/internal/services/ServiceMessages.java | 68 ---
.../ioc/internal/services/StringLocation.java | 65 ---
.../ioc/internal/services/TypeCoercerImpl.java | 508 -------------------
.../ioc/internal/util/InheritanceSearch.java | 159 ------
.../ioc/internal/util/InternalUtils.java | 138 +++--
.../ioc/internal/util/LockSupport.java | 89 ----
.../ioc/internal/util/MessageFormatterImpl.java | 65 ---
.../ioc/internal/util/MessagesImpl.java | 74 ---
.../ioc/modules/TapestryIOCModule.java | 297 +----------
.../tapestry5/ioc/util/AbstractMessages.java | 94 ----
.../apache/tapestry5/ioc/util/TimeInterval.java | 195 -------
.../tapestry5/util/StringToEnumCoercion.java | 91 ----
60 files changed, 3855 insertions(+), 3665 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/build.gradle
----------------------------------------------------------------------
diff --git a/beanmodel/build.gradle b/beanmodel/build.gradle
index c09ebdd..fba43b7 100644
--- a/beanmodel/build.gradle
+++ b/beanmodel/build.gradle
@@ -26,6 +26,7 @@ dependencies {
compile project(":plastic")
compile project(":tapestry5-annotations")
compile project(":commons")
+ compile "org.slf4j:slf4j-api:${versions.slf4j}"
// Antlr3 tool path used with the antlr3 task
antlr3 "org.antlr:antlr:3.5.2"
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
new file mode 100644
index 0000000..8cef66e
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/beaneditor/BeanModelSourceBuilder.java
@@ -0,0 +1,121 @@
+// Copyright 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.beaneditor;
+
+import java.util.ArrayList;
+import java.util.Collection;
+
+import javax.naming.OperationNotSupportedException;
+
+import org.apache.tapestry5.internal.services.BeanModelSourceImpl;
+import org.apache.tapestry5.internal.services.PropertyConduitSourceImpl;
+import org.apache.tapestry5.internal.services.StringInterner;
+import org.apache.tapestry5.internal.services.StringInternerImpl;
+import org.apache.tapestry5.ioc.Configuration;
+import org.apache.tapestry5.ioc.ObjectLocator;
+import org.apache.tapestry5.ioc.internal.BasicTypeCoercions;
+import org.apache.tapestry5.ioc.internal.services.PlasticProxyFactoryImpl;
+import org.apache.tapestry5.ioc.internal.services.PropertyAccessImpl;
+import org.apache.tapestry5.ioc.internal.services.TypeCoercerImpl;
+import org.apache.tapestry5.ioc.services.CoercionTuple;
+import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
+import org.apache.tapestry5.ioc.services.PropertyAccess;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.services.BeanModelSource;
+import org.apache.tapestry5.services.DataTypeAnalyzer;
+import org.apache.tapestry5.services.PropertyConduitSource;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Utility class for creating {@link BeanModelSource} instances without
+ * Tapestry-IoC. Usage of Tapestry-IoC is still recommended.
+ */
+public class BeanModelSourceBuilder {
+
+ private TypeCoercer typeCoercer;
+ private PropertyAccess propertyAccess;
+ private PropertyConduitSource propertyConduitSource;
+ private PlasticProxyFactory plasticProxyFactory;
+ private DataTypeAnalyzer dataTypeAnalyzer;
+ private ObjectLocator objectLocator;
+ private StringInterner stringInterner;
+
+ /**
+ * Sets the {@link TypeCoercer} to be used.
+ */
+ public BeanModelSourceBuilder setTypeCoercer(TypeCoercer typeCoercer) {
+ this.typeCoercer = typeCoercer;
+// propertyAccess = new PropertyAcc
+ return this;
+ }
+
+ public BeanModelSource build()
+ {
+
+ if (typeCoercer == null)
+ {
+ createTypeCoercer();
+ }
+
+ if (propertyAccess == null)
+ {
+ propertyAccess = new PropertyAccessImpl();
+ }
+
+ if (stringInterner == null)
+ {
+ stringInterner = new StringInternerImpl();
+ }
+
+ if (plasticProxyFactory == null)
+ {
+ plasticProxyFactory = new PlasticProxyFactoryImpl(getClass().getClassLoader(), LoggerFactory.getLogger(PlasticProxyFactory.class));
+ }
+
+ if (propertyConduitSource == null)
+ {
+ propertyConduitSource = new PropertyConduitSourceImpl(propertyAccess, plasticProxyFactory, typeCoercer, stringInterner);
+ }
+
+ return new BeanModelSourceImpl(typeCoercer, propertyAccess, propertyConduitSource, plasticProxyFactory, dataTypeAnalyzer, objectLocator);
+
+ }
+
+ private void createTypeCoercer() {
+ CoercionTupleConfiguration configuration = new CoercionTupleConfiguration();
+ BasicTypeCoercions.provideBasicTypeCoercions(configuration);
+ typeCoercer = new TypeCoercerImpl(configuration.getTuples());
+ }
+
+ final private static class CoercionTupleConfiguration implements Configuration<CoercionTuple> {
+
+ final private Collection<CoercionTuple> tuples = new ArrayList<CoercionTuple>();
+
+ @Override
+ public void add(CoercionTuple tuble) {
+ tuples.add(tuble);
+ }
+
+ @Override
+ public void addInstance(Class<? extends CoercionTuple> clazz) {
+ throw new RuntimeException("Not implemented");
+ }
+
+ public Collection<CoercionTuple> getTuples() {
+ return tuples;
+ }
+
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalBeanModelUtils.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalBeanModelUtils.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalBeanModelUtils.java
deleted file mode 100644
index 88f4c7f..0000000
--- a/beanmodel/src/main/java/org/apache/tapestry5/internal/InternalBeanModelUtils.java
+++ /dev/null
@@ -1,145 +0,0 @@
-// 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.internal;
-
-import java.lang.annotation.Annotation;
-import java.lang.reflect.Method;
-import java.util.regex.Pattern;
-
-import org.apache.tapestry5.ioc.AnnotationProvider;
-import org.apache.tapestry5.ioc.Messages;
-import org.apache.tapestry5.ioc.internal.NullAnnotationProvider;
-import org.apache.tapestry5.ioc.internal.util.InternalStringUtils;
-
-/**
- * Some methods broken off tapestry-core's InternalUtils to avoid bringing the whole class
- * plus its multiple dependencies to the BeanModel package.
- */
-public class InternalBeanModelUtils {
-
- public static final Pattern NON_WORD_PATTERN = Pattern.compile("[^\\w]");
-
- /**
- * @since 5.3
- */
- private final static AnnotationProvider NULL_ANNOTATION_PROVIDER = new NullAnnotationProvider();
-
-
- /**
- * Used to convert a property expression into a key that can be used to locate various resources (Blocks, messages,
- * etc.). Strips out any punctuation characters, leaving just words characters (letters, number and the
- * underscore).
- *
- * @param expression a property expression
- * @return the expression with punctuation removed
- */
- public static String extractIdFromPropertyExpression(String expression)
- {
- return replace(expression, NON_WORD_PATTERN, "");
- }
-
- public static String replace(String input, Pattern pattern, String replacement)
- {
- return pattern.matcher(input).replaceAll(replacement);
- }
-
- /**
- * Looks for a label within the messages based on the id. If found, it is used, otherwise the name is converted to a
- * user presentable form.
- */
- public static String defaultLabel(String id, Messages messages, String propertyExpression)
- {
- String key = id + "-label";
-
- if (messages.contains(key))
- return messages.get(key);
-
- return toUserPresentable(extractIdFromPropertyExpression(InternalStringUtils.lastTerm(propertyExpression)));
- }
-
- /**
- * Capitalizes the string, and inserts a space before each upper case character (or sequence of upper case
- * characters). Thus "userId" becomes "User Id", etc. Also, converts underscore into space (and capitalizes the
- * following word), thus "user_id" also becomes "User Id".
- */
- public static String toUserPresentable(String id)
- {
- StringBuilder builder = new StringBuilder(id.length() * 2);
-
- char[] chars = id.toCharArray();
- boolean postSpace = true;
- boolean upcaseNext = true;
-
- for (char ch : chars)
- {
- if (upcaseNext)
- {
- builder.append(Character.toUpperCase(ch));
- upcaseNext = false;
-
- continue;
- }
-
- if (ch == '_')
- {
- builder.append(' ');
- upcaseNext = true;
- continue;
- }
-
- boolean upperCase = Character.isUpperCase(ch);
-
- if (upperCase && !postSpace)
- builder.append(' ');
-
- builder.append(ch);
-
- postSpace = upperCase;
- }
-
- return builder.toString();
- }
-
- /**
- * @since 5.3
- */
- public static AnnotationProvider toAnnotationProvider(final Class element)
- {
- return new AnnotationProvider()
- {
- @Override
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
- {
- return annotationClass.cast(element.getAnnotation(annotationClass));
- }
- };
- }
-
- public static AnnotationProvider toAnnotationProvider(final Method element)
- {
- if (element == null)
- return NULL_ANNOTATION_PROVIDER;
-
- return new AnnotationProvider()
- {
- @Override
- public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
- {
- return element.getAnnotation(annotationClass);
- }
- };
- }
-
-
-}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/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
index b72a3d6..3b71f0e 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelImpl.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelImpl.java
@@ -22,7 +22,7 @@ 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.InternalStringUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
import org.apache.tapestry5.ioc.services.TypeCoercer;
import org.apache.tapestry5.ioc.util.AvailableValues;
import org.apache.tapestry5.ioc.util.UnknownValueException;
@@ -93,7 +93,7 @@ public class BeanModelImpl<T> implements BeanModel<T>
private void validateNewPropertyName(String propertyName)
{
- assert InternalStringUtils.isNonBlank(propertyName);
+ assert InternalCommonsUtils.isNonBlank(propertyName);
if (properties.containsKey(propertyName))
throw new RuntimeException(String.format(
"Bean editor model for %s already contains a property model for property '%s'.",
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelUtils.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelUtils.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelUtils.java
index 154ee79..207fb97 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelUtils.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/BeanModelUtils.java
@@ -15,7 +15,7 @@
package org.apache.tapestry5.internal.beaneditor;
import org.apache.tapestry5.beaneditor.BeanModel;
-import org.apache.tapestry5.ioc.internal.util.InternalStringUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
/**
* Utilities used in a few places to modify an existing {@link BeanModel}.
@@ -54,7 +54,7 @@ public final class BeanModelUtils
private static final String join(String firstList, String optionalSecondList)
{
- if (InternalStringUtils.isBlank(optionalSecondList))
+ if (InternalCommonsUtils.isBlank(optionalSecondList))
return firstList;
return firstList + "," + optionalSecondList;
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/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
index 4632818..24d0b2d 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/PropertyModelImpl.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/beaneditor/PropertyModelImpl.java
@@ -20,9 +20,9 @@ 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.InternalBeanModelUtils;
import org.apache.tapestry5.ioc.Messages;
-import org.apache.tapestry5.ioc.internal.util.InternalStringUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
import org.apache.tapestry5.plastic.PlasticUtils;
@SuppressWarnings("all")
@@ -48,9 +48,9 @@ public class PropertyModelImpl implements PropertyModel
this.name = name;
this.conduit = conduit;
- id = InternalBeanModelUtils.extractIdFromPropertyExpression(name);
+ id = InternalCommonsUtils.extractIdFromPropertyExpression(name);
- label = InternalBeanModelUtils.defaultLabel(id, messages, name);
+ label = InternalCommonsUtils.defaultLabel(id, messages, name);
// TAP5-2305
if (conduit != null)
@@ -87,7 +87,7 @@ public class PropertyModelImpl implements PropertyModel
public PropertyModel label(String label)
{
- assert InternalStringUtils.isNonBlank(label);
+ assert InternalCommonsUtils.isNonBlank(label);
this.label = label;
return this;
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
index 4ce072e..09d234c 100644
--- a/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
+++ b/beanmodel/src/main/java/org/apache/tapestry5/internal/services/PropertyConduitSourceImpl.java
@@ -19,7 +19,6 @@ import org.antlr.runtime.CommonTokenStream;
import org.antlr.runtime.tree.Tree;
import org.apache.tapestry5.PropertyConduit;
import org.apache.tapestry5.PropertyConduit2;
-import org.apache.tapestry5.internal.InternalBeanModelUtils;
import org.apache.tapestry5.internal.InternalPropertyConduit;
import org.apache.tapestry5.internal.antlr.PropertyExpressionLexer;
import org.apache.tapestry5.internal.antlr.PropertyExpressionParser;
@@ -30,7 +29,8 @@ import org.apache.tapestry5.ioc.annotations.PostInjection;
import org.apache.tapestry5.ioc.internal.NullAnnotationProvider;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.internal.util.GenericsUtils;
-import org.apache.tapestry5.ioc.internal.util.InternalStringUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
import org.apache.tapestry5.ioc.services.*;
import org.apache.tapestry5.ioc.util.AvailableValues;
import org.apache.tapestry5.ioc.util.ExceptionUtils;
@@ -1107,7 +1107,7 @@ public class PropertyConduitSourceImpl implements PropertyConduitSource
String message = String.format("Node %s was type %s, but was expected to be (one of) %s.",
node.toStringTree(), PropertyExpressionParser.tokenNames[node.getType()],
- InternalStringUtils.joinSorted(tokenNames));
+ InternalCommonsUtils.joinSorted(tokenNames));
return new RuntimeException(message);
}
@@ -1260,7 +1260,7 @@ public class PropertyConduitSourceImpl implements PropertyConduitSource
Type returnType = GenericsUtils.extractActualType(activeType, method);
- return new Term(returnType, toUniqueId(method), InternalBeanModelUtils.toAnnotationProvider(method), new InstructionBuilderCallback()
+ return new Term(returnType, toUniqueId(method), InternalCommonsUtils.toAnnotationProvider(method), new InstructionBuilderCallback()
{
public void doBuild(InstructionBuilder builder)
{
@@ -1364,7 +1364,7 @@ public class PropertyConduitSourceImpl implements PropertyConduitSource
public PropertyConduit create(Class rootClass, String expression)
{
assert rootClass != null;
- assert InternalStringUtils.isNonBlank(expression);
+ assert InternalCommonsUtils.isNonBlank(expression);
MultiKey key = new MultiKey(rootClass, expression);
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java
new file mode 100644
index 0000000..5d6dfec
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/ClassPropertyAdapterImpl.java
@@ -0,0 +1,249 @@
+// Copyright 2006, 2007, 2008, 2010, 2011, 2012 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.ioc.internal.services;
+
+import static org.apache.tapestry5.ioc.internal.util.CollectionFactory.newCaseInsensitiveMap;
+
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.GenericsUtils;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
+import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
+import org.apache.tapestry5.ioc.services.PropertyAdapter;
+
+public class ClassPropertyAdapterImpl implements ClassPropertyAdapter
+{
+ private final Map<String, PropertyAdapter> adapters = newCaseInsensitiveMap();
+
+ private final Class beanType;
+
+ public ClassPropertyAdapterImpl(Class beanType, List<PropertyDescriptor> descriptors)
+ {
+ this.beanType = beanType;
+
+ // lazy init
+ Map<String, List<Method>> nonBridgeMethods = null;
+
+ for (PropertyDescriptor pd : descriptors)
+ {
+ // Indexed properties will have a null propertyType (and a non-null
+ // indexedPropertyType). We ignore indexed properties.
+
+ final Class<?> thisPropertyType = pd.getPropertyType();
+ if (thisPropertyType == null)
+ continue;
+
+ Method readMethod = pd.getReadMethod();
+ Method writeMethod = pd.getWriteMethod();
+
+ // TAP5-1493
+ if (readMethod != null && readMethod.isBridge())
+ {
+ if (nonBridgeMethods == null)
+ {
+ nonBridgeMethods = groupNonBridgeMethodsByName(beanType);
+ }
+ readMethod = findMethodWithSameNameAndParamCount(readMethod, nonBridgeMethods);
+ }
+
+ // TAP5-1548, TAP5-1885: trying to find a getter which Introspector missed
+ if (readMethod == null) {
+ final String prefix = thisPropertyType != boolean.class ? "get" : "is";
+ try
+ {
+ Method method = beanType.getMethod(prefix + capitalize(pd.getName()));
+ final Class<?> returnType = method.getReturnType();
+ if (returnType.equals(thisPropertyType) || returnType.isInstance(thisPropertyType)) {
+ readMethod = method;
+ }
+ }
+ catch (SecurityException e) {
+ // getter not usable.
+ }
+ catch (NoSuchMethodException e)
+ {
+ // getter doesn't exist.
+ }
+ }
+
+ if (writeMethod != null && writeMethod.isBridge())
+ {
+ if (nonBridgeMethods == null)
+ {
+ nonBridgeMethods = groupNonBridgeMethodsByName(beanType);
+ }
+ writeMethod = findMethodWithSameNameAndParamCount(writeMethod, nonBridgeMethods);
+ }
+
+ // TAP5-1548, TAP5-1885: trying to find a setter which Introspector missed
+ if (writeMethod == null) {
+ try
+ {
+ Method method = beanType.getMethod("set" + capitalize(pd.getName()), pd.getPropertyType());
+ final Class<?> returnType = method.getReturnType();
+ if (returnType.equals(void.class)) {
+ writeMethod = method;
+ }
+ }
+ catch (SecurityException e) {
+ // setter not usable.
+ }
+ catch (NoSuchMethodException e)
+ {
+ // setter doesn't exist.
+ }
+ }
+
+ Class propertyType = readMethod == null ? thisPropertyType : GenericsUtils.extractGenericReturnType(
+ beanType, readMethod);
+
+ PropertyAdapter pa = new PropertyAdapterImpl(this, pd.getName(), propertyType, readMethod, writeMethod);
+
+ adapters.put(pa.getName(), pa);
+ }
+
+ // Now, add any public fields (even if static) that do not conflict
+
+ for (Field f : beanType.getFields())
+ {
+ String name = f.getName();
+
+ if (!adapters.containsKey(name))
+ {
+ Class propertyType = GenericsUtils.extractGenericFieldType(beanType, f);
+ PropertyAdapter pa = new PropertyAdapterImpl(this, name, propertyType, f);
+
+ adapters.put(name, pa);
+ }
+ }
+ }
+
+ private static String capitalize(String name)
+ {
+ return Character.toUpperCase(name.charAt(0)) + name.substring(1);
+ }
+
+ /**
+ * Find a replacement for the method (if one exists)
+ * @param method A method
+ * @param groupedMethods Methods mapped by name
+ * @return A method from groupedMethods with the same name / param count
+ * (default to providedmethod if none found)
+ */
+ private Method findMethodWithSameNameAndParamCount(Method method, Map<String, List<Method>> groupedMethods) {
+ List<Method> methodGroup = groupedMethods.get(method.getName());
+ if (methodGroup != null)
+ {
+ for (Method nonBridgeMethod : methodGroup)
+ {
+ if (nonBridgeMethod.getParameterTypes().length == method.getParameterTypes().length)
+ {
+ // return the non-bridge method with the same name / argument count
+ return nonBridgeMethod;
+ }
+ }
+ }
+
+ // default to the provided method
+ return method;
+ }
+
+ /**
+ * Find all of the public methods that are not bridge methods and
+ * group them by method name
+ *
+ * {@see Method#isBridge()}
+ * @param type Bean type
+ * @return
+ */
+ private Map<String, List<Method>> groupNonBridgeMethodsByName(Class type)
+ {
+ Map<String, List<Method>> methodGroupsByName = CollectionFactory.newMap();
+ for (Method method : type.getMethods())
+ {
+ if (!method.isBridge())
+ {
+ List<Method> methodGroup = methodGroupsByName.get(method.getName());
+ if (methodGroup == null)
+ {
+ methodGroup = CollectionFactory.newList();
+ methodGroupsByName.put(method.getName(), methodGroup);
+ }
+ methodGroup.add(method);
+ }
+ }
+ return methodGroupsByName;
+ }
+
+ @Override
+ public Class getBeanType()
+ {
+ return beanType;
+ }
+
+ @Override
+ public String toString()
+ {
+ String names = InternalCommonsUtils.joinSorted(adapters.keySet());
+
+ return String.format("<ClassPropertyAdaptor %s: %s>", beanType.getName(), names);
+ }
+
+ @Override
+ public List<String> getPropertyNames()
+ {
+ return InternalCommonsUtils.sortedKeys(adapters);
+ }
+
+ @Override
+ public PropertyAdapter getPropertyAdapter(String name)
+ {
+ return adapters.get(name);
+ }
+
+ @Override
+ public Object get(Object instance, String propertyName)
+ {
+ return adaptorFor(propertyName).get(instance);
+ }
+
+ @Override
+ public void set(Object instance, String propertyName, Object value)
+ {
+ adaptorFor(propertyName).set(instance, value);
+ }
+
+ @Override
+ public Annotation getAnnotation(Object instance, String propertyName, Class<? extends Annotation> annotationClass) {
+ return adaptorFor(propertyName).getAnnotation(annotationClass);
+ }
+
+ private PropertyAdapter adaptorFor(String name)
+ {
+ PropertyAdapter pa = adapters.get(name);
+
+ if (pa == null)
+ throw new IllegalArgumentException(ServiceMessages.noSuchProperty(beanType, name));
+
+ return pa;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticClassListenerLogger.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticClassListenerLogger.java b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticClassListenerLogger.java
new file mode 100644
index 0000000..8ebdede
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticClassListenerLogger.java
@@ -0,0 +1,47 @@
+// Copyright 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.ioc.internal.services;
+
+import org.apache.tapestry5.plastic.ClassType;
+import org.apache.tapestry5.plastic.PlasticClassEvent;
+import org.apache.tapestry5.plastic.PlasticClassListener;
+import org.slf4j.Logger;
+import org.slf4j.Marker;
+import org.slf4j.MarkerFactory;
+
+public class PlasticClassListenerLogger implements PlasticClassListener
+{
+ private final Logger logger;
+
+ public PlasticClassListenerLogger(Logger logger)
+ {
+ this.logger = logger;
+ }
+
+ @Override
+ public void classWillLoad(PlasticClassEvent event)
+ {
+ if (logger.isDebugEnabled())
+ {
+ Marker marker = MarkerFactory.getMarker(event.getPrimaryClassName());
+
+ String extendedClassName = event.getType() == ClassType.PRIMARY ? event.getPrimaryClassName() : String
+ .format("%s (%s for %s)", event.getClassName(), event.getType(), event.getPrimaryClassName());
+
+ logger.debug(marker,
+ String.format("Loading class %s:\n%s", extendedClassName, event.getDissasembledBytecode()));
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java
new file mode 100644
index 0000000..a23ecec
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PlasticProxyFactoryImpl.java
@@ -0,0 +1,285 @@
+// Copyright 2011, 2012 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.ioc.internal.services;
+
+import org.apache.tapestry5.internal.plastic.PlasticInternalUtils;
+import org.apache.tapestry5.internal.plastic.asm.Type;
+import org.apache.tapestry5.internal.plastic.asm.tree.*;
+import org.apache.tapestry5.ioc.Location;
+import org.apache.tapestry5.ioc.ObjectCreator;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.internal.util.InternalCommonsUtils;
+import org.apache.tapestry5.ioc.services.PlasticProxyFactory;
+import org.apache.tapestry5.plastic.*;
+import org.slf4j.Logger;
+
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Member;
+import java.lang.reflect.Method;
+import java.util.List;
+import java.util.Map;
+
+public class PlasticProxyFactoryImpl implements PlasticProxyFactory
+{
+ public static final String INTERNAL_GET_DELEGATE = "_____internalGetDelegate_DONT_CALL_THIS_METHOD_____";
+
+ private final PlasticManager manager;
+
+ private final Map<String, Location> memberToLocation = CollectionFactory.newConcurrentMap();
+
+ public PlasticProxyFactoryImpl(ClassLoader parentClassLoader, Logger logger)
+ {
+ this(PlasticManager.withClassLoader(parentClassLoader).create(), logger);
+ }
+
+ public PlasticProxyFactoryImpl(PlasticManager manager, Logger logger)
+ {
+ assert manager != null;
+
+ this.manager = manager;
+
+ if (logger != null)
+ {
+ manager.addPlasticClassListener(new PlasticClassListenerLogger(logger));
+ }
+ }
+
+ @Override
+ public ClassLoader getClassLoader()
+ {
+ return manager.getClassLoader();
+ }
+
+ @Override
+ public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, Class<? extends T> implementationType, PlasticClassTransformer callback)
+ {
+ return manager.createProxy(interfaceType, implementationType, callback);
+ }
+
+ @Override
+ public <T> ClassInstantiator<T> createProxy(Class<T> interfaceType, PlasticClassTransformer callback)
+ {
+ return manager.createProxy(interfaceType, callback);
+ }
+
+
+ @Override
+ public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType,
+ Class<? extends T> implementationType)
+ {
+ return manager.createProxyTransformation(interfaceType, implementationType);
+ }
+
+ @Override
+ public <T> PlasticClassTransformation<T> createProxyTransformation(Class<T> interfaceType)
+ {
+ return createProxyTransformation(interfaceType, null);
+ }
+
+ @Override
+ public <T> T createProxy(final Class<T> interfaceType, final ObjectCreator<T> creator, final String description)
+ { return createProxy(interfaceType, null, creator, description);
+ }
+
+ @Override
+ public <T> T createProxy(final Class<T> interfaceType, final Class<? extends T> implementationType,
+ final ObjectCreator<T> creator, final String description)
+ {
+ assert creator != null;
+ assert InternalCommonsUtils.isNonBlank(description);
+
+ ClassInstantiator<T> instantiator = createProxy(interfaceType, implementationType, new PlasticClassTransformer()
+ {
+ @Override
+ public void transform(PlasticClass plasticClass)
+ {
+ final PlasticField objectCreatorField = plasticClass.introduceField(ObjectCreator.class, "creator")
+ .inject(creator);
+
+ final String interfaceTypeName = interfaceType.getName();
+ PlasticMethod delegateMethod = plasticClass.introducePrivateMethod(interfaceTypeName, "delegate",
+ null, null);
+
+ final InstructionBuilderCallback returnCreateObject = new InstructionBuilderCallback()
+ {
+ @Override
+ public void doBuild(InstructionBuilder builder)
+ {
+ builder.loadThis().getField(objectCreatorField);
+ builder.invoke(ObjectCreator.class, Object.class, "createObject");
+ builder.checkcast(interfaceType).returnResult();
+ }
+ };
+
+ delegateMethod.changeImplementation(returnCreateObject);
+
+ for (Method method : interfaceType.getMethods())
+ {
+ plasticClass.introduceMethod(method).delegateTo(delegateMethod);
+ }
+
+ // TA5-2235
+ MethodDescription getDelegateMethodDescription =
+ new MethodDescription(interfaceType.getName(), INTERNAL_GET_DELEGATE);
+ plasticClass.introduceMethod(getDelegateMethodDescription, returnCreateObject);
+
+ plasticClass.addToString(description);
+
+ }
+ });
+
+ return interfaceType.cast(instantiator.newInstance());
+ }
+
+ private ClassNode readClassNode(Class clazz)
+ {
+ byte[] bytecode = PlasticInternalUtils.readBytecodeForClass(manager.getClassLoader(), clazz.getName(), false);
+
+ return bytecode == null ? null : PlasticInternalUtils.convertBytecodeToClassNode(bytecode);
+ }
+
+ @Override
+ public Location getMethodLocation(final Method method)
+ {
+ ObjectCreator<String> descriptionCreator = new ObjectCreator<String>()
+ {
+ @Override
+ public String createObject()
+ {
+ return InternalCommonsUtils.asString(method);
+ }
+ };
+
+ return getMemberLocation(method, method.getName(), Type.getMethodDescriptor(method),
+ descriptionCreator);
+ }
+
+ @Override
+ public Location getConstructorLocation(final Constructor constructor)
+ {
+ ObjectCreator<String> descriptionCreator = new ObjectCreator<String>()
+ {
+ @Override
+ public String createObject()
+ {
+ StringBuilder builder = new StringBuilder(constructor.getDeclaringClass().getName()).append("(");
+ String sep = "";
+
+ for (Class parameterType : constructor.getParameterTypes())
+ {
+ builder.append(sep);
+ builder.append(parameterType.getSimpleName());
+
+ sep = ", ";
+ }
+
+ builder.append(")");
+
+ return builder.toString();
+ }
+ };
+
+ return getMemberLocation(constructor, "<init>", Type.getConstructorDescriptor(constructor),
+ descriptionCreator);
+ }
+
+ @Override
+ public void clearCache()
+ {
+ memberToLocation.clear();
+ }
+
+
+ public Location getMemberLocation(Member member, String methodName, String memberTypeDesc, ObjectCreator<String> textDescriptionCreator)
+ {
+ String className = member.getDeclaringClass().getName();
+
+ String key = className + ":" + methodName + ":" + memberTypeDesc;
+
+ Location location = memberToLocation.get(key);
+
+ if (location == null)
+ {
+ location = constructMemberLocation(member, methodName, memberTypeDesc, textDescriptionCreator.createObject());
+
+ memberToLocation.put(key, location);
+ }
+
+ return location;
+
+ }
+
+ private Location constructMemberLocation(Member member, String methodName, String memberTypeDesc, String textDescription)
+ {
+
+ ClassNode classNode = readClassNode(member.getDeclaringClass());
+
+ if (classNode == null)
+ {
+ throw new RuntimeException(String.format("Unable to read class file for %s (to gather line number information).",
+ textDescription));
+ }
+
+ for (MethodNode mn : (List<MethodNode>) classNode.methods)
+ {
+ if (mn.name.equals(methodName) && mn.desc.equals(memberTypeDesc))
+ {
+ int lineNumber = findFirstLineNumber(mn.instructions);
+
+ // If debugging info is not available, we may lose the line number data, in which case,
+ // just generate the Location from the textDescription.
+
+ if (lineNumber < 1)
+ {
+ break;
+ }
+
+ String description = String.format("%s (at %s:%d)", textDescription, classNode.sourceFile, lineNumber);
+
+ return new StringLocation(description, lineNumber);
+ }
+ }
+
+ // Didn't find it. Odd.
+
+ return new StringLocation(textDescription, 0);
+ }
+
+ private int findFirstLineNumber(InsnList instructions)
+ {
+ for (AbstractInsnNode node = instructions.getFirst(); node != null; node = node.getNext())
+ {
+ if (node instanceof LineNumberNode)
+ {
+ return ((LineNumberNode) node).line;
+ }
+ }
+
+ return -1;
+ }
+
+ @Override
+ public void addPlasticClassListener(PlasticClassListener listener)
+ {
+ manager.addPlasticClassListener(listener);
+ }
+
+ @Override
+ public void removePlasticClassListener(PlasticClassListener listener)
+ {
+ manager.removePlasticClassListener(listener);
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java
new file mode 100644
index 0000000..8dd1e02
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAccessImpl.java
@@ -0,0 +1,217 @@
+// Copyright 2006, 2007, 2008, 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.ioc.internal.services;
+
+import java.beans.BeanInfo;
+import java.beans.IntrospectionException;
+import java.beans.Introspector;
+import java.beans.PropertyDescriptor;
+import java.lang.annotation.Annotation;
+import java.lang.reflect.Method;
+import java.lang.reflect.Modifier;
+import java.util.Arrays;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
+import org.apache.tapestry5.ioc.services.PropertyAccess;
+
+@SuppressWarnings("unchecked")
+public class PropertyAccessImpl implements PropertyAccess
+{
+ private final Map<Class, ClassPropertyAdapter> adapters = CollectionFactory.newConcurrentMap();
+
+ @Override
+ public Object get(Object instance, String propertyName)
+ {
+ return getAdapter(instance).get(instance, propertyName);
+ }
+
+ @Override
+ public void set(Object instance, String propertyName, Object value)
+ {
+ getAdapter(instance).set(instance, propertyName, value);
+ }
+
+ @Override
+ public Annotation getAnnotation(Object instance, String propertyName, Class<? extends Annotation> annotationClass) {
+ return getAdapter(instance).getAnnotation(instance, propertyName, annotationClass);
+ }
+
+
+ /**
+ * Clears the cache of adapters and asks the {@link Introspector} to clear its cache.
+ */
+ @Override
+ public synchronized void clearCache()
+ {
+ adapters.clear();
+
+ Introspector.flushCaches();
+ }
+
+ @Override
+ public ClassPropertyAdapter getAdapter(Object instance)
+ {
+ return getAdapter(instance.getClass());
+ }
+
+ @Override
+ public ClassPropertyAdapter getAdapter(Class forClass)
+ {
+ ClassPropertyAdapter result = adapters.get(forClass);
+
+ if (result == null)
+ {
+ result = buildAdapter(forClass);
+ adapters.put(forClass, result);
+ }
+
+ return result;
+ }
+
+ /**
+ * Builds a new adapter and updates the _adapters cache. This not only guards access to the adapter cache, but also
+ * serializes access to the Java Beans Introspector, which is not thread safe. In addition, handles the case where
+ * the class in question is an interface, accumulating properties inherited from super-classes.
+ */
+ private synchronized ClassPropertyAdapter buildAdapter(Class forClass)
+ {
+ // In some race conditions, we may hit this method for the same class multiple times.
+ // We just let it happen, replacing the old ClassPropertyAdapter with a new one.
+
+ try
+ {
+ BeanInfo info = Introspector.getBeanInfo(forClass);
+
+ List<PropertyDescriptor> descriptors = CollectionFactory.newList();
+
+ addAll(descriptors, info.getPropertyDescriptors());
+
+ // TAP5-921 - Introspector misses interface methods not implemented in an abstract class
+ if (forClass.isInterface() || Modifier.isAbstract(forClass.getModifiers()) )
+ addPropertiesFromExtendedInterfaces(forClass, descriptors);
+
+ addPropertiesFromScala(forClass, descriptors);
+
+ return new ClassPropertyAdapterImpl(forClass, descriptors);
+ }
+ catch (Throwable ex)
+ {
+ throw new RuntimeException(ex);
+ }
+ }
+
+ private <T> void addAll(List<T> list, T[] array)
+ {
+ list.addAll(Arrays.asList(array));
+ }
+
+ private void addPropertiesFromExtendedInterfaces(Class forClass, List<PropertyDescriptor> descriptors)
+ throws IntrospectionException
+ {
+ LinkedList<Class> queue = CollectionFactory.newLinkedList();
+
+ // Seed the queue
+ addAll(queue, forClass.getInterfaces());
+
+ while (!queue.isEmpty())
+ {
+ Class c = queue.removeFirst();
+
+ BeanInfo info = Introspector.getBeanInfo(c);
+
+ // Duplicates occur and are filtered out in ClassPropertyAdapter which stores
+ // a property name to descriptor map.
+ addAll(descriptors, info.getPropertyDescriptors());
+ addAll(queue, c.getInterfaces());
+ }
+ }
+
+ private void addPropertiesFromScala(Class forClass, List<PropertyDescriptor> descriptors)
+ throws IntrospectionException
+ {
+ for (Method method : forClass.getMethods())
+ {
+ addPropertyIfScalaGetterMethod(forClass, descriptors, method);
+ }
+ }
+
+ private void addPropertyIfScalaGetterMethod(Class forClass, List<PropertyDescriptor> descriptors, Method method)
+ throws IntrospectionException
+ {
+ if (!isScalaGetterMethod(method))
+ return;
+
+ PropertyDescriptor propertyDescriptor = new PropertyDescriptor(method.getName(), forClass, method.getName(),
+ null);
+
+ // found a getter, looking for the setter now
+ try
+ {
+ Method setterMethod = findScalaSetterMethod(forClass, method);
+
+ propertyDescriptor.setWriteMethod(setterMethod);
+ }
+ catch (NoSuchMethodException e)
+ {
+ // ignore
+ }
+
+ // check if the same property was already discovered with java bean accessors
+
+ addScalaPropertyIfNoJavaBeansProperty(descriptors, propertyDescriptor, method);
+ }
+
+ private void addScalaPropertyIfNoJavaBeansProperty(List<PropertyDescriptor> descriptors,
+ PropertyDescriptor propertyDescriptor, Method getterMethod)
+ {
+ boolean found = false;
+
+ for (PropertyDescriptor currentPropertyDescriptor : descriptors)
+ {
+ if (currentPropertyDescriptor.getName().equals(getterMethod.getName()))
+ {
+ found = true;
+
+ break;
+ }
+ }
+
+ if (!found)
+ descriptors.add(propertyDescriptor);
+ }
+
+ private Method findScalaSetterMethod(Class forClass, Method getterMethod) throws NoSuchMethodException
+ {
+ return forClass.getMethod(getterMethod.getName() + "_$eq", getterMethod.getReturnType());
+ }
+
+ private boolean isScalaGetterMethod(Method method)
+ {
+ try
+ {
+ return Modifier.isPublic(method.getModifiers()) && method.getParameterTypes().length == 0
+ && !method.getReturnType().equals(Void.TYPE)
+ && method.getDeclaringClass().getDeclaredField(method.getName()) != null;
+ }
+ catch (NoSuchFieldException ex)
+ {
+ return false;
+ }
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
----------------------------------------------------------------------
diff --git a/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
new file mode 100644
index 0000000..97685ef
--- /dev/null
+++ b/beanmodel/src/main/java/org/apache/tapestry5/ioc/internal/services/PropertyAdapterImpl.java
@@ -0,0 +1,273 @@
+// 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.
+// 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.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.AnnotationProvider;
+import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
+import org.apache.tapestry5.ioc.services.PropertyAdapter;
+import org.apache.tapestry5.ioc.util.ExceptionUtils;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.*;
+import java.util.List;
+
+public class PropertyAdapterImpl implements PropertyAdapter
+{
+ private final ClassPropertyAdapter classAdapter;
+
+ private final String name;
+
+ private final Method readMethod;
+
+ private final Method writeMethod;
+
+ private final Class type;
+
+ private final boolean castRequired;
+
+ // Synchronized by this; lazily initialized
+ private AnnotationProvider annotationProvider;
+
+ private final Field field;
+
+ private final Class declaringClass;
+
+ PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Method readMethod,
+ Method writeMethod)
+ {
+ this.classAdapter = classAdapter;
+ this.name = name;
+ this.type = type;
+
+ this.readMethod = readMethod;
+ this.writeMethod = writeMethod;
+
+ declaringClass = readMethod != null ? readMethod.getDeclaringClass() : writeMethod.getDeclaringClass();
+
+ castRequired = readMethod != null && readMethod.getReturnType() != type;
+
+ field = null;
+ }
+
+ PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Field field)
+ {
+ this.classAdapter = classAdapter;
+ this.name = name;
+ this.type = type;
+
+ this.field = field;
+
+ declaringClass = field.getDeclaringClass();
+
+ castRequired = field.getType() != type;
+
+ readMethod = null;
+ writeMethod = null;
+ }
+
+ @Override
+ public String getName()
+ {
+ return name;
+ }
+
+ @Override
+ public Method getReadMethod()
+ {
+ return readMethod;
+ }
+
+ @Override
+ public Class getType()
+ {
+ return type;
+ }
+
+ @Override
+ public Method getWriteMethod()
+ {
+ return writeMethod;
+ }
+
+ @Override
+ public boolean isRead()
+ {
+ return field != null || readMethod != null;
+ }
+
+ @Override
+ public boolean isUpdate()
+ {
+ return writeMethod != null || (field != null && !isFinal(field));
+ }
+
+ private boolean isFinal(Member member)
+ {
+ return Modifier.isFinal(member.getModifiers());
+ }
+
+ @Override
+ public Object get(Object instance)
+ {
+ if (field == null && readMethod == null)
+ {
+ throw new UnsupportedOperationException(String.format("Class %s does not provide an accessor ('getter') method for property '%s'.", toClassName(instance), name));
+ }
+
+ Throwable fail;
+
+ try
+ {
+ if (field == null)
+ return readMethod.invoke(instance);
+ else
+ return field.get(instance);
+ } catch (InvocationTargetException ex)
+ {
+ fail = ex.getTargetException();
+ } catch (Exception ex)
+ {
+ fail = ex;
+ }
+
+ throw new RuntimeException(ServiceMessages.readFailure(name, instance, fail), fail);
+ }
+
+ @Override
+ public void set(Object instance, Object value)
+ {
+ if (field == null && writeMethod == null)
+ {
+ throw new UnsupportedOperationException(String.format("Class %s does not provide a mutator ('setter') method for property '%s'.",
+ toClassName(instance),
+ name
+ ));
+ }
+
+ Throwable fail;
+
+ try
+ {
+ if (field == null)
+ writeMethod.invoke(instance, value);
+ else
+ field.set(instance, value);
+
+ return;
+ } catch (InvocationTargetException ex)
+ {
+ fail = ex.getTargetException();
+ } catch (Exception ex)
+ {
+ fail = ex;
+ }
+
+ throw new RuntimeException(String.format("Error updating property '%s' of %s: %s",
+ name, toClassName(instance),
+ ExceptionUtils.toMessage(fail)), fail);
+ }
+
+ private String toClassName(Object instance)
+ {
+ return instance == null ? "<null>" : instance.getClass().getName();
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ return getAnnnotationProvider().getAnnotation(annotationClass);
+ }
+
+ /**
+ * Creates (as needed) the annotation provider for this property.
+ */
+ private synchronized AnnotationProvider getAnnnotationProvider()
+ {
+ if (annotationProvider == null)
+ {
+ List<AnnotationProvider> providers = CollectionFactory.newList();
+
+ if (readMethod != null)
+ providers.add(new AccessableObjectAnnotationProvider(readMethod));
+
+ if (writeMethod != null)
+ providers.add(new AccessableObjectAnnotationProvider(writeMethod));
+
+ // There's an assumption here, that the fields match the property name (we ignore case
+ // which leads to a manageable ambiguity) and that the field and the getter/setter
+ // are in the same class (i.e., that we don't have a getter exposing a protected field inherted
+ // from a base class, or some other oddity).
+
+ Class cursor = getBeanType();
+
+ out:
+ while (cursor != null)
+ {
+ for (Field f : cursor.getDeclaredFields())
+ {
+ if (f.getName().equalsIgnoreCase(name))
+ {
+ providers.add(new AccessableObjectAnnotationProvider(f));
+
+ break out;
+ }
+ }
+
+ cursor = cursor.getSuperclass();
+ }
+
+ annotationProvider = AnnotationProviderChain.create(providers);
+ }
+
+ return annotationProvider;
+ }
+
+ @Override
+ public boolean isCastRequired()
+ {
+ return castRequired;
+ }
+
+ @Override
+ public ClassPropertyAdapter getClassAdapter()
+ {
+ return classAdapter;
+ }
+
+ @Override
+ public Class getBeanType()
+ {
+ return classAdapter.getBeanType();
+ }
+
+ @Override
+ public boolean isField()
+ {
+ return field != null;
+ }
+
+ @Override
+ public Field getField()
+ {
+ return field;
+ }
+
+ @Override
+ public Class getDeclaringClass()
+ {
+ return declaringClass;
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/build.gradle
----------------------------------------------------------------------
diff --git a/commons/build.gradle b/commons/build.gradle
index 76850ef..98ae8bf 100644
--- a/commons/build.gradle
+++ b/commons/build.gradle
@@ -10,6 +10,7 @@ buildDir = 'target/gradle-build'
dependencies {
compile project(":plastic")
compile project(":tapestry5-annotations")
+ compile project(":tapestry-func")
}
jar {
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/src/main/java/org/apache/tapestry5/internal/services/StringInternerImpl.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/internal/services/StringInternerImpl.java b/commons/src/main/java/org/apache/tapestry5/internal/services/StringInternerImpl.java
new file mode 100644
index 0000000..fae9ab8
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/internal/services/StringInternerImpl.java
@@ -0,0 +1,54 @@
+// Copyright 2009, 2012 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.ioc.internal.util.CollectionFactory;
+import org.apache.tapestry5.services.ComponentClasses;
+import org.apache.tapestry5.services.InvalidationEventHub;
+
+import javax.annotation.PostConstruct;
+
+import java.util.Map;
+
+public class StringInternerImpl implements StringInterner
+{
+ private final Map<String, String> cache = CollectionFactory.newConcurrentMap();
+
+ @PostConstruct
+ public void setupInvalidation(@ComponentClasses InvalidationEventHub hub)
+ {
+ hub.clearOnInvalidation(cache);
+ }
+
+ public String intern(String string)
+ {
+ String result = cache.get(string);
+
+ // Not yet in the cache? Add it.
+
+ if (result == null)
+ {
+ cache.put(string, string);
+ result = string;
+ }
+
+ return result;
+ }
+
+ public String format(String format, Object... arguments)
+ {
+ return intern(String.format(format, arguments));
+ }
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/src/main/java/org/apache/tapestry5/ioc/Configuration.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/Configuration.java b/commons/src/main/java/org/apache/tapestry5/ioc/Configuration.java
new file mode 100644
index 0000000..03814f5
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/Configuration.java
@@ -0,0 +1,53 @@
+// Copyright 2006, 2008, 2009 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.ioc;
+
+/**
+ * Object passed into a service contributor method that allows the method provide contributed values to the service's
+ * configuration.
+ * <p/>
+ * A service can <em>collect</em> contributions in three different ways:
+ * <ul>
+ * <li>As an un-ordered collection of values</li>
+ * <li>As an ordered list of values (where each value has a unique id, pre-requisites and post-requisites)</li>
+ * <li>As a map of keys and values
+ * </ul>
+ * <p/>
+ * This implementation is used for un-ordered configuration data.
+ * <p/>
+ * The service defines the <em>type</em> of contribution, in terms of a base class or service interface. Contributions
+ * must be compatible with the type.
+ */
+public interface Configuration<T>
+{
+ /**
+ * Adds an object to the service's contribution.
+ *
+ * @param object
+ * to add to the service's configuration
+ */
+ void add(T object);
+
+ /**
+ * Automatically instantiates an instance of the class, with dependencies injected, and adds it to the
+ * configuration. When the configuration type is an interface and the class to be contributed is a local file,
+ * then a reloadable proxy for the class will be created and contributed.
+ *
+ * @param clazz
+ * what class to instantiate
+ * @since 5.1.0.0
+ */
+ void addInstance(Class<? extends T> clazz);
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/src/main/java/org/apache/tapestry5/ioc/MappedConfiguration.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/MappedConfiguration.java b/commons/src/main/java/org/apache/tapestry5/ioc/MappedConfiguration.java
new file mode 100644
index 0000000..47c6026
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/MappedConfiguration.java
@@ -0,0 +1,81 @@
+// Copyright 2006, 2008, 2009, 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.ioc;
+
+/**
+ * Object passed into a service contributor method that allows the method provide contributed values to the service's
+ * configuration.
+ * <p/>
+ * A service can <em>collect</em> contributions in three different ways:
+ * <ul>
+ * <li>As an un-ordered collection of values</li>
+ * <li>As an ordered list of values (where each value has a unique id, pre-requisites and post-requisites)</li>
+ * <li>As a map of keys and values
+ * </ul>
+ * <p/>
+ * The service defines the <em>type</em> of contribution, in terms of a base class or service interface. Contributions
+ * must be compatible with the type.
+ */
+public interface MappedConfiguration<K, V>
+{
+
+ /**
+ * Adds a keyed object to the service's contribution.
+ *
+ * @param key
+ * unique id for the value
+ * @param value
+ * to contribute
+ * @throws IllegalArgumentException
+ * if key is not unique
+ */
+ void add(K key, V value);
+
+ /**
+ * Overrides an existing contribution by its key.
+ *
+ * @param key
+ * unique id of value to override
+ * @param value
+ * new value, or null to remove the key entirely
+ * @since 5.1.0.0
+ */
+ void override(K key, V value);
+
+ /**
+ * Adds a keyed object as an instantiated instance (with dependencies injected) of a class. When the value
+ * type is an interface and the class to be contributed is a local file,
+ * then a reloadable proxy for the value class will be created and contributed.
+ *
+ * @param key
+ * unique id for the value
+ * @param clazz
+ * class to instantiate and contribute
+ * @since 5.1.0.0
+ */
+ void addInstance(K key, Class<? extends V> clazz);
+
+ /**
+ * Overrides an existing contribution with a new instance. When the value
+ * type is an interface and the class to be contributed is a local file,
+ * then a reloadable proxy for the value class will be created and contributed.
+ *
+ * @param key
+ * unique id of value to override
+ * @param clazz
+ * class to instantiate as override
+ */
+ void overrideInstance(K key, Class<? extends V> clazz);
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/src/main/java/org/apache/tapestry5/ioc/OrderedConfiguration.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/OrderedConfiguration.java b/commons/src/main/java/org/apache/tapestry5/ioc/OrderedConfiguration.java
new file mode 100644
index 0000000..9151381
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/OrderedConfiguration.java
@@ -0,0 +1,84 @@
+// Copyright 2006, 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.ioc;
+
+/**
+ * Object passed into a service contributor method that allows the method provide contributed values to the service's
+ * configuration.
+ * <p/>
+ * A service can <em>collect</em> contributions in three different ways:
+ * <ul>
+ * <li>As an un-ordered collection of values</li>
+ * <li>As an ordered list of values (where each value has a unique id, pre-requisites and post-requisites)</li>
+ * <li>As a map of keys and values
+ * </ul>
+ * <p/>
+ * The service defines the <em>type</em> of contribution, in terms of a base class or service interface. Contributions
+ * must be compatible with the type, or be {@linkplain org.apache.tapestry5.ioc.services.TypeCoercer coercable} to the type.
+ *
+ * @see org.apache.tapestry5.ioc.annotations.Contribute
+ * @see org.apache.tapestry5.ioc.annotations.UsesConfiguration
+ */
+public interface OrderedConfiguration<T>
+{
+ /**
+ * Adds an ordered object to a service's contribution. Each object has an id (which must be unique). Optionally,
+ * pre-requisites (a list of ids that must precede this object) and post-requisites (ids that must follow) can be
+ * provided.
+ * <p/>
+ * <p>If no constraints are supplied, then an implicit constraint is supplied: after the previously
+ * contributed id <em>within the same contribution method</em>.
+ *
+ * @param id a unique id for the object; the id will be fully qualified with the contributing module's id
+ * @param constraints used to order the object relative to other contributed objects
+ * @param object to add to the service's configuration
+ */
+ void add(String id, T object, String... constraints);
+
+ /**
+ * Overrides a normally contributed object. Each override must match a single normally contributed object.
+ *
+ * @param id identifies object to override
+ * @param object overriding object (may be null)
+ * @param constraints constraints for the overridden object, replacing constraints for the original object (even if
+ * omitted, in which case the override object will have no ordering constraints)
+ * @since 5.1.0.0
+ */
+ void override(String id, T object, String... constraints);
+
+ /**
+ * Adds an ordered object by instantiating (with dependencies) the indicated class. When the configuration type is
+ * an interface and the class to be contributed is a local file,
+ * then a reloadable proxy for the class will be created and contributed.
+ *
+ * @param id of contribution (used for ordering)
+ * @param clazz class to instantiate
+ * @param constraints used to order the object relative to other contributed objects
+ * @since 5.1.0.0
+ */
+ void addInstance(String id, Class<? extends T> clazz, String... constraints);
+
+ /**
+ * Instantiates an object and adds it as an override. When the configuration type is an interface and the class to
+ * be contributed is a local file, then a reloadable proxy for the class will be created and contributed.
+ *
+ * @param id of object to override
+ * @param clazz to instantiate
+ * @param constraints constraints for the overridden object, replacing constraints for the original object (even if
+ * omitted, in which case the override object will have no ordering constraints)
+ * @since 5.1.0.0
+ */
+ void overrideInstance(String id, Class<? extends T> clazz, String... constraints);
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicTypeCoercions.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicTypeCoercions.java b/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicTypeCoercions.java
new file mode 100644
index 0000000..f7bde31
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/internal/BasicTypeCoercions.java
@@ -0,0 +1,342 @@
+// Copyright 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.ioc.internal;
+
+import java.io.File;
+import java.lang.reflect.Array;
+import java.math.BigDecimal;
+import java.math.BigInteger;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.Collections;
+import java.util.List;
+
+import org.apache.tapestry5.func.Flow;
+import org.apache.tapestry5.ioc.Configuration;
+import org.apache.tapestry5.ioc.services.Coercion;
+import org.apache.tapestry5.ioc.services.CoercionTuple;
+import org.apache.tapestry5.ioc.services.TypeCoercer;
+import org.apache.tapestry5.ioc.util.TimeInterval;
+
+/**
+ * Class that provides Tapestry-IoC's basic type coercions.
+ * @see TypeCoercer
+ * @see Coercion
+ */
+public class BasicTypeCoercions
+{
+ /**
+ * Provides the basic type coercions to a {@link Configuration} instance.
+ */
+ public static void provideBasicTypeCoercions(Configuration<CoercionTuple> configuration)
+ {
+ add(configuration, Object.class, String.class, new Coercion<Object, String>()
+ {
+ @Override
+ public String coerce(Object input)
+ {
+ return input.toString();
+ }
+ });
+
+ add(configuration, Object.class, Boolean.class, new Coercion<Object, Boolean>()
+ {
+ @Override
+ public Boolean coerce(Object input)
+ {
+ return input != null;
+ }
+ });
+
+ add(configuration, String.class, Double.class, new Coercion<String, Double>()
+ {
+ @Override
+ public Double coerce(String input)
+ {
+ return new Double(input);
+ }
+ });
+
+ // String to BigDecimal is important, as String->Double->BigDecimal would lose
+ // precision.
+
+ add(configuration, String.class, BigDecimal.class, new Coercion<String, BigDecimal>()
+ {
+ @Override
+ public BigDecimal coerce(String input)
+ {
+ return new BigDecimal(input);
+ }
+ });
+
+ add(configuration, BigDecimal.class, Double.class, new Coercion<BigDecimal, Double>()
+ {
+ @Override
+ public Double coerce(BigDecimal input)
+ {
+ return input.doubleValue();
+ }
+ });
+
+ add(configuration, String.class, BigInteger.class, new Coercion<String, BigInteger>()
+ {
+ @Override
+ public BigInteger coerce(String input)
+ {
+ return new BigInteger(input);
+ }
+ });
+
+ add(configuration, String.class, Long.class, new Coercion<String, Long>()
+ {
+ @Override
+ public Long coerce(String input)
+ {
+ return new Long(input);
+ }
+ });
+
+ add(configuration, Long.class, Byte.class, new Coercion<Long, Byte>()
+ {
+ @Override
+ public Byte coerce(Long input)
+ {
+ return input.byteValue();
+ }
+ });
+
+ add(configuration, Long.class, Short.class, new Coercion<Long, Short>()
+ {
+ @Override
+ public Short coerce(Long input)
+ {
+ return input.shortValue();
+ }
+ });
+
+ add(configuration, Long.class, Integer.class, new Coercion<Long, Integer>()
+ {
+ @Override
+ public Integer coerce(Long input)
+ {
+ return input.intValue();
+ }
+ });
+
+ add(configuration, Number.class, Long.class, new Coercion<Number, Long>()
+ {
+ @Override
+ public Long coerce(Number input)
+ {
+ return input.longValue();
+ }
+ });
+
+ add(configuration, Double.class, Float.class, new Coercion<Double, Float>()
+ {
+ @Override
+ public Float coerce(Double input)
+ {
+ return input.floatValue();
+ }
+ });
+
+ add(configuration, Long.class, Double.class, new Coercion<Long, Double>()
+ {
+ @Override
+ public Double coerce(Long input)
+ {
+ return input.doubleValue();
+ }
+ });
+
+ add(configuration, String.class, Boolean.class, new Coercion<String, Boolean>()
+ {
+ @Override
+ public Boolean coerce(String input)
+ {
+ String trimmed = input == null ? "" : input.trim();
+
+ if (trimmed.equalsIgnoreCase("false") || trimmed.length() == 0)
+ return false;
+
+ // Any non-blank string but "false"
+
+ return true;
+ }
+ });
+
+ add(configuration, Number.class, Boolean.class, new Coercion<Number, Boolean>()
+ {
+ @Override
+ public Boolean coerce(Number input)
+ {
+ return input.longValue() != 0;
+ }
+ });
+
+ add(configuration, Void.class, Boolean.class, new Coercion<Void, Boolean>()
+ {
+ @Override
+ public Boolean coerce(Void input)
+ {
+ return false;
+ }
+ });
+
+ add(configuration, Collection.class, Boolean.class, new Coercion<Collection, Boolean>()
+ {
+ @Override
+ public Boolean coerce(Collection input)
+ {
+ return !input.isEmpty();
+ }
+ });
+
+ add(configuration, Object.class, List.class, new Coercion<Object, List>()
+ {
+ @Override
+ public List coerce(Object input)
+ {
+ return Collections.singletonList(input);
+ }
+ });
+
+ add(configuration, Object[].class, List.class, new Coercion<Object[], List>()
+ {
+ @Override
+ public List coerce(Object[] input)
+ {
+ return Arrays.asList(input);
+ }
+ });
+
+ add(configuration, Object[].class, Boolean.class, new Coercion<Object[], Boolean>()
+ {
+ @Override
+ public Boolean coerce(Object[] input)
+ {
+ return input != null && input.length > 0;
+ }
+ });
+
+ add(configuration, Float.class, Double.class, new Coercion<Float, Double>()
+ {
+ @Override
+ public Double coerce(Float input)
+ {
+ return input.doubleValue();
+ }
+ });
+
+ Coercion primitiveArrayCoercion = new Coercion<Object, List>()
+ {
+ @Override
+ public List<Object> coerce(Object input)
+ {
+ int length = Array.getLength(input);
+ Object[] array = new Object[length];
+ for (int i = 0; i < length; i++)
+ {
+ array[i] = Array.get(input, i);
+ }
+ return Arrays.asList(array);
+ }
+ };
+
+ add(configuration, byte[].class, List.class, primitiveArrayCoercion);
+ add(configuration, short[].class, List.class, primitiveArrayCoercion);
+ add(configuration, int[].class, List.class, primitiveArrayCoercion);
+ add(configuration, long[].class, List.class, primitiveArrayCoercion);
+ add(configuration, float[].class, List.class, primitiveArrayCoercion);
+ add(configuration, double[].class, List.class, primitiveArrayCoercion);
+ add(configuration, char[].class, List.class, primitiveArrayCoercion);
+ add(configuration, boolean[].class, List.class, primitiveArrayCoercion);
+
+ add(configuration, String.class, File.class, new Coercion<String, File>()
+ {
+ @Override
+ public File coerce(String input)
+ {
+ return new File(input);
+ }
+ });
+
+ add(configuration, String.class, TimeInterval.class, new Coercion<String, TimeInterval>()
+ {
+ @Override
+ public TimeInterval coerce(String input)
+ {
+ return new TimeInterval(input);
+ }
+ });
+
+ add(configuration, TimeInterval.class, Long.class, new Coercion<TimeInterval, Long>()
+ {
+ @Override
+ public Long coerce(TimeInterval input)
+ {
+ return input.milliseconds();
+ }
+ });
+
+ add(configuration, Object.class, Object[].class, new Coercion<Object, Object[]>()
+ {
+ @Override
+ public Object[] coerce(Object input)
+ {
+ return new Object[]
+ {input};
+ }
+ });
+
+ add(configuration, Collection.class, Object[].class, new Coercion<Collection, Object[]>()
+ {
+ @Override
+ public Object[] coerce(Collection input)
+ {
+ return input.toArray();
+ }
+ });
+
+ configuration.add(CoercionTuple.create(Flow.class, List.class, new Coercion<Flow, List>()
+ {
+ @Override
+ public List coerce(Flow input)
+ {
+ return input.toList();
+ }
+ }));
+
+ configuration.add(CoercionTuple.create(Flow.class, Boolean.class, new Coercion<Flow, Boolean>()
+ {
+ @Override
+ public Boolean coerce(Flow input)
+ {
+ return !input.isEmpty();
+ }
+ }));
+
+
+ }
+
+ private static <S, T> void add(Configuration<CoercionTuple> configuration, Class<S> sourceType,
+ Class<T> targetType, Coercion<S, T> coercion)
+ {
+ configuration.add(CoercionTuple.create(sourceType, targetType, coercion));
+ }
+
+
+
+}
http://git-wip-us.apache.org/repos/asf/tapestry-5/blob/c7bf35ce/commons/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java
----------------------------------------------------------------------
diff --git a/commons/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java b/commons/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java
new file mode 100644
index 0000000..2acfd0d
--- /dev/null
+++ b/commons/src/main/java/org/apache/tapestry5/ioc/internal/services/AccessableObjectAnnotationProvider.java
@@ -0,0 +1,46 @@
+// Copyright 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.ioc.internal.services;
+
+import org.apache.tapestry5.ioc.AnnotationProvider;
+
+import java.lang.annotation.Annotation;
+import java.lang.reflect.AccessibleObject;
+
+/**
+ * Provides access to annotations of an accessable object such as a {@link java.lang.reflect.Method} or {@link
+ * java.lang.reflect.Field}.
+ */
+public class AccessableObjectAnnotationProvider implements AnnotationProvider
+{
+ private final AccessibleObject object;
+
+ public AccessableObjectAnnotationProvider(AccessibleObject object)
+ {
+ this.object = object;
+ }
+
+ @Override
+ public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
+ {
+ return object.getAnnotation(annotationClass);
+ }
+
+ @Override
+ public String toString()
+ {
+ return String.format("AnnotationProvider[%s]", object);
+ }
+}