Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
49 changes: 49 additions & 0 deletions Injector/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm
# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839

# User-specific stuff:
.idea/**/workspace.xml
.idea/**/tasks.xml
.idea/dictionaries

# Sensitive or high-churn files:
.idea/**/dataSources/
.idea/**/dataSources.ids
.idea/**/dataSources.xml
.idea/**/dataSources.local.xml
.idea/**/sqlDataSources.xml
.idea/**/dynamic.xml
.idea/**/uiDesigner.xml

# Gradle:
.idea/**/gradle.xml
.idea/**/libraries

# CMake
cmake-build-debug/

# Mongo Explorer plugin:
.idea/**/mongoSettings.xml

## File-based project format:
*.iws

## Plugin-specific files:

# IntelliJ
out/

# mpeltonen/sbt-idea plugin
.idea_modules/

# JIRA plugin
atlassian-ide-plugin.xml

# Cursive Clojure plugin
.idea/replstate.xml

# Crashlytics plugin (for Android Studio and IntelliJ)
com_crashlytics_export_strings.xml
crashlytics.properties
crashlytics-build.properties
fabric.properties
16 changes: 16 additions & 0 deletions Injector/.idea/compiler.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 13 additions & 0 deletions Injector/.idea/misc.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

8 changes: 8 additions & 0 deletions Injector/.idea/modules.xml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

18 changes: 18 additions & 0 deletions Injector/Injector.iml
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<module org.jetbrains.idea.maven.project.MavenProjectsManager.isMavenModule="true" type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" LANGUAGE_LEVEL="JDK_1_8">
<output url="file://$MODULE_DIR$/target/classes" />
<output-test url="file://$MODULE_DIR$/target/test-classes" />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/src/main/java" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/src/main/resources" type="java-resource" />
<sourceFolder url="file://$MODULE_DIR$/src/test/java" isTestSource="true" />
<excludeFolder url="file://$MODULE_DIR$/target" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="Maven: junit:junit:4.12" level="project" />
<orderEntry type="library" name="Maven: org.hamcrest:hamcrest-core:1.3" level="project" />
<orderEntry type="library" name="Maven: org.jetbrains:annotations:13.0" level="project" />
</component>
</module>
36 changes: 36 additions & 0 deletions Injector/pom.xml
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>

<groupId>ru.spbau.mit.kazakov.Injector</groupId>
<artifactId>Injector</artifactId>
<version>1.0-SNAPSHOT</version>
<build>
<plugins>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<configuration>
<source>1.8</source>
<target>1.8</target>
</configuration>
</plugin>
</plugins>
</build>

<dependencies>
<dependency>
<groupId>junit</groupId>
<artifactId>junit</artifactId>
<version>4.12</version>
</dependency>
<dependency>
<groupId>org.jetbrains</groupId>
<artifactId>annotations</artifactId>
<version>13.0</version>
</dependency>
</dependencies>

</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.spbau.mit.kazakov.Injector;

/**
* Thrown when found multiple implementations for a dependency.
*/
public class AmbiguousImplementationException extends Exception {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.spbau.mit.kazakov.Injector;

/**
* Thrown if found no implementation for a dependency.
*/
public class ImplementationNotFoundException extends Exception {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.spbau.mit.kazakov.Injector;

/**
* Thrown if found a circular dependency.
*/
public class InjectionCycleException extends Exception {
}
103 changes: 103 additions & 0 deletions Injector/src/main/java/ru/spbau/mit/kazakov/Injector/Injector.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
package ru.spbau.mit.kazakov.Injector;

import org.jetbrains.annotations.NotNull;

import java.lang.reflect.Constructor;
import java.lang.reflect.Modifier;
import java.util.*;


/**
* A class for creating an object of specified class using given classes.
*/
public class Injector {
/**
* Creates an object of specified class using classes from specified container.
*
* @param rootClassName specified class
* @param implementations for creating an object
* @return created object
* @throws AmbiguousImplementationException when found multiple implementations for a dependency
* @throws ImplementationNotFoundException if found no implementation for a dependency
* @throws InjectionCycleException if found a circular dependency
* @throws ClassNotFoundException if found not existing class
*/
@NotNull
public static Object initialize(@NotNull String rootClassName, @NotNull List<String> implementations) throws Exception {
return create(rootClassName, implementations, new TreeSet<>(), new HashMap<>());
}

/**
* Creates an object of specified class using classes from specified container.
*
* @param className specified class
* @param implementations for creating an object
* @param demanded already required dependencies
* @param created already created objects
* @return created object
* @throws AmbiguousImplementationException when found multiple implementations for a dependency
* @throws ImplementationNotFoundException if found no implementation for a dependency
* @throws InjectionCycleException if found a circular dependency
* @throws ClassNotFoundException if found not existing class
*/
@NotNull
private static Object create(@NotNull String className, @NotNull List<String> implementations,
@NotNull Set<String> demanded,
@NotNull Map<String, Object> created) throws Exception {
if (demanded.contains(className)) {
if (created.containsKey(className)) {
return created.get(className);
} else {
throw new InjectionCycleException();
}
}
demanded.add(className);

Class<?> toCreate = Class.forName(className);
if (Modifier.isAbstract(toCreate.getModifiers()) || toCreate.isInterface()) {
String foundImplementation = null;
for (String implementation : implementations) {
Class<?> implementationClass = Class.forName(implementation);
if (toCreate.isAssignableFrom(implementationClass) && !toCreate.equals(implementationClass)) {
if (foundImplementation != null) {
throw new AmbiguousImplementationException();
} else {
foundImplementation = implementation;
}
}
}
if (foundImplementation == null) {
throw new ImplementationNotFoundException();
}
Object implementationObject = create(foundImplementation, implementations, demanded, created);
created.put(className, implementationObject);
return implementationObject;
}

Constructor<?> constructor = toCreate.getConstructors()[0];
Class<?>[] parameters = constructor.getParameterTypes();
ArrayList<Object> createdParameters = new ArrayList<>();
for (Class<?> parameter : parameters) {
String parameterName = parameter.getCanonicalName();
Class<?> paramClass = Class.forName(parameterName);
if (!paramClass.isInterface() && !Modifier.isAbstract(paramClass.getModifiers())) {
boolean found = false;
for (String implementationName : implementations) {
if (parameterName.equals(implementationName)) {
found = true;
break;
}
}
if (!found) {
throw new ImplementationNotFoundException();
}
}
createdParameters.add(create(parameterName, implementations,
demanded, created));
}
Object classInstance = constructor.newInstance(createdParameters.toArray());
created.put(className, classInstance);
return classInstance;
}
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

import static org.junit.Assert.*;

public class ClassWithCycleDependency1 {
public final ClassWithCycleDependency2 dependency;
static int numberOfObjects = 0;

public ClassWithCycleDependency1(ClassWithCycleDependency2 dependency) {
this.dependency = dependency;
numberOfObjects++;
assertTrue(numberOfObjects <= 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

import static org.junit.Assert.assertTrue;

public class ClassWithCycleDependency2 {
public final ClassWithCycleDependency3 dependency;
static int numberOfObjects = 0;

public ClassWithCycleDependency2(ClassWithCycleDependency3 dependency) {
this.dependency = dependency;
numberOfObjects++;
assertTrue(numberOfObjects <= 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

import static org.junit.Assert.assertTrue;

public class ClassWithCycleDependency3 {
public final ClassWithCycleDependency1 dependency;
static int numberOfObjects = 0;

public ClassWithCycleDependency3(ClassWithCycleDependency1 dependency) {
this.dependency = dependency;
numberOfObjects++;
assertTrue(numberOfObjects <= 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

import static org.junit.Assert.assertTrue;

public class ClassWithOneClassDependency {
public final ClassWithoutDependencies dependency;
static int numberOfObjects = 0;

public ClassWithOneClassDependency(ClassWithoutDependencies dependency) {
this.dependency = dependency;
numberOfObjects++;
assertTrue(numberOfObjects <= 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

import static org.junit.Assert.assertTrue;

public class ClassWithOneInterfaceDependency {
public final Interface dependency;
static int numberOfObjects = 0;

public ClassWithOneInterfaceDependency(Interface dependency) {
this.dependency = dependency;
numberOfObjects++;
assertTrue(numberOfObjects <= 1);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

public class ClassWithoutDependencies {

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

public interface Interface {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package ru.spbau.mit.kazakov.Injector.testClasses;

public class InterfaceImpl implements Interface {
}
Loading