1 /* |
1 /* |
2 * Copyright (c) 2003, 2016, Oracle and/or its affiliates. All rights reserved. |
2 * Copyright (c) 2003, 2019, Oracle and/or its affiliates. All rights reserved. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. |
4 * |
4 * |
5 * This code is free software; you can redistribute it and/or modify it |
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 |
6 * under the terms of the GNU General Public License version 2 only, as |
7 * published by the Free Software Foundation. Oracle designates this |
7 * published by the Free Software Foundation. Oracle designates this |
39 * |
39 * |
40 * @author Josh Bloch |
40 * @author Josh Bloch |
41 * @since 1.5 |
41 * @since 1.5 |
42 */ |
42 */ |
43 class AnnotationInvocationHandler implements InvocationHandler, Serializable { |
43 class AnnotationInvocationHandler implements InvocationHandler, Serializable { |
|
44 @java.io.Serial |
44 private static final long serialVersionUID = 6182022883658399397L; |
45 private static final long serialVersionUID = 6182022883658399397L; |
45 private final Class<? extends Annotation> type; |
46 private final Class<? extends Annotation> type; |
|
47 @SuppressWarnings("serial") // Not statically typed as Serializable |
46 private final Map<String, Object> memberValues; |
48 private final Map<String, Object> memberValues; |
47 |
49 |
48 AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { |
50 AnnotationInvocationHandler(Class<? extends Annotation> type, Map<String, Object> memberValues) { |
49 Class<?>[] superInterfaces = type.getInterfaces(); |
51 Class<?>[] superInterfaces = type.getInterfaces(); |
50 if (!type.isAnnotation() || |
52 if (!type.isAnnotation() || |
143 StringBuilder result = new StringBuilder(128); |
145 StringBuilder result = new StringBuilder(128); |
144 result.append('@'); |
146 result.append('@'); |
145 result.append(type.getName()); |
147 result.append(type.getName()); |
146 result.append('('); |
148 result.append('('); |
147 boolean firstMember = true; |
149 boolean firstMember = true; |
148 for (Map.Entry<String, Object> e : memberValues.entrySet()) { |
150 Set<Map.Entry<String, Object>> entries = memberValues.entrySet(); |
|
151 boolean loneValue = entries.size() == 1; |
|
152 for (Map.Entry<String, Object> e : entries) { |
149 if (firstMember) |
153 if (firstMember) |
150 firstMember = false; |
154 firstMember = false; |
151 else |
155 else |
152 result.append(", "); |
156 result.append(", "); |
153 |
157 |
154 result.append(e.getKey()); |
158 String key = e.getKey(); |
155 result.append('='); |
159 if (!loneValue || !"value".equals(key)) { |
|
160 result.append(key); |
|
161 result.append('='); |
|
162 } |
|
163 loneValue = false; |
156 result.append(memberValueToString(e.getValue())); |
164 result.append(memberValueToString(e.getValue())); |
157 } |
165 } |
158 result.append(')'); |
166 result.append(')'); |
159 return result.toString(); |
167 return result.toString(); |
160 } |
168 } |
176 return toSourceString((double) value); |
184 return toSourceString((double) value); |
177 else if (type == Float.class) |
185 else if (type == Float.class) |
178 return toSourceString((float) value); |
186 return toSourceString((float) value); |
179 else if (type == Long.class) |
187 else if (type == Long.class) |
180 return toSourceString((long) value); |
188 return toSourceString((long) value); |
|
189 else if (type == Byte.class) |
|
190 return toSourceString((byte) value); |
181 else |
191 else |
182 return value.toString(); |
192 return value.toString(); |
183 } else { |
193 } else { |
184 Stream<String> stringStream; |
194 Stream<String> stringStream; |
185 if (type == byte[].class) |
195 if (type == byte[].class) |
219 * Translates a Class value to a form suitable for use in the |
229 * Translates a Class value to a form suitable for use in the |
220 * string representation of an annotation. |
230 * string representation of an annotation. |
221 */ |
231 */ |
222 private static String toSourceString(Class<?> clazz) { |
232 private static String toSourceString(Class<?> clazz) { |
223 Class<?> finalComponent = clazz; |
233 Class<?> finalComponent = clazz; |
224 StringBuilder arrayBackets = new StringBuilder(); |
234 StringBuilder arrayBrackets = new StringBuilder(); |
225 |
235 |
226 while(finalComponent.isArray()) { |
236 while(finalComponent.isArray()) { |
227 finalComponent = finalComponent.getComponentType(); |
237 finalComponent = finalComponent.getComponentType(); |
228 arrayBackets.append("[]"); |
238 arrayBrackets.append("[]"); |
229 } |
239 } |
230 |
240 |
231 return finalComponent.getName() + arrayBackets.toString() + ".class" ; |
241 return finalComponent.getName() + arrayBrackets.toString() + ".class"; |
232 } |
242 } |
233 |
243 |
234 private static String toSourceString(float f) { |
244 private static String toSourceString(float f) { |
235 if (Float.isFinite(f)) |
245 if (Float.isFinite(f)) |
236 return Float.toString(f) + "f" ; |
246 return Float.toString(f) + "f" ; |
254 } |
264 } |
255 |
265 |
256 private static String toSourceString(char c) { |
266 private static String toSourceString(char c) { |
257 StringBuilder sb = new StringBuilder(4); |
267 StringBuilder sb = new StringBuilder(4); |
258 sb.append('\''); |
268 sb.append('\''); |
259 if (c == '\'') |
269 sb.append(quote(c)); |
260 sb.append("\\'"); |
270 return sb.append('\'') .toString(); |
261 else |
271 } |
262 sb.append(c); |
272 |
263 return sb.append('\'') |
273 /** |
264 .toString(); |
274 * Escapes a character if it has an escape sequence or is |
|
275 * non-printable ASCII. Leaves non-ASCII characters alone. |
|
276 */ |
|
277 private static String quote(char ch) { |
|
278 switch (ch) { |
|
279 case '\b': return "\\b"; |
|
280 case '\f': return "\\f"; |
|
281 case '\n': return "\\n"; |
|
282 case '\r': return "\\r"; |
|
283 case '\t': return "\\t"; |
|
284 case '\'': return "\\'"; |
|
285 case '\"': return "\\\""; |
|
286 case '\\': return "\\\\"; |
|
287 default: |
|
288 return (isPrintableAscii(ch)) |
|
289 ? String.valueOf(ch) |
|
290 : String.format("\\u%04x", (int) ch); |
|
291 } |
|
292 } |
|
293 |
|
294 /** |
|
295 * Is a character printable ASCII? |
|
296 */ |
|
297 private static boolean isPrintableAscii(char ch) { |
|
298 return ch >= ' ' && ch <= '~'; |
|
299 } |
|
300 |
|
301 private static String toSourceString(byte b) { |
|
302 return String.format("(byte)0x%02x", b); |
265 } |
303 } |
266 |
304 |
267 private static String toSourceString(long ell) { |
305 private static String toSourceString(long ell) { |
268 String str = String.valueOf(ell); |
306 return String.valueOf(ell) + "L"; |
269 return (ell < Integer.MIN_VALUE || ell > Integer.MAX_VALUE) |
|
270 ? (str + 'L') : str; |
|
271 } |
307 } |
272 |
308 |
273 /** |
309 /** |
274 * Return a string suitable for use in the string representation |
310 * Return a string suitable for use in the string representation |
275 * of an annotation. |
311 * of an annotation. |
276 */ |
312 */ |
277 private static String toSourceString(String s) { |
313 private static String toSourceString(String s) { |
278 StringBuilder sb = new StringBuilder(); |
314 StringBuilder sb = new StringBuilder(); |
279 sb.append('"'); |
315 sb.append('"'); |
280 // Escape embedded quote characters, if present, but don't do |
316 for (int i = 0; i < s.length(); i++) { |
281 // anything more heroic. |
317 sb.append(quote(s.charAt(i))); |
282 sb.append(s.replace("\"", "\\\"")); |
318 } |
283 sb.append('"'); |
319 sb.append('"'); |
284 return sb.toString(); |
320 return sb.toString(); |
285 } |
321 } |
286 |
322 |
287 private static Stream<String> convert(byte[] values) { |
323 private static Stream<String> convert(byte[] values) { |
288 List<String> list = new ArrayList<>(values.length); |
324 List<String> list = new ArrayList<>(values.length); |
289 for (byte b : values) |
325 for (byte b : values) |
290 list.add(Byte.toString(b)); |
326 list.add(toSourceString(b)); |
291 return list.stream(); |
327 return list.stream(); |
292 } |
328 } |
293 |
329 |
294 private static Stream<String> convert(char[] values) { |
330 private static Stream<String> convert(char[] values) { |
295 List<String> list = new ArrayList<>(values.length); |
331 List<String> list = new ArrayList<>(values.length); |
567 if (type == boolean[].class) |
603 if (type == boolean[].class) |
568 return Arrays.hashCode((boolean[]) value); |
604 return Arrays.hashCode((boolean[]) value); |
569 return Arrays.hashCode((Object[]) value); |
605 return Arrays.hashCode((Object[]) value); |
570 } |
606 } |
571 |
607 |
|
608 @java.io.Serial |
572 private void readObject(java.io.ObjectInputStream s) |
609 private void readObject(java.io.ObjectInputStream s) |
573 throws java.io.IOException, ClassNotFoundException { |
610 throws java.io.IOException, ClassNotFoundException { |
574 ObjectInputStream.GetField fields = s.readFields(); |
611 ObjectInputStream.GetField fields = s.readFields(); |
575 |
612 |
576 @SuppressWarnings("unchecked") |
613 @SuppressWarnings("unchecked") |