blob: 484af9fd5460eeeaf518ead8d8cf8dafe2a43663 [file] [log] [blame]
// Copyright 2006, 2008, 2010 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.
package org.apache.tapestry5.ioc.internal.services;
import org.apache.tapestry5.ioc.AnnotationProvider;
import org.apache.tapestry5.ioc.internal.util.CollectionFactory;
import org.apache.tapestry5.ioc.services.ClassPropertyAdapter;
import org.apache.tapestry5.ioc.services.PropertyAdapter;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.List;
public class PropertyAdapterImpl implements PropertyAdapter
{
private final ClassPropertyAdapter classAdapter;
private final String name;
private final Method readMethod;
private final Method writeMethod;
private final Class type;
private final boolean castRequired;
private AnnotationProvider annotationProvider;
private final Field field;
private final Class declaringClass;
PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Method readMethod,
Method writeMethod)
{
this.classAdapter = classAdapter;
this.name = name;
this.type = type;
this.readMethod = readMethod;
this.writeMethod = writeMethod;
declaringClass = readMethod != null ? readMethod.getDeclaringClass() : writeMethod.getDeclaringClass();
castRequired = readMethod != null && readMethod.getReturnType() != type;
field = null;
}
PropertyAdapterImpl(ClassPropertyAdapter classAdapter, String name, Class type, Field field)
{
this.classAdapter = classAdapter;
this.name = name;
this.type = type;
this.field = field;
declaringClass = field.getDeclaringClass();
castRequired = field.getType() != type;
readMethod = null;
writeMethod = null;
}
public String getName()
{
return name;
}
public Method getReadMethod()
{
return readMethod;
}
public Class getType()
{
return type;
}
public Method getWriteMethod()
{
return writeMethod;
}
public boolean isRead()
{
return field != null || readMethod != null;
}
public boolean isUpdate()
{
return field != null || writeMethod != null;
}
public Object get(Object instance)
{
if (field == null && readMethod == null)
throw new UnsupportedOperationException(ServiceMessages.readNotSupported(instance, name));
Throwable fail;
try
{
if (field == null)
return readMethod.invoke(instance);
else
return field.get(instance);
}
catch (InvocationTargetException ex)
{
fail = ex.getTargetException();
}
catch (Exception ex)
{
fail = ex;
}
throw new RuntimeException(ServiceMessages.readFailure(name, instance, fail), fail);
}
public void set(Object instance, Object value)
{
if (field == null && writeMethod == null)
throw new UnsupportedOperationException(ServiceMessages.writeNotSupported(instance, name));
Throwable fail;
try
{
if (field == null)
writeMethod.invoke(instance, value);
else
field.set(instance, value);
return;
}
catch (InvocationTargetException ex)
{
fail = ex.getTargetException();
}
catch (Exception ex)
{
fail = ex;
}
throw new RuntimeException(ServiceMessages.writeFailure(name, instance, fail), fail);
}
public <T extends Annotation> T getAnnotation(Class<T> annotationClass)
{
return getAnnnotationProvider().getAnnotation(annotationClass);
}
/**
* Creates (as needed) the annotation provider for this property.
*/
private synchronized AnnotationProvider getAnnnotationProvider()
{
if (annotationProvider == null)
{
List<AnnotationProvider> providers = CollectionFactory.newList();
if (readMethod != null)
providers.add(new AccessableObjectAnnotationProvider(readMethod));
if (writeMethod != null)
providers.add(new AccessableObjectAnnotationProvider(writeMethod));
// There's an assumption here, that the fields match the property name (we ignore case
// which leads to a manageable ambiguity) and that the field and the getter/setter
// are in the same class (i.e., that we don't have a getter exposing a protected field inherted
// from a base class, or some other oddity).
Class cursor = getBeanType();
out: while (cursor != null)
{
for (Field f : cursor.getDeclaredFields())
{
if (f.getName().equalsIgnoreCase(name))
{
providers.add(new AccessableObjectAnnotationProvider(f));
break out;
}
}
cursor = cursor.getSuperclass();
}
annotationProvider = AnnotationProviderChain.create(providers);
}
return annotationProvider;
}
public boolean isCastRequired()
{
return castRequired;
}
public ClassPropertyAdapter getClassAdapter()
{
return classAdapter;
}
public Class getBeanType()
{
return classAdapter.getBeanType();
}
public boolean isField()
{
return field != null;
}
public Field getField()
{
return field;
}
public Class getDeclaringClass()
{
return declaringClass;
}
}