package org.apache.ojb.broker.metadata.fieldaccess;
/* Copyright 2003-2005 The Apache Software Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;
import org.apache.commons.lang.StringUtils;
import org.apache.ojb.broker.core.proxy.ProxyHelper;
import org.apache.ojb.broker.metadata.MetadataException;
import org.apache.ojb.broker.util.ClassHelper;
import org.apache.ojb.broker.util.logging.Logger;
/**
* A {@link PersistentField} implementation using
* JavaBeans compliant calls only to access persistent attributes.
* No Reflection is needed. But for each attribute xxx there must be
* public getXxx() and setXxx() methods. In metadata the field name must be
* the bean compliant 'xxx'.
*
* @version $Id: PersistentFieldIntrospectorImpl.java,v 1.11.2.2 2005/12/21 22:26:41 tomdz Exp $
*/
public class PersistentFieldIntrospectorImpl extends PersistentFieldBase
{
private static final long serialVersionUID = 8805309492150404444L;
private Class type;
private transient List propertyGraph;
public PersistentFieldIntrospectorImpl()
{
super();
}
public PersistentFieldIntrospectorImpl(Class aClass, String aPropertyName)
{
super(aClass, aPropertyName);
}
public Class getType()
{
if (type == null)
{
type = getPropertyDescriptor().getPropertyType();
}
return type;
}
public void set(Object target, Object value) throws MetadataException
{
if(target == null) return;
List propertyDescriptors = getPropertyGraph();
int size = propertyDescriptors.size() - 1;
PropertyDescriptor pd;
for (int i = 0; i < size; i++)
{
Object attribute;
pd = (PropertyDescriptor) propertyDescriptors.get(i);
attribute = getValueFrom(pd, target);
if (attribute != null || value != null)
{
if (attribute == null)
{
try
{
attribute = ClassHelper.newInstance(pd.getPropertyType());
}
catch (Exception e)
{
throw new MetadataException("Can't instantiate nested object of type '"
+ pd.getPropertyType() + "' for field '"
+ pd.getName() + "'", e);
}
}
setValueFor(pd, target, attribute);
}
else
{
return;
}
target = attribute;
}
pd = (PropertyDescriptor) propertyDescriptors.get(size);
setValueFor(pd, target, value);
}
public Object get(Object target) throws MetadataException
{
List propertyDescriptors = getPropertyGraph();
for (int i = 0; i < propertyDescriptors.size(); i++)
{
PropertyDescriptor pd = (PropertyDescriptor) propertyDescriptors.get(i);
target = getValueFrom(pd, target);
if (target == null) break;
}
return target;
}
private Object getValueFrom(PropertyDescriptor pd, Object target)
{
if (target == null) return null;
Method m = pd.getReadMethod();
if (m != null)
{
try
{
return m.invoke(ProxyHelper.getRealObject(target), null);
}
catch (Throwable e)
{
logProblem(pd, target, null, "Can't read value from given object");
throw new MetadataException("Error invoking method:" + m.getName() + " in object " + target.getClass().getName(), e);
}
}
else
{
throw new MetadataException("Can't get ReadMethod for property:" + pd.getName() + " in object " + target.getClass().getName());
}
}
private void setValueFor(PropertyDescriptor pd, Object target, Object value)
{
Method m = pd.getWriteMethod();
Object[] args = {value};
if (m != null)
{
try
{
/**
* MBAIRD: it is safe to call getParameterTypes()[0] because this is
* the "set" method and it needs to take one parameter only.
* we need to be able to set values to null. We can only set something to null if
* the type is not a primitive (assignable from Object).
*/
if ((value != null) || !m.getParameterTypes()[0].isPrimitive())
{
m.invoke(ProxyHelper.getRealObject(target), args);
}
}
catch (Throwable e)
{
logProblem(pd, target, value, "Can't set value on given object.");
throw new MetadataException("Error invoking method:" + m.getName() + " in object:" + target.getClass().getName(), e);
}
}
else
{
throw new MetadataException("Can't get WriteMethod for property:" + pd.getName() + " in object:" + target.getClass().getName());
}
}
private List getPropertyGraph()
{
if (propertyGraph == null)
{
propertyGraph = buildPropertyGraph();
}
return propertyGraph;
}
private List buildPropertyGraph()
{
List result = new ArrayList();
String[] fields = StringUtils.split(getName(), PATH_TOKEN);
PropertyDescriptor pd = null;
for (int i = 0; i < fields.length; i++)
{
String fieldName = fields[i];
if (pd == null)
{
pd = findPropertyDescriptor(getDeclaringClass(), fieldName);
}
else
{
pd = findPropertyDescriptor(pd.getPropertyType(), fieldName);
}
result.add(pd);
}
return result;
}
/**
* Get the PropertyDescriptor for aClass and aPropertyName
*/
protected static PropertyDescriptor findPropertyDescriptor(Class aClass, String aPropertyName)
{
BeanInfo info;
PropertyDescriptor[] pd;
PropertyDescriptor descriptor = null;
try
{
info = Introspector.getBeanInfo(aClass);
pd = info.getPropertyDescriptors();
for (int i = 0; i < pd.length; i++)
{
if (pd[i].getName().equals(aPropertyName))
{
descriptor = pd[i];
break;
}
}
if (descriptor == null)
{
/*
* Daren Drummond: Throw here so we are consistent
* with PersistentFieldDefaultImpl.
*/
throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName());
}
return descriptor;
}
catch (IntrospectionException ex)
{
/*
* Daren Drummond: Throw here so we are consistent
* with PersistentFieldDefaultImpl.
*/
throw new MetadataException("Can't find property " + aPropertyName + " in " + aClass.getName(), ex);
}
}
/**
* Returns the PropertyDescriptor.
*
* @return java.beans.PropertyDescriptor
*/
protected PropertyDescriptor getPropertyDescriptor()
{
return (PropertyDescriptor) getPropertyGraph().get(getPropertyGraph().size() - 1);
}
/**
* This implementation returns always 'false'.
*/
public boolean makeAccessible()
{
return false;
}
/**
* Always returns 'false'.
*
* @see PersistentField#usesAccessorsAndMutators
*/
public boolean usesAccessorsAndMutators()
{
return true;
}
/**
* Let's give the user some hints as to what could be wrong.
*/
protected void logProblem(PropertyDescriptor pd, Object anObject, Object aValue, String msg)
{
Logger logger = getLog();
logger.error("Error in [PersistentFieldPropertyImpl], " + msg);
logger.error("Declaring class [" + getDeclaringClass().getName() + "]");
logger.error("Property Name [" + getName() + "]");
logger.error("Property Type [" + pd.getPropertyType().getName() + "]");
if (anObject != null)
{
logger.error("anObject was class [" + anObject.getClass().getName() + "]");
}
else
{
logger.error("anObject was null");
}
if (aValue != null)
{
logger.error("aValue was class [" + aValue.getClass().getName() + "]");
}
else
{
logger.error("aValue was null");
}
}
}