001: package com.sun.codemodel;
002:
003: import java.lang.reflect.InvocationHandler;
004: import java.lang.reflect.Method;
005: import java.lang.reflect.Proxy;
006: import java.lang.reflect.Type;
007: import java.lang.reflect.ParameterizedType;
008: import java.lang.reflect.InvocationTargetException;
009: import java.lang.annotation.Annotation;
010: import java.util.Map;
011: import java.util.HashMap;
012:
013: /**
014: * Dynamically implements the typed annotation writer interfaces.
015: *
016: * @author Kohsuke Kawaguchi
017: */
018: class TypedAnnotationWriter<A extends Annotation, W extends JAnnotationWriter<A>>
019: implements InvocationHandler, JAnnotationWriter<A> {
020: /**
021: * This is what we are writing to.
022: */
023: private final JAnnotationUse use;
024:
025: /**
026: * The annotation that we are writing.
027: */
028: private final Class<A> annotation;
029:
030: /**
031: * The type of the writer.
032: */
033: private final Class<W> writerType;
034:
035: /**
036: * Keeps track of writers for array members.
037: * Lazily created.
038: */
039: private Map<String, JAnnotationArrayMember> arrays;
040:
041: public TypedAnnotationWriter(Class<A> annotation, Class<W> writer,
042: JAnnotationUse use) {
043: this .annotation = annotation;
044: this .writerType = writer;
045: this .use = use;
046: }
047:
048: public JAnnotationUse getAnnotationUse() {
049: return use;
050: }
051:
052: public Class<A> getAnnotationType() {
053: return annotation;
054: }
055:
056: public Object invoke(Object proxy, Method method, Object[] args)
057: throws Throwable {
058:
059: if (method.getDeclaringClass() == JAnnotationWriter.class) {
060: try {
061: return method.invoke(this , args);
062: } catch (InvocationTargetException e) {
063: throw e.getTargetException();
064: }
065: }
066:
067: String name = method.getName();
068: Object arg = null;
069: if (args != null && args.length > 0)
070: arg = args[0];
071:
072: // check how it's defined on the annotation
073: Method m = annotation.getDeclaredMethod(name);
074: Class<?> rt = m.getReturnType();
075:
076: // array value
077: if (rt.isArray()) {
078: return addArrayValue(proxy, name, rt.getComponentType(),
079: method.getReturnType(), arg);
080: }
081:
082: // sub annotation
083: if (Annotation.class.isAssignableFrom(rt)) {
084: Class<? extends Annotation> r = (Class<? extends Annotation>) rt;
085: return new TypedAnnotationWriter(r, method.getReturnType(),
086: use.annotationParam(name, r)).createProxy();
087: }
088:
089: // scalar value
090:
091: if (arg instanceof JType) {
092: JType targ = (JType) arg;
093: checkType(Class.class, rt);
094: if (m.getDefaultValue() != null) {
095: // check the default
096: if (targ.equals(targ.owner().ref(
097: (Class) m.getDefaultValue())))
098: return proxy; // defaulted
099: }
100: use.param(name, targ);
101: return proxy;
102: }
103:
104: // other Java built-in types
105: checkType(arg.getClass(), rt);
106: if (m.getDefaultValue() != null
107: && m.getDefaultValue().equals(arg))
108: // defaulted. no need to write out.
109: return proxy;
110:
111: if (arg instanceof String) {
112: use.param(name, (String) arg);
113: return proxy;
114: }
115: if (arg instanceof Boolean) {
116: use.param(name, (Boolean) arg);
117: return proxy;
118: }
119: if (arg instanceof Integer) {
120: use.param(name, (Integer) arg);
121: return proxy;
122: }
123: if (arg instanceof Class) {
124: use.param(name, (Class) arg);
125: return proxy;
126: }
127: if (arg instanceof Enum) {
128: use.param(name, (Enum) arg);
129: return proxy;
130: }
131:
132: throw new IllegalArgumentException(
133: "Unable to handle this method call "
134: + method.toString());
135: }
136:
137: private Object addArrayValue(Object proxy, String name,
138: Class itemType, Class expectedReturnType, Object arg) {
139: if (arrays == null)
140: arrays = new HashMap<String, JAnnotationArrayMember>();
141: JAnnotationArrayMember m = arrays.get(name);
142: if (m == null) {
143: m = use.paramArray(name);
144: arrays.put(name, m);
145: }
146:
147: // sub annotation
148: if (Annotation.class.isAssignableFrom(itemType)) {
149: Class<? extends Annotation> r = (Class<? extends Annotation>) itemType;
150: if (!JAnnotationWriter.class
151: .isAssignableFrom(expectedReturnType))
152: throw new IllegalArgumentException(
153: "Unexpected return type " + expectedReturnType);
154: return new TypedAnnotationWriter(r, expectedReturnType, m
155: .annotate(r)).createProxy();
156: }
157:
158: // primitive
159: if (arg instanceof JType) {
160: checkType(Class.class, itemType);
161: m.param((JType) arg);
162: return proxy;
163: }
164: checkType(arg.getClass(), itemType);
165: if (arg instanceof String) {
166: m.param((String) arg);
167: return proxy;
168: }
169: if (arg instanceof Boolean) {
170: m.param((Boolean) arg);
171: return proxy;
172: }
173: if (arg instanceof Integer) {
174: m.param((Integer) arg);
175: return proxy;
176: }
177: if (arg instanceof Class) {
178: m.param((Class) arg);
179: return proxy;
180: }
181: // TODO: enum constant. how should we handle it?
182:
183: throw new IllegalArgumentException(
184: "Unable to handle this method call ");
185: }
186:
187: /**
188: * Check if the type of the argument matches our expectation.
189: * If not, report an error.
190: */
191: private void checkType(Class actual, Class expected) {
192: if (expected == actual || expected.isAssignableFrom(actual))
193: return; // no problem
194:
195: if (expected == JCodeModel.boxToPrimitive.get(actual))
196: return; // no problem
197:
198: throw new IllegalArgumentException("Expected " + expected
199: + " but found " + actual);
200: }
201:
202: /**
203: * Creates a proxy and returns it.
204: */
205: private W createProxy() {
206: return (W) Proxy.newProxyInstance(writerType.getClassLoader(),
207: new Class[] { writerType }, this );
208: }
209:
210: /**
211: * Creates a new typed annotation writer.
212: */
213: static <W extends JAnnotationWriter<?>> W create(Class<W> w,
214: JAnnotatable annotatable) {
215: Class<? extends Annotation> a = findAnnotationType(w);
216: return (W) new TypedAnnotationWriter(a, w, annotatable
217: .annotate(a)).createProxy();
218: }
219:
220: private static Class<? extends Annotation> findAnnotationType(
221: Class clazz) {
222: for (Type t : clazz.getGenericInterfaces()) {
223: if (t instanceof ParameterizedType) {
224: ParameterizedType p = (ParameterizedType) t;
225: if (p.getRawType() == JAnnotationWriter.class)
226: return (Class<? extends Annotation>) p
227: .getActualTypeArguments()[0];
228: }
229: if (t instanceof Class) {
230: // recurisve search
231: Class<? extends Annotation> r = findAnnotationType((Class) t);
232: if (r != null)
233: return r;
234: }
235: }
236: return null;
237: }
238: }
|