View Javadoc
1   package com.atlassian.plugin.hostcontainer;
2   
3   import java.lang.reflect.Constructor;
4   import java.lang.reflect.InvocationTargetException;
5   import java.util.ArrayList;
6   import java.util.Collection;
7   import java.util.Collections;
8   import java.util.Comparator;
9   import java.util.HashMap;
10  import java.util.List;
11  import java.util.Map;
12  import java.util.Set;
13  import java.util.TreeSet;
14  
15  /**
16   * Constructs module instances, matching the constructor with the largest number of arguments first. The objects to
17   * pass to the constructor are retrieved from the passed map of classes and objects. The classes are matched on an
18   * exact class match.
19   *
20   * @since 2.2.0
21   */
22  public class SimpleConstructorHostContainer implements HostContainer {
23      private final Map<Class<?>, Object> context;
24  
25      public SimpleConstructorHostContainer(final Map<Class<?>, Object> context) {
26          final Map<Class<?>, Object> tmp = new HashMap<>(context);
27          tmp.put(HostContainer.class, this);
28          this.context = Collections.unmodifiableMap(tmp);
29      }
30  
31      /**
32       * Creates a class instance, performing dependency injection using the initialised context map
33       *
34       * @param moduleClass The target object class
35       * @return The instance
36       * @throws IllegalArgumentException Wraps any exceptions thrown during the constructor call
37       */
38      public <T> T create(final Class<T> moduleClass) throws IllegalArgumentException {
39          for (final Constructor<T> constructor : findConstructorsLargestFirst(moduleClass)) {
40              final List<Object> params = new ArrayList<>();
41              for (final Class<?> paramType : constructor.getParameterTypes()) {
42                  if (context.containsKey(paramType)) {
43                      params.add(context.get(paramType));
44                  }
45              }
46              if (constructor.getParameterTypes().length != params.size()) {
47                  continue;
48              }
49  
50              try {
51                  return constructor.newInstance(params.toArray());
52              } catch (final InstantiationException e) {
53                  throw new IllegalArgumentException(e);
54              } catch (final IllegalAccessException e) {
55                  throw new IllegalArgumentException(e);
56              } catch (final InvocationTargetException e) {
57                  throw new IllegalArgumentException(e);
58              }
59          }
60  
61          throw new IllegalArgumentException("Unable to match any constructor for class " + moduleClass);
62      }
63  
64      @SuppressWarnings("unchecked")
65      private <T> Collection<Constructor<T>> findConstructorsLargestFirst(final Class<T> moduleClass) {
66          final Set<Constructor<T>> constructors = new TreeSet<Constructor<T>>(new Comparator<Constructor<T>>() {
67              public int compare(final Constructor<T> first, final Constructor<T> second) {
68                  // @TODO this only sorts via largest, and therefore it also causes any of the same length to get dropped from the set, see TreeSet for more details
69                  return Integer.valueOf(second.getParameterTypes().length).compareTo(first.getParameterTypes().length);
70              }
71          });
72          for (final Constructor<?> constructor : moduleClass.getConstructors()) {
73              constructors.add((Constructor<T>) constructor);
74          }
75          return constructors;
76      }
77  }