diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..02cf4b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,84 @@ +# +# Created with help from www.gitignore.io +# + + +####################################################################### +# Intellij +# Created by https://www.gitignore.io/api/intellij +# +# Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm, CLion, Android Studio and Webstorm +# Reference: https://intellij-support.jetbrains.com/hc/en-us/articles/206544839 + +## File-based project format: +*.iws + +## Plugin-specific files: + +# IntelliJ +/out/ +/.idea/ +/target/ + +# mpeltonen/sbt-idea plugin +.idea_modules/ + +# JIRA plugin +atlassian-ide-plugin.xml + +# Crashlytics plugin (for Android Studio and IntelliJ) +com_crashlytics_export_strings.xml +crashlytics.properties +crashlytics-build.properties +fabric.properties + +### Intellij Patch ### +*.iml +*.ipr + + +####################################################################### +# Maven +# Created by https://www.gitignore.io/api/maven +# +target/ +pom.xml.tag +pom.xml.releaseBackup +pom.xml.versionsBackup +pom.xml.next +release.properties +dependency-reduced-pom.xml +buildNumber.properties +.mvn/timing.properties + + +####################################################################### +# OSX +# Created by https://www.gitignore.io/api/osx +# +.DS_Store +.AppleDouble +.LSOverride + +# Icon must end with two \r +Icon + + +# Thumbnails +._* + +# Files that might appear in the root of a volume +.DocumentRevisions-V100 +.fseventsd +.Spotlight-V100 +.TemporaryItems +.Trashes +.VolumeIcon.icns + +# Directories potentially created on remote AFP share +.AppleDB +.AppleDesktop +Network Trash Folder +Temporary Items +.apdisk + diff --git a/.travis.yml b/.travis.yml new file mode 100644 index 0000000..d205732 --- /dev/null +++ b/.travis.yml @@ -0,0 +1,17 @@ +language: java +install: true + +script: +- mvn verify +- mvn compile +- mvn test +- mvn validate +- mvn jar:jar +- mvn jar:test-jar +- mvn source:jar +- mvn source:test-jar +- mvn surefire:test +- mvn clean package +- mvn clean install +jdk: + - oraclejdk8 diff --git a/README.md b/README.md index b721212..ef511f0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,6 @@ + #### About Feather + [Feather](http://zsoltherpai.github.io/feather) is an ultra-lightweight dependency injection ([JSR-330](https://jcp.org/en/jsr/detail?id=330 "JSR-330")) library for Java and Android. Dependency injection frameworks are often perceived as "magical" and complex. Feather - with just a few hundred lines of code - is probably the easiest, tiniest, most obvious one, diff --git a/feather/src/main/java/org/codejargon/feather/Feather.java b/feather/src/main/java/org/codejargon/feather/Feather.java index d024f2d..8cf8217 100644 --- a/feather/src/main/java/org/codejargon/feather/Feather.java +++ b/feather/src/main/java/org/codejargon/feather/Feather.java @@ -1,18 +1,38 @@ package org.codejargon.feather; -import javax.inject.*; +import javax.inject.Provider; +import javax.inject.Singleton; +import javax.inject.Inject; +import javax.inject.Qualifier; import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.lang.reflect.Constructor; +import java.lang.reflect.Field; import java.lang.reflect.*; -import java.util.*; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; +import java.util.LinkedHashSet; +import java.util.Map; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; +/** + * + * @author edwin.njeru + * + */ public class Feather { private final Map> providers = new ConcurrentHashMap<>(); private final Map singletons = new ConcurrentHashMap<>(); - private final Map injectFields = new ConcurrentHashMap<>(0); + //private final Map injectFields = new ConcurrentHashMap<>(0); /** * Constructs Feather with configuration modules + * + * @param modules varargs of modules classes + * + * @return an instance of Feather */ public static Feather with(Object... modules) { return new Feather(Arrays.asList(modules)); @@ -20,6 +40,9 @@ public static Feather with(Object... modules) { /** * Constructs Feather with configuration modules + * + * @param modules {@code Iterable} collection of configuration modules + * @return */ public static Feather with(Iterable modules) { return new Feather(modules); @@ -73,12 +96,15 @@ public Provider provider(Key key) { /** * Injects fields to the target object + * + * @param target Object */ public void injectFields(Object target) { - if (!injectFields.containsKey(target.getClass())) { + /*if (!injectFields.containsKey(target.getClass())) { injectFields.put(target.getClass(), injectFields(target.getClass())); - } - for (Object[] f: injectFields.get(target.getClass())) { + }*/ + //for (Object[] f: injectFields.get(target.getClass())) { + for (Object[] f: injectFields(target.getClass())) { Field field = (Field) f[0]; Key key = (Key) f[2]; try { @@ -89,10 +115,11 @@ public void injectFields(Object target) { } } - @SuppressWarnings("unchecked") + @SuppressWarnings({ "unchecked", "rawtypes" }) private Provider provider(final Key key, Set chain) { if (!providers.containsKey(key)) { - final Constructor constructor = constructor(key); + @SuppressWarnings("rawtypes") + final Constructor constructor = constructor(key); final Provider[] paramProviders = paramProviders(key, constructor.getParameterTypes(), constructor.getGenericParameterTypes(), constructor.getParameterAnnotations(), chain); providers.put(key, singletonProvider(key, key.type.getAnnotation(Singleton.class), new Provider() { @Override @@ -109,7 +136,8 @@ public Object get() { return (Provider) providers.get(key); } - private void providerMethod(final Object module, final Method m) { + @SuppressWarnings("unchecked") + private void providerMethod(final Object module, final Method m) { final Key key = Key.of(m.getReturnType(), qualifier(m.getAnnotations())); if (providers.containsKey(key)) { throw new FeatherException(String.format("%s has multiple providers, module %s", key.toString(), module.getClass())); @@ -192,6 +220,10 @@ public Object get() { return providers; } + /** + * @return an array of fully-constructed and injected instances fetched from an + * array of @param paramProviders, which are then used as parameters + */ private static Object[] params(Provider[] paramProviders) { Object[] params = new Object[paramProviders.length]; for (int i = 0; i < paramProviders.length; ++i) { @@ -200,7 +232,16 @@ private static Object[] params(Provider[] paramProviders) { return params; } - private static Set append(Set set, Key newKey) { + /** + * Appends a @{code Key} @param newKey to an existing {@code Set} @param set of + * keys, and returns a {@code Set} of key containing the new {@code Key}. + * If the {@code Set} of {@code Key} is null, a serializable immutable set containing only + * the @param newKey is returned + * + * @return Set append {@code Key} {@code Set} + */ + @SuppressWarnings("rawtypes") + private static Set append(Set set, Key newKey) { if (set != null && !set.isEmpty()) { Set appended = new LinkedHashSet<>(set); appended.add(newKey); @@ -210,6 +251,14 @@ private static Set append(Set set, Key newKey) { } } + /** + * Creates a map represented as a two-dimensional array of {@code Inject} annotated + * fields with their corresponding {@code Provider} types, or Parameterised types or + * type arguments. + * + * @param target Class target + * @return two-dimensional Object[][] array + */ private static Object[][] injectFields(Class target) { Set fields = fields(target); Object[][] fs = new Object[fields.size()][]; @@ -227,6 +276,13 @@ private static Object[][] injectFields(Class target) { return fs; } + /** + * Returns accessible set of fields ({@code Field}) in a given class type + * which are annotated with the {@code Inject} {@code Annotation} + * + * @param type Class type + * @return Set fields + */ private static Set fields(Class type) { Class current = type; Set fields = new HashSet<>(); @@ -242,7 +298,16 @@ private static Set fields(Class type) { return fields; } - private static String chain(Set chain, Key lastKey) { + /** + * Returns a String representing a concatenation of Keys from a {@code Set} + * of {@code Key} and appends the last {@code Key} + * + * @param chain Set chain + * @param {@code Key} lastKey + * @return "->" concatenated string of {@code Key} names + */ + @SuppressWarnings("rawtypes") + private static String chain(Set chain, Key lastKey) { StringBuilder chainString = new StringBuilder(); for (Key key : chain) { chainString.append(key.toString()).append(" -> "); @@ -250,7 +315,17 @@ private static String chain(Set chain, Key lastKey) { return chainString.append(lastKey.toString()).toString(); } - private static Constructor constructor(Key key) { + /** + * Fetches a constructor which is annotated with {@code Inject} from a {@code Key}. + * If such a constructor does not exist, the noargs constructor is used instead. + * If both do not exist and a module provider does not also exist then a + * {@code FeatherException} is thrown + * + * @param {@code Key} + * @return {@code Constructor} + */ + @SuppressWarnings("rawtypes") + private static Constructor constructor(Key key) { Constructor inject = null; Constructor noarg = null; for (Constructor c : key.type.getDeclaredConstructors()) { @@ -273,6 +348,13 @@ private static Constructor constructor(Key key) { } } + /** + * Creates a hashSet of methods from a given type which are annotated with the + * {@code Provides} annotation + * + * @param type Class type + * @return Set containing {@code Provides} annotation + */ private static Set providers(Class type) { Class current = type; Set providers = new HashSet<>(); @@ -288,6 +370,14 @@ private static Set providers(Class type) { return providers; } + /** + * Fetches a {@code Qualifier} {@code Annotation} from an array of annotations. + * If a {@code Qualifier} {@code Annotation} is not found in the array, a null pointer + * is returned + * + * @param annotations + * @return {@code Qualifier} {@code Annotation} + */ private static Annotation qualifier(Annotation[] annotations) { for (Annotation annotation : annotations) { if (annotation.annotationType().isAnnotationPresent(Qualifier.class)) { @@ -297,6 +387,14 @@ private static Annotation qualifier(Annotation[] annotations) { return null; } + /** + * Checks is a Provider exists in a subclass of the current class we are currently + * looping through + * + * @param {@code Method} + * @param discoveredMethods Set of discoveredMethods + * @return boolean whether or not a provider exists in a subclass + */ private static boolean providerInSubClass(Method method, Set discoveredMethods) { for (Method discovered : discoveredMethods) { if (discovered.getName().equals(method.getName()) && Arrays.equals(method.getParameterTypes(), discovered.getParameterTypes())) { diff --git a/feather/src/main/java/org/codejargon/feather/FeatherException.java b/feather/src/main/java/org/codejargon/feather/FeatherException.java index c52f1db..a9469e2 100644 --- a/feather/src/main/java/org/codejargon/feather/FeatherException.java +++ b/feather/src/main/java/org/codejargon/feather/FeatherException.java @@ -1,7 +1,12 @@ package org.codejargon.feather; public class FeatherException extends RuntimeException { - FeatherException(String message) { + /** + * + */ + private static final long serialVersionUID = -1606897572808444593L; + + FeatherException(String message) { super(message); } diff --git a/feather/src/main/java/org/codejargon/feather/Provides.java b/feather/src/main/java/org/codejargon/feather/Provides.java index 309a7bc..544899b 100644 --- a/feather/src/main/java/org/codejargon/feather/Provides.java +++ b/feather/src/main/java/org/codejargon/feather/Provides.java @@ -1,6 +1,10 @@ package org.codejargon.feather; -import java.lang.annotation.*; +import java.lang.annotation.ElementType; +import java.lang.annotation.Retention; +import java.lang.annotation.RetentionPolicy; +import java.lang.annotation.Target; + @Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD) diff --git a/pom.xml b/pom.xml index e7652c1..2168e38 100644 --- a/pom.xml +++ b/pom.xml @@ -1,148 +1,322 @@ - 4.0.0 - org.codejargon.feather - feather-parent - 1.0.1-SNAPSHOT - pom - Feather Parent - https://github.com/zsoltherpai/feather - - feather - - + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + 4.0.0 + org.codejargon.feather + feather-parent + 1.0.1-SNAPSHOT + pom + Feather Parent + https://github.com/zsoltherpai/feather + + feather + + Feather is an ultra-lightweight dependency injection (JSR-330) library for Java and Android. - - - The Apache Software License, Version 2.0 - http://www.apache.org/licenses/LICENSE-2.0.txt - repo - - - - - Zsolt Herpai - zsolt.herpai@gmail.com - - - - UTF-8 - 1.7 - 1.7 - - - scm:git:https://github.com/zsoltherpai/feather.git - scm:git:git@github.com:zsoltherpai/feather.git - https://github.com/zsoltherpai/feather - - - - junit - junit - 4.12 - test - - - org.mockito - mockito-core - 1.9.5 - test - - - - - ossrh-release - - - ossrh - https://oss.sonatype.org/content/repositories/snapshots - - - ossrh - https://oss.sonatype.org/service/local/staging/deploy/maven2/ - - - - - - org.apache.maven.plugins - maven-source-plugin - 2.2.1 - - - attach-sources - - jar-no-fork - - - - - - org.apache.maven.plugins - maven-javadoc-plugin - 2.9.1 - - - attach-javadocs - - jar - - - - - - org.sonatype.plugins - nexus-staging-maven-plugin - 1.6.3 - true - - ossrh - https://oss.sonatype.org/ - true - - - - org.apache.maven.plugins - maven-gpg-plugin - 1.5 - - - sign-artifacts - verify - - sign - - - - - - org.apache.maven.plugins - maven-compiler-plugin - 3.3 + + + The Apache Software License, Version 2.0 + http://www.apache.org/licenses/LICENSE-2.0.txt + repo + + + + + Zsolt Herpai + zsolt.herpai@gmail.com + + + + + UTF-8 + 1.7 + 1.7 + + + 1.6.3 + 2.2.1 + 2.9.1 + 3.3 + 2.4 + 3.1.0-RC6 + 0.7.9 + 2.5.2 + 3.0.1 + 2.6 + 3.0.0-M1 + 2.9 + + + scm:git:https://github.com/zsoltherpai/feather.git + scm:git:git@github.com:zsoltherpai/feather.git + https://github.com/zsoltherpai/feather + + + + junit + junit + 4.12 + test + + + org.mockito + mockito-core + 1.9.5 + test + + + + + ossrh-release + + + ossrh + https://oss.sonatype.org/content/repositories/snapshots + + + ossrh + https://oss.sonatype.org/service/local/staging/deploy/maven2/ + + + + + + org.apache.maven.plugins + maven-source-plugin + 2.2.1 + + + attach-sources + + jar-no-fork + + + + + + org.apache.maven.plugins + maven-javadoc-plugin + 2.9.1 + + + attach-javadocs + + jar + + + + + + org.sonatype.plugins + nexus-staging-maven-plugin + ${nexus.staging.maven.plugin.version} + true + + ossrh + https://oss.sonatype.org/ + true + + + + org.apache.maven.plugins + maven-gpg-plugin + 1.5 + + + sign-artifacts + verify + + sign + + + + + + org.apache.maven.plugins + maven-compiler-plugin + ${maven.compiler.plugin.version} + + true + false + ${maven.compiler.source} + ${maven.compiler.target} + + + + + + + + + + ${basedir} + + README.md + CHANGELOG.md + LICENSE.txt + + + + + + org.apache.maven.plugins + maven-source-plugin + ${maven.source.plugin.version} + + + attach-sources + + jar-no-fork + + package + + + + + org.apache.maven.plugins + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + + attach-javadocs + + jar + + package + + + + + org.apache.maven.plugins + maven-compiler-plugin + 3.3 + + ${maven.compiler.source} + ${maven.compiler.target} + + + + org.apache.maven.plugins + maven-jar-plugin + ${maven.jar.plugin.version} + + + org.apache.maven.plugins + maven-enforcer-plugin + ${maven.enforcer.plugin.version} + + + enforce-maven + + enforce + - true - false - 1.7 - 1.7 + + + ${maven.compiler.source} + + + ${maven.compiler.source} + + - - - - - - + + + + + + com.github.spotbugs + spotbugs-maven-plugin + ${spotbugs.maven.plugin.version} + + + com.github.spotbugs + spotbugs + 3.1.0 + + + + + + + org.jacoco + jacoco-maven-plugin + ${jacoco-maven-plugin.version} + + + jacoco-initialize + + prepare-agent + + + + jacoco-site + package + + report + + + + + + + + org.apache.maven.plugins - maven-compiler-plugin - 3.3 + maven-javadoc-plugin + ${maven.javadoc.plugin.version} + + + true + + http://download.oracle.com/javase/1.8.0/docs/api/ + http://download.oracle.com/javaee/1.8/api/ + http://commons.apache.org/digester/apidocs/ + + + + + org.apache.maven.plugins + maven-project-info-reports-plugin + ${maven.project.info.reports.plugin.version} + + false + + + + org.codehaus.mojo + cobertura-maven-plugin + ${cobertura.maven.plugin.version} + + true + + html + xml + + + + + + org.codehaus.mojo + findbugs-maven-plugin + ${findbugs.maven.plugin.version} + + Normal + Default + + + + org.apache.maven.plugins + maven-pmd-plugin + ${maven.pmd.plugin.version} - 1.7 - 1.7 + ${maven.compiler.target} - + \ No newline at end of file