]> git.somenet.org - pub/jan/dst18.git/blob - ass2-di/src/main/java/dst/ass2/di/impl/InjectionControllerImpl.java
[2.2.2] Transparent DI works.
[pub/jan/dst18.git] / ass2-di / src / main / java / dst / ass2 / di / impl / InjectionControllerImpl.java
1 package dst.ass2.di.impl;
2
3 import dst.ass2.di.IInjectionController;
4 import dst.ass2.di.InjectionException;
5 import dst.ass2.di.annotation.Component;
6 import dst.ass2.di.annotation.ComponentId;
7 import dst.ass2.di.annotation.Inject;
8 import dst.ass2.di.annotation.Scope;
9
10 import java.lang.reflect.Field;
11 import java.lang.reflect.InvocationTargetException;
12 import java.util.Map;
13 import java.util.concurrent.ConcurrentHashMap;
14 import java.util.concurrent.atomic.AtomicLong;
15
16 public class InjectionControllerImpl implements IInjectionController {
17     private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>();
18     private final AtomicLong ids = new AtomicLong(0L);
19     private final boolean isTransparent;
20
21     public InjectionControllerImpl(boolean b) {
22         isTransparent = b;
23     }
24
25     private void initializeClass(Object obj, Class<?> clazz, Long id) throws IllegalAccessException {
26         if (clazz == null || !clazz.isAnnotationPresent(Component.class)) {
27             return; // nothing to be done here.
28         }
29
30         boolean idPresent = false;
31         for (Field f : clazz.getDeclaredFields()) {
32
33             // handle @ComponentID
34             if (f.isAnnotationPresent(ComponentId.class)) {
35                 idPresent = true;
36                 if (f.getType() != Long.class) {
37                     throw new InjectionException(f.getName() + " is of wrong type");
38                 }
39
40                 boolean accessible = f.isAccessible();
41                 f.setAccessible(true);
42                 if (id == null) {
43                     id = ids.getAndIncrement();
44                 }
45                 f.set(obj, id);
46                 f.setAccessible(accessible);
47             }
48
49             // handle @Inject
50             if (f.isAnnotationPresent(Inject.class)) {
51                 Class<?> fieldType = f.getType();
52                 if (f.getAnnotation(Inject.class).specificType() != Void.class) {
53                     fieldType = f.getAnnotation(Inject.class).specificType();
54                 }
55                 if (!fieldType.isAnnotationPresent(Component.class)) {
56                     continue;
57                 }
58
59                 Object fieldObject = singletons.get(fieldType);
60
61                 if (fieldObject == null) {
62                     try {
63                         try {
64                             fieldObject = fieldType.getConstructor(Inject.class).newInstance(f.getAnnotation(Inject.class));
65                         } catch (InvocationTargetException | NoSuchMethodException e) {
66                             fieldObject = fieldType.newInstance();
67                         }
68                     } catch (InstantiationException e) {
69                         e.printStackTrace();
70                     }
71                     // only do this in standalone mode.
72                     if (!isTransparent) {
73                         initialize(fieldObject);
74                     }
75                 }
76
77                 boolean accessible = f.isAccessible();
78                 f.setAccessible(true);
79                 try {
80                     f.set(obj, fieldObject);
81                 } catch (IllegalAccessException | IllegalArgumentException e) {
82                     if (f.getAnnotation(Inject.class).required()) {
83                         throw new InjectionException(e);
84                     }
85                 }
86                 f.setAccessible(accessible);
87             }
88         }
89
90         if (!idPresent) {
91             throw new InjectionException(clazz + " has no @ComponentID");
92         }
93
94         // only do this in standalone mode.
95         if (!isTransparent) initializeClass(obj, clazz.getSuperclass(), id);
96     }
97
98
99     @Override
100     public void initialize(Object obj) throws InjectionException {
101         if (!obj.getClass().isAnnotationPresent(Component.class)) {
102             throw new InjectionException(obj.getClass() + " isn't annotated with: @Component");
103         }
104         if (singletons.get(obj.getClass()) != null) {
105             throw new InjectionException(obj.getClass() + " singleton already initialized");
106         }
107
108         try {
109             initializeClass(obj, obj.getClass(), null);
110         } catch (IllegalAccessException e) {
111             throw new InjectionException(e);
112         }
113
114         // add singleton to singleton map, if singleton.
115         if (obj.getClass().getAnnotation(Component.class).scope().equals(Scope.SINGLETON)) {
116             singletons.put(obj.getClass(), obj);
117         }
118     }
119
120     @Override
121     public <T> T getSingletonInstance(Class<T> clazz) throws InjectionException {
122         if (!clazz.isAnnotationPresent(Component.class)) {
123             throw new InjectionException(clazz + " isn't annotated with: @Component");
124         }
125         if (clazz.getAnnotation(Component.class).scope().equals(Scope.PROTOTYPE)) {
126             throw new InjectionException(clazz + " is not a SINGLETON");
127         }
128
129         T single = (T) singletons.get(clazz);
130         if (single == null) {
131             try {
132                 single = (T) clazz.newInstance();
133             } catch (InstantiationException | IllegalAccessException e) {
134                 e.printStackTrace();
135             }
136
137             // only do this in standalone mode.
138             if (!isTransparent) {
139                 initialize(single);
140             }
141         }
142         return single;
143     }
144 }