All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.vaadin.data.util.BeanUtil Maven / Gradle / Ivy

There is a newer version: 8.27.3
Show newest version
/*
 * Copyright (C) 2000-2024 Vaadin Ltd
 *
 * This program is available under Vaadin Commercial License and Service Terms.
 *
 * See  for the full
 * license.
 */
package com.vaadin.data.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
import java.io.Serializable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

import com.vaadin.data.validator.BeanValidator;

/**
 * Utility class for Java Beans information access.
 *
 * @since 7.4
 *
 * @author Vaadin Ltd
 */
public final class BeanUtil implements Serializable {
    // Prevent instantiation of util class
    private BeanUtil() {
    }

    /**
     * Returns the property descriptors of a class or an interface.
     *
     * For an interface, superinterfaces are also iterated as Introspector does
     * not take them into account (Oracle Java bug 4275879), but in that case,
     * both the setter and the getter for a property must be in the same
     * interface and should not be overridden in subinterfaces for the discovery
     * to work correctly.
     * 

* NOTE : This utility method relies on introspection (and returns * PropertyDescriptor) which is a part of java.beans package. The latter * package could require bigger JDK in the future (with Java 9+). So it may * be changed in the future. *

* For interfaces, the iteration is depth first and the properties of * superinterfaces are returned before those of their subinterfaces. * * @param beanType * the type whose properties to query * @return a list of property descriptors of the given type * @throws IntrospectionException * if the introspection fails */ public static List getBeanPropertyDescriptors( final Class beanType) throws IntrospectionException { // Oracle bug 4275879: Introspector does not consider superinterfaces of // an interface if (beanType.isInterface()) { List propertyDescriptors = new ArrayList<>(); for (Class cls : beanType.getInterfaces()) { propertyDescriptors.addAll(getBeanPropertyDescriptors(cls)); } BeanInfo info = Introspector.getBeanInfo(beanType); propertyDescriptors.addAll(getPropertyDescriptors(info)); return propertyDescriptors; } else { BeanInfo info = Introspector.getBeanInfo(beanType); return getPropertyDescriptors(info); } } /** * Returns the type of the property with the given name and declaring class. * The property name may refer to a nested property, e.g. * "property.subProperty" or "property.subProperty1.subProperty2". The * property must have a public read method (or a chain of read methods in * case of a nested property). * * @param beanType * the type declaring the property * @param propertyName * the name of the property * @return the property type * @throws IntrospectionException * if the introspection fails */ public static Class getPropertyType(Class beanType, String propertyName) throws IntrospectionException { PropertyDescriptor descriptor = getPropertyDescriptor(beanType, propertyName); if (descriptor != null) { return descriptor.getPropertyType(); } else { return null; } } /** * Returns the property descriptor for the property of the given name and * declaring class. The property name may refer to a nested property, e.g. * "property.subProperty" or "property.subProperty1.subProperty2". The * property must have a public read method (or a chain of read methods in * case of a nested property). * * @param beanType * the type declaring the property * @param propertyName * the name of the property * @return the corresponding descriptor * @throws IntrospectionException * if the introspection fails */ public static PropertyDescriptor getPropertyDescriptor(Class beanType, String propertyName) throws IntrospectionException { if (propertyName.contains(".")) { String[] parts = propertyName.split("\\.", 2); // Get the type of the field in the bean class Class propertyBean = getPropertyType(beanType, parts[0]); // Find the rest from the sub type return getPropertyDescriptor(propertyBean, parts[1]); } else { List descriptors = getBeanPropertyDescriptors( beanType); for (PropertyDescriptor descriptor : descriptors) { final Method getMethod = descriptor.getReadMethod(); if (descriptor.getName().equals(propertyName) && getMethod != null && getMethod.getDeclaringClass() != Object.class) { return descriptor; } } return null; } } /** * Returns whether an implementation of JSR-303 version 1.0 or 1.1 is * present on the classpath. If this method returns false, trying to create * a {@code BeanValidator} instance will throw an * {@code IllegalStateException}. If an implementation is not found, logs a * level {@code FINE} message the first time it is run. * * @return {@code true} if bean validation is available, {@code false} * otherwise. */ public static boolean checkBeanValidationAvailable() { return LazyValidationAvailability.BEAN_VALIDATION_AVAILABLE; } // Workaround for Java6 bug JDK-6788525. Do nothing for JDK7+. private static List getPropertyDescriptors( BeanInfo beanInfo) { PropertyDescriptor[] descriptors = beanInfo.getPropertyDescriptors(); List result = new ArrayList<>(descriptors.length); for (PropertyDescriptor descriptor : descriptors) { try { Method readMethod = getMethodFromBridge( descriptor.getReadMethod()); if (readMethod != null) { Method writeMethod = getMethodFromBridge( descriptor.getWriteMethod(), readMethod.getReturnType()); if (writeMethod == null) { writeMethod = descriptor.getWriteMethod(); } PropertyDescriptor descr = new PropertyDescriptor( descriptor.getName(), readMethod, writeMethod); result.add(descr); } else { result.add(descriptor); } } catch (SecurityException ignore) { // handle next descriptor } catch (IntrospectionException e) { result.add(descriptor); } } return result; } /** * Return declared method for which {@code bridgeMethod} is generated. If * {@code bridgeMethod} is not a bridge method then return null. */ private static Method getMethodFromBridge(Method bridgeMethod) throws SecurityException { if (bridgeMethod == null) { return null; } return getMethodFromBridge(bridgeMethod, bridgeMethod.getParameterTypes()); } /** * Return declared method for which {@code bridgeMethod} is generated using * its {@code paramTypes}. If {@code bridgeMethod} is not a bridge method * then return null. */ private static Method getMethodFromBridge(Method bridgeMethod, Class... paramTypes) throws SecurityException { if (bridgeMethod == null || !bridgeMethod.isBridge()) { return null; } try { return bridgeMethod.getDeclaringClass() .getMethod(bridgeMethod.getName(), paramTypes); } catch (NoSuchMethodException e) { return null; } } /** * Checks if the object is serializable or not. To be used in assertion * checks only, since the check might be a bit heavyweight. * * @param obj * to be checked * @return {@code true} * @throws AssertionError * if the object is not serializable */ public static boolean checkSerialization(Object obj) { try { ObjectOutputStream dummyObjectOutputStream = new ObjectOutputStream( new OutputStream() { @Override public void write(int b) { } @SuppressWarnings("NullableProblems") @Override public void write(byte[] ignored) { } @SuppressWarnings("NullableProblems") @Override public void write(byte[] b, int off, int len) { } }); dummyObjectOutputStream.writeObject(obj); } catch (Throwable e) { throw new AssertionError( "Formatter supplier should be serializable", e); } return true; } private static class LazyValidationAvailability implements Serializable { private static final boolean BEAN_VALIDATION_AVAILABLE = isAvailable(); private static boolean isAvailable() { try { Class clazz = Class.forName("javax.validation.Validation"); Method method = clazz.getMethod("buildDefaultValidatorFactory"); method.invoke(null); return true; } catch (ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) { Logger.getLogger(BeanValidator.class.getName()).log(Level.INFO, "A JSR-303 bean validation implementation not found on the classpath or could not be initialized. " + BeanValidator.class.getSimpleName() + " cannot be used.", e); return false; } catch (IllegalAccessException | IllegalArgumentException e) { throw new RuntimeException( "Unable to invoke javax.validation.Validation.buildDefaultValidatorFactory()", e); } } private LazyValidationAvailability() { } } }





© 2015 - 2024 Weber Informatics LLC | Privacy Policy