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 */
016 package org.joda.beans.impl.direct;
017
018 import java.lang.annotation.Annotation;
019 import java.lang.reflect.Field;
020 import java.lang.reflect.Type;
021 import java.util.Arrays;
022 import java.util.Collections;
023 import java.util.List;
024 import java.util.NoSuchElementException;
025
026 import org.joda.beans.Bean;
027 import org.joda.beans.MetaBean;
028 import org.joda.beans.PropertyReadWrite;
029 import 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 */
039 public 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 metaBean the meta-bean, not null
056 * @param propertyName the property name, not empty
057 * @param propertyType the property type, not null
058 */
059 public static <P> DirectMetaProperty<P> ofReadWrite(
060 MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) {
061 Field field = findField(metaBean, propertyName);
062 return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.READ_WRITE, field);
063 }
064
065 /**
066 * Factory to create a read-write meta-property avoiding duplicate generics.
067 *
068 * @param metaBean the meta-bean, not null
069 * @param propertyName the property name, not empty
070 * @param propertyType the property type, not null
071 */
072 public static <P> DirectMetaProperty<P> ofReadOnly(
073 MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) {
074 Field field = findField(metaBean, propertyName);
075 return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.READ_ONLY, field);
076 }
077
078 /**
079 * Factory to create a read-write meta-property avoiding duplicate generics.
080 *
081 * @param metaBean the meta-bean, not null
082 * @param propertyName the property name, not empty
083 * @param propertyType the property type, not null
084 */
085 public static <P> DirectMetaProperty<P> ofWriteOnly(
086 MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) {
087 Field field = findField(metaBean, propertyName);
088 return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.WRITE_ONLY, field);
089 }
090
091 private static Field findField(MetaBean metaBean, String propertyName) {
092 Field field = null;
093 Class<?> cls = metaBean.beanType();
094 while (cls != DirectBean.class) {
095 try {
096 field = cls.getDeclaredField(propertyName);
097 break;
098 } catch (NoSuchFieldException ex) {
099 try {
100 field = cls.getDeclaredField("_" + propertyName);
101 break;
102 } catch (NoSuchFieldException ex2) {
103 cls = cls.getSuperclass();
104 }
105 }
106 }
107 return field;
108 }
109
110 /**
111 * Constructor.
112 *
113 * @param metaBean the meta-bean, not null
114 * @param propertyName the property name, not empty
115 * @param declaringType the declaring type, not null
116 * @param propertyType the property type, not null
117 * @param readWrite the read-write type, not null
118 * @param field the reflected field, not null
119 */
120 private DirectMetaProperty(MetaBean metaBean, String propertyName, Class<?> declaringType,
121 Class<P> propertyType, PropertyReadWrite readWrite, Field field) {
122 super(propertyName);
123 if (metaBean == null) {
124 throw new NullPointerException("MetaBean must not be null");
125 }
126 if (declaringType == null) {
127 throw new NullPointerException("Declaring type must not be null");
128 }
129 if (propertyType == null) {
130 throw new NullPointerException("Property type must not be null");
131 }
132 if (readWrite == null) {
133 throw new NullPointerException("PropertyReadWrite must not be null");
134 }
135 this.metaBean = metaBean;
136 this.propertyType = propertyType;
137 this.declaringType = declaringType;
138 this.readWrite = readWrite;
139 this.field = field; // may be null
140 }
141
142 //-----------------------------------------------------------------------
143 @Override
144 public MetaBean metaBean() {
145 return metaBean;
146 }
147
148 @Override
149 public Class<?> declaringType() {
150 return declaringType;
151 }
152
153 @Override
154 public Class<P> propertyType() {
155 return propertyType;
156 }
157
158 @Override
159 public Type propertyGenericType() {
160 if (field == null) {
161 return propertyType;
162 }
163 return field.getGenericType();
164 }
165
166 @Override
167 public PropertyReadWrite readWrite() {
168 return readWrite;
169 }
170
171 @Override
172 public <A extends Annotation> A annotation(Class<A> annotationClass) {
173 if (field == null) {
174 throw new UnsupportedOperationException("Field not found for property: " + name());
175 }
176 A annotation = field.getAnnotation(annotationClass);
177 if (annotation == null) {
178 throw new NoSuchElementException("Unknown annotation: " + annotationClass.getName());
179 }
180 return annotation;
181 }
182
183 @Override
184 public List<Annotation> annotations() {
185 if (field == null) {
186 return Collections.emptyList();
187 }
188 return Arrays.asList(field.getDeclaredAnnotations());
189 }
190
191 //-----------------------------------------------------------------------
192 @SuppressWarnings("unchecked")
193 @Override
194 public P get(Bean bean) {
195 return (P) ((DirectBean) bean).propertyGet(name(), false);
196 }
197
198 @Override
199 public void set(Bean bean, Object value) {
200 ((DirectBean) bean).propertySet(name(), value, false);
201 }
202
203 }