3 import static org.springframework.util.ReflectionUtils.FieldCallback;
4 import static org.springframework.util.ReflectionUtils.FieldFilter;
5 import static org.springframework.util.ReflectionUtils.doWithFields;
6 import static org.springframework.util.ReflectionUtils.getField;
7 import static org.springframework.util.ReflectionUtils.makeAccessible;
9 import java.lang.reflect.Field;
10 import java.util.ArrayList;
11 import java.util.HashMap;
12 import java.util.List;
15 import org.springframework.core.annotation.AnnotationUtils;
16 import org.springframework.util.Assert;
18 import dst.ass2.di.annotation.Component;
19 import dst.ass2.di.annotation.ComponentId;
20 import dst.ass2.di.annotation.Inject;
23 * Contains some utilities for testing dependency injection.
25 public final class InjectionUtils {
26 private InjectionUtils() {
30 * Returns the component ID of the given object declared by a certain type.
32 * The ID must be declared by the given type.
33 * Inherited fields or fields of super classes are not checked.<br/>
34 * If {@code type} is {@code null}, the actual type of the object is used instead.
36 * @param component the object
37 * @param type the type of the object to retrieve (may be {@code null})
38 * @return the component ID or {@code null} if none was found
39 * @see Class#getDeclaredFields()
41 public static Long getId(Object component, Class<?> type) {
42 Assert.notNull(AnnotationUtils.findAnnotation(component.getClass(), Component.class), "Object is not annotated with @Component: " + component);
43 type = type != null ? type : component.getClass();
44 for (Field field : type.getDeclaredFields()) {
45 if (field.isAnnotationPresent(ComponentId.class)) {
46 makeAccessible(field);
47 return (Long) getField(field, component);
54 * Returns all component ID fields (including inherited and hidden) and their values of the given object.
56 * @param component the component to check
57 * @return the fields and their values
59 public static Map<Field, Long> getIdsOfHierarchy(Object component) {
60 Assert.notNull(AnnotationUtils.findAnnotation(component.getClass(), Component.class), "Object is not annotated with @Component: " + component);
61 ComponentIdCallback callback = new ComponentIdCallback(component);
62 doWithFields(component.getClass(), callback, new ComponentIdFilter());
67 * Traverses the given object graph and returns all component IDs.
69 * @param component the component to check
70 * @param map the map to store the IDs
71 * @return the component IDs of the objects
72 * @see #getIdsOfHierarchy(Object)
74 public static Map<Object, Map<Field, Long>> getIdsOfObjectGraph(final Object component, Map<Object, Map<Field, Long>> map) {
75 map = map != null ? map : new HashMap<Object, Map<Field, Long>>();
76 if (component != null && !map.containsKey(component)) {
77 final Map<Object, Map<Field, Long>> finalMap = map;
78 map.put(component, getIdsOfHierarchy(component));
79 doWithFields(component.getClass(), new FieldCallback() {
81 public void doWith(Field field) {
82 makeAccessible(field);
83 Object value = getField(field, component);
85 finalMap.putAll(getIdsOfObjectGraph(value, finalMap));
88 }, new InjectFilter());
94 * Returns a list of all component IDs retrieved by {@link #getIdsOfObjectGraph(Object, Map)}.<br/>
96 * @param component the component to check
97 * @return the component IDs
99 public static List<Long> getIds(Object component) {
100 Map<Object, Map<Field, Long>> map = getIdsOfObjectGraph(component, null);
101 List<Long> ids = new ArrayList<Long>();
102 for (Map.Entry<Object, Map<Field, Long>> entry : map.entrySet()) {
103 for (Map.Entry<Field, Long> id : entry.getValue().entrySet()) {
104 ids.add(id.getValue());
111 * Returns the names of all declared fields and their values of the given object declared by a certain type.
113 * The ID must be declared by the given type.
114 * Inherited fields or fields of super classes are not checked.<br/>
115 * If {@code type} is {@code null}, the actual type of the object is used instead.
117 * @param obj the object
118 * @param type the type of the object to retrieve (may be {@code null})
119 * @return all declared field names and the field values
121 public static Map<String, Object> getFields(Object obj, Class<?> type) {
122 type = type != null ? type : obj.getClass();
123 Map<String, Object> map = new HashMap<String, Object>();
124 for (Field field : type.getDeclaredFields()) {
125 makeAccessible(field);
126 map.put(field.getName(), getField(field, obj));
132 * Callback invoked on each component ID in the hierarchy.
134 private static class ComponentIdCallback implements FieldCallback {
135 protected final Map<Field, Long> ids = new HashMap<Field, Long>();
136 protected Object component;
138 private ComponentIdCallback(Object component) {
139 this.component = component;
143 public void doWith(Field field) throws IllegalAccessException {
144 makeAccessible(field);
145 ids.put(field, (Long) field.get(component));
151 * Callback used to filter component IDs.<br/>
152 * A component ID is a field of type {@link Long} annotated with {@link ComponentId @ComponentId}.
154 private static class ComponentIdFilter implements FieldFilter {
156 public boolean matches(Field field) {
157 return field.getType() == Long.class && field.isAnnotationPresent(ComponentId.class);
162 * Callback used to filter fields marked to be injected i.e., annotated with {@link Inject @Inject}.
164 private static class InjectFilter implements FieldFilter {
166 public boolean matches(Field field) {
167 return field.isAnnotationPresent(Inject.class);