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  {
24      private final Map<Class<?>, Object> context;
25  
26      public SimpleConstructorHostContainer(final Map<Class<?>, Object> context)
27      {
28          final Map<Class<?>, Object> tmp = new HashMap<Class<?>, Object>(context);
29          tmp.put(HostContainer.class, this);
30          this.context = Collections.unmodifiableMap(tmp);
31      }
32  
33      /**
34       * Creates a class instance, performing dependency injection using the initialised context map
35       *
36       * @param moduleClass The target object class
37       * @return The instance
38       * @throws IllegalArgumentException Wraps any exceptions thrown during the constructor call
39       */
40      public <T> T create(final Class<T> moduleClass) throws IllegalArgumentException
41      {
42          for (final Constructor<T> constructor : findConstructorsLargestFirst(moduleClass))
43          {
44              final List<Object> params = new ArrayList<Object>();
45              for (final Class<?> paramType : constructor.getParameterTypes())
46              {
47                  if (context.containsKey(paramType))
48                  {
49                      params.add(context.get(paramType));
50                  }
51              }
52              if (constructor.getParameterTypes().length != params.size())
53              {
54                  continue;
55              }
56  
57              try
58              {
59                  return constructor.newInstance(params.toArray());
60              }
61              catch (final InstantiationException e)
62              {
63                  throw new IllegalArgumentException(e);
64              }
65              catch (final IllegalAccessException e)
66              {
67                  throw new IllegalArgumentException(e);
68              }
69              catch (final InvocationTargetException e)
70              {
71                  throw new IllegalArgumentException(e);
72              }
73          }
74  
75          throw new IllegalArgumentException("Unable to match any constructor for class " + moduleClass);
76      }
77  
78      @SuppressWarnings("unchecked")
79      private <T> Collection<Constructor<T>> findConstructorsLargestFirst(final Class<T> moduleClass)
80      {
81          final Set<Constructor<T>> constructors = new TreeSet<Constructor<T>>(new Comparator<Constructor<T>>()
82          {
83              public int compare(final Constructor<T> first, final Constructor<T> second)
84              {
85                  // @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
86                  return Integer.valueOf(second.getParameterTypes().length).compareTo(first.getParameterTypes().length);
87              }
88          });
89          for (final Constructor<?> constructor : moduleClass.getConstructors())
90          {
91              constructors.add((Constructor<T>) constructor);
92          }
93          return constructors;
94      }
95  }