--- /dev/null
+package dst.ass2.di.impl;
+
+import dst.ass2.di.IInjectionController;
+import dst.ass2.di.InjectionException;
+import dst.ass2.di.annotation.Component;
+import dst.ass2.di.annotation.ComponentId;
+import dst.ass2.di.annotation.Inject;
+import dst.ass2.di.annotation.Scope;
+
+import java.lang.reflect.Field;
+import java.lang.reflect.InvocationTargetException;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
+import java.util.concurrent.atomic.AtomicLong;
+
+public class InjectionControllerImpl implements IInjectionController {
+ private final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>();
+ private final AtomicLong ids = new AtomicLong(0L);
+
+
+ private void initializeClass(Object obj, Class<?> clazz, Long id) throws IllegalAccessException {
+ if (clazz == null || !clazz.isAnnotationPresent(Component.class)) {
+ return; // nothing to be done here.
+ }
+
+ boolean idPresent = false;
+ for (Field f : clazz.getDeclaredFields()) {
+
+ // handle @ComponentID
+ if (f.isAnnotationPresent(ComponentId.class)) {
+ idPresent = true;
+ if (f.getType() != Long.class) {
+ throw new InjectionException(f.getName() + " is of wrong type");
+ }
+
+ boolean accessible = f.isAccessible();
+ f.setAccessible(true);
+ if (id == null) {
+ id = ids.getAndIncrement();
+ }
+ f.set(obj, id);
+ f.setAccessible(accessible);
+ }
+
+ // handle @Inject
+ if (f.isAnnotationPresent(Inject.class)) {
+ Class<?> fieldType = f.getType();
+ if (f.getAnnotation(Inject.class).specificType() != Void.class) {
+ fieldType = f.getAnnotation(Inject.class).specificType();
+ }
+ if (!fieldType.isAnnotationPresent(Component.class)) {
+ continue;
+ }
+
+ Object fieldObject = singletons.get(fieldType);
+
+ if (fieldObject == null) {
+ try {
+ try {
+ fieldObject = fieldType.getConstructor(Inject.class).newInstance(f.getAnnotation(Inject.class));
+ } catch (InvocationTargetException | NoSuchMethodException e) {
+ fieldObject = fieldType.newInstance();
+ }
+ } catch (InstantiationException e) {
+ e.printStackTrace();
+ }
+ initialize(fieldObject);
+ }
+
+ boolean accessible = f.isAccessible();
+ f.setAccessible(true);
+ try {
+ f.set(obj, fieldObject);
+ } catch (IllegalAccessException | IllegalArgumentException e) {
+ if (f.getAnnotation(Inject.class).required()) {
+ throw new InjectionException(e);
+ }
+ }
+ f.setAccessible(accessible);
+ }
+ }
+
+ if (!idPresent) {
+ throw new InjectionException(clazz + " has no @ComponentID");
+ }
+
+ initializeClass(obj, clazz.getSuperclass(), id);
+ }
+
+
+ @Override
+ public void initialize(Object obj) throws InjectionException {
+ if (!obj.getClass().isAnnotationPresent(Component.class)) {
+ throw new InjectionException(obj.getClass() + " isn't annotated with: @Component");
+ }
+ if (singletons.get(obj.getClass()) != null) {
+ throw new InjectionException(obj.getClass() + " singleton already initialized");
+ }
+
+ try {
+ initializeClass(obj, obj.getClass(), null);
+ } catch (IllegalAccessException e) {
+ throw new InjectionException(e);
+ }
+
+ // add singleton to singleton map, if singleton.
+ if (obj.getClass().getAnnotation(Component.class).scope().equals(Scope.SINGLETON)) {
+ singletons.put(obj.getClass(), obj);
+ }
+ }
+
+ @Override
+ public <T> T getSingletonInstance(Class<T> clazz) throws InjectionException {
+ if (!clazz.isAnnotationPresent(Component.class)) {
+ throw new InjectionException(clazz + " isn't annotated with: @Component");
+ }
+ if (clazz.getAnnotation(Component.class).scope().equals(Scope.PROTOTYPE)) {
+ throw new InjectionException(clazz + " is not a SINGLETON");
+ }
+
+ T single = (T) singletons.get(clazz);
+ if (single == null) {
+ try {
+ single = (T) clazz.newInstance();
+ } catch (InstantiationException | IllegalAccessException e) {
+ e.printStackTrace();
+ }
+ initialize(single);
+ }
+ return single;
+ }
+}