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.reflection;
017
018 import java.lang.reflect.Field;
019 import java.lang.reflect.Modifier;
020 import java.util.Collections;
021 import java.util.HashMap;
022 import java.util.Map;
023 import java.util.NoSuchElementException;
024
025 import org.joda.beans.Bean;
026 import org.joda.beans.BeanBuilder;
027 import org.joda.beans.MetaBean;
028 import org.joda.beans.MetaProperty;
029 import org.joda.beans.PropertyMap;
030 import org.joda.beans.impl.BasicBeanBuilder;
031 import org.joda.beans.impl.BasicPropertyMap;
032
033 /**
034 * A standard meta-bean implementation.
035 * <p>
036 * This is the standard implementation of a meta-bean.
037 * It requires that the bean implements {@code Bean} and has a no-arguments constructor.
038 *
039 * @author Stephen Colebourne
040 */
041 public final class ReflectiveMetaBean implements MetaBean {
042
043 /** The bean type. */
044 private final Class<? extends Bean> beanType;
045 /** The meta-property instances of the bean. */
046 private final Map<String, MetaProperty<?>> metaPropertyMap;
047
048 /**
049 * Factory to create a meta-bean avoiding duplicate generics.
050 *
051 * @param <B> the type of the bean
052 * @param beanClass the bean class, not null
053 */
054 public static <B extends Bean> ReflectiveMetaBean of(Class<B> beanClass) {
055 return new ReflectiveMetaBean(beanClass);
056 }
057
058 /**
059 * Constructor.
060 *
061 * @param beanType the bean type, not null
062 */
063 @SuppressWarnings("unchecked")
064 private ReflectiveMetaBean(Class<? extends Bean> beanType) {
065 if (beanType == null) {
066 throw new NullPointerException("Bean class must not be null");
067 }
068 this.beanType = beanType;
069 Map<String, MetaProperty<?>> map = new HashMap<String, MetaProperty<?>>();
070 Field[] fields = beanType.getDeclaredFields();
071 for (Field field : fields) {
072 if (MetaProperty.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) {
073 field.setAccessible(true);
074 MetaProperty<Object> mp;
075 try {
076 mp = (MetaProperty<Object>) field.get(null);
077 if (mp instanceof ReflectiveMetaProperty) {
078 ((ReflectiveMetaProperty<Object>) mp).setMetaBean(this);
079 }
080 } catch (IllegalArgumentException ex) {
081 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex);
082 } catch (IllegalAccessException ex) {
083 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex);
084 }
085 if (mp == null) {
086 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName() + ": Value must not be null");
087 }
088 map.put(mp.name(), mp);
089 }
090 }
091
092 this.metaPropertyMap = Collections.unmodifiableMap(map);
093 }
094
095 //-----------------------------------------------------------------------
096 @Override
097 public BeanBuilder<Bean> builder() {
098 try {
099 Bean bean = beanType.newInstance();
100 return new BasicBeanBuilder<Bean>(bean);
101 } catch (InstantiationException ex) {
102 throw new UnsupportedOperationException("Bean cannot be created: " + beanName(), ex);
103 } catch (IllegalAccessException ex) {
104 throw new UnsupportedOperationException("Bean cannot be created: " + beanName(), ex);
105 }
106 }
107
108 @Override
109 public PropertyMap createPropertyMap(Bean bean) {
110 return BasicPropertyMap.of(bean);
111 }
112
113 //-----------------------------------------------------------------------
114 @Override
115 public String beanName() {
116 return beanType.getName();
117 }
118
119 @Override
120 public Class<? extends Bean> beanType() {
121 return beanType;
122 }
123
124 //-----------------------------------------------------------------------
125 @Override
126 public int metaPropertyCount() {
127 return metaPropertyMap.size();
128 }
129
130 @Override
131 public boolean metaPropertyExists(String propertyName) {
132 return metaPropertyMap.containsKey(propertyName);
133 }
134
135 @SuppressWarnings("unchecked")
136 @Override
137 public <R> MetaProperty<R> metaProperty(String propertyName) {
138 MetaProperty<?> metaProperty = metaPropertyMap.get(propertyName);
139 if (metaProperty == null) {
140 throw new NoSuchElementException("Property not found: " + propertyName);
141 }
142 return (MetaProperty<R>) metaProperty;
143 }
144
145 @Override
146 public Iterable<MetaProperty<?>> metaPropertyIterable() {
147 return metaPropertyMap.values();
148 }
149
150 @Override
151 public Map<String, MetaProperty<?>> metaPropertyMap() {
152 return metaPropertyMap;
153 }
154
155 //-----------------------------------------------------------------------
156 @Override
157 public boolean equals(Object obj) {
158 if (obj instanceof ReflectiveMetaBean) {
159 ReflectiveMetaBean other = (ReflectiveMetaBean) obj;
160 return this.beanType.equals(other.beanType);
161 }
162 return false;
163 }
164
165 @Override
166 public int hashCode() {
167 return beanType.hashCode() + 3;
168 }
169
170 /**
171 * Returns a string that summarises the meta-bean.
172 *
173 * @return a summary string, not null
174 */
175 @Override
176 public String toString() {
177 return "MetaBean:" + beanName();
178 }
179
180 }