001: /*
002: * Spoon - http://spoon.gforge.inria.fr/
003: * Copyright (C) 2006 INRIA Futurs <renaud.pawlak@inria.fr>
004: *
005: * This software is governed by the CeCILL-C License under French law and
006: * abiding by the rules of distribution of free software. You can use, modify
007: * and/or redistribute the software under the terms of the CeCILL-C license as
008: * circulated by CEA, CNRS and INRIA at http://www.cecill.info.
009: *
010: * This program is distributed in the hope that it will be useful, but WITHOUT
011: * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
012: * FITNESS FOR A PARTICULAR PURPOSE. See the CeCILL-C License for more details.
013: *
014: * The fact that you are presently reading this means that you have had
015: * knowledge of the CeCILL-C license and that you accept its terms.
016: */
017:
018: package spoon.support.reflect.declaration;
019:
020: import java.lang.annotation.Annotation;
021: import java.lang.reflect.Array;
022: import java.lang.reflect.InvocationHandler;
023: import java.lang.reflect.Method;
024: import java.lang.reflect.Proxy;
025: import java.util.ArrayList;
026: import java.util.Arrays;
027: import java.util.Collection;
028: import java.util.List;
029: import java.util.Map;
030: import java.util.TreeMap;
031: import java.util.Map.Entry;
032:
033: import spoon.reflect.code.CtCodeElement;
034: import spoon.reflect.code.CtFieldAccess;
035: import spoon.reflect.code.CtLiteral;
036: import spoon.reflect.declaration.CtAnnotation;
037: import spoon.reflect.declaration.CtAnnotationType;
038: import spoon.reflect.declaration.CtField;
039: import spoon.reflect.declaration.CtSimpleType;
040: import spoon.reflect.eval.PartialEvaluator;
041: import spoon.reflect.reference.CtFieldReference;
042: import spoon.reflect.reference.CtTypeReference;
043: import spoon.reflect.visitor.CtVisitor;
044:
045: /**
046: * The implementation for {@link spoon.reflect.declaration.CtAnnotation}.
047: *
048: * @author Renaud Pawlak
049: */
050: public class CtAnnotationImpl<A extends Annotation> extends
051: CtElementImpl implements CtAnnotation<A> {
052: class AnnotationInvocationHandler implements InvocationHandler {
053: CtAnnotation<? extends Annotation> annotation;
054:
055: public AnnotationInvocationHandler(
056: CtAnnotation<? extends Annotation> annotation) {
057: super ();
058: this .annotation = annotation;
059: }
060:
061: public Object invoke(Object proxy, Method method, Object[] args)
062: throws Throwable {
063: String fieldname = method.getName();
064: if (fieldname.equals("toString")) {
065: return CtAnnotationImpl.this .toString();
066: } else if (fieldname.equals("annotationType")) {
067: return annotation.getAnnotationType().getActualClass();
068: }
069: return getElementValue(fieldname);
070: }
071: }
072:
073: private static final long serialVersionUID = 1L;
074:
075: CtTypeReference<A> annotationType;
076:
077: Map<String, Object> elementValues = new TreeMap<String, Object>() {
078:
079: private static final long serialVersionUID = 3501647177461995350L;
080:
081: @Override
082: public Object put(String key, Object value) {
083: if (value instanceof Class[]) {
084: Class<?>[] valsNew = (Class<?>[]) value;
085: ArrayList<CtTypeReference<?>> ret = new ArrayList<CtTypeReference<?>>(
086: valsNew.length);
087:
088: for (int i = 0; i < valsNew.length; i++) {
089: Class<?> class1 = valsNew[i];
090: ret.add(i, getFactory().Type().createReference(
091: class1));
092: }
093: return super .put(key, ret);
094: }
095: if (value instanceof Class) {
096: return super .put(key, getFactory().Type()
097: .createReference((Class<?>) value));
098: }
099: return super .put(key, value);
100:
101: }
102:
103: };
104:
105: public CtAnnotationImpl() {
106: super ();
107: }
108:
109: public void accept(CtVisitor visitor) {
110: visitor.visitCtAnnotation(this );
111: }
112:
113: protected void appendValues(String elementName, Object... values) {
114: if (!elementValues.containsKey(elementName)) {
115: elementValues.put(elementName, values);
116: } else {
117: Object o = elementValues.get(elementName);
118: if (o.getClass().isArray()) {
119: List<Object> tmp = new ArrayList<Object>();
120: Object[] old = (Object[]) o;
121: for (Object a : old) {
122: tmp.add(a);
123: }
124: for (Object a : values) {
125: tmp.add(a);
126: }
127: elementValues.put(elementName, tmp.toArray());
128: } else {
129: // o is not a array
130: if (values.length > 1) {
131: throw new RuntimeException(
132: "Cannot add array to a non-array value");
133: }
134: elementValues.put(elementName, values[0]);
135: }
136: }
137: }
138:
139: @SuppressWarnings("unchecked")
140: public A getActualAnnotation() {
141: return (A) Proxy.newProxyInstance(annotationType
142: .getActualClass().getClassLoader(),
143: new Class[] { annotationType.getActualClass() },
144: new AnnotationInvocationHandler(this ));
145: }
146:
147: @SuppressWarnings("unchecked")
148: private Object convertValue(Object value) {
149: if (value instanceof CtFieldReference) {
150: Class c = ((CtFieldReference) value).getDeclaringType()
151: .getActualClass();
152: if (((CtFieldReference) value).getSimpleName().equals(
153: "class")) {
154: return c;
155: }
156: CtField field = ((CtFieldReference) value).getDeclaration();
157: if (Enum.class.isAssignableFrom(c)) {
158: // Value references a Enum field
159: return Enum.valueOf(c, ((CtFieldReference) value)
160: .getSimpleName());
161: }
162: // Value is a static final
163: return convertValue(field.getDefaultExpression());
164: } else if (value instanceof CtFieldAccess) {
165: // Get variable
166: return convertValue(((CtFieldAccess) value).getVariable());
167: } else if (value instanceof CtAnnotation) {
168: // Get proxy
169: return ((CtAnnotation) value).getActualAnnotation();
170: } else if (value instanceof CtLiteral) {
171: // Replace literal by his value
172: return ((CtLiteral) value).getValue();
173: } else if (value instanceof CtCodeElement) {
174: // Evaluate code elements
175: PartialEvaluator eval = getFactory().Eval()
176: .createPartialEvaluator();
177: Object ret = eval.evaluate(((CtCodeElement) value)
178: .getParent(), (CtCodeElement) value);
179: if (!(ret instanceof CtCodeElement)) {
180: return convertValue(ret);
181: }
182: return ret;
183: } else if (value instanceof CtTypeReference) {
184: // Get RT class for References
185: return ((CtTypeReference) value).getActualClass();
186: }
187: return value;
188: }
189:
190: private Class<?> getElementType(String name) {
191: // Try by CT reflection
192: CtSimpleType<?> t = getAnnotationType().getDeclaration();
193: if (t != null) {
194: CtField<?> f = t.getField(name);
195: return f.getType().getActualClass();
196: }
197: // Try with RT reflection
198: Class<?> c = getAnnotationType().getActualClass();
199: for (Method m : c.getMethods()) {
200: if (m.getName().equals(name)) {
201: return m.getReturnType();
202: }
203: }
204: return null;
205: }
206:
207: public CtTypeReference<A> getAnnotationType() {
208: return annotationType;
209: }
210:
211: private Object getDefaultValue(String fieldName) {
212: Object ret = null;
213: CtAnnotationType<?> at = (CtAnnotationType<?>) getAnnotationType()
214: .getDeclaration();
215: if (at != null) {
216: CtField<?> f = at.getField(fieldName);
217: ret = f.getDefaultExpression();
218: }
219: return ret;
220: }
221:
222: public Object getElementValue(String key) {
223: Object ret = null;
224: ret = elementValues.get(key);
225: if (ret == null) {
226: ret = getDefaultValue(key);
227: }
228: if (ret == null) {
229: ret = getReflectValue(key);
230: }
231:
232: Class<?> type = getElementType(key);
233:
234: if (type.isArray()) {
235: if (!(ret instanceof Collection)) {
236: List<Object> lst = new ArrayList<Object>();
237:
238: if (ret.getClass().isArray()) {
239: Object[] temp = (Object[]) ret;
240: lst.addAll(Arrays.asList(temp));
241: } else {
242: lst.add(ret);
243: }
244: ret = lst;
245:
246: }
247: Collection<?> col = (Collection<?>) ret;
248: Object[] array = (Object[]) Array.newInstance(type
249: .getComponentType(), col.size());
250: int i = 0;
251: for (Object obj : col) {
252: array[i++] = convertValue(obj);
253: }
254: ret = array;
255: } else {
256: ret = convertValue(ret);
257: }
258:
259: if (type.isPrimitive()) {
260: if ((type == boolean.class)
261: && (ret.getClass() != boolean.class)) {
262: ret = Boolean.parseBoolean(ret.toString());
263: } else if ((type == byte.class)
264: && (ret.getClass() != byte.class)) {
265: ret = Byte.parseByte(ret.toString());
266: } else if ((type == char.class)
267: && (ret.getClass() != char.class)) {
268: ret = ret.toString().charAt(0);
269: } else if ((type == double.class)
270: && (ret.getClass() != double.class)) {
271: ret = Double.parseDouble(ret.toString());
272: } else if ((type == float.class)
273: && (ret.getClass() != float.class)) {
274: ret = Float.parseFloat(ret.toString());
275: } else if ((type == int.class)
276: && (ret.getClass() != int.class)) {
277: ret = Integer.parseInt(ret.toString());
278: } else if ((type == long.class)
279: && (ret.getClass() != long.class)) {
280: ret = Long.parseLong(ret.toString());
281: }
282: }
283: return ret;
284: }
285:
286: public Map<String, Object> getElementValues() {
287: return elementValues;
288: }
289:
290: private Object getReflectValue(String fieldname) {
291: try {
292: Class<?> c = getAnnotationType().getActualClass();
293: Method m = c.getMethod(fieldname);
294: return m.getDefaultValue();
295: } catch (Exception e) {
296: throw new RuntimeException(e);
297: }
298: }
299:
300: @SuppressWarnings("unchecked")
301: public void setAnnotationType(
302: CtTypeReference<? extends Annotation> annotationType) {
303: this .annotationType = (CtTypeReference<A>) annotationType;
304: }
305:
306: public void setElementValues(Map<String, Object> values) {
307: for (Entry<String, Object> e : values.entrySet()) {
308: this.elementValues.put(e.getKey(), e.getValue());
309: }
310: }
311: }
|