Skip to content

Commit a64e45e

Browse files
add support for exception throwing methods. Issue #200
1 parent fae45f4 commit a64e45e

File tree

7 files changed

+145
-18
lines changed

7 files changed

+145
-18
lines changed

toothpick-compiler/src/main/java/toothpick/compiler/common/ToothpickProcessor.java

+11
Original file line numberDiff line numberDiff line change
@@ -377,6 +377,17 @@ protected List<ParamInjectionTarget> getParamInjectionTargetList(
377377
return paramInjectionTargetList;
378378
}
379379

380+
protected List<TypeElement> getExceptionTypes(ExecutableElement methodElement) {
381+
List<TypeElement> exceptionClassNames = new ArrayList<>();
382+
for (TypeMirror thrownTypeMirror : methodElement.getThrownTypes()) {
383+
DeclaredType thrownDeclaredType = (DeclaredType) thrownTypeMirror;
384+
TypeElement thrownType = (TypeElement) thrownDeclaredType.asElement();
385+
exceptionClassNames.add(thrownType);
386+
}
387+
388+
return exceptionClassNames;
389+
}
390+
380391
protected FieldInjectionTarget createFieldOrParamInjectionTarget(
381392
VariableElement variableElement) {
382393
final TypeElement memberTypeElement =

toothpick-compiler/src/main/java/toothpick/compiler/memberinjector/MemberInjectorProcessor.java

+1
Original file line numberDiff line numberDiff line change
@@ -190,6 +190,7 @@ private MethodInjectionTarget createMethodInjectionTarget(ExecutableElement meth
190190
MethodInjectionTarget methodInjectionTarget =
191191
new MethodInjectionTarget(enclosingElement, methodName, isOverride);
192192
methodInjectionTarget.parameters.addAll(getParamInjectionTargetList(methodElement));
193+
methodInjectionTarget.exceptionTypes.addAll(getExceptionTypes(methodElement));
193194

194195
return methodInjectionTarget;
195196
}

toothpick-compiler/src/main/java/toothpick/compiler/memberinjector/generators/MemberInjectorGenerator.java

+15
Original file line numberDiff line numberDiff line change
@@ -149,7 +149,22 @@ private void emitInjectMethods(
149149
}
150150
injectedMethodCallStatement.append(")");
151151

152+
boolean isMethodThrowingExceptions = !methodInjectionTarget.exceptionTypes.isEmpty();
153+
if (isMethodThrowingExceptions) {
154+
injectMethodBuilder.beginControlFlow("try");
155+
}
152156
injectMethodBuilder.addStatement(injectedMethodCallStatement.toString());
157+
if (isMethodThrowingExceptions) {
158+
int exceptionCounter = 1;
159+
for (TypeElement exceptionType : methodInjectionTarget.exceptionTypes) {
160+
injectMethodBuilder.nextControlFlow("catch ($T e$L)", exceptionType, exceptionCounter);
161+
injectMethodBuilder.addStatement(
162+
"throw new $T(e$L)", RuntimeException.class, exceptionCounter);
163+
exceptionCounter++;
164+
}
165+
166+
injectMethodBuilder.endControlFlow();
167+
}
153168
}
154169
}
155170

toothpick-compiler/src/main/java/toothpick/compiler/memberinjector/targets/MethodInjectionTarget.java

+2-1
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,8 @@ public final class MethodInjectionTarget {
2626
public final List<ParamInjectionTarget> parameters = new ArrayList<>();
2727
public final TypeElement enclosingClass;
2828
public final String methodName;
29-
public boolean isOverride;
29+
public final boolean isOverride;
30+
public final List<TypeElement> exceptionTypes = new ArrayList<>();
3031

3132
public MethodInjectionTarget(TypeElement enclosingClass, String methodName, boolean isOverride) {
3233
this.enclosingClass = enclosingClass;

toothpick-compiler/src/test/java/toothpick/compiler/memberinjector/MethodMemberInjectorTest.java

+105
Original file line numberDiff line numberDiff line change
@@ -392,4 +392,109 @@ public void testOverrideMethodInjection() {
392392
.and()
393393
.generatesSources(expectedSource);
394394
}
395+
396+
@Test
397+
public void testMethodInjection_withException() {
398+
JavaFileObject source =
399+
JavaFileObjects.forSourceString(
400+
"test.TestMethodInjection",
401+
Joiner.on('\n')
402+
.join( //
403+
"package test;", //
404+
"import javax.inject.Inject;", //
405+
"public class TestMethodInjection {", //
406+
" @Inject", //
407+
" public void m(Foo foo) throws Exception {}", //
408+
"}", //
409+
"class Foo {}" //
410+
));
411+
412+
JavaFileObject expectedSource =
413+
JavaFileObjects.forSourceString(
414+
"test/TestMethodInjection__MemberInjector",
415+
Joiner.on('\n')
416+
.join( //
417+
"package test;", //
418+
"", //
419+
"import java.lang.Exception;", //
420+
"import java.lang.Override;", //
421+
"import java.lang.RuntimeException;", //
422+
"import toothpick.MemberInjector;", //
423+
"import toothpick.Scope;", //
424+
"", //
425+
"public final class TestMethodInjection__MemberInjector implements MemberInjector<TestMethodInjection> {", //
426+
" @Override", //
427+
" public void inject(TestMethodInjection target, Scope scope) {", //
428+
" Foo param1 = scope.getInstance(Foo.class);", //
429+
" try {", //
430+
" target.m(param1);", //
431+
" } catch(Exception e1) {", //
432+
" throw new RuntimeException(e1);", //
433+
" } ", //
434+
" }", //
435+
"}" //
436+
));
437+
438+
assert_()
439+
.about(javaSource())
440+
.that(source)
441+
.processedWith(memberInjectorProcessors())
442+
.compilesWithoutError()
443+
.and()
444+
.generatesSources(expectedSource);
445+
}
446+
447+
@Test
448+
public void testMethodInjection_withExceptions() {
449+
JavaFileObject source =
450+
JavaFileObjects.forSourceString(
451+
"test.TestMethodInjection",
452+
Joiner.on('\n')
453+
.join( //
454+
"package test;", //
455+
"import javax.inject.Inject;", //
456+
"public class TestMethodInjection {", //
457+
" @Inject", //
458+
" public void m(Foo foo) throws Exception, Throwable {}", //
459+
"}", //
460+
"class Foo {}" //
461+
));
462+
463+
JavaFileObject expectedSource =
464+
JavaFileObjects.forSourceString(
465+
"test/TestMethodInjection__MemberInjector",
466+
Joiner.on('\n')
467+
.join( //
468+
"package test;", //
469+
"", //
470+
"import java.lang.Exception;", //
471+
"import java.lang.Override;", //
472+
"import java.lang.RuntimeException;", //
473+
"import java.lang.Throwable;", //
474+
"import toothpick.MemberInjector;", //
475+
"import toothpick.Scope;", //
476+
"", //
477+
"public final class TestMethodInjection__MemberInjector implements MemberInjector<TestMethodInjection> {", //
478+
" @Override", //
479+
" public void inject(TestMethodInjection target, Scope scope) {", //
480+
" Foo param1 = scope.getInstance(Foo.class);", //
481+
" try {", //
482+
" target.m(param1);", //
483+
" } catch(Exception e1) {", //
484+
" throw new RuntimeException(e1);", //
485+
" } catch(Throwable e2) {", //
486+
" throw new RuntimeException(e2);", //
487+
" } ", //
488+
" }", //
489+
"}" //
490+
));
491+
492+
assert_()
493+
.about(javaSource())
494+
.that(source)
495+
.processedWith(memberInjectorProcessors())
496+
.compilesWithoutError()
497+
.and()
498+
.generatesSources(expectedSource);
499+
}
395500
}

toothpick-runtime/src/main/java/toothpick/ScopeImpl.java

+6-7
Original file line numberDiff line numberDiff line change
@@ -315,8 +315,7 @@ private void installModule(boolean isTestModule, Module module) {
315315
* it is scoped, it will be scoped in the appropriate scope, if not it will be added to the pool
316316
* of un-scoped providers. Note that
317317
*
318-
* @param clazz the {@link Class} of {@code T} for which we lookup an {@link
319-
* InternalProvider}.
318+
* @param clazz the {@link Class} of {@code T} for which we lookup an {@link InternalProvider}.
320319
* @param bindingName the potential name of the provider when it was bound (which means we always
321320
* returned a scoped provider if name is not null).
322321
* @param <T> the type for which we lookup an {@link InternalProvider}.
@@ -373,7 +372,8 @@ private void installModule(boolean isTestModule, Module module) {
373372
if (factory.hasScopeAnnotation()) {
374373
// the new provider will have to work in the current scope
375374
Scope targetScope = factory.getTargetScope(this);
376-
InternalScopedProvider<? extends T> newProvider = new InternalScopedProvider<>(targetScope, factory);
375+
InternalScopedProvider<? extends T> newProvider =
376+
new InternalScopedProvider<>(targetScope, factory);
377377
// it is bound to its target scope only if it has a scope annotation.
378378
// lock free installing a provider means there could have been one set concurrently since last
379379
// testing
@@ -402,8 +402,7 @@ private void installModule(boolean isTestModule, Module module) {
402402
* @return the scoped provider for class {@code clazz} and {@code bindingName}. Returns {@code
403403
* null} is there is no such scoped provider.
404404
*/
405-
private <T> InternalProvider<? extends T> getScopedProvider(
406-
Class<T> clazz, String bindingName) {
405+
private <T> InternalProvider<? extends T> getScopedProvider(Class<T> clazz, String bindingName) {
407406
return getInternalProvider(clazz, bindingName, true);
408407
}
409408

@@ -508,8 +507,8 @@ private <T> InternalProvider<? extends T> installUnScopedProvider(
508507
* @param <T> the type of {@code clazz}.
509508
* <p>Note to maintainers : we don't use this method directly, both {@link
510509
* #installScopedProvider(Class, String, InternalProvider, boolean)} and {@link
511-
* #installUnScopedProvider(Class, String, InternalProvider)} are a facade of this method
512-
* and make the calls more clear.
510+
* #installUnScopedProvider(Class, String, InternalProvider)} are a facade of this method and
511+
* make the calls more clear.
513512
*/
514513
private <T> InternalProvider<? extends T> installInternalProvider(
515514
Class<T> clazz,

toothpick-runtime/src/test/java/toothpick/ReleasableTest.java

+5-10
Original file line numberDiff line numberDiff line change
@@ -79,8 +79,7 @@ public void testReleasableSingleton_byBinding_shouldBeReleased() throws Exceptio
7979
});
8080
IFoo foo = scope.getInstance(IFoo.class);
8181
IFoo foo2 = scope.getInstance(IFoo.class);
82-
InternalProvider internalProvider =
83-
scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
82+
InternalProvider internalProvider = scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
8483
assertThat(internalProvider.instance, notNullValue());
8584

8685
// WHEN
@@ -106,8 +105,7 @@ public void testReleasableSingleton_byBinding_shouldBeReleased() throws Exceptio
106105
scope.supportScopeAnnotation(CustomScope.class);
107106
IFoo foo = scope.getInstance(IFoo.class);
108107
IFoo foo2 = scope.getInstance(IFoo.class);
109-
InternalProvider internalProvider =
110-
scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
108+
InternalProvider internalProvider = scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
111109
// provider instance is released when it creates a singleton
112110
// whether or not it is a singleton and whether or not releasable
113111
assertThat(internalProvider.providerInstance, notNullValue());
@@ -146,8 +144,7 @@ public void testReleasableSingleton_byBinding_shouldBeReleased() throws Exceptio
146144
});
147145
IFoo foo = scope.getInstance(IFoo.class);
148146
IFoo foo2 = scope.getInstance(IFoo.class);
149-
InternalProvider internalProvider =
150-
scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
147+
InternalProvider internalProvider = scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
151148
// provider instance is released when it creates a singleton
152149
// whether or not it is a singleton and whether or not releasable
153150
assertThat(internalProvider.providerInstance, notNullValue());
@@ -176,8 +173,7 @@ public void testProviderReleasableSingleton_byAnnotation_shouldReleaseProviderIn
176173
scope.supportScopeAnnotation(CustomScope.class);
177174
IFoo foo = scope.getInstance(IFoo.class);
178175
IFoo foo2 = scope.getInstance(IFoo.class);
179-
InternalProvider internalProvider =
180-
scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
176+
InternalProvider internalProvider = scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
181177
// provider instance is released when it creates a singleton
182178
// whether or not it is a singleton and whether or not releasable
183179
assertThat(internalProvider.providerInstance, notNullValue());
@@ -206,8 +202,7 @@ public void testProviderReleasableSingleton_byBinding_shouldReleaseProviderInsta
206202
scope.supportScopeAnnotation(CustomScope.class);
207203
IFoo foo = scope.getInstance(IFoo.class);
208204
IFoo foo2 = scope.getInstance(IFoo.class);
209-
InternalProvider internalProvider =
210-
scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
205+
InternalProvider internalProvider = scope.mapClassesToUnNamedScopedProviders.get(IFoo.class);
211206
// provider instance is released when it creates a singleton
212207
// whether or not it is a singleton and whether or not releasable
213208
assertThat(internalProvider.providerInstance, notNullValue());

0 commit comments

Comments
 (0)