View Javadoc

1   /*
2    * Copyright 2012 Vincent Demeester<vincent+shortbrain@demeester.fr>.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License"); you may not
5    * use this file except in compliance with the License. You may obtain a copy of
6    * 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, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations under
14   * the License.
15   */
16  package org.shortbrain.vaadin.container.annotation.reader;
17  
18  import java.lang.annotation.Annotation;
19  import java.lang.reflect.Field;
20  
21  /**
22   * Generic annotation reader.
23   * 
24   * This currently only work on annotation for Type.
25   * 
26   * @author Vincent Demeester <vincent@demeester.fr>
27   * @param <T>
28   *            type of the Annotation.
29   * 
30   */
31  public abstract class BeanAbstractAnnotationReader<T> {
32  
33  	private final Class<?> originalBeanClass;
34  	private final Class<?> beanClass;
35  	private T metadata;
36  
37  	/**
38  	 * Creates a BeanAbstractAnnotationReader.
39  	 * 
40  	 * @param originalBeanClass
41  	 *            the original bean type.
42  	 * @param beanClass
43  	 *            the <em>annotated</em> bean type (might be a parent).
44  	 * @throws IllegalArgumentException
45  	 *             if the originalBeanClass or beanClass is null.
46  	 */
47  	public BeanAbstractAnnotationReader(Class<?> originalBeanClass,
48  			Class<?> beanClass) {
49  		if (originalBeanClass == null || beanClass == null) {
50  			throw new IllegalArgumentException(
51  					"originalBeanClass and beanClass cannot be null.");
52  		}
53  		this.originalBeanClass = originalBeanClass;
54  		this.beanClass = beanClass;
55  	}
56  
57  	/**
58  	 * @return the originalEntityClass
59  	 */
60  	protected Class<?> getOriginalBeanClass() {
61  		return originalBeanClass;
62  	}
63  
64  	/**
65  	 * @return the entityClass
66  	 */
67  	protected Class<?> getBeanClass() {
68  		return beanClass;
69  	}
70  
71  	public void setMetadatas(T metadata) {
72  		this.metadata = metadata;
73  	}
74  
75  	public T getMetadatas() {
76  		return metadata;
77  	}
78  
79  	/**
80  	 * Get the type of the given propertyAttribute
81  	 * 
82  	 * @param propertyAttribute
83  	 * @return
84  	 * @throws SecurityException
85  	 * @throws NoSuchFieldException
86  	 *             if the field is not found or if propertyAttribute is null.
87  	 */
88  	protected Class<?> getPropertyType(String propertyAttribute)
89  			throws SecurityException, NoSuchFieldException {
90  		if (propertyAttribute == null) {
91  			throw new NoSuchFieldException("no null field.");
92  		}
93  		Class<?> ret = null;
94  		if (propertyAttribute.contains(".")) {
95  		    // Recursive className detection
96  		    String[] propertyAttributes = propertyAttribute.split("\\.");
97  		    ret = getNestedPropertyType(propertyAttributes);
98  		} else {
99  			Field field = getField(getOriginalBeanClass(), propertyAttribute);
100 			ret = field.getType();
101 		}
102 		return ret;
103 	}
104 	
105 	private Class<?> getNestedPropertyType(String[] propertyAttributes) throws NoSuchFieldException {
106 	    Class<?> klass = getOriginalBeanClass();
107 	    String fieldName = propertyAttributes[0];
108 	    for (int i = 1; i < propertyAttributes.length; i++) {
109             Field nestedField = getField(klass, fieldName);
110             fieldName = propertyAttributes[i];
111             klass = nestedField.getType();
112         }
113 	    Field field = getField(klass, fieldName);
114 	    return field.getType();
115 	}
116 	
117 	/**
118 	 * Get the nested Field for the given class, name and nested name.
119 	 * 
120 	 * @param klass
121 	 * @param fieldName
122 	 * @param nestedFieldName
123 	 * @return
124 	 */
125 	protected Field getNestedField(Class<?> klass, String fieldName, String nestedFieldName) throws NoSuchFieldException {
126         if (klass == null || fieldName == null || nestedFieldName == null) {
127             throw new IllegalArgumentException(
128                     "klass nor fieldName nor nestedField can be null.");
129         }
130         Field nestedField = null;
131         Field field = getField(klass, fieldName);
132         Class<?> nestedKlass = field.getType();
133         nestedField = getField(nestedKlass, nestedFieldName);
134         return nestedField;
135 	}
136 
137 	/**
138 	 * Get the Field for the given class and parents.
139 	 * 
140 	 * @param klass
141 	 *            the class where we will look for the field.
142 	 * @param fieldName
143 	 *            the name of the field.
144 	 * @return the field or null if we cannot find the field.
145 	 * @throws IllegalArgumentException
146 	 *             if klass or fieldName are null.
147 	 */
148 	protected Field getField(Class<?> klass, String fieldName) throws NoSuchFieldException {
149 		if (klass == null || fieldName == null) {
150 			throw new IllegalArgumentException(
151 					"klass or fieldName cannot be null.");
152 		}
153 		Field field = null;
154 		if (klass != Object.class) {
155 			try {
156 				field = klass.getDeclaredField(fieldName);
157 			} catch (NoSuchFieldException e) {
158 				field = getField(klass.getSuperclass(), fieldName);
159 			}
160 		}
161 		if (field == null) {
162             throw new NoSuchFieldException("No field " + fieldName
163                     + " for class " + klass.getName()
164                     + ".");
165 		}
166 		return field;
167 	}
168 
169 	/**
170 	 * Get the annotated type by walking in superclass to find the annotation
171 	 * 
172 	 * @param beanClass
173 	 *            The type that should be annotated
174 	 * @param annotationClass
175 	 *            the type of the annotation
176 	 * @return the "real" type annotated, either entityClass or a parent.
177 	 * @throws IllegalArgumentException
178 	 *             if entityClass or annotationClass are null or if entityClass
179 	 *             (and its parent) is not annotated with annotationClass.
180 	 */
181 	public static Class<?> getAnnotatedClass(Class<?> beanClass,
182 			Class<? extends Annotation> annotationClass) {
183 		if (beanClass == null || annotationClass == null) {
184 			throw new IllegalArgumentException(
185 					"beanClass and annotationClass cannot be null.");
186 		}
187 		Class<?> ret = null;
188 		if (!beanClass.isAnnotationPresent(annotationClass)) {
189 			// On remonte dans la hierarchie jusqu'à Object
190 			if (beanClass.getSuperclass() != Object.class) {
191 				ret = getAnnotatedClass(beanClass.getSuperclass(),
192 						annotationClass);
193 			}
194 			if (ret == null) {
195 				throw new IllegalArgumentException(
196 						"beanClass and its super classes are not annotated with "
197 								+ annotationClass.getSimpleName() + ".");
198 			}
199 		} else {
200 			ret = beanClass;
201 		}
202 		return ret;
203 	}
204 
205 }