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 <thiag...@apache.org> Authored: Sat Dec 6 19:45:14 2014 -0200 Committer: Thiago H. de Paula Figueiredo <thiag...@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); + } +}