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 */ 016package org.joda.beans.impl.reflection; 017 018import java.lang.reflect.Field; 019import java.lang.reflect.Modifier; 020import java.util.Collections; 021import java.util.HashMap; 022import java.util.Map; 023import java.util.NoSuchElementException; 024 025import org.joda.beans.Bean; 026import org.joda.beans.BeanBuilder; 027import org.joda.beans.MetaBean; 028import org.joda.beans.MetaProperty; 029import org.joda.beans.PropertyMap; 030import org.joda.beans.impl.BasicBeanBuilder; 031import 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 */ 041public 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 * @return the meta-bean, not null 054 */ 055 public static <B extends Bean> ReflectiveMetaBean of(Class<B> beanClass) { 056 return new ReflectiveMetaBean(beanClass); 057 } 058 059 /** 060 * Constructor. 061 * 062 * @param beanType the bean type, not null 063 */ 064 @SuppressWarnings("unchecked") 065 private ReflectiveMetaBean(Class<? extends Bean> beanType) { 066 if (beanType == null) { 067 throw new NullPointerException("Bean class must not be null"); 068 } 069 this.beanType = beanType; 070 Map<String, MetaProperty<?>> map = new HashMap<String, MetaProperty<?>>(); 071 Field[] fields = beanType.getDeclaredFields(); 072 for (Field field : fields) { 073 if (MetaProperty.class.isAssignableFrom(field.getType()) && Modifier.isStatic(field.getModifiers())) { 074 field.setAccessible(true); 075 MetaProperty<Object> mp; 076 try { 077 mp = (MetaProperty<Object>) field.get(null); 078 if (mp instanceof ReflectiveMetaProperty) { 079 ((ReflectiveMetaProperty<Object>) mp).setMetaBean(this); 080 } 081 } catch (IllegalArgumentException ex) { 082 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex); 083 } catch (IllegalAccessException ex) { 084 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName(), ex); 085 } 086 if (mp == null) { 087 throw new UnsupportedOperationException("MetaProperty cannot be created: " + field.getName() + ": Value must not be null"); 088 } 089 map.put(mp.name(), mp); 090 } 091 } 092 093 this.metaPropertyMap = Collections.unmodifiableMap(map); 094 } 095 096 //----------------------------------------------------------------------- 097 @Override 098 public BeanBuilder<Bean> builder() { 099 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}