/*
* The MIT License (MIT)
*
* Copyright (c) 2014-2015 Cloudee Huang ( https://github.com/cloudeecn / cloudeecn@gmail.com )
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
package works.cirno.mocha.parameter.value;
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.concurrent.ConcurrentHashMap;
import works.cirno.mocha.InvokeContext;
import works.cirno.mocha.parameter.name.Parameter;
public class BeanParameterSource implements ParameterSource {
private ConcurrentHashMap<Class<?>, Boolean> notBean = new ConcurrentHashMap<Class<?>, Boolean>();
private ParameterSource propertySource;
ParameterSource getPropertySource() {
return propertySource;
}
void setPropertySource(ParameterSource propertySource) {
this.propertySource = propertySource;
}
@Override
public Object getParameterValue(InvokeContext ctx, Parameter parameter) {
Class<?> type = parameter.getType();
String paramName = parameter.getName();
if (notBean.contains(type)) {
return NOT_HERE;
} else {
try {
BeanInfo info;
Constructor<?> constructor;
try {
if ((type.getModifiers() & (Modifier.ABSTRACT)) != 0) {
notBean.put(type, true);
return NOT_HERE;
}
info = Introspector.getBeanInfo(type);
constructor = type.getConstructor();
if ((constructor.getModifiers() & (Modifier.PUBLIC)) == 0) {
notBean.put(type, true);
return NOT_HERE;
}
} catch (NoSuchMethodException | IntrospectionException e) {
notBean.put(type, true);
return NOT_HERE;
}
Object result = constructor.newInstance();
PropertyDescriptor[] propertyDescriptors = info
.getPropertyDescriptors();
StringBuilder nameBuilder = new StringBuilder(64);
boolean propertyGot = false;
for (PropertyDescriptor pd : propertyDescriptors) {
Class<?> propertyType = pd.getPropertyType();
Method writeMethod = pd.getWriteMethod();
if (propertyType != null && writeMethod != null) {
String propertyName = pd.getName();
nameBuilder.setLength(0);
nameBuilder.append(paramName).append('.')
.append(propertyName);
Parameter propertyParameter = new Parameter(nameBuilder.toString(), propertyType);
Object value = propertySource.getParameterValue(ctx, propertyParameter);
if (value != NOT_HERE) {
propertyGot = true;
if (writeMethod != null) {
try{
writeMethod.invoke(result, value);
}catch(IllegalArgumentException e){
throw new IllegalArgumentException(
"Expected type: " + writeMethod.getParameterTypes()[0].getName()
+ " actual type: " + value.getClass().getName(),
e);
}
}
}
}
}
return propertyGot ? result : NOT_HERE;
} catch (InstantiationException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException ex) {
throw new RuntimeException("Can't setup bean parameter " + paramName + "(" + type + ")", ex);
}
}
}
}