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    }