Skip to content

Commit 842004d

Browse files
Matt WilsonSquare
authored andcommitted
Recursivly visit annotations.
1 parent efb1073 commit 842004d

File tree

2 files changed

+156
-17
lines changed

2 files changed

+156
-17
lines changed

src/org/jruby/util/CodegenUtils.java

Lines changed: 45 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -237,25 +237,38 @@ public static void visitAnnotation(AnnotationVisitor av, Class<? extends Annotat
237237
}
238238

239239
public static void visitAnnotationFields(AnnotationVisitor visitor, Map<String, Object> fields) {
240+
try {
240241
for (Map.Entry<String, Object> fieldEntry : fields.entrySet()) {
241242
Object value = fieldEntry.getValue();
242-
if (value.getClass().isArray()) {
243-
Object[] values = (Object[]) value;
244-
AnnotationVisitor arrayV = visitor.visitArray(fieldEntry.getKey());
245-
for (int i = 0; i < values.length; i++) {
246-
Map<String, Object> map = new HashMap<String, Object>();
247-
map.put(null, values[i]);
248-
visitAnnotationFields(arrayV, map);
249-
}
250-
arrayV.visitEnd();
251-
} else if (value.getClass().isEnum()) {
252-
visitor.visitEnum(fieldEntry.getKey(), ci(value.getClass()), value.toString());
253-
} else if (value instanceof Class) {
254-
visitor.visit(fieldEntry.getKey(), Type.getType((Class) value));
255-
} else {
256-
visitor.visit(fieldEntry.getKey(), value);
243+
if (value instanceof Map) {
244+
Map<Class, Map<String, Object>> nestedAnnotationMap = (Map<Class, Map<String, Object>>) value;
245+
Map<String, Object> nestedFields = new HashMap<String, Object>();
246+
247+
for (Map.Entry<Class, Map<String, Object>> nestedAnnotation : nestedAnnotationMap.entrySet()) {
248+
AnnotationVisitor annotationV = visitor.visitAnnotation(fieldEntry.getKey(), Type.getType(nestedAnnotation.getKey()).getDescriptor());
249+
visitAnnotationFields(annotationV, nestedAnnotation.getValue());
250+
annotationV.visitEnd();
251+
}
252+
} else if (value.getClass().isArray()) {
253+
Object[] values = (Object[]) value;
254+
AnnotationVisitor arrayV = visitor.visitArray(fieldEntry.getKey());
255+
for (int i = 0; i < values.length; i++) {
256+
Map<String, Object> map = new HashMap<String, Object>();
257+
map.put(null, values[i]);
258+
visitAnnotationFields(arrayV, map);
257259
}
260+
arrayV.visitEnd();
261+
} else if (value.getClass().isEnum()) {
262+
visitor.visitEnum(fieldEntry.getKey(), ci(value.getClass()), value.toString());
263+
} else if (value instanceof Class) {
264+
visitor.visit(fieldEntry.getKey(), Type.getType((Class) value));
265+
} else {
266+
visitor.visit(fieldEntry.getKey(), value);
267+
}
258268
}
269+
} catch(ClassCastException e) {
270+
throw new InvalidAnnotationDescriptorException("Fields " + fields + " did not match annotation format. See CodegenUtils#visitAnnotationFields for format", e);
271+
}
259272
}
260273

261274
public static Class getBoxType(Class type) {
@@ -281,4 +294,21 @@ public static Class getBoxType(Class type) {
281294
throw new RuntimeException("Not a native type: " + type);
282295
}
283296
}
297+
298+
public static class InvalidAnnotationDescriptorException extends RuntimeException {
299+
public InvalidAnnotationDescriptorException() {
300+
}
301+
302+
public InvalidAnnotationDescriptorException(String s) {
303+
super(s);
304+
}
305+
306+
public InvalidAnnotationDescriptorException(String s, Throwable throwable) {
307+
super(s, throwable);
308+
}
309+
310+
public InvalidAnnotationDescriptorException(Throwable throwable) {
311+
super(throwable);
312+
}
313+
}
284314
}

test/org/jruby/test/TestCodegenUtils.java

Lines changed: 111 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -131,8 +131,78 @@ public void testvisitAnnotationFields_whenArrayOfClasses_visitsArrayAndEachEleme
131131
assertEquals(expectedEventList, logger.getEventList());
132132
}
133133

134-
public void testvisitAnnotationFields_whenArrayOfMixedTypes_visitsArrayAndEachElementAsAppropriate() {
134+
public void testvisitAnnotationFields_whenArrayOfMixedTypes_visitsArrayAndEachElementAsAppropriate() {
135+
Class<?>[] classes = {SimpleClass.class, Object.class};
136+
fields.put("myClass", classes);
137+
SimpleEnum[] simpleEnums = {SimpleEnum.FirstValue, SimpleEnum.SecondValue};
138+
fields.put("myEnum", simpleEnums);
139+
String[] strings = {"hello", "world"};
140+
fields.put("string", strings);
141+
142+
CodegenUtils.visitAnnotationFields(logger, fields);
143+
144+
assertEquals(12, logger.getEventList().size());
145+
List<Event> expectedEventList = Arrays.asList(
146+
new VisitArrayEvent("myClass"),
147+
new VisitEvent(null, Type.getType(SimpleClass.class)),
148+
new VisitEvent(null, Type.getType(Object.class)),
149+
new VisitEndEvent(),
150+
new VisitArrayEvent("myEnum"),
151+
new VisitEnumEvent(null, ci(SimpleEnum.class), SimpleEnum.FirstValue.name()),
152+
new VisitEnumEvent(null, ci(SimpleEnum.class), SimpleEnum.SecondValue.name()),
153+
new VisitEndEvent(),
154+
new VisitArrayEvent("string"),
155+
new VisitEvent(null, "hello"),
156+
new VisitEvent(null, "world"),
157+
new VisitEndEvent());
158+
159+
assertEquals(expectedEventList, logger.getEventList());
160+
}
161+
162+
private @interface NestedInterface {
163+
SimpleEnum myEnum();
164+
}
165+
166+
private @interface SimpleInterface {
167+
NestedInterface value();
168+
}
169+
public void testvisitAnnotationFields_whenNestedAnnotations_visitsAnnotationsRecursivly() {
170+
Map<String, Object> nestedInterfaceFields = new LinkedHashMap<String, Object>();
171+
nestedInterfaceFields.put("myEnum", SimpleEnum.SecondValue);
172+
Map<Class, Map<String,Object>> nestedInterfaceStructure = new LinkedHashMap<Class, Map<String, Object>>();
173+
174+
nestedInterfaceStructure.put(NestedInterface.class, nestedInterfaceFields);
175+
176+
fields.put("value", nestedInterfaceStructure);
177+
178+
CodegenUtils.visitAnnotationFields(logger, fields);
179+
180+
assertEquals(3, logger.getEventList().size());
181+
List<Event> expectedEventList = Arrays.asList(
182+
new VisitAnnotationEvent("value", Type.getType(NestedInterface.class).getDescriptor()),
183+
new VisitEnumEvent("myEnum", ci(SimpleEnum.class), SimpleEnum.SecondValue.name()),
184+
new VisitEndEvent());
185+
186+
assertEquals(expectedEventList, logger.getEventList());
187+
}
188+
189+
190+
public void testvisitAnnotationFields_whenSuppliedInvalidNestedAnnotationMap_throwsError() {
191+
Exception thrown = null;
135192

193+
Map<Class, Object> nestedInterfaceStructure = new LinkedHashMap<Class, Object>();
194+
nestedInterfaceStructure.put(NestedInterface.class, SimpleEnum.SecondValue.name());
195+
196+
fields.put("value", nestedInterfaceStructure);
197+
198+
try {
199+
CodegenUtils.visitAnnotationFields(logger, fields);
200+
} catch(RuntimeException e) {
201+
thrown = e;
202+
}
203+
204+
assertNotNull(thrown);
205+
assertEquals(CodegenUtils.InvalidAnnotationDescriptorException.class, thrown.getClass());
136206
}
137207

138208
public void testvisitAnnotationFields_whenArrayOfMixedTypes_visitsEachTypeAppropriately() {
@@ -325,6 +395,44 @@ public String toString() {
325395
}
326396
}
327397

398+
private static class VisitAnnotationEvent implements Event {
399+
private final String key;
400+
private final String value;
401+
402+
public VisitAnnotationEvent(String key, String value) {
403+
this.key = key;
404+
this.value = value;
405+
}
406+
407+
@Override
408+
public boolean equals(Object o) {
409+
if (this == o) return true;
410+
if (o == null || getClass() != o.getClass()) return false;
411+
412+
VisitAnnotationEvent that = (VisitAnnotationEvent) o;
413+
414+
if (key != null ? !key.equals(that.key) : that.key != null) return false;
415+
if (value != null ? !value.equals(that.value) : that.value != null) return false;
416+
417+
return true;
418+
}
419+
420+
@Override
421+
public int hashCode() {
422+
int result = key != null ? key.hashCode() : 0;
423+
result = 31 * result + (value != null ? value.hashCode() : 0);
424+
return result;
425+
}
426+
427+
@Override
428+
public String toString() {
429+
return "VisitAnnotationEvent{" +
430+
"key='" + key + '\'' +
431+
", value='" + value + '\'' +
432+
'}';
433+
}
434+
}
435+
328436
private static class AnnotationVisitorLogger extends AnnotationVisitor {
329437
private final List<Event> eventList;
330438

@@ -342,7 +450,8 @@ public void visitEnum(String s, String s2, String s3) {
342450
}
343451

344452
public AnnotationVisitor visitAnnotation(String s, String s2) {
345-
return null;
453+
eventList.add(new VisitAnnotationEvent(s, s2));
454+
return this;
346455
}
347456

348457
public AnnotationVisitor visitArray(String s) {

0 commit comments

Comments
 (0)