package com.bradmcevoy.property; import com.bradmcevoy.http.Resource; import com.bradmcevoy.http.exceptions.NotAuthorizedException; import java.beans.PropertyDescriptor; import java.lang.reflect.InvocationTargetException; import java.util.ArrayList; import java.util.List; import javax.xml.namespace.QName; import org.apache.commons.beanutils.PropertyUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * A read/write source of properties which uses getter/setter style properties * on the resource class. * * This is similar in concept to form variable binding in web frameworks like * struts and MVC. * * @author brad */ public class BeanPropertySource implements PropertySource { private static final Logger log = LoggerFactory.getLogger( BeanPropertySource.class ); private static final Object[] NOARGS = new Object[0]; public Object getProperty( QName name, Resource r ) throws NotAuthorizedException { PropertyDescriptor pd = getPropertyDescriptor( r, name.getLocalPart() ); if( pd == null ) { throw new IllegalArgumentException( "no prop: " + name.getLocalPart() + " on " + r.getClass() ); } try { return pd.getReadMethod().invoke( r, NOARGS ); } catch( Exception ex ) { if( ex.getCause() instanceof NotAuthorizedException ) { NotAuthorizedException na = (NotAuthorizedException) ex.getCause(); throw na; } else { throw new RuntimeException( name.toString(), ex ); } } } public void setProperty( QName name, Object value, Resource r ) throws NotAuthorizedException { log.debug( "setProperty: " + name + " = " + value ); PropertyDescriptor pd = getPropertyDescriptor( r, name.getLocalPart() ); try { pd.getWriteMethod().invoke( r, value ); } catch( Exception ex ) { if( ex.getCause() instanceof NotAuthorizedException ) { NotAuthorizedException na = (NotAuthorizedException) ex.getCause(); throw na; } else { if( value == null ) { log.error( "Exception setting property: " + name.toString() + " to null" ); } else { log.error( "Exception setting property: " + name.toString() + " to value: " + value + " class:" + value.getClass() ); } throw new RuntimeException( name.toString(), ex ); } } } public PropertyMetaData getPropertyMetaData( QName name, Resource r ) { log.debug( "getPropertyMetaData" ); BeanPropertyResource anno = getAnnotation( r ); if( anno == null ) { log.debug( " no annotation: " + r.getClass()); return PropertyMetaData.UNKNOWN; } if( !name.getNamespaceURI().equals( anno.value() ) ) { log.debug( "different namespace",anno.value(),name.getNamespaceURI()); return PropertyMetaData.UNKNOWN; } PropertyDescriptor pd = getPropertyDescriptor( r, name.getLocalPart() ); if( pd == null || pd.getReadMethod() == null ) { log.debug( "no read method"); return PropertyMetaData.UNKNOWN; } else { if( log.isDebugEnabled() ) { log.debug( "writable: " + anno.writable() + " - " + ( pd.getWriteMethod() != null ) ); } boolean writable = anno.writable() && ( pd.getWriteMethod() != null ); if( writable ) { return new PropertyMetaData( PropertyAccessibility.WRITABLE, pd.getPropertyType() ); } else { return new PropertyMetaData( PropertyAccessibility.READ_ONLY, pd.getPropertyType() ); } } } public void clearProperty( QName name, Resource r ) throws NotAuthorizedException { setProperty( name, null, r ); } public List<QName> getAllPropertyNames( Resource r ) { BeanPropertyResource anno = getAnnotation( r ); if( anno == null ) return null; PropertyDescriptor[] pds = PropertyUtils.getPropertyDescriptors( r ); List<QName> list = new ArrayList<QName>(); for( PropertyDescriptor pd : pds ) { if( pd.getReadMethod() != null ) { list.add( new QName( anno.value(), pd.getName() ) ); } } return list; } private BeanPropertyResource getAnnotation( Resource r ) { return r.getClass().getAnnotation( BeanPropertyResource.class ); } private PropertyDescriptor getPropertyDescriptor( Resource r, String name ) { try { PropertyDescriptor pd = PropertyUtils.getPropertyDescriptor( r, name ); return pd; } catch( IllegalAccessException ex ) { throw new RuntimeException( ex ); } catch( InvocationTargetException ex ) { throw new RuntimeException( ex ); } catch( NoSuchMethodException ex ) { return null; } } }