View Javadoc

1   /*
2    *  Copyright 2001-2013 Stephen Colebourne
3    *
4    *  Licensed under the Apache License, Version 2.0 (the "License");
5    *  you may not use this file except in compliance with the License.
6    *  You may obtain a copy of the License at
7    *
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    *
10   *  Unless required by applicable law or agreed to in writing, software
11   *  distributed under the License is distributed on an "AS IS" BASIS,
12   *  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   *  See the License for the specific language governing permissions and
14   *  limitations under the License.
15   */
16  package org.joda.beans.impl.direct;
17  
18  import java.lang.annotation.Annotation;
19  import java.lang.reflect.Field;
20  import java.lang.reflect.Type;
21  import java.util.Arrays;
22  import java.util.Collections;
23  import java.util.List;
24  import java.util.NoSuchElementException;
25  
26  import org.joda.beans.Bean;
27  import org.joda.beans.MetaBean;
28  import org.joda.beans.PropertyReadWrite;
29  import org.joda.beans.impl.BasicMetaProperty;
30  
31  /**
32   * A meta-property implementation designed for use by {@code DirectBean}.
33   * <p>
34   * This meta-property uses reflection to find the {@code Field} to obtain the annotations.
35   * 
36   * @param <P>  the type of the property content
37   * @author Stephen Colebourne
38   */
39  public final class DirectMetaProperty<P> extends BasicMetaProperty<P> {
40  
41      /** The meta-bean. */
42      private final MetaBean metaBean;
43      /** The property type. */
44      private final Class<P> propertyType;
45      /** The declaring type. */
46      private final Class<?> declaringType;
47      /** The field implementing the property. */
48      private final Field field;
49      /** The read-write type. */
50      private final PropertyReadWrite readWrite;
51  
52      /**
53       * Factory to create a read-write meta-property avoiding duplicate generics.
54       * 
55       * @param <P>  the property type
56       * @param metaBean  the meta-bean, not null
57       * @param propertyName  the property name, not empty
58       * @param declaringType  the type declaring the property, not null
59       * @param propertyType  the property type, not null
60       * @return the property, not null
61       */
62      public static <P> DirectMetaProperty<P> ofReadWrite(
63              MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) {
64          Field field = findField(metaBean, propertyName);
65          return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.READ_WRITE, field);
66      }
67  
68      /**
69       * Factory to create a read-write meta-property avoiding duplicate generics.
70       * 
71       * @param <P>  the property type
72       * @param metaBean  the meta-bean, not null
73       * @param propertyName  the property name, not empty
74       * @param declaringType  the type declaring the property, not null
75       * @param propertyType  the property type, not null
76       * @return the property, not null
77       */
78      public static <P> DirectMetaProperty<P> ofReadOnly(
79              MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) {
80          Field field = findField(metaBean, propertyName);
81          return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.READ_ONLY, field);
82      }
83  
84      /**
85       * Factory to create a read-write meta-property avoiding duplicate generics.
86       * 
87       * @param <P>  the property type
88       * @param metaBean  the meta-bean, not null
89       * @param propertyName  the property name, not empty
90       * @param declaringType  the type declaring the property, not null
91       * @param propertyType  the property type, not null
92       * @return the property, not null
93       */
94      public static <P> DirectMetaProperty<P> ofWriteOnly(
95              MetaBean metaBean, String propertyName, Class<?> declaringType, Class<P> propertyType) {
96          Field field = findField(metaBean, propertyName);
97          return new DirectMetaProperty<P>(metaBean, propertyName, declaringType, propertyType, PropertyReadWrite.WRITE_ONLY, field);
98      }
99  
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 }