This is an automated email from the ASF dual-hosted git repository. ggregory pushed a commit to branch master in repository https://gitbox.apache.org/repos/asf/commons-beanutils.git
The following commit(s) were added to refs/heads/master by this push: new eda89e67 Javadoc @see tags do not need to use a FQCN for classes in java.lang eda89e67 is described below commit eda89e6716b1f83266cc72fab0ac8c743c7be17f Author: Gary Gregory <garydgreg...@gmail.com> AuthorDate: Mon Aug 29 07:05:30 2022 -0400 Javadoc @see tags do not need to use a FQCN for classes in java.lang --- .../commons/beanutils2/ConstructorUtils.java | 898 ++++++++++----------- .../beanutils2/ContextClassLoaderLocal.java | 430 +++++----- .../apache/commons/beanutils2/DynaProperty.java | 686 ++++++++-------- .../beanutils2/converters/EnumConverter.java | 186 ++--- 4 files changed, 1100 insertions(+), 1100 deletions(-) diff --git a/src/main/java/org/apache/commons/beanutils2/ConstructorUtils.java b/src/main/java/org/apache/commons/beanutils2/ConstructorUtils.java index f1e04e36..135ccf71 100644 --- a/src/main/java/org/apache/commons/beanutils2/ConstructorUtils.java +++ b/src/main/java/org/apache/commons/beanutils2/ConstructorUtils.java @@ -1,449 +1,449 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.commons.beanutils2; - -import java.lang.reflect.Array; -import java.lang.reflect.Constructor; -import java.lang.reflect.InvocationTargetException; -import java.lang.reflect.Modifier; - -/** - * <p> Utility reflection methods focused on constructors, modeled after {@link MethodUtils}. </p> - * - * <h2>Known Limitations</h2> - * <h3>Accessing Public Constructors In A Default Access Superclass</h3> - * <p>There is an issue when invoking public constructors contained in a default access superclass. - * Reflection locates these constructors fine and correctly assigns them as public. - * However, an {@code IllegalAccessException} is thrown if the constructors is invoked.</p> - * - * <p>{@code ConstructorUtils} contains a workaround for this situation. - * It will attempt to call {@code setAccessible} on this constructor. - * If this call succeeds, then the method can be invoked as normal. - * This call will only succeed when the application has sufficient security privileges. - * If this call fails then a warning will be logged and the method may fail.</p> - * - */ -public class ConstructorUtils { - - /** - * Returns a constructor with single argument. - * @param <T> the type of the constructor - * @param klass the class to be constructed - * @param parameterType The constructor parameter type - * @return null if matching accessible constructor can not be found. - * @see Class#getConstructor - * @see #getAccessibleConstructor(java.lang.reflect.Constructor) - */ - public static <T> Constructor<T> getAccessibleConstructor( - final Class<T> klass, - final Class<?> parameterType) { - - final Class<?>[] parameterTypes = { parameterType }; - return getAccessibleConstructor(klass, parameterTypes); - } - - /** - * Returns a constructor given a class and signature. - * @param <T> the type to be constructed - * @param klass the class to be constructed - * @param parameterTypes the parameter array - * @return null if matching accessible constructor can not be found - * @see Class#getConstructor - * @see #getAccessibleConstructor(java.lang.reflect.Constructor) - */ - public static <T> Constructor<T> getAccessibleConstructor( - final Class<T> klass, - final Class<?>[] parameterTypes) { - - try { - return getAccessibleConstructor( - klass.getConstructor(parameterTypes)); - } catch (final NoSuchMethodException e) { - return null; - } - } - - /** - * Returns accessible version of the given constructor. - * @param <T> the type of the constructor - * @param ctor prototype constructor object. - * @return {@code null} if accessible constructor can not be found. - * @see java.lang.SecurityManager - */ - public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) { - - // Make sure we have a method to check - if (ctor == null) { - return null; - } - - // If the requested method is not public we cannot call it - if (!Modifier.isPublic(ctor.getModifiers())) { - return null; - } - - // If the declaring class is public, we are done - final Class<T> clazz = ctor.getDeclaringClass(); - if (Modifier.isPublic(clazz.getModifiers())) { - return ctor; - } - - // what else can we do? - return null; - } - - /** - * <p>Find an accessible constructor with compatible parameters. - * Compatible parameters mean that every method parameter is assignable from - * the given parameters. In other words, it finds constructor that will take - * the parameters given.</p> - * - * <p>First it checks if there is constructor matching the exact signature. - * If no such, all the constructors of the class are tested if their signatures - * are assignment compatible with the parameter types. - * The first matching constructor is returned.</p> - * - * @param <T> the type of the class to be inspected - * @param clazz find constructor for this class - * @param parameterTypes find method with compatible parameters - * @return a valid Constructor object. If there's no matching constructor, returns {@code null}. - */ - private static <T> Constructor<T> getMatchingAccessibleConstructor( - final Class<T> clazz, - final Class<?>[] parameterTypes) { - // see if we can find the method directly - // most of the time this works and it's much faster - try { - final Constructor<T> ctor = clazz.getConstructor(parameterTypes); - try { - // - // XXX Default access superclass workaround - // - // When a public class has a default access superclass - // with public methods, these methods are accessible. - // Calling them from compiled code works fine. - // - // Unfortunately, using reflection to invoke these methods - // seems to (wrongly) to prevent access even when the method - // modifier is public. - // - // The following workaround solves the problem but will only - // work from sufficiently privileges code. - // - // Better workarounds would be gratefully accepted. - // - ctor.setAccessible(true); - } catch (final SecurityException se) { - /* SWALLOW, if workaround fails don't fret. */ - } - return ctor; - - } catch (final NoSuchMethodException e) { /* SWALLOW */ - } - - // search through all methods - final int paramSize = parameterTypes.length; - final Constructor<?>[] ctors = clazz.getConstructors(); - for (final Constructor<?> ctor2 : ctors) { - // compare parameters - final Class<?>[] ctorParams = ctor2.getParameterTypes(); - final int ctorParamSize = ctorParams.length; - if (ctorParamSize == paramSize) { - boolean match = true; - for (int n = 0; n < ctorParamSize; n++) { - if (!MethodUtils - .isAssignmentCompatible( - ctorParams[n], - parameterTypes[n])) { - match = false; - break; - } - } - - if (match) { - // get accessible version of method - final Constructor<?> ctor = getAccessibleConstructor(ctor2); - if (ctor != null) { - try { - ctor.setAccessible(true); - } catch (final SecurityException se) { - /* Swallow SecurityException - * TODO: Why? - */ - } - @SuppressWarnings("unchecked") - final - // Class.getConstructors() actually returns constructors - // of type T, so it is safe to cast. - Constructor<T> typedCtor = (Constructor<T>) ctor; - return typedCtor; - } - } - } - } - - return null; - } - - /** - * <p>Convenience method returning new instance of {@code klazz} using a single argument constructor. - * The formal parameter type is inferred from the actual values of {@code arg}. - * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> - * - * <p>The signatures should be assignment compatible.</p> - * - * @param <T> the type of the object to be constructed - * @param klass the class to be constructed. - * @param arg the actual argument. May be null (this will result in calling the default constructor). - * @return new instance of {@code klazz} - * - * @throws NoSuchMethodException If the constructor cannot be found - * @throws IllegalAccessException If an error occurs accessing the constructor - * @throws InvocationTargetException If an error occurs invoking the constructor - * @throws InstantiationException If an error occurs instantiating the class - * - * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) - */ - public static <T> T invokeConstructor(final Class<T> klass, final Object arg) - throws - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException, - InstantiationException { - - final Object[] args = toArray(arg); - return invokeConstructor(klass, args); - } - - /** - * <p>Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. - * The formal parameter types are inferred from the actual values of {@code args}. - * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> - * - * <p>The signatures should be assignment compatible.</p> - * - * @param <T> the type of the object to be constructed - * @param klass the class to be constructed. - * @param args actual argument array. May be null (this will result in calling the default constructor). - * @return new instance of {@code klazz} - * - * @throws NoSuchMethodException If the constructor cannot be found - * @throws IllegalAccessException If an error occurs accessing the constructor - * @throws InvocationTargetException If an error occurs invoking the constructor - * @throws InstantiationException If an error occurs instantiating the class - * - * @see #invokeConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) - */ - public static <T> T invokeConstructor(final Class<T> klass, Object[] args) - throws - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException, - InstantiationException { - - if (null == args) { - args = BeanUtils.EMPTY_OBJECT_ARRAY; - } - final int arguments = args.length; - final Class<?>[] parameterTypes = new Class<?>[arguments]; - for (int i = 0; i < arguments; i++) { - parameterTypes[i] = args[i].getClass(); - } - return invokeConstructor(klass, args, parameterTypes); - } - - /** - * <p>Returns new instance of {@code klazz} created using constructor - * with signature {@code parameterTypes</code> and actual arguments <code>args}.</p> - * - * <p>The signatures should be assignment compatible.</p> - * - * @param <T> the type of the object to be constructed - * @param klass the class to be constructed. - * @param args actual argument array. May be null (this will result in calling the default constructor). - * @param parameterTypes parameter types array - * @return new instance of {@code klazz} - * - * @throws NoSuchMethodException if matching constructor cannot be found - * @throws IllegalAccessException thrown on the constructor's invocation - * @throws InvocationTargetException thrown on the constructor's invocation - * @throws InstantiationException thrown on the constructor's invocation - * @see Constructor#newInstance - */ - public static <T> T invokeConstructor( - final Class<T> klass, - Object[] args, - Class<?>[] parameterTypes) - throws - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException, - InstantiationException { - - if (parameterTypes == null) { - parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY; - } - if (args == null) { - args = BeanUtils.EMPTY_OBJECT_ARRAY; - } - - final Constructor<T> ctor = - getMatchingAccessibleConstructor(klass, parameterTypes); - if (null == ctor) { - throw new NoSuchMethodException( - "No such accessible constructor on object: " + klass.getName()); - } - return ctor.newInstance(args); - } - - /** - * <p>Convenience method returning new instance of {@code klazz} using a single argument constructor. - * The formal parameter type is inferred from the actual values of {@code arg}. - * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> - * - * <p>The signatures should match exactly.</p> - * - * @param <T> the type of the object to be constructed - * @param klass the class to be constructed. - * @param arg the actual argument. May be null (this will result in calling the default constructor). - * @return new instance of {@code klazz} - * - * @throws NoSuchMethodException If the constructor cannot be found - * @throws IllegalAccessException If an error occurs accessing the constructor - * @throws InvocationTargetException If an error occurs invoking the constructor - * @throws InstantiationException If an error occurs instantiating the class - * - * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) - */ - public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg) - throws - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException, - InstantiationException { - - final Object[] args = toArray(arg); - return invokeExactConstructor(klass, args); - } - - /** - * <p>Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. - * The formal parameter types are inferred from the actual values of {@code args}. - * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> - * - * <p>The signatures should match exactly.</p> - * - * @param <T> the type of the object to be constructed - * @param klass the class to be constructed. - * @param args actual argument array. May be null (this will result in calling the default constructor). - * @return new instance of {@code klazz} - * - * @throws NoSuchMethodException If the constructor cannot be found - * @throws IllegalAccessException If an error occurs accessing the constructor - * @throws InvocationTargetException If an error occurs invoking the constructor - * @throws InstantiationException If an error occurs instantiating the class - * - * @see #invokeExactConstructor(java.lang.Class, java.lang.Object[], java.lang.Class[]) - */ - public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args) - throws - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException, - InstantiationException { - - if (null == args) { - args = BeanUtils.EMPTY_OBJECT_ARRAY; - } - final int arguments = args.length; - final Class<?>[] parameterTypes = new Class[arguments]; - for (int i = 0; i < arguments; i++) { - parameterTypes[i] = args[i].getClass(); - } - return invokeExactConstructor(klass, args, parameterTypes); - } - - /** - * <p>Returns new instance of {@code klazz} created using constructor - * with signature {@code parameterTypes} and actual arguments - * {@code args}.</p> - * - * <p>The signatures should match exactly.</p> - * - * @param <T> the type of the object to be constructed - * @param klass the class to be constructed. - * @param args actual argument array. May be null (this will result in calling the default constructor). - * @param parameterTypes parameter types array - * @return new instance of {@code klazz} - * - * @throws NoSuchMethodException if matching constructor cannot be found - * @throws IllegalAccessException thrown on the constructor's invocation - * @throws InvocationTargetException thrown on the constructor's invocation - * @throws InstantiationException thrown on the constructor's invocation - * @see Constructor#newInstance - */ - public static <T> T invokeExactConstructor( - final Class<T> klass, - Object[] args, - Class<?>[] parameterTypes) - throws - NoSuchMethodException, - IllegalAccessException, - InvocationTargetException, - InstantiationException { - - if (args == null) { - args = BeanUtils.EMPTY_OBJECT_ARRAY; - } - - if (parameterTypes == null) { - parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY; - } - - final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes); - if (null == ctor) { - throw new NoSuchMethodException( - "No such accessible constructor on object: " + klass.getName()); - } - return ctor.newInstance(args); - } - - private static Object[] toArray(final Object arg) { - Object[] args = null; - if (arg != null) { - args = new Object[] { arg }; - } - return args; - } - - - /** - * Delegates to {@link Array#newInstance(Class, int)}. - * - * @param <T> Component type. - * - * @param componentType See {@link Array#newInstance(Class, int)}. - * @param length See {@link Array#newInstance(Class, int)}. - * @return See {@link Array#newInstance(Class, int)}. - */ - @SuppressWarnings("unchecked") - public static <T> T[] newArray(Class<T> componentType, int length) { - return (T[]) Array.newInstance(componentType, length); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.commons.beanutils2; + +import java.lang.reflect.Array; +import java.lang.reflect.Constructor; +import java.lang.reflect.InvocationTargetException; +import java.lang.reflect.Modifier; + +/** + * <p> Utility reflection methods focused on constructors, modeled after {@link MethodUtils}. </p> + * + * <h2>Known Limitations</h2> + * <h3>Accessing Public Constructors In A Default Access Superclass</h3> + * <p>There is an issue when invoking public constructors contained in a default access superclass. + * Reflection locates these constructors fine and correctly assigns them as public. + * However, an {@code IllegalAccessException} is thrown if the constructors is invoked.</p> + * + * <p>{@code ConstructorUtils} contains a workaround for this situation. + * It will attempt to call {@code setAccessible} on this constructor. + * If this call succeeds, then the method can be invoked as normal. + * This call will only succeed when the application has sufficient security privileges. + * If this call fails then a warning will be logged and the method may fail.</p> + * + */ +public class ConstructorUtils { + + /** + * Returns a constructor with single argument. + * @param <T> the type of the constructor + * @param klass the class to be constructed + * @param parameterType The constructor parameter type + * @return null if matching accessible constructor can not be found. + * @see Class#getConstructor + * @see #getAccessibleConstructor(java.lang.reflect.Constructor) + */ + public static <T> Constructor<T> getAccessibleConstructor( + final Class<T> klass, + final Class<?> parameterType) { + + final Class<?>[] parameterTypes = { parameterType }; + return getAccessibleConstructor(klass, parameterTypes); + } + + /** + * Returns a constructor given a class and signature. + * @param <T> the type to be constructed + * @param klass the class to be constructed + * @param parameterTypes the parameter array + * @return null if matching accessible constructor can not be found + * @see Class#getConstructor + * @see #getAccessibleConstructor(java.lang.reflect.Constructor) + */ + public static <T> Constructor<T> getAccessibleConstructor( + final Class<T> klass, + final Class<?>[] parameterTypes) { + + try { + return getAccessibleConstructor( + klass.getConstructor(parameterTypes)); + } catch (final NoSuchMethodException e) { + return null; + } + } + + /** + * Returns accessible version of the given constructor. + * @param <T> the type of the constructor + * @param ctor prototype constructor object. + * @return {@code null} if accessible constructor can not be found. + * @see SecurityManager + */ + public static <T> Constructor<T> getAccessibleConstructor(final Constructor<T> ctor) { + + // Make sure we have a method to check + if (ctor == null) { + return null; + } + + // If the requested method is not public we cannot call it + if (!Modifier.isPublic(ctor.getModifiers())) { + return null; + } + + // If the declaring class is public, we are done + final Class<T> clazz = ctor.getDeclaringClass(); + if (Modifier.isPublic(clazz.getModifiers())) { + return ctor; + } + + // what else can we do? + return null; + } + + /** + * <p>Find an accessible constructor with compatible parameters. + * Compatible parameters mean that every method parameter is assignable from + * the given parameters. In other words, it finds constructor that will take + * the parameters given.</p> + * + * <p>First it checks if there is constructor matching the exact signature. + * If no such, all the constructors of the class are tested if their signatures + * are assignment compatible with the parameter types. + * The first matching constructor is returned.</p> + * + * @param <T> the type of the class to be inspected + * @param clazz find constructor for this class + * @param parameterTypes find method with compatible parameters + * @return a valid Constructor object. If there's no matching constructor, returns {@code null}. + */ + private static <T> Constructor<T> getMatchingAccessibleConstructor( + final Class<T> clazz, + final Class<?>[] parameterTypes) { + // see if we can find the method directly + // most of the time this works and it's much faster + try { + final Constructor<T> ctor = clazz.getConstructor(parameterTypes); + try { + // + // XXX Default access superclass workaround + // + // When a public class has a default access superclass + // with public methods, these methods are accessible. + // Calling them from compiled code works fine. + // + // Unfortunately, using reflection to invoke these methods + // seems to (wrongly) to prevent access even when the method + // modifier is public. + // + // The following workaround solves the problem but will only + // work from sufficiently privileges code. + // + // Better workarounds would be gratefully accepted. + // + ctor.setAccessible(true); + } catch (final SecurityException se) { + /* SWALLOW, if workaround fails don't fret. */ + } + return ctor; + + } catch (final NoSuchMethodException e) { /* SWALLOW */ + } + + // search through all methods + final int paramSize = parameterTypes.length; + final Constructor<?>[] ctors = clazz.getConstructors(); + for (final Constructor<?> ctor2 : ctors) { + // compare parameters + final Class<?>[] ctorParams = ctor2.getParameterTypes(); + final int ctorParamSize = ctorParams.length; + if (ctorParamSize == paramSize) { + boolean match = true; + for (int n = 0; n < ctorParamSize; n++) { + if (!MethodUtils + .isAssignmentCompatible( + ctorParams[n], + parameterTypes[n])) { + match = false; + break; + } + } + + if (match) { + // get accessible version of method + final Constructor<?> ctor = getAccessibleConstructor(ctor2); + if (ctor != null) { + try { + ctor.setAccessible(true); + } catch (final SecurityException se) { + /* Swallow SecurityException + * TODO: Why? + */ + } + @SuppressWarnings("unchecked") + final + // Class.getConstructors() actually returns constructors + // of type T, so it is safe to cast. + Constructor<T> typedCtor = (Constructor<T>) ctor; + return typedCtor; + } + } + } + } + + return null; + } + + /** + * <p>Convenience method returning new instance of {@code klazz} using a single argument constructor. + * The formal parameter type is inferred from the actual values of {@code arg}. + * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> + * + * <p>The signatures should be assignment compatible.</p> + * + * @param <T> the type of the object to be constructed + * @param klass the class to be constructed. + * @param arg the actual argument. May be null (this will result in calling the default constructor). + * @return new instance of {@code klazz} + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the constructor + * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeConstructor(Class, Object[], Class[]) + */ + public static <T> T invokeConstructor(final Class<T> klass, final Object arg) + throws + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException { + + final Object[] args = toArray(arg); + return invokeConstructor(klass, args); + } + + /** + * <p>Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. + * The formal parameter types are inferred from the actual values of {@code args}. + * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> + * + * <p>The signatures should be assignment compatible.</p> + * + * @param <T> the type of the object to be constructed + * @param klass the class to be constructed. + * @param args actual argument array. May be null (this will result in calling the default constructor). + * @return new instance of {@code klazz} + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the constructor + * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeConstructor(Class, Object[], Class[]) + */ + public static <T> T invokeConstructor(final Class<T> klass, Object[] args) + throws + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException { + + if (null == args) { + args = BeanUtils.EMPTY_OBJECT_ARRAY; + } + final int arguments = args.length; + final Class<?>[] parameterTypes = new Class<?>[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeConstructor(klass, args, parameterTypes); + } + + /** + * <p>Returns new instance of {@code klazz} created using constructor + * with signature {@code parameterTypes</code> and actual arguments <code>args}.</p> + * + * <p>The signatures should be assignment compatible.</p> + * + * @param <T> the type of the object to be constructed + * @param klass the class to be constructed. + * @param args actual argument array. May be null (this will result in calling the default constructor). + * @param parameterTypes parameter types array + * @return new instance of {@code klazz} + * + * @throws NoSuchMethodException if matching constructor cannot be found + * @throws IllegalAccessException thrown on the constructor's invocation + * @throws InvocationTargetException thrown on the constructor's invocation + * @throws InstantiationException thrown on the constructor's invocation + * @see Constructor#newInstance + */ + public static <T> T invokeConstructor( + final Class<T> klass, + Object[] args, + Class<?>[] parameterTypes) + throws + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException { + + if (parameterTypes == null) { + parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY; + } + if (args == null) { + args = BeanUtils.EMPTY_OBJECT_ARRAY; + } + + final Constructor<T> ctor = + getMatchingAccessibleConstructor(klass, parameterTypes); + if (null == ctor) { + throw new NoSuchMethodException( + "No such accessible constructor on object: " + klass.getName()); + } + return ctor.newInstance(args); + } + + /** + * <p>Convenience method returning new instance of {@code klazz} using a single argument constructor. + * The formal parameter type is inferred from the actual values of {@code arg}. + * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> + * + * <p>The signatures should match exactly.</p> + * + * @param <T> the type of the object to be constructed + * @param klass the class to be constructed. + * @param arg the actual argument. May be null (this will result in calling the default constructor). + * @return new instance of {@code klazz} + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the constructor + * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeExactConstructor(Class, Object[], Class[]) + */ + public static <T> T invokeExactConstructor(final Class<T> klass, final Object arg) + throws + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException { + + final Object[] args = toArray(arg); + return invokeExactConstructor(klass, args); + } + + /** + * <p>Returns new instance of {@code klazz</code> created using the actual arguments <code>args}. + * The formal parameter types are inferred from the actual values of {@code args}. + * See {@link #invokeExactConstructor(Class, Object[], Class[])} for more details.</p> + * + * <p>The signatures should match exactly.</p> + * + * @param <T> the type of the object to be constructed + * @param klass the class to be constructed. + * @param args actual argument array. May be null (this will result in calling the default constructor). + * @return new instance of {@code klazz} + * + * @throws NoSuchMethodException If the constructor cannot be found + * @throws IllegalAccessException If an error occurs accessing the constructor + * @throws InvocationTargetException If an error occurs invoking the constructor + * @throws InstantiationException If an error occurs instantiating the class + * + * @see #invokeExactConstructor(Class, Object[], Class[]) + */ + public static <T> T invokeExactConstructor(final Class<T> klass, Object[] args) + throws + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException { + + if (null == args) { + args = BeanUtils.EMPTY_OBJECT_ARRAY; + } + final int arguments = args.length; + final Class<?>[] parameterTypes = new Class[arguments]; + for (int i = 0; i < arguments; i++) { + parameterTypes[i] = args[i].getClass(); + } + return invokeExactConstructor(klass, args, parameterTypes); + } + + /** + * <p>Returns new instance of {@code klazz} created using constructor + * with signature {@code parameterTypes} and actual arguments + * {@code args}.</p> + * + * <p>The signatures should match exactly.</p> + * + * @param <T> the type of the object to be constructed + * @param klass the class to be constructed. + * @param args actual argument array. May be null (this will result in calling the default constructor). + * @param parameterTypes parameter types array + * @return new instance of {@code klazz} + * + * @throws NoSuchMethodException if matching constructor cannot be found + * @throws IllegalAccessException thrown on the constructor's invocation + * @throws InvocationTargetException thrown on the constructor's invocation + * @throws InstantiationException thrown on the constructor's invocation + * @see Constructor#newInstance + */ + public static <T> T invokeExactConstructor( + final Class<T> klass, + Object[] args, + Class<?>[] parameterTypes) + throws + NoSuchMethodException, + IllegalAccessException, + InvocationTargetException, + InstantiationException { + + if (args == null) { + args = BeanUtils.EMPTY_OBJECT_ARRAY; + } + + if (parameterTypes == null) { + parameterTypes = BeanUtils.EMPTY_CLASS_ARRAY; + } + + final Constructor<T> ctor = getAccessibleConstructor(klass, parameterTypes); + if (null == ctor) { + throw new NoSuchMethodException( + "No such accessible constructor on object: " + klass.getName()); + } + return ctor.newInstance(args); + } + + private static Object[] toArray(final Object arg) { + Object[] args = null; + if (arg != null) { + args = new Object[] { arg }; + } + return args; + } + + + /** + * Delegates to {@link Array#newInstance(Class, int)}. + * + * @param <T> Component type. + * + * @param componentType See {@link Array#newInstance(Class, int)}. + * @param length See {@link Array#newInstance(Class, int)}. + * @return See {@link Array#newInstance(Class, int)}. + */ + @SuppressWarnings("unchecked") + public static <T> T[] newArray(Class<T> componentType, int length) { + return (T[]) Array.newInstance(componentType, length); + } + +} diff --git a/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java b/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java index 5e3764e7..605395f4 100644 --- a/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java +++ b/src/main/java/org/apache/commons/beanutils2/ContextClassLoaderLocal.java @@ -1,216 +1,216 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.commons.beanutils2; - -import java.util.Map; -import java.util.WeakHashMap; - -/** - * An instance of this class represents a value that is provided per (thread) - * context classloader. - * - * <p>Occasionally it is necessary to store data in "global" variables - * (including uses of the Singleton pattern). In applications which have only - * a single classloader such data can simply be stored as "static" members on - * some class. When multiple classloaders are involved, however, this approach - * can fail; in particular, this doesn't work when the code may be run within a - * servlet container or a j2ee container, and the class on which the static - * member is defined is loaded via a "shared" classloader that is visible to all - * components running within the container. This class provides a mechanism for - * associating data with a ClassLoader instance, which ensures that when the - * code runs in such a container each component gets its own copy of the - * "global" variable rather than unexpectedly sharing a single copy of the - * variable with other components that happen to be running in the same - * container at the same time (eg servlets or EJBs.)</p> - * - * <p>This class is strongly patterned after the java.lang.ThreadLocal - * class, which performs a similar task in allowing data to be associated - * with a particular thread.</p> - * - * <p>When code that uses this class is run as a "normal" application, ie - * not within a container, the effect is identical to just using a static - * member variable to store the data, because Thread.getContextClassLoader - * always returns the same classloader (the system classloader).</p> - * - * <p>Expected usage is as follows:</p> - * <pre> - * <code> - * public class SomeClass { - * private static final ContextClassLoaderLocal<String> global - * = new ContextClassLoaderLocal<String>() { - * protected String initialValue() { - * return new String("Initial value"); - * }; - * - * public void testGlobal() { - * String s = global.get(); - * System.out.println("global value:" + s); - * buf.set("New Value"); - * } - * </code> - * </pre> - * - * <p><strong>Note:</strong> This class takes some care to ensure that when - * a component which uses this class is "undeployed" by a container the - * component-specific classloader and all its associated classes (and their - * static variables) are garbage-collected. Unfortunately there is one - * scenario in which this does <i>not</i> work correctly and there - * is unfortunately no known workaround other than ensuring that the - * component (or its container) calls the "unset" method on this class for - * each instance of this class when the component is undeployed. The problem - * occurs if: - * <ul> - * <li>the class containing a static instance of this class was loaded via - * a shared classloader, and</li> - * <li>the value stored in the instance is an object whose class was loaded - * via the component-specific classloader (or any of the objects it refers - * to were loaded via that classloader).</li> - * </ul> - * <p>The result is that the map managed by this object still contains a strong - * reference to the stored object, which contains a strong reference to the - * classloader that loaded it, meaning that although the container has - * "undeployed" the component the component-specific classloader and all the - * related classes and static variables cannot be garbage-collected. This is - * not expected to be an issue with the commons-beanutils library as the only - * classes which use this class are BeanUtilsBean and ConvertUtilsBean and - * there is no obvious reason for a user of the beanutils library to subclass - * either of those classes.</p> - * - * <p><strong>Note:</strong> A WeakHashMap bug in several 1.3 JVMs results in - * a memory leak for those JVMs.</p> - * - * <p><strong>Note:</strong> Of course all of this would be unnecessary if - * containers required each component to load the full set of classes it - * needs, ie avoided providing classes loaded via a "shared" classloader.</p> - * - * @param <T> the type of data stored in an instance - * @see java.lang.Thread#getContextClassLoader - */ -public class ContextClassLoaderLocal<T> { - private final Map<ClassLoader, T> valueByClassLoader = new WeakHashMap<>(); - private boolean globalValueInitialized; - private T globalValue; - - /** - * Constructs a context classloader instance - */ - public ContextClassLoaderLocal() { - } - - /** - * Returns the initial value for this ContextClassLoaderLocal - * variable. This method will be called once per Context ClassLoader for - * each ContextClassLoaderLocal, the first time it is accessed - * with get or set. If the programmer desires ContextClassLoaderLocal variables - * to be initialized to some value other than null, ContextClassLoaderLocal must - * be subclassed, and this method overridden. Typically, an anonymous - * inner class will be used. Typical implementations of initialValue - * will call an appropriate constructor and return the newly constructed - * object. - * - * @return a new Object to be used as an initial value for this ContextClassLoaderLocal - */ - protected T initialValue() { - return null; - } - - /** - * Gets the instance which provides the functionality for {@link BeanUtils}. - * This is a pseudo-singleton - an single instance is provided per (thread) context classloader. - * This mechanism provides isolation for web apps deployed in the same container. - * @return the object currently associated with the context-classloader of the current thread. - */ - public synchronized T get() { - // synchronizing the whole method is a bit slower - // but guarantees no subtle threading problems, and there's no - // need to synchronize valueByClassLoader - - // make sure that the map is given a change to purge itself - valueByClassLoader.isEmpty(); - try { - - final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if (contextClassLoader != null) { - - T value = valueByClassLoader.get(contextClassLoader); - if (value == null - && !valueByClassLoader.containsKey(contextClassLoader)) { - value = initialValue(); - valueByClassLoader.put(contextClassLoader, value); - } - return value; - - } - - } catch (final SecurityException e) { /* SWALLOW - should we log this? */ } - - // if none or exception, return the globalValue - if (!globalValueInitialized) { - globalValue = initialValue(); - globalValueInitialized = true; - }//else already set - return globalValue; - } - - /** - * Sets the value - a value is provided per (thread) context classloader. - * This mechanism provides isolation for web apps deployed in the same container. - * - * @param value the object to be associated with the entrant thread's context classloader - */ - public synchronized void set(final T value) { - // synchronizing the whole method is a bit slower - // but guarantees no subtle threading problems - - // make sure that the map is given a change to purge itself - valueByClassLoader.isEmpty(); - try { - - final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - if (contextClassLoader != null) { - valueByClassLoader.put(contextClassLoader, value); - return; - } - - } catch (final SecurityException e) { /* SWALLOW - should we log this? */ } - - // if in doubt, set the global value - globalValue = value; - globalValueInitialized = true; - } - - /** - * Unsets the value associated with the current thread's context classloader - */ - public synchronized void unset() { - try { - - final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); - unset(contextClassLoader); - - } catch (final SecurityException e) { /* SWALLOW - should we log this? */ } - } - - /** - * Unsets the value associated with the given classloader - * @param classLoader The classloader to <i>unset</i> for - */ - public synchronized void unset(final ClassLoader classLoader) { - valueByClassLoader.remove(classLoader); - } +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.commons.beanutils2; + +import java.util.Map; +import java.util.WeakHashMap; + +/** + * An instance of this class represents a value that is provided per (thread) + * context classloader. + * + * <p>Occasionally it is necessary to store data in "global" variables + * (including uses of the Singleton pattern). In applications which have only + * a single classloader such data can simply be stored as "static" members on + * some class. When multiple classloaders are involved, however, this approach + * can fail; in particular, this doesn't work when the code may be run within a + * servlet container or a j2ee container, and the class on which the static + * member is defined is loaded via a "shared" classloader that is visible to all + * components running within the container. This class provides a mechanism for + * associating data with a ClassLoader instance, which ensures that when the + * code runs in such a container each component gets its own copy of the + * "global" variable rather than unexpectedly sharing a single copy of the + * variable with other components that happen to be running in the same + * container at the same time (eg servlets or EJBs.)</p> + * + * <p>This class is strongly patterned after the java.lang.ThreadLocal + * class, which performs a similar task in allowing data to be associated + * with a particular thread.</p> + * + * <p>When code that uses this class is run as a "normal" application, ie + * not within a container, the effect is identical to just using a static + * member variable to store the data, because Thread.getContextClassLoader + * always returns the same classloader (the system classloader).</p> + * + * <p>Expected usage is as follows:</p> + * <pre> + * <code> + * public class SomeClass { + * private static final ContextClassLoaderLocal<String> global + * = new ContextClassLoaderLocal<String>() { + * protected String initialValue() { + * return new String("Initial value"); + * }; + * + * public void testGlobal() { + * String s = global.get(); + * System.out.println("global value:" + s); + * buf.set("New Value"); + * } + * </code> + * </pre> + * + * <p><strong>Note:</strong> This class takes some care to ensure that when + * a component which uses this class is "undeployed" by a container the + * component-specific classloader and all its associated classes (and their + * static variables) are garbage-collected. Unfortunately there is one + * scenario in which this does <i>not</i> work correctly and there + * is unfortunately no known workaround other than ensuring that the + * component (or its container) calls the "unset" method on this class for + * each instance of this class when the component is undeployed. The problem + * occurs if: + * <ul> + * <li>the class containing a static instance of this class was loaded via + * a shared classloader, and</li> + * <li>the value stored in the instance is an object whose class was loaded + * via the component-specific classloader (or any of the objects it refers + * to were loaded via that classloader).</li> + * </ul> + * <p>The result is that the map managed by this object still contains a strong + * reference to the stored object, which contains a strong reference to the + * classloader that loaded it, meaning that although the container has + * "undeployed" the component the component-specific classloader and all the + * related classes and static variables cannot be garbage-collected. This is + * not expected to be an issue with the commons-beanutils library as the only + * classes which use this class are BeanUtilsBean and ConvertUtilsBean and + * there is no obvious reason for a user of the beanutils library to subclass + * either of those classes.</p> + * + * <p><strong>Note:</strong> A WeakHashMap bug in several 1.3 JVMs results in + * a memory leak for those JVMs.</p> + * + * <p><strong>Note:</strong> Of course all of this would be unnecessary if + * containers required each component to load the full set of classes it + * needs, ie avoided providing classes loaded via a "shared" classloader.</p> + * + * @param <T> the type of data stored in an instance + * @see Thread#getContextClassLoader + */ +public class ContextClassLoaderLocal<T> { + private final Map<ClassLoader, T> valueByClassLoader = new WeakHashMap<>(); + private boolean globalValueInitialized; + private T globalValue; + + /** + * Constructs a context classloader instance + */ + public ContextClassLoaderLocal() { + } + + /** + * Returns the initial value for this ContextClassLoaderLocal + * variable. This method will be called once per Context ClassLoader for + * each ContextClassLoaderLocal, the first time it is accessed + * with get or set. If the programmer desires ContextClassLoaderLocal variables + * to be initialized to some value other than null, ContextClassLoaderLocal must + * be subclassed, and this method overridden. Typically, an anonymous + * inner class will be used. Typical implementations of initialValue + * will call an appropriate constructor and return the newly constructed + * object. + * + * @return a new Object to be used as an initial value for this ContextClassLoaderLocal + */ + protected T initialValue() { + return null; + } + + /** + * Gets the instance which provides the functionality for {@link BeanUtils}. + * This is a pseudo-singleton - an single instance is provided per (thread) context classloader. + * This mechanism provides isolation for web apps deployed in the same container. + * @return the object currently associated with the context-classloader of the current thread. + */ + public synchronized T get() { + // synchronizing the whole method is a bit slower + // but guarantees no subtle threading problems, and there's no + // need to synchronize valueByClassLoader + + // make sure that the map is given a change to purge itself + valueByClassLoader.isEmpty(); + try { + + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + + T value = valueByClassLoader.get(contextClassLoader); + if (value == null + && !valueByClassLoader.containsKey(contextClassLoader)) { + value = initialValue(); + valueByClassLoader.put(contextClassLoader, value); + } + return value; + + } + + } catch (final SecurityException e) { /* SWALLOW - should we log this? */ } + + // if none or exception, return the globalValue + if (!globalValueInitialized) { + globalValue = initialValue(); + globalValueInitialized = true; + }//else already set + return globalValue; + } + + /** + * Sets the value - a value is provided per (thread) context classloader. + * This mechanism provides isolation for web apps deployed in the same container. + * + * @param value the object to be associated with the entrant thread's context classloader + */ + public synchronized void set(final T value) { + // synchronizing the whole method is a bit slower + // but guarantees no subtle threading problems + + // make sure that the map is given a change to purge itself + valueByClassLoader.isEmpty(); + try { + + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + if (contextClassLoader != null) { + valueByClassLoader.put(contextClassLoader, value); + return; + } + + } catch (final SecurityException e) { /* SWALLOW - should we log this? */ } + + // if in doubt, set the global value + globalValue = value; + globalValueInitialized = true; + } + + /** + * Unsets the value associated with the current thread's context classloader + */ + public synchronized void unset() { + try { + + final ClassLoader contextClassLoader = Thread.currentThread().getContextClassLoader(); + unset(contextClassLoader); + + } catch (final SecurityException e) { /* SWALLOW - should we log this? */ } + } + + /** + * Unsets the value associated with the given classloader + * @param classLoader The classloader to <i>unset</i> for + */ + public synchronized void unset(final ClassLoader classLoader) { + valueByClassLoader.remove(classLoader); + } } \ No newline at end of file diff --git a/src/main/java/org/apache/commons/beanutils2/DynaProperty.java b/src/main/java/org/apache/commons/beanutils2/DynaProperty.java index a4c99aa8..44fdd45b 100644 --- a/src/main/java/org/apache/commons/beanutils2/DynaProperty.java +++ b/src/main/java/org/apache/commons/beanutils2/DynaProperty.java @@ -1,343 +1,343 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.commons.beanutils2; - -import java.io.IOException; -import java.io.ObjectInputStream; -import java.io.ObjectOutputStream; -import java.io.Serializable; -import java.io.StreamCorruptedException; -import java.util.List; -import java.util.Map; -import java.util.Objects; - -/** - * <p>The metadata describing an individual property of a DynaBean.</p> - * - * <p>The meta contains an <em>optional</em> content type property ({@link #getContentType}) - * for use by mapped and iterated properties. - * A mapped or iterated property may choose to indicate the type it expects. - * The DynaBean implementation may choose to enforce this type on its entries. - * Alternatively, an implementation may choose to ignore this property. - * All keys for maps must be of type String so no meta data is needed for map keys.</p> - */ -public class DynaProperty implements Serializable { - - private static final long serialVersionUID = -3084907613499830175L; - - /* - * There are issues with serializing primitive class types on certain JVM versions - * (including java 1.3). - * This class uses a custom serialization implementation that writes an integer - * for these primitive class. - * This list of constants are the ones used in serialization. - * If these values are changed, then older versions will no longer be read correctly - */ - private static final int BOOLEAN_TYPE = 1; - private static final int BYTE_TYPE = 2; - private static final int CHAR_TYPE = 3; - private static final int DOUBLE_TYPE = 4; - private static final int FLOAT_TYPE = 5; - private static final int INT_TYPE = 6; - private static final int LONG_TYPE = 7; - private static final int SHORT_TYPE = 8; - - /** - * Constructs a property that accepts any data type. - * - * @param name Name of the property being described - */ - public DynaProperty(final String name) { - this(name, Object.class); - } - - /** - * Constructs a property of the specified data type. - * - * @param name Name of the property being described - * @param type Java class representing the property data type - */ - public DynaProperty(final String name, final Class<?> type) { - this.name = name; - this.type = type; - if (type != null && type.isArray()) { - this.contentType = type.getComponentType(); - } - } - - /** - * Constructs an indexed or mapped {@code DynaProperty} that supports (pseudo)-introspection - * of the content type. - * - * @param name Name of the property being described - * @param type Java class representing the property data type - * @param contentType Class that all indexed or mapped elements are instances of - */ - public DynaProperty(final String name, final Class<?> type, final Class<?> contentType) { - this.name = name; - this.type = type; - this.contentType = contentType; - } - - /** Property name */ - protected String name; - /** - * Get the name of this property. - * @return the name of the property - */ - public String getName() { - return this.name; - } - - /** Property type */ - protected transient Class<?> type; - - /** - * <p>Gets the Java class representing the data type of the underlying property - * values.</p> - * - * <p>There are issues with serializing primitive class types on certain JVM versions - * (including java 1.3). - * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p> - * - * <p><strong>Please leave this field as {@code transient}</strong></p> - * - * @return the property type - */ - public Class<?> getType() { - return this.type; - } - - /** The <em>(optional)</em> type of content elements for indexed {@code DynaProperty} */ - protected transient Class<?> contentType; - - /** - * Empty array. - */ - static final DynaProperty[] EMPTY_DYNA_PROPERTY_ARRAY = {}; - - /** - * Gets the <em>(optional)</em> type of the indexed content for {@code DynaProperty}'s - * that support this feature. - * - * <p>There are issues with serializing primitive class types on certain JVM versions - * (including java 1.3). - * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p> - * - * @return the Class for the content type if this is an indexed {@code DynaProperty} - * and this feature is supported. Otherwise null. - */ - public Class<?> getContentType() { - return contentType; - } - - /** - * Does this property represent an indexed value (ie an array or List)? - * - * @return {@code true} if the property is indexed (i.e. is a List or - * array), otherwise {@code false} - */ - public boolean isIndexed() { - if (type == null) { - return false; - } - if (type.isArray() || List.class.isAssignableFrom(type)) { - return true; - } - return false; - } - - /** - * Does this property represent a mapped value (ie a Map)? - * - * @return {@code true} if the property is a Map - * otherwise {@code false} - */ - public boolean isMapped() { - if (type == null) { - return false; - } - return Map.class.isAssignableFrom(type); - - } - - /** - * Checks this instance against the specified Object for equality. Overrides the - * default reference test for equality provided by {@link java.lang.Object#equals(Object)} - * @param obj The object to compare to - * @return {@code true} if object is a dyna property with the same name - * type and content type, otherwise {@code false} - * @since 1.8.0 - */ - @Override - public boolean equals(final Object obj) { - boolean result; - - result = obj == this; - - if (!result && obj instanceof DynaProperty) { - final DynaProperty that = (DynaProperty) obj; - result = - (Objects.equals(this.name, that.name)) && - (Objects.equals(this.type, that.type)) && - (Objects.equals(this.contentType, that.contentType)); - } - - return result; - } - - /** - * @return the hashcode for this dyna property - * @see java.lang.Object#hashCode - * @since 1.8.0 - */ - @Override - public int hashCode() { - int result = 1; - - result = result * 31 + (name == null ? 0 : name.hashCode()); - result = result * 31 + (type == null ? 0 : type.hashCode()); - result = result * 31 + (contentType == null ? 0 : contentType.hashCode()); - - return result; - } - - /** - * Gets a String representation of this Object. - * @return a String representation of the dyna property - */ - @Override - public String toString() { - final StringBuilder sb = new StringBuilder("DynaProperty[name="); - sb.append(this.name); - sb.append(",type="); - sb.append(this.type); - if (isMapped() || isIndexed()) { - sb.append(" <").append(this.contentType).append(">"); - } - sb.append("]"); - return sb.toString(); - } - - /** - * Writes this object safely. - * There are issues with serializing primitive class types on certain JVM versions - * (including java 1.3). - * This method provides a workaround. - * - * @param out {@link ObjectOutputStream} to write object to - * @throws IOException if the object can't be written - */ - private void writeObject(final ObjectOutputStream out) throws IOException { - writeAnyClass(this.type,out); - - if (isMapped() || isIndexed()) { - writeAnyClass(this.contentType,out); - } - - // write out other values - out.defaultWriteObject(); - } - - /** - * Write a class using safe encoding to workaround java 1.3 serialization bug. - */ - private void writeAnyClass(final Class<?> clazz, final ObjectOutputStream out) throws IOException { - // safely write out any class - int primitiveType = 0; - if (Boolean.TYPE.equals(clazz)) { - primitiveType = BOOLEAN_TYPE; - } else if (Byte.TYPE.equals(clazz)) { - primitiveType = BYTE_TYPE; - } else if (Character.TYPE.equals(clazz)) { - primitiveType = CHAR_TYPE; - } else if (Double.TYPE.equals(clazz)) { - primitiveType = DOUBLE_TYPE; - } else if (Float.TYPE.equals(clazz)) { - primitiveType = FLOAT_TYPE; - } else if (Integer.TYPE.equals(clazz)) { - primitiveType = INT_TYPE; - } else if (Long.TYPE.equals(clazz)) { - primitiveType = LONG_TYPE; - } else if (Short.TYPE.equals(clazz)) { - primitiveType = SHORT_TYPE; - } - - if (primitiveType == 0) { - // then it's not a primitive type - out.writeBoolean(false); - out.writeObject(clazz); - } else { - // we'll write out a constant instead - out.writeBoolean(true); - out.writeInt(primitiveType); - } - } - - /** - * Reads field values for this object safely. - * There are issues with serializing primitive class types on certain JVM versions - * (including java 1.3). - * This method provides a workaround. - * - * @param in {@link ObjectInputStream} to read object from - * @throws StreamCorruptedException when the stream data values are outside expected range - * @throws IOException if the input stream can't be read - * @throws ClassNotFoundException When trying to read an object of class that is not on the classpath - */ - private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { - this.type = readAnyClass(in); - - if (isMapped() || isIndexed()) { - this.contentType = readAnyClass(in); - } - - // read other values - in.defaultReadObject(); - } - - /** - * Reads a class using safe encoding to workaround java 1.3 serialization bug. - */ - private Class<?> readAnyClass(final ObjectInputStream in) throws IOException, ClassNotFoundException { - // read back type class safely - if (in.readBoolean()) { - // it's a type constant - switch (in.readInt()) { - - case BOOLEAN_TYPE: return Boolean.TYPE; - case BYTE_TYPE: return Byte.TYPE; - case CHAR_TYPE: return Character.TYPE; - case DOUBLE_TYPE: return Double.TYPE; - case FLOAT_TYPE: return Float.TYPE; - case INT_TYPE: return Integer.TYPE; - case LONG_TYPE: return Long.TYPE; - case SHORT_TYPE: return Short.TYPE; - default: - // something's gone wrong - throw new StreamCorruptedException( - "Invalid primitive type. " - + "Check version of beanutils used to serialize is compatible."); - - } - - } - // it's another class - return (Class<?>) in.readObject(); - } -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.commons.beanutils2; + +import java.io.IOException; +import java.io.ObjectInputStream; +import java.io.ObjectOutputStream; +import java.io.Serializable; +import java.io.StreamCorruptedException; +import java.util.List; +import java.util.Map; +import java.util.Objects; + +/** + * <p>The metadata describing an individual property of a DynaBean.</p> + * + * <p>The meta contains an <em>optional</em> content type property ({@link #getContentType}) + * for use by mapped and iterated properties. + * A mapped or iterated property may choose to indicate the type it expects. + * The DynaBean implementation may choose to enforce this type on its entries. + * Alternatively, an implementation may choose to ignore this property. + * All keys for maps must be of type String so no meta data is needed for map keys.</p> + */ +public class DynaProperty implements Serializable { + + private static final long serialVersionUID = -3084907613499830175L; + + /* + * There are issues with serializing primitive class types on certain JVM versions + * (including java 1.3). + * This class uses a custom serialization implementation that writes an integer + * for these primitive class. + * This list of constants are the ones used in serialization. + * If these values are changed, then older versions will no longer be read correctly + */ + private static final int BOOLEAN_TYPE = 1; + private static final int BYTE_TYPE = 2; + private static final int CHAR_TYPE = 3; + private static final int DOUBLE_TYPE = 4; + private static final int FLOAT_TYPE = 5; + private static final int INT_TYPE = 6; + private static final int LONG_TYPE = 7; + private static final int SHORT_TYPE = 8; + + /** + * Constructs a property that accepts any data type. + * + * @param name Name of the property being described + */ + public DynaProperty(final String name) { + this(name, Object.class); + } + + /** + * Constructs a property of the specified data type. + * + * @param name Name of the property being described + * @param type Java class representing the property data type + */ + public DynaProperty(final String name, final Class<?> type) { + this.name = name; + this.type = type; + if (type != null && type.isArray()) { + this.contentType = type.getComponentType(); + } + } + + /** + * Constructs an indexed or mapped {@code DynaProperty} that supports (pseudo)-introspection + * of the content type. + * + * @param name Name of the property being described + * @param type Java class representing the property data type + * @param contentType Class that all indexed or mapped elements are instances of + */ + public DynaProperty(final String name, final Class<?> type, final Class<?> contentType) { + this.name = name; + this.type = type; + this.contentType = contentType; + } + + /** Property name */ + protected String name; + /** + * Get the name of this property. + * @return the name of the property + */ + public String getName() { + return this.name; + } + + /** Property type */ + protected transient Class<?> type; + + /** + * <p>Gets the Java class representing the data type of the underlying property + * values.</p> + * + * <p>There are issues with serializing primitive class types on certain JVM versions + * (including java 1.3). + * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p> + * + * <p><strong>Please leave this field as {@code transient}</strong></p> + * + * @return the property type + */ + public Class<?> getType() { + return this.type; + } + + /** The <em>(optional)</em> type of content elements for indexed {@code DynaProperty} */ + protected transient Class<?> contentType; + + /** + * Empty array. + */ + static final DynaProperty[] EMPTY_DYNA_PROPERTY_ARRAY = {}; + + /** + * Gets the <em>(optional)</em> type of the indexed content for {@code DynaProperty}'s + * that support this feature. + * + * <p>There are issues with serializing primitive class types on certain JVM versions + * (including java 1.3). + * Therefore, this field <strong>must not be serialized using the standard methods</strong>.</p> + * + * @return the Class for the content type if this is an indexed {@code DynaProperty} + * and this feature is supported. Otherwise null. + */ + public Class<?> getContentType() { + return contentType; + } + + /** + * Does this property represent an indexed value (ie an array or List)? + * + * @return {@code true} if the property is indexed (i.e. is a List or + * array), otherwise {@code false} + */ + public boolean isIndexed() { + if (type == null) { + return false; + } + if (type.isArray() || List.class.isAssignableFrom(type)) { + return true; + } + return false; + } + + /** + * Does this property represent a mapped value (ie a Map)? + * + * @return {@code true} if the property is a Map + * otherwise {@code false} + */ + public boolean isMapped() { + if (type == null) { + return false; + } + return Map.class.isAssignableFrom(type); + + } + + /** + * Checks this instance against the specified Object for equality. Overrides the + * default reference test for equality provided by {@link java.lang.Object#equals(Object)} + * @param obj The object to compare to + * @return {@code true} if object is a dyna property with the same name + * type and content type, otherwise {@code false} + * @since 1.8.0 + */ + @Override + public boolean equals(final Object obj) { + boolean result; + + result = obj == this; + + if (!result && obj instanceof DynaProperty) { + final DynaProperty that = (DynaProperty) obj; + result = + (Objects.equals(this.name, that.name)) && + (Objects.equals(this.type, that.type)) && + (Objects.equals(this.contentType, that.contentType)); + } + + return result; + } + + /** + * @return the hashcode for this dyna property + * @see Object#hashCode + * @since 1.8.0 + */ + @Override + public int hashCode() { + int result = 1; + + result = result * 31 + (name == null ? 0 : name.hashCode()); + result = result * 31 + (type == null ? 0 : type.hashCode()); + result = result * 31 + (contentType == null ? 0 : contentType.hashCode()); + + return result; + } + + /** + * Gets a String representation of this Object. + * @return a String representation of the dyna property + */ + @Override + public String toString() { + final StringBuilder sb = new StringBuilder("DynaProperty[name="); + sb.append(this.name); + sb.append(",type="); + sb.append(this.type); + if (isMapped() || isIndexed()) { + sb.append(" <").append(this.contentType).append(">"); + } + sb.append("]"); + return sb.toString(); + } + + /** + * Writes this object safely. + * There are issues with serializing primitive class types on certain JVM versions + * (including java 1.3). + * This method provides a workaround. + * + * @param out {@link ObjectOutputStream} to write object to + * @throws IOException if the object can't be written + */ + private void writeObject(final ObjectOutputStream out) throws IOException { + writeAnyClass(this.type,out); + + if (isMapped() || isIndexed()) { + writeAnyClass(this.contentType,out); + } + + // write out other values + out.defaultWriteObject(); + } + + /** + * Write a class using safe encoding to workaround java 1.3 serialization bug. + */ + private void writeAnyClass(final Class<?> clazz, final ObjectOutputStream out) throws IOException { + // safely write out any class + int primitiveType = 0; + if (Boolean.TYPE.equals(clazz)) { + primitiveType = BOOLEAN_TYPE; + } else if (Byte.TYPE.equals(clazz)) { + primitiveType = BYTE_TYPE; + } else if (Character.TYPE.equals(clazz)) { + primitiveType = CHAR_TYPE; + } else if (Double.TYPE.equals(clazz)) { + primitiveType = DOUBLE_TYPE; + } else if (Float.TYPE.equals(clazz)) { + primitiveType = FLOAT_TYPE; + } else if (Integer.TYPE.equals(clazz)) { + primitiveType = INT_TYPE; + } else if (Long.TYPE.equals(clazz)) { + primitiveType = LONG_TYPE; + } else if (Short.TYPE.equals(clazz)) { + primitiveType = SHORT_TYPE; + } + + if (primitiveType == 0) { + // then it's not a primitive type + out.writeBoolean(false); + out.writeObject(clazz); + } else { + // we'll write out a constant instead + out.writeBoolean(true); + out.writeInt(primitiveType); + } + } + + /** + * Reads field values for this object safely. + * There are issues with serializing primitive class types on certain JVM versions + * (including java 1.3). + * This method provides a workaround. + * + * @param in {@link ObjectInputStream} to read object from + * @throws StreamCorruptedException when the stream data values are outside expected range + * @throws IOException if the input stream can't be read + * @throws ClassNotFoundException When trying to read an object of class that is not on the classpath + */ + private void readObject(final ObjectInputStream in) throws IOException, ClassNotFoundException { + this.type = readAnyClass(in); + + if (isMapped() || isIndexed()) { + this.contentType = readAnyClass(in); + } + + // read other values + in.defaultReadObject(); + } + + /** + * Reads a class using safe encoding to workaround java 1.3 serialization bug. + */ + private Class<?> readAnyClass(final ObjectInputStream in) throws IOException, ClassNotFoundException { + // read back type class safely + if (in.readBoolean()) { + // it's a type constant + switch (in.readInt()) { + + case BOOLEAN_TYPE: return Boolean.TYPE; + case BYTE_TYPE: return Byte.TYPE; + case CHAR_TYPE: return Character.TYPE; + case DOUBLE_TYPE: return Double.TYPE; + case FLOAT_TYPE: return Float.TYPE; + case INT_TYPE: return Integer.TYPE; + case LONG_TYPE: return Long.TYPE; + case SHORT_TYPE: return Short.TYPE; + default: + // something's gone wrong + throw new StreamCorruptedException( + "Invalid primitive type. " + + "Check version of beanutils used to serialize is compatible."); + + } + + } + // it's another class + return (Class<?>) in.readObject(); + } +} diff --git a/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java b/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java index a4e2b334..ce0035f2 100644 --- a/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java +++ b/src/main/java/org/apache/commons/beanutils2/converters/EnumConverter.java @@ -1,93 +1,93 @@ -/* - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.commons.beanutils2.converters; - -/** - * {@link org.apache.commons.beanutils2.Converter} implementation that handles conversion - * to and from <b>java.lang.Enum</b> objects. - * <p> - * Can be configured to either return a <i>default value</i> or throw a - * {@code ConversionException} if a conversion error occurs. - * </p> - * - * @param <E> The enum type subclass - * @since 2.0 - * @see java.lang.Enum - */ -public final class EnumConverter<E extends Enum<E>> extends AbstractConverter<Enum<E>> { - - /** - * Constructs a <b>java.lang.Enum</b> <i>Converter</i> that throws - * a {@code ConversionException} if an error occurs. - */ - public EnumConverter() { - } - - /** - * Constructs a <b>java.lang.Enum</b> <i>Converter</i> that returns - * a default value if an error occurs. - * - * @param defaultValue The default value to be returned - * if the value to be converted is missing or an error - * occurs converting the value. - */ - public EnumConverter(final Enum<E> defaultValue) { - super(defaultValue); - } - - /** - * Gets the default type this {@code Converter} handles. - * - * @return The default type this {@code Converter} handles. - * @since 2.0 - */ - @SuppressWarnings({"unchecked", "rawtypes"}) - @Override - protected Class<Enum<E>> getDefaultType() { - return (Class) Enum.class; - } - - /** - * <p>Converts a java.lang.Enum or object into a String.</p> - * - * @param <R> Target type of the conversion. - * @param type Data type to which this value should be converted. - * @param value The input value to be converted. - * @return The converted value. - * @throws Throwable if an error occurs converting to the specified type - * @since 2.0 - */ - @SuppressWarnings({"rawtypes"}) - @Override - protected <R> R convertToType(final Class<R> type, final Object value) throws Throwable { - if (Enum.class.isAssignableFrom(type)) { - final String enumValue = String.valueOf(value); - final R[] constants = type.getEnumConstants(); - if (constants == null) { - throw conversionException(type, value); - } - for (final R candidate : constants) { - if (((Enum)candidate).name().equalsIgnoreCase(enumValue)) { - return candidate; - } - } - } - - throw conversionException(type, value); - } - -} +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.commons.beanutils2.converters; + +/** + * {@link org.apache.commons.beanutils2.Converter} implementation that handles conversion + * to and from <b>java.lang.Enum</b> objects. + * <p> + * Can be configured to either return a <i>default value</i> or throw a + * {@code ConversionException} if a conversion error occurs. + * </p> + * + * @param <E> The enum type subclass + * @since 2.0 + * @see Enum + */ +public final class EnumConverter<E extends Enum<E>> extends AbstractConverter<Enum<E>> { + + /** + * Constructs a <b>java.lang.Enum</b> <i>Converter</i> that throws + * a {@code ConversionException} if an error occurs. + */ + public EnumConverter() { + } + + /** + * Constructs a <b>java.lang.Enum</b> <i>Converter</i> that returns + * a default value if an error occurs. + * + * @param defaultValue The default value to be returned + * if the value to be converted is missing or an error + * occurs converting the value. + */ + public EnumConverter(final Enum<E> defaultValue) { + super(defaultValue); + } + + /** + * Gets the default type this {@code Converter} handles. + * + * @return The default type this {@code Converter} handles. + * @since 2.0 + */ + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + protected Class<Enum<E>> getDefaultType() { + return (Class) Enum.class; + } + + /** + * <p>Converts a java.lang.Enum or object into a String.</p> + * + * @param <R> Target type of the conversion. + * @param type Data type to which this value should be converted. + * @param value The input value to be converted. + * @return The converted value. + * @throws Throwable if an error occurs converting to the specified type + * @since 2.0 + */ + @SuppressWarnings({"rawtypes"}) + @Override + protected <R> R convertToType(final Class<R> type, final Object value) throws Throwable { + if (Enum.class.isAssignableFrom(type)) { + final String enumValue = String.valueOf(value); + final R[] constants = type.getEnumConstants(); + if (constants == null) { + throw conversionException(type, value); + } + for (final R candidate : constants) { + if (((Enum)candidate).name().equalsIgnoreCase(enumValue)) { + return candidate; + } + } + } + + throw conversionException(type, value); + } + +}