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.reflection;
17  
18  import java.lang.reflect.Field;
19  import java.lang.reflect.Modifier;
20  import java.util.Collections;
21  import java.util.HashMap;
22  import java.util.Map;
23  import java.util.NoSuchElementException;
24  
25  import org.joda.beans.Bean;
26  import org.joda.beans.BeanBuilder;
27  import org.joda.beans.MetaBean;
28  import org.joda.beans.MetaProperty;
29  import org.joda.beans.PropertyMap;
30  import org.joda.beans.impl.BasicBeanBuilder;
31  import org.joda.beans.impl.BasicPropertyMap;
32  
33  /**
34   * A standard meta-bean implementation.
35   * <p>
36   * This is the standard implementation of a meta-bean.
37   * It requires that the bean implements {@code Bean} and has a no-arguments constructor.
38   * 
39   * @author Stephen Colebourne
40   */
41  public final class ReflectiveMetaBean implements MetaBean {
42  
43      /** The bean type. */
44      private final Class<? extends Bean> beanType;
45      /** The meta-property instances of the bean. */
46      private final Map<String, MetaProperty<?>> metaPropertyMap;
47  
48      /**
49       * Factory to create a meta-bean avoiding duplicate generics.
50       * 
51       * @param <B>  the type of the bean
52       * @param beanClass  the bean class, not null
53       * @return the meta-bean, not null
54       */
55      public static <B extends Bean> ReflectiveMetaBean of(Class<B> beanClass) {
56          return new ReflectiveMetaBean(beanClass);
57      }
58  
59      /**
60       * Constructor.
61       * 
62       * @param beanType  the bean type, not null
63       */
64      @SuppressWarnings("unchecked")
65      private ReflectiveMetaBean(Class<? extends Bean> beanType) {
66          if (beanType == null) {
67              throw new NullPointerException("Bean class must not be null");
68          }
69          this.beanType = beanType;
70          Map<String, MetaProperty<?>> map = new HashMap<String, MetaProperty<?>>();
71          Field[] fields = beanType.getDeclaredFields();
72          for (Field field : fields) {
73              if (MetaProperty.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) {
74                  field.setAccessible(true);
75                  MetaProperty<Object> mp;
76                  try {
77                      mp = (MetaProperty<Object>) field.get(null);
78                      if (mp instanceof ReflectiveMetaProperty) {
79                          ((ReflectiveMetaProperty<Object>) mp).setMetaBean(this);
80                      }
81                  } catch (IllegalArgumentException ex) {
82                      throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex);
83                  } catch (IllegalAccessException ex) {
84                      throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex);
85                  }
86                  if (mp == null) {
87                      throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName() + ": Value must not be null");
88                  }
89                  map.put(mp.name(), mp);
90              }
91          }
92          
93          this.metaPropertyMap = Collections.unmodifiableMap(map);
94      }
95  
96      //-----------------------------------------------------------------------
97      @Override
98      public BeanBuilder<Bean> builder() {
99          try {
100             Bean bean = beanType.newInstance();
101             return new BasicBeanBuilder<Bean>(bean);
102         } catch (InstantiationException ex) {
103             throw new UnsupportedOperationException("Bean cannot be created: " + beanName(), ex);
104         } catch (IllegalAccessException ex) {
105             throw new UnsupportedOperationException("Bean cannot be created: " + beanName(), ex);
106         }
107     }
108 
109     @Override
110     public PropertyMap createPropertyMap(Bean bean) {
111         return BasicPropertyMap.of(bean);
112     }
113 
114     //-----------------------------------------------------------------------
115     @Override
116     public String beanName() {
117         return beanType.getName();
118     }
119 
120     @Override
121     public Class<? extends Bean> beanType() {
122         return beanType;
123     }
124 
125     //-----------------------------------------------------------------------
126     @Override
127     public int metaPropertyCount() {
128         return metaPropertyMap.size();
129     }
130 
131     @Override
132     public boolean metaPropertyExists(String propertyName) {
133         return metaPropertyMap.containsKey(propertyName);
134     }
135 
136     @SuppressWarnings("unchecked")
137     @Override
138     public <R> MetaProperty<R> metaProperty(String propertyName) {
139         MetaProperty<?> metaProperty = metaPropertyMap.get(propertyName);
140         if (metaProperty == null) {
141             throw new NoSuchElementException("Property not found: " + propertyName);
142         }
143         return (MetaProperty<R>) metaProperty;
144     }
145 
146     @Override
147     public Iterable<MetaProperty<?>> metaPropertyIterable() {
148         return metaPropertyMap.values();
149     }
150 
151     @Override
152     public Map<String, MetaProperty<?>> metaPropertyMap() {
153         return metaPropertyMap;
154     }
155 
156     //-----------------------------------------------------------------------
157     @Override
158     public boolean equals(Object obj) {
159         if (obj instanceof ReflectiveMetaBean) {
160             ReflectiveMetaBean other = (ReflectiveMetaBean) obj;
161             return this.beanType.equals(other.beanType);
162         }
163         return false;
164     }
165 
166     @Override
167     public int hashCode() {
168         return beanType.hashCode() + 3;
169     }
170 
171     /**
172      * Returns a string that summarises the meta-bean.
173      * 
174      * @return a summary string, not null
175      */
176     @Override
177     public String toString() {
178         return "MetaBean:" + beanName();
179     }
180 
181 }