Thu, 12 Oct 2017 19:44:07 +0800
merge
1 /*
2 * Copyright (c) 1997, 2010, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
26 package com.sun.codemodel.internal;
28 import java.lang.reflect.InvocationHandler;
29 import java.lang.reflect.Method;
30 import java.lang.reflect.Proxy;
31 import java.lang.reflect.Type;
32 import java.lang.reflect.ParameterizedType;
33 import java.lang.reflect.InvocationTargetException;
34 import java.lang.annotation.Annotation;
35 import java.util.Map;
36 import java.util.HashMap;
38 /**
39 * Dynamically implements the typed annotation writer interfaces.
40 *
41 * @author Kohsuke Kawaguchi
42 */
43 class TypedAnnotationWriter<A extends Annotation,W extends JAnnotationWriter<A>>
44 implements InvocationHandler, JAnnotationWriter<A> {
45 /**
46 * This is what we are writing to.
47 */
48 private final JAnnotationUse use;
50 /**
51 * The annotation that we are writing.
52 */
53 private final Class<A> annotation;
55 /**
56 * The type of the writer.
57 */
58 private final Class<W> writerType;
60 /**
61 * Keeps track of writers for array members.
62 * Lazily created.
63 */
64 private Map<String,JAnnotationArrayMember> arrays;
66 public TypedAnnotationWriter(Class<A> annotation, Class<W> writer, JAnnotationUse use) {
67 this.annotation = annotation;
68 this.writerType = writer;
69 this.use = use;
70 }
72 public JAnnotationUse getAnnotationUse() {
73 return use;
74 }
76 public Class<A> getAnnotationType() {
77 return annotation;
78 }
80 @SuppressWarnings("unchecked")
81 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
83 if(method.getDeclaringClass()==JAnnotationWriter.class) {
84 try {
85 return method.invoke(this,args);
86 } catch (InvocationTargetException e) {
87 throw e.getTargetException();
88 }
89 }
91 String name = method.getName();
92 Object arg=null;
93 if(args!=null && args.length>0)
94 arg = args[0];
96 // check how it's defined on the annotation
97 Method m = annotation.getDeclaredMethod(name);
98 Class<?> rt = m.getReturnType();
100 // array value
101 if(rt.isArray()) {
102 return addArrayValue(proxy,name,rt.getComponentType(),method.getReturnType(),arg);
103 }
105 // sub annotation
106 if(Annotation.class.isAssignableFrom(rt)) {
107 Class<? extends Annotation> r = (Class<? extends Annotation>)rt;
108 return new TypedAnnotationWriter(
109 r,method.getReturnType(),use.annotationParam(name,r)).createProxy();
110 }
112 // scalar value
114 if(arg instanceof JType) {
115 JType targ = (JType) arg;
116 checkType(Class.class,rt);
117 if(m.getDefaultValue()!=null) {
118 // check the default
119 if(targ.equals(targ.owner().ref((Class)m.getDefaultValue())))
120 return proxy; // defaulted
121 }
122 use.param(name,targ);
123 return proxy;
124 }
126 // other Java built-in types
127 checkType(arg.getClass(),rt);
128 if(m.getDefaultValue()!=null && m.getDefaultValue().equals(arg))
129 // defaulted. no need to write out.
130 return proxy;
132 if(arg instanceof String) {
133 use.param(name,(String)arg);
134 return proxy;
135 }
136 if(arg instanceof Boolean) {
137 use.param(name,(Boolean)arg);
138 return proxy;
139 }
140 if(arg instanceof Integer) {
141 use.param(name,(Integer)arg);
142 return proxy;
143 }
144 if(arg instanceof Class) {
145 use.param(name,(Class)arg);
146 return proxy;
147 }
148 if(arg instanceof Enum) {
149 use.param(name,(Enum)arg);
150 return proxy;
151 }
153 throw new IllegalArgumentException("Unable to handle this method call "+method.toString());
154 }
156 @SuppressWarnings("unchecked")
157 private Object addArrayValue(Object proxy,String name, Class itemType, Class expectedReturnType, Object arg) {
158 if(arrays==null)
159 arrays = new HashMap<String,JAnnotationArrayMember>();
160 JAnnotationArrayMember m = arrays.get(name);
161 if(m==null) {
162 m = use.paramArray(name);
163 arrays.put(name,m);
164 }
166 // sub annotation
167 if(Annotation.class.isAssignableFrom(itemType)) {
168 Class<? extends Annotation> r = (Class<? extends Annotation>)itemType;
169 if(!JAnnotationWriter.class.isAssignableFrom(expectedReturnType))
170 throw new IllegalArgumentException("Unexpected return type "+expectedReturnType);
171 return new TypedAnnotationWriter(r,expectedReturnType,m.annotate(r)).createProxy();
172 }
174 // primitive
175 if(arg instanceof JType) {
176 checkType(Class.class,itemType);
177 m.param((JType)arg);
178 return proxy;
179 }
180 checkType(arg.getClass(),itemType);
181 if(arg instanceof String) {
182 m.param((String)arg);
183 return proxy;
184 }
185 if(arg instanceof Boolean) {
186 m.param((Boolean)arg);
187 return proxy;
188 }
189 if(arg instanceof Integer) {
190 m.param((Integer)arg);
191 return proxy;
192 }
193 if(arg instanceof Class) {
194 m.param((Class)arg);
195 return proxy;
196 }
197 // TODO: enum constant. how should we handle it?
199 throw new IllegalArgumentException("Unable to handle this method call ");
200 }
203 /**
204 * Check if the type of the argument matches our expectation.
205 * If not, report an error.
206 */
207 private void checkType(Class<?> actual, Class<?> expected) {
208 if(expected==actual || expected.isAssignableFrom(actual))
209 return; // no problem
211 if( expected==JCodeModel.boxToPrimitive.get(actual) )
212 return; // no problem
214 throw new IllegalArgumentException("Expected "+expected+" but found "+actual);
215 }
217 /**
218 * Creates a proxy and returns it.
219 */
220 @SuppressWarnings("unchecked")
221 private W createProxy() {
222 return (W)Proxy.newProxyInstance(
223 SecureLoader.getClassClassLoader(writerType),new Class[]{writerType},this);
224 }
226 /**
227 * Creates a new typed annotation writer.
228 */
229 @SuppressWarnings("unchecked")
230 static <W extends JAnnotationWriter<?>> W create(Class<W> w, JAnnotatable annotatable) {
231 Class<? extends Annotation> a = findAnnotationType(w);
232 return (W)new TypedAnnotationWriter(a,w,annotatable.annotate(a)).createProxy();
233 }
235 private static Class<? extends Annotation> findAnnotationType(Class<?> clazz) {
236 for( Type t : clazz.getGenericInterfaces()) {
237 if(t instanceof ParameterizedType) {
238 ParameterizedType p = (ParameterizedType) t;
239 if(p.getRawType()==JAnnotationWriter.class)
240 return (Class<? extends Annotation>)p.getActualTypeArguments()[0];
241 }
242 if(t instanceof Class<?>) {
243 // recursive search
244 Class<? extends Annotation> r = findAnnotationType((Class<?>)t);
245 if(r!=null) return r;
246 }
247 }
248 return null;
249 }
250 }