3434import java .util .logging .Level ;
3535import java .util .logging .Logger ;
3636import javax .annotation .Nullable ;
37+ import javax .inject .Provider ;
3738
3839/**
3940 * Manager for handling commands. This allows you to easily process commands,
@@ -77,9 +78,10 @@ public abstract class CommandsManager<T> {
7778 protected Map <Method , Map <String , Method >> commands = new HashMap <Method , Map <String , Method >>();
7879
7980 /**
80- * Used to store the instances associated with a method.
81+ * Used to store the providers associated with a method.
8182 */
82- protected Map <Method , Object > instances = new HashMap <Method , Object >();
83+ protected Map <Class , Object > instances = new HashMap <>();
84+ protected Map <Method , Provider > providers = new HashMap <>();
8385
8486 /**
8587 * Mapping of commands (not including aliases) with a description. This
@@ -136,8 +138,12 @@ public List<Command> registerAndReturn(Class<?> cls) {
136138 * @return Commands Registered
137139 */
138140 public List <Command > registerMethods (Class <?> cls , Method parent ) {
141+ return registerMethods (cls , parent , null );
142+ }
143+
144+ public <C > List <Command > registerMethods (Class <C > cls , @ Nullable Method parent , @ Nullable Provider <? extends C > provider ) {
139145 try {
140- return registerMethods (cls , parent , null );
146+ return registerMethods0 (cls , parent , provider );
141147 } catch (InvocationTargetException | IllegalAccessException | InstantiationException e ) {
142148 throw new CommandRegistrationException ("Failed to register commands in class " + cls .getName (), e );
143149 }
@@ -148,10 +154,10 @@ public List<Command> registerMethods(Class<?> cls, Method parent) {
148154 *
149155 * @param cls the class to register
150156 * @param parent the parent method
151- * @param obj the object whose methods will become commands if they are annotated
157+ * @param provider provides instances of the command method, or null to use the {@link Injector}
152158 * @return a list of commands
153159 */
154- private List <Command > registerMethods (Class <? > cls , Method parent , Object obj ) throws IllegalAccessException , InstantiationException , InvocationTargetException {
160+ private < C > List <Command > registerMethods0 (Class <C > cls , Method parent , @ Nullable Provider <? extends C > provider ) throws IllegalAccessException , InstantiationException , InvocationTargetException {
155161 Map <String , Method > map ;
156162 List <Command > registered = new ArrayList <Command >();
157163
@@ -186,24 +192,35 @@ private List<Command> registerMethods(Class<?> cls, Method parent, Object obj) t
186192
187193 // We want to be able invoke with an instance
188194 if (!isStatic ) {
189- if (obj == null ) {
190- if (injector != null ) {
191- obj = injector .getInstance (cls );
192- }
193-
194- if (obj == null ) {
195- String text = "Failed to get an instance of " + cls .getName () +
196- " for command method " + method .getDeclaringClass ().getName () + "#" + method .getName ();
197- if (injector == null ) {
198- text += " (no Injector is available to create it)" ;
199- } else {
200- text += " (the Injector returned null when asked for one)" ;
195+ if (provider == null && injector != null ) {
196+ // If we weren't given a Provider, try to get one from the Injector
197+ provider = injector .getProviderOrNull (cls );
198+ if (provider == null ) {
199+ // If we can't get a provider, check if we have already instantiated the class
200+ C instance = (C ) instances .get (cls );
201+ if (instance == null ) {
202+ // If we haven't, do that now and save it
203+ instance = (C ) injector .getInstance (cls );
204+ instances .put (cls , instance );
201205 }
202- throw new CommandRegistrationException (text );
206+ // Generate a provider that just returns the saved instance
207+ final C finalInstance = instance ;
208+ provider = () -> finalInstance ;
203209 }
204210 }
205211
206- instances .put (method , obj );
212+ if (provider != null ) {
213+ providers .put (method , provider );
214+ } else {
215+ String text = "Failed to get an instance/provider of " + cls .getName () +
216+ " for command method " + method .getDeclaringClass ().getName () + "#" + method .getName ();
217+ if (injector == null ) {
218+ text += " (no Injector is available to create it)" ;
219+ } else {
220+ text += " (the Injector returned null when asked for one)" ;
221+ }
222+ throw new CommandRegistrationException (text );
223+ }
207224 }
208225
209226 // Build a list of commands and their usage details, at least for
@@ -253,7 +270,7 @@ private List<Command> registerMethods(Class<?> cls, Method parent, Object obj) t
253270 }
254271
255272 if (cls .getSuperclass () != null ) {
256- registerMethods (cls .getSuperclass (), parent , obj );
273+ registerMethods0 (cls .getSuperclass (), parent , provider );
257274 }
258275
259276 return registered ;
@@ -551,7 +568,8 @@ private List<String> executeMethod(Method parent, boolean completing, String[] a
551568
552569 methodArgs [0 ] = context ;
553570
554- Object instance = instances .get (method );
571+ Provider provider = providers .get (method );
572+ Object instance = provider == null ? null : provider .get ();
555573
556574 // If we get here while completing, it means the method's return type is a List<String>,
557575 // and we never want to use the default completion. So if it returns null, convert it to
0 commit comments