001/* 002 * Copyright 2001-2013 Stephen Colebourne 003 * 004 * Licensed under the Apache License, Version 2.0 (the "License"); 005 * you may not use this file except in compliance with the License. 006 * You may obtain a copy of the License at 007 * 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * 010 * Unless required by applicable law or agreed to in writing, software 011 * distributed under the License is distributed on an "AS IS" BASIS, 012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 013 * See the License for the specific language governing permissions and 014 * limitations under the License. 015 */ 016package org.joda.beans.impl.direct; 017 018import java.lang.annotation.Annotation; 019import java.lang.reflect.Field; 020import java.lang.reflect.Type; 021import java.util.Arrays; 022import java.util.Collections; 023import java.util.List; 024import java.util.NoSuchElementException; 025 026import org.joda.beans.Bean; 027import org.joda.beans.MetaBean; 028import org.joda.beans.PropertyReadWrite; 029import org.joda.beans.impl.BasicMetaProperty; 030 031/** 032 * A meta-property implementation designed for use by {@code DirectBean}. 033 * <p> 034 * This meta-property uses reflection to find the {@code Field} to obtain the annotations. 035 * 036 * @param <P> the type of the property content 037 * @author Stephen Colebourne 038 */ 039public final class DirectMetaProperty<P> extends BasicMetaProperty<P> { 040 041 /** The meta-bean. */ 042 private final MetaBean metaBean; 043 /** The property type. */ 044 private final Class<P> propertyType; 045 /** The declaring type. */ 046 private final Class<?> declaringType; 047 /** The field implementing the property. */ 048 private final Field field; 049 /** The read-write type. */ 050 private final PropertyReadWrite readWrite; 051 052 /** 053 * Factory to create a read-write meta-property avoiding duplicate generics. 054 * 055 * @param <P> the property type 056 * @param metaBean the meta-bean, not null 057 * @param propertyName the property name, not empty 058 * @param declaringType the type declaring the property, not null 059 * @param propertyType the property type, not null 060 * @return the property, not null 061 */ 062 public static <P> DirectMetaProperty<P> ofReadWrite( 063 MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) { 064 Field field = findField(metaBean, propertyName); 065 return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.READ_WRITE, field); 066 } 067 068 /** 069 * Factory to create a read-write meta-property avoiding duplicate generics. 070 * 071 * @param <P> the property type 072 * @param metaBean the meta-bean, not null 073 * @param propertyName the property name, not empty 074 * @param declaringType the type declaring the property, not null 075 * @param propertyType the property type, not null 076 * @return the property, not null 077 */ 078 public static <P> DirectMetaProperty<P> ofReadOnly( 079 MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) { 080 Field field = findField(metaBean, propertyName); 081 return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.READ_ONLY, field); 082 } 083 084 /** 085 * Factory to create a read-write meta-property avoiding duplicate generics. 086 * 087 * @param <P> the property type 088 * @param metaBean the meta-bean, not null 089 * @param propertyName the property name, not empty 090 * @param declaringType the type declaring the property, not null 091 * @param propertyType the property type, not null 092 * @return the property, not null 093 */ 094 public static <P> DirectMetaProperty<P> ofWriteOnly( 095 MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) { 096 Field field = findField(metaBean, propertyName); 097 return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.WRITE_ONLY, field); 098 } 099 100 private static Field findField(MetaBean metaBean, String propertyName) { 101 Field field = null; 102 Class<?> cls = metaBean.beanType(); 103 while (cls != DirectBean.class) { 104 try { 105 field = cls.getDeclaredField(propertyName); 106 break; 107 } catch (NoSuchFieldException ex) { 108 try { 109 field = cls.getDeclaredField("_" + propertyName); 110 break; 111 } catch (NoSuchFieldException ex2) { 112 cls = cls.getSuperclass(); 113 } 114 } 115 } 116 return field; 117 } 118 119 /** 120 * Constructor. 121 * 122 * @param metaBean the meta-bean, not null 123 * @param propertyName the property name, not empty 124 * @param declaringType the declaring type, not null 125 * @param propertyType the property type, not null 126 * @param readWrite the read-write type, not null 127 * @param field the reflected field, not null 128 */ 129 private DirectMetaProperty(MetaBean metaBean, String propertyName, Class<?> declaringType, 130 Class<P> propertyType, PropertyReadWrite readWrite, Field field) { 131 super(propertyName); 132 if (metaBean == null) { 133 throw new NullPointerException("MetaBean must not be null"); 134 } 135 if (declaringType == null) { 136 throw new NullPointerException("Declaring type must not be null"); 137 } 138 if (propertyType == null) { 139 throw new NullPointerException("Property type must not be null"); 140 } 141 if (readWrite == null) { 142 throw new NullPointerException("PropertyReadWrite must not be null"); 143 } 144 this.metaBean = metaBean; 145 this.propertyType = propertyType; 146 this.declaringType = declaringType; 147 this.readWrite = readWrite; 148 this.field = field; // may be null 149 } 150 151 //----------------------------------------------------------------------- 152 @Override 153 public MetaBean metaBean() { 154 return metaBean; 155 } 156 157 @Override 158 public Class<?> declaringType() { 159 return declaringType; 160 } 161 162 @Override 163 public Class<P> propertyType() { 164 return propertyType; 165 } 166 167 @Override 168 public Type propertyGenericType() { 169 if (field == null) { 170 return propertyType; 171 } 172 return field.getGenericType(); 173 } 174 175 @Override 176 public PropertyReadWrite readWrite() { 177 return readWrite; 178 } 179 180 @Override 181 public <A extends Annotation> A annotation(Class<A> annotationClass) { 182 if (field == null) { 183 throw new UnsupportedOperationException("Field not found for property: " + name()); 184 } 185 A annotation = field.getAnnotation(annotationClass); 186 if (annotation == null) { 187 throw new NoSuchElementException("Unknown annotation: " + annotationClass.getName()); 188 } 189 return annotation; 190 } 191 192 @Override 193 public List<Annotation> annotations() { 194 if (field == null) { 195 return Collections.emptyList(); 196 } 197 return Arrays.asList(field.getDeclaredAnnotations()); 198 } 199 200 //----------------------------------------------------------------------- 201 @SuppressWarnings("unchecked") 202 @Override 203 public P get(Bean bean) { 204 return (P) ((DirectBean) bean).propertyGet(name(), false); 205 } 206 207 @Override 208 public void set(Bean bean, Object value) { 209 ((DirectBean) bean).propertySet(name(), value, false); 210 } 211 212}