001 /*
002 * Copyright 2001-2010 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;
017
018 import java.lang.reflect.Field;
019 import java.lang.reflect.Modifier;
020 import java.util.HashMap;
021 import java.util.Map;
022 import java.util.NoSuchElementException;
023
024 /**
025 * A standard meta-bean implementation.
026 * <p>
027 * This is the standard implementation of a meta-bean.
028 * It requires that the bean implements {@code Bean} and has a no-arguments constructor.
029 *
030 * @param <B> the type of the bean
031 * @author Stephen Colebourne
032 */
033 public final class StandardMetaBean<B extends Bean<B>> implements MetaBean<B> {
034
035 /** The bean class. */
036 private final Class<B> beanClass;
037 /** The meta-property instances of the bean. */
038 private final Map<String, MetaProperty<B, ?>> metaPropertyMap;
039
040 /**
041 * Factory to create a meta-bean avoiding duplicate generics.
042 *
043 * @param beanClass the bean class, not null
044 */
045 public static <B extends Bean<B>> StandardMetaBean<B> of(Class<B> beanClass) {
046 return new StandardMetaBean<B>(beanClass);
047 }
048
049 /**
050 * Creates a property binding the bean to the meta-property.
051 *
052 * @param beanClass the bean class, not null
053 */
054 @SuppressWarnings("unchecked")
055 private StandardMetaBean(Class<B> beanClass) {
056 Beans.checkNotNull(beanClass, "Class must not be null");
057 this.beanClass = beanClass;
058 Map<String, MetaProperty<B,?>> map = new HashMap<String, MetaProperty<B,?>>();
059 Field[] fields = beanClass.getFields();
060 for (Field field : fields) {
061 if (MetaProperty.class.isAssignableFrom(field.getType()) &&
062 Modifier.isStatic(field.getModifiers()) && Modifier.isPublic(field.getModifiers())) {
063 MetaProperty<B, ?> mp;
064 try {
065 mp = (MetaProperty<B, ?>) field.get(null);
066 } catch (IllegalArgumentException ex) {
067 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex);
068 } catch (IllegalAccessException ex) {
069 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex);
070 }
071 map.put(mp.name(), mp);
072 }
073 }
074
075 this.metaPropertyMap = map;
076 }
077
078 //-----------------------------------------------------------------------
079 @Override
080 public Bean<B> createBean() {
081 try {
082 return beanClass.newInstance();
083 } catch (InstantiationException ex) {
084 throw new UnsupportedOperationException("Bean cannot be created: " + name(), ex);
085 } catch (IllegalAccessException ex) {
086 throw new UnsupportedOperationException("Bean cannot be created: " + name(), ex);
087 }
088 }
089
090 @Override
091 public Map<String, Property<B, ?>> createPropertyMap(B bean) {
092 return StandardPropertyMap.of(bean);
093 }
094
095 //-----------------------------------------------------------------------
096 @Override
097 public String name() {
098 return beanClass.getName();
099 }
100
101 @Override
102 public Class<B> beanClass() {
103 return beanClass;
104 }
105
106 //-----------------------------------------------------------------------
107 @Override
108 public MetaProperty<B, ?> metaProperty(String propertyName) {
109 MetaProperty<B, ?> metaProperty = metaPropertyMap.get(propertyName);
110 if (metaProperty == null) {
111 throw new NoSuchElementException("Property not found: " + propertyName);
112 }
113 return metaProperty;
114 }
115
116 @Override
117 public Map<String, MetaProperty<B, ?>> metaPropertyMap() {
118 return metaPropertyMap;
119 }
120
121 //-----------------------------------------------------------------------
122 /**
123 * Returns a string that summarizes the property.
124 *
125 * @return a summary string, never null
126 */
127 @Override
128 public String toString() {
129 return "MetaBean:" + name();
130 }
131
132 }