001: /*
002: * Copyright 2007 Google Inc.
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License"); you may not
005: * use this file except in compliance with the License. You may obtain a copy of
006: * 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, WITHOUT
012: * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
013: * License for the specific language governing permissions and limitations under
014: * the License.
015: */
016: package com.google.gwt.dev.jdt;
017:
018: import com.google.gwt.core.ext.typeinfo.JAnnotationMethod;
019: import com.google.gwt.core.ext.typeinfo.JClassType;
020: import com.google.gwt.core.ext.typeinfo.JMethod;
021: import com.google.gwt.core.ext.typeinfo.JType;
022:
023: import java.lang.annotation.Annotation;
024: import java.lang.reflect.InvocationHandler;
025: import java.lang.reflect.InvocationTargetException;
026: import java.lang.reflect.Method;
027: import java.lang.reflect.Proxy;
028: import java.util.Arrays;
029: import java.util.Map;
030:
031: /**
032: * Creates proxies for annotation objects that...
033: */
034: class AnnotationProxyFactory {
035: /**
036: * {@link InvocationHandler} implementation used by all
037: * {@link java.lang.annotation.Annotation Annotation} proxies created by the
038: * {@link TypeOracle}.
039: */
040: private static class AnnotationProxyInvocationHandler implements
041: InvocationHandler {
042: /**
043: * The resolved class of this annotation.
044: */
045: private Class<? extends Annotation> annotationClass;
046:
047: private final JClassType annotationType;
048:
049: /**
050: * Maps method names onto values. Note that methods on annotation types
051: * cannot be overloaded because they have zero arguments.
052: */
053: private final Map<String, Object> identifierToValue;
054:
055: /**
056: * A reference to the enclosing proxy object.
057: */
058: private Annotation proxy;
059:
060: public AnnotationProxyInvocationHandler(
061: JClassType annotationType,
062: Map<String, Object> identifierToValue,
063: Class<? extends Annotation> annotationClass) {
064: this .annotationType = annotationType;
065: this .identifierToValue = identifierToValue;
066: this .annotationClass = annotationClass;
067: }
068:
069: @Override
070: public boolean equals(Object other) {
071: if (proxy == other) {
072: return true;
073: }
074: if (!annotationClass.isInstance(other)) {
075: return false;
076: }
077: try {
078: for (Method method : annotationClass
079: .getDeclaredMethods()) {
080: Object myVal = method.invoke(proxy);
081: Object otherVal = method.invoke(other);
082:
083: if (myVal instanceof Object[]) {
084: if (!Arrays.equals((Object[]) myVal,
085: (Object[]) otherVal)) {
086: return false;
087: }
088: } else if (myVal instanceof boolean[]) {
089: if (!Arrays.equals((boolean[]) myVal,
090: (boolean[]) otherVal)) {
091: return false;
092: }
093: } else if (myVal instanceof byte[]) {
094: if (!Arrays.equals((byte[]) myVal,
095: (byte[]) otherVal)) {
096: return false;
097: }
098: } else if (myVal instanceof char[]) {
099: if (!Arrays.equals((char[]) myVal,
100: (char[]) otherVal)) {
101: return false;
102: }
103: } else if (myVal instanceof short[]) {
104: if (!Arrays.equals((short[]) myVal,
105: (short[]) otherVal)) {
106: return false;
107: }
108: } else if (myVal instanceof int[]) {
109: if (!Arrays.equals((int[]) myVal,
110: (int[]) otherVal)) {
111: return false;
112: }
113: } else if (myVal instanceof long[]) {
114: if (!Arrays.equals((long[]) myVal,
115: (long[]) otherVal)) {
116: return false;
117: }
118: } else if (myVal instanceof float[]) {
119: if (!Arrays.equals((float[]) myVal,
120: (float[]) otherVal)) {
121: return false;
122: }
123: } else if (myVal instanceof double[]) {
124: if (!Arrays.equals((double[]) myVal,
125: (double[]) otherVal)) {
126: return false;
127: }
128: } else {
129: if (!myVal.equals(otherVal)) {
130: return false;
131: }
132: }
133: }
134: } catch (IllegalArgumentException e) {
135: throw new RuntimeException(e);
136: } catch (IllegalAccessException e) {
137: throw new RuntimeException(e);
138: } catch (InvocationTargetException e) {
139: throw new RuntimeException(e.getTargetException());
140: }
141: return true;
142: }
143:
144: @Override
145: public int hashCode() {
146: int sum = 0;
147: try {
148: for (Method method : annotationClass
149: .getDeclaredMethods()) {
150: Object myVal = method.invoke(proxy);
151: int memberHash;
152: if (myVal instanceof Object[]) {
153: memberHash = Arrays.hashCode((Object[]) myVal);
154: } else if (myVal instanceof boolean[]) {
155: memberHash = Arrays.hashCode((boolean[]) myVal);
156: } else if (myVal instanceof byte[]) {
157: memberHash = Arrays.hashCode((byte[]) myVal);
158: } else if (myVal instanceof char[]) {
159: memberHash = Arrays.hashCode((char[]) myVal);
160: } else if (myVal instanceof short[]) {
161: memberHash = Arrays.hashCode((short[]) myVal);
162: } else if (myVal instanceof int[]) {
163: memberHash = Arrays.hashCode((int[]) myVal);
164: } else if (myVal instanceof long[]) {
165: memberHash = Arrays.hashCode((long[]) myVal);
166: } else if (myVal instanceof float[]) {
167: memberHash = Arrays.hashCode((float[]) myVal);
168: } else if (myVal instanceof double[]) {
169: memberHash = Arrays.hashCode((double[]) myVal);
170: } else {
171: memberHash = myVal.hashCode();
172: }
173: // See doc for Annotation.hashCode.
174: memberHash ^= 127 * method.getName().hashCode();
175: sum += memberHash;
176: }
177: } catch (IllegalArgumentException e) {
178: throw new RuntimeException(e);
179: } catch (IllegalAccessException e) {
180: throw new RuntimeException(e);
181: } catch (InvocationTargetException e) {
182: throw new RuntimeException(e.getTargetException());
183: }
184: return sum;
185: }
186:
187: /*
188: * (non-Javadoc)
189: *
190: * @see java.lang.reflect.InvocationHandler#invoke(java.lang.Object,
191: * java.lang.reflect.Method, java.lang.Object[])
192: */
193: public Object invoke(Object proxy, Method method, Object[] args)
194: throws Throwable {
195:
196: String name = method.getName();
197:
198: // See if the value was explicitly declared
199: Object value = identifierToValue.get(name);
200: if (value != null) {
201: assert (method.getReturnType().isAssignableFrom(value
202: .getClass()));
203: return value;
204: }
205:
206: // Try to find a method on the interface.
207: JMethod jMethod = annotationType.findMethod(name,
208: new JType[0]);
209: if (jMethod != null) {
210: JAnnotationMethod annotationMethod = jMethod
211: .isAnnotationMethod();
212: assert (annotationMethod != null);
213: return annotationMethod.getDefaultValue();
214: }
215:
216: if (method.getDeclaringClass() == Annotation.class
217: && "annotationType".equals(method.getName())) {
218: return annotationClass;
219: }
220:
221: /*
222: * Maybe it's an Object method, just delegate to myself.
223: */
224: return method.invoke(this , args);
225: }
226:
227: public void setProxy(Annotation proxy) {
228: this .proxy = proxy;
229: }
230:
231: @Override
232: public String toString() {
233: final StringBuilder msg = new StringBuilder();
234: msg.append('@').append(
235: annotationType.getQualifiedSourceName())
236: .append('(');
237: boolean first = true;
238: try {
239: for (Method method : annotationClass
240: .getDeclaredMethods()) {
241: if (first) {
242: first = false;
243: } else {
244: msg.append(", ");
245: }
246: msg.append(method.getName()).append('=');
247: Object myVal = method.invoke(proxy);
248: if (myVal.getClass().isArray()) {
249: msg.append(java.util.Arrays
250: .deepToString((Object[]) myVal));
251: } else {
252: msg.append(myVal);
253: }
254: }
255: } catch (IllegalArgumentException e) {
256: throw new RuntimeException(e);
257: } catch (IllegalAccessException e) {
258: throw new RuntimeException(e);
259: } catch (InvocationTargetException e) {
260: throw new RuntimeException(e.getTargetException());
261: }
262: msg.append(')');
263: return msg.toString();
264: }
265: }
266:
267: public static Annotation create(
268: Class<? extends Annotation> annotationClass,
269: JClassType annotationType,
270: Map<String, Object> identifierToValue) {
271: AnnotationProxyInvocationHandler annotationInvocationHandler = new AnnotationProxyInvocationHandler(
272: annotationType, identifierToValue, annotationClass);
273: Annotation proxy = (Annotation) Proxy.newProxyInstance(
274: AnnotationProxyFactory.class.getClassLoader(),
275: new Class<?>[] { java.lang.annotation.Annotation.class,
276: annotationClass }, annotationInvocationHandler);
277: annotationInvocationHandler.setProxy(proxy);
278: return proxy;
279: }
280: }
|