You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

SimpleInjector.java 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. /*
  2. * Copyright (c) 2006-2013 DMDirc Developers
  3. *
  4. * Permission is hereby granted, free of charge, to any person obtaining a copy
  5. * of this software and associated documentation files (the "Software"), to deal
  6. * in the Software without restriction, including without limitation the rights
  7. * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
  8. * copies of the Software, and to permit persons to whom the Software is
  9. * furnished to do so, subject to the following conditions:
  10. *
  11. * The above copyright notice and this permission notice shall be included in
  12. * all copies or substantial portions of the Software.
  13. *
  14. * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
  15. * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
  16. * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
  17. * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
  18. * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
  19. * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
  20. * SOFTWARE.
  21. */
  22. package com.dmdirc.util;
  23. import java.lang.reflect.Constructor;
  24. import java.util.HashMap;
  25. import java.util.Map;
  26. /**
  27. * Facilitates simple injection of parameters into a constructor.
  28. * <p>
  29. * Injectors may be given a set of parent injectors, from which they will
  30. * inherit parameters. No checking is performed to prevent circular references
  31. * within parents.
  32. */
  33. public class SimpleInjector {
  34. /** The parent injectors, if any. */
  35. private final SimpleInjector[] parents;
  36. /** A mapping of known classes to the objects that should be injected. */
  37. private final Map<Class<?>, Object> parameters = new HashMap<>();
  38. /**
  39. * Creates a new injector which will inherit injection parameters from
  40. * the specified parent. Parent graphs must be acyclic.
  41. *
  42. * @param parents The injectors to inherit parameters from.
  43. */
  44. public SimpleInjector(final SimpleInjector ... parents) {
  45. this.parents = parents;
  46. }
  47. /**
  48. * Adds a new injectable object to this injector. If a constructor requires
  49. * an instance of the specified class, it will be given the specified
  50. * object. If an association has already been made for the specified class,
  51. * it will be replaced with the new object.
  52. *
  53. * @param clazz The type of parameter which should be injected
  54. * @param object The value to inject for the parameter
  55. */
  56. public void addParameter(final Class<?> clazz, final Object object) {
  57. parameters.put(clazz, object);
  58. }
  59. /**
  60. * Adds the specified object as an injectable parameter for all of its
  61. * known classes and interface. This is equivalent to calling
  62. * {@link #addParameter(java.lang.Class, java.lang.Object)} for the object's
  63. * class, all superclasses, and all interfaces.
  64. *
  65. * @param object The object to be injected
  66. */
  67. public void addParameter(final Object object) {
  68. // Iterate the object hierarchy up
  69. Class<?> target = object.getClass();
  70. do {
  71. addParameter(target, object);
  72. // Add all interfaces
  73. for (Class<?> iface : target.getInterfaces()) {
  74. addParameter(iface, object);
  75. }
  76. target = target.getSuperclass();
  77. } while (target != null);
  78. }
  79. /**
  80. * Retrieves a mapping of known injectable types to their corresponding
  81. * values. This mapping will also include mappings defined in all of this
  82. * injector's parents.
  83. *
  84. * @return A map of injectable parameters
  85. */
  86. public Map<Class<?>, Object> getParameters() {
  87. final Map<Class<?>, Object> localParams = new HashMap<>(parameters.size());
  88. for (SimpleInjector parent : parents) {
  89. localParams.putAll(parent.getParameters());
  90. }
  91. // Note: insert local parameters after so they override parent params.
  92. localParams.putAll(parameters);
  93. return localParams;
  94. }
  95. /**
  96. * Attempts to create a new instance of the specified class by injecting
  97. * parameters into the constructor(s). If no constructors are found which
  98. * can be satisfied by the known parameters, or if all satisfiable
  99. * constructors throw exceptions, an IllegalArgumentException will be
  100. * thrown.
  101. * <p>
  102. * If multiple constructors are available that are satisfiable using the
  103. * known arguments, no guarantee is given as to which will be used.
  104. *
  105. * @param <T> The type of object being instantiated
  106. * @param clazz The class to create an instance of
  107. * @return An instance of the specified class
  108. * @throws IllegalArgumentException If the class could not be instantiated
  109. */
  110. @SuppressWarnings("unchecked")
  111. public <T> T createInstance(final Class<T> clazz) {
  112. final Map<Class<?>, Object> params = getParameters();
  113. Throwable throwable = null;
  114. for (Constructor<?> rawCtor : clazz.getConstructors()) {
  115. final Constructor<T> ctor = (Constructor<T>) rawCtor;
  116. final Object[] args = new Object[ctor.getParameterTypes().length];
  117. int i = 0;
  118. for (Class<?> paramType : ctor.getParameterTypes()) {
  119. if (params.containsKey(paramType)) {
  120. args[i++] = params.get(paramType);
  121. } else {
  122. break;
  123. }
  124. }
  125. if (i == args.length) {
  126. try {
  127. return ctor.newInstance(args);
  128. } catch (ReflectiveOperationException ex) {
  129. throwable = ex;
  130. }
  131. }
  132. }
  133. if (throwable == null) {
  134. throw new IllegalArgumentException("No declared constructors "
  135. + "could be satisfied with known parameters");
  136. }
  137. throw new IllegalArgumentException("A satisfiable constructor was "
  138. + "found but threw an exception", throwable);
  139. }
  140. }