Skip to content
Permalink
Browse files

LOGBACK-1071: Introduced lightweight java beans abstraction in order to

get rid of dependency on java.beans package, which is not included in
java 8 compact3 profile.
  • Loading branch information
Max Urech
Max Urech committed Mar 17, 2016
1 parent fc0ef9a commit 3aa7822abbff8f97170c36f21472974c670d5395
@@ -14,7 +14,7 @@
package ch.qos.logback.classic.gaffer

import java.lang.reflect.Method
import java.beans.Introspector
import ch.qos.logback.core.joran.util.beans.BeanUtil

/**
* @author Ceki Gücü
@@ -27,7 +27,7 @@ class PropertyUtil {
}

static NestingType nestingType(Object obj, String name) {
def decapitalizedName = Introspector.decapitalize(name)
def decapitalizedName = BeanUtil.convertToLowerCamelCase(name)
if (obj.hasProperty(decapitalizedName)) {
return NestingType.SINGLE;
}
@@ -40,7 +40,7 @@ class PropertyUtil {
static void attach(NestingType nestingType, Object component, Object subComponent, String name) {
switch (nestingType) {
case NestingType.SINGLE:
name = Introspector.decapitalize(name)
name = BeanUtil.convertToLowerCamelCase(name)
component."${name}" = subComponent;
break;
case NestingType.AS_COLLECTION:
@@ -14,17 +14,14 @@
// Contributors: Georg Lundesgaard
package ch.qos.logback.core.joran.util;

import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;

import java.beans.MethodDescriptor;
import java.beans.PropertyDescriptor;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

import ch.qos.logback.core.joran.spi.DefaultClass;
import ch.qos.logback.core.joran.spi.DefaultNestedComponentRegistry;
import ch.qos.logback.core.joran.util.beans.BeanDescription;
import ch.qos.logback.core.joran.util.beans.BeanDescriptionFactory;
import ch.qos.logback.core.joran.util.beans.BeanUtil;
import ch.qos.logback.core.spi.ContextAwareBase;
import ch.qos.logback.core.util.AggregationType;
import ch.qos.logback.core.util.PropertySetterException;
@@ -34,72 +31,57 @@
* {@link #setProperty setProperty(name,value)} in order to invoke setters on
* the Object specified in the constructor. This class relies on the JavaBeans
* {@link Introspector} to analyze the given Object Class using reflection.
*
*
* <p>
* Usage:
*
*
* <pre>
* PropertySetter ps = new PropertySetter(anObject);
* ps.set(&quot;name&quot;, &quot;Joe&quot;);
* ps.set(&quot;age&quot;, &quot;32&quot;);
* ps.set(&quot;isMale&quot;, &quot;true&quot;);
* </pre>
*
*
* will cause the invocations anObject.setName("Joe"), anObject.setAge(32), and
* setMale(true) if such methods exist with those signatures. Otherwise an
* {@link IntrospectionException} are thrown.
*
*
* @author Anders Kristensen
* @author Ceki Gulcu
*/
public class PropertySetter extends ContextAwareBase {

protected Object obj;
protected Class<?> objClass;
protected PropertyDescriptor[] propertyDescriptors;
protected MethodDescriptor[] methodDescriptors;
protected final Object obj;
protected final Class<?> objClass;
protected final BeanDescription beanDescription;

/**
* Create a new PropertySetter for the specified Object. This is done in
* preparation for invoking {@link #setProperty} one or more times.
*
*
* @param obj
* the object for which to set properties
*/
public PropertySetter(Object obj) {
this.obj = obj;
this.objClass = obj.getClass();
this.beanDescription = BeanDescriptionFactory.INSTANCE.create(obj.getClass());
}

/**
* Uses JavaBeans {@link Introspector} to computer setters of object to be
* configured.
*/
protected void introspect() {
try {
BeanInfo bi = Introspector.getBeanInfo(obj.getClass());
propertyDescriptors = bi.getPropertyDescriptors();
methodDescriptors = bi.getMethodDescriptors();
} catch (IntrospectionException ex) {
addError("Failed to introspect " + obj + ": " + ex.getMessage());
propertyDescriptors = new PropertyDescriptor[0];
methodDescriptors = new MethodDescriptor[0];
}
}

/**
* Set a property on this PropertySetter's Object. If successful, this method
* will invoke a setter method on the underlying Object. The setter is the one
* for the specified property name and the value is determined partly from the
* setter argument type and partly from the value specified in the call to
* this method.
*
*
* <p>
* If the setter expects a String no conversion is necessary. If it expects an
* int, then an attempt is made to convert 'value' to an int using new
* Integer(value). If the setter expects a boolean, the conversion is by new
* Boolean(value).
*
*
* @param name
* name of the property
* @param value
@@ -109,25 +91,22 @@ public void setProperty(String name, String value) {
if (value == null) {
return;
}

name = Introspector.decapitalize(name);

PropertyDescriptor prop = getPropertyDescriptor(name);

if (prop == null) {
addWarn("No such property [" + name + "] in " + objClass.getName() + ".");
Method setter = findSetterMethod(name);
if (setter == null) {
addWarn("No setter for property [" + name + "] in " + objClass.getName() + ".");
} else {
try {
setProperty(prop, name, value);
setProperty(setter, name, value);
} catch (PropertySetterException ex) {
addWarn("Failed to set property [" + name + "] to value \"" + value + "\". ", ex);
}
}
}

/**

/**
* Set the named property given a {@link PropertyDescriptor}.
*
*
* @param prop
* A PropertyDescriptor describing the characteristics of the
* property to set.
@@ -136,18 +115,8 @@ public void setProperty(String name, String value) {
* @param value
* The value of the property.
*/
public void setProperty(PropertyDescriptor prop, String name, String value) throws PropertySetterException {
Method setter = prop.getWriteMethod();

if (setter == null) {
throw new PropertySetterException("No setter for property [" + name + "].");
}

Class<?>[] paramTypes = setter.getParameterTypes();

if (paramTypes.length != 1) {
throw new PropertySetterException("#params for setter != 1");
}
private void setProperty(Method setter, String name, String value) throws PropertySetterException {
Class<?>[] paramTypes = setter.getParameterTypes();

Object arg;

@@ -185,28 +154,23 @@ public AggregationType computeAggregationType(String name) {
}
}

Method setterMethod = findSetterMethod(name);
if (setterMethod != null) {
return computeRawAggregationType(setterMethod);
Method setter = findSetterMethod(name);
if (setter != null) {
return computeRawAggregationType(setter);
} else {
// we have failed
return AggregationType.NOT_FOUND;
}
}

private Method findAdderMethod(String name) {
name = capitalizeFirstLetter(name);
return getMethod("add" + name);
String propertyName = BeanUtil.INSTANCE.toLowerCamelCase(name);
return beanDescription.getAdder(propertyName);
}

private Method findSetterMethod(String name) {
String dName = Introspector.decapitalize(name);
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dName);
if (propertyDescriptor != null) {
return propertyDescriptor.getWriteMethod();
} else {
return null;
}
String propertyName = BeanUtil.INSTANCE.toLowerCamelCase(name);
return beanDescription.getSetter(propertyName);
}

private Class<?> getParameterClassForMethod(Method method) {
@@ -235,7 +199,7 @@ private AggregationType computeRawAggregationType(Method method) {

/**
* Can the given clazz instantiable with certainty?
*
*
* @param clazz
* The class to test for instantiability
* @return true if clazz can be instantiated, and false otherwise.
@@ -319,16 +283,7 @@ public void addBasicProperty(String name, String strValue) {
}

public void setComplexProperty(String name, Object complexProperty) {
String dName = Introspector.decapitalize(name);
PropertyDescriptor propertyDescriptor = getPropertyDescriptor(dName);

if (propertyDescriptor == null) {
addWarn("Could not find PropertyDescriptor for [" + name + "] in " + objClass.getName());

return;
}

Method setter = propertyDescriptor.getWriteMethod();
Method setter =findSetterMethod(name);

if (setter == null) {
addWarn("Not setter method for property [" + name + "] in " + obj.getClass().getName());
@@ -372,36 +327,6 @@ private String capitalizeFirstLetter(String name) {
return name.substring(0, 1).toUpperCase() + name.substring(1);
}

protected Method getMethod(String methodName) {
if (methodDescriptors == null) {
introspect();
}

for (int i = 0; i < methodDescriptors.length; i++) {
if (methodName.equals(methodDescriptors[i].getName())) {
return methodDescriptors[i].getMethod();
}
}

return null;
}

protected PropertyDescriptor getPropertyDescriptor(String name) {
if (propertyDescriptors == null) {
introspect();
}

for (int i = 0; i < propertyDescriptors.length; i++) {
// System.out.println("Comparing " + name + " against "
// + propertyDescriptors[i].getName());
if (name.equals(propertyDescriptors[i].getName())) {
// System.out.println("matched");
return propertyDescriptors[i];
}
}

return null;
}

public Object getObj() {
return obj;
@@ -0,0 +1,69 @@
package ch.qos.logback.core.joran.util.beans;

import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;

/**
* Lightweight pendant to the java.beans.BeanInfo class.
* An instance of this class encapsulates the properties of a certain class.
* The properties are the public setters and getters.
* In addition the 'add-er'-methods are included, which are the public methods which start with the prefix 'add'.
*
* @author urechm
*
*/
public class BeanDescription {

private final Class<?> clazz;

private final Map<String, Method> propertyNameToGetter;

private final Map<String, Method> propertyNameToSetter;

private final Map<String, Method> propertyNameToAdder;

/**
* Scope protected since only the {@link BeanDescriptionFactory} must create BeanDescriptions in order to guarantee consistency between the given parameters.
* @param clazz of the bean.
* @param propertyNameToGetter map of property names to the associated getter.
* @param propertyNameToSetter map of property names to the associated setter.
* @param propertyNameToAdder map of property names to the associated adder.
*/
protected BeanDescription(Class<?> clazz,Map<String, Method> propertyNameToGetter,
Map<String, Method> propertyNameToSetter, Map<String, Method> propertyNameToAdder) {
this.clazz = clazz;
this.propertyNameToGetter = Collections.unmodifiableMap(propertyNameToGetter);
this.propertyNameToSetter = Collections.unmodifiableMap(propertyNameToSetter);
this.propertyNameToAdder = Collections.unmodifiableMap(propertyNameToAdder);
}

public Class<?> getClazz() {
return clazz;
}

public Map<String, Method> getPropertyNameToGetter() {
return propertyNameToGetter;
}

public Map<String, Method> getPropertyNameToSetter() {
return propertyNameToSetter;
}

public Method getGetter(String propertyName) {
return propertyNameToGetter.get(propertyName);
}

public Method getSetter(String propertyName) {
return propertyNameToSetter.get(propertyName);
}

public Map<String, Method> getPropertyNameToAdder() {
return propertyNameToAdder;
}

public Method getAdder(String propertyName) {
return propertyNameToAdder.get(propertyName);
}

}

0 comments on commit 3aa7822

Please sign in to comment.
You can’t perform that action at this time.