Skip to content

Commit cbdc6a2

Browse files
committed
Support for @EnumSelector annotated methods.
1 parent ea72ff2 commit cbdc6a2

9 files changed

Lines changed: 283 additions & 50 deletions

File tree

Documentation.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -597,6 +597,12 @@ public interface MyHelper {
597597
// Provide documentation for the method.
598598
@Documented(String[] value)
599599

600+
// Marks a method has being the target of an enum
601+
// selector. The method must accept zero arguments,
602+
// and return a Consumer instance which accepts the
603+
// enum value.
604+
@EnumSelector
605+
600606
// Provide an alternate name to use for the interface
601607
// or class's corresponding generated name, instead of
602608
// the default (`XyzBuilder`).

flapi-runtime/src/main/java/unquietcode/tools/flapi/runtime/BlockInvocationHandler.java

Lines changed: 58 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,37 @@ private Object invokeAndReturn(Method method, Object[] originalArgs, Object prox
101101
chain = info.chainInfo();
102102
}
103103

104-
return invokeAndReturn(method, originalArgs, proxy, info, chain);
104+
if (method.getAnnotation(EnumSelectorHint.class) != null) {
105+
return invokeEnumSelector(method, originalArgs, proxy, info, chain);
106+
} else {
107+
return invokeAndReturn(method, originalArgs, proxy, info, chain);
108+
}
109+
}
110+
111+
private Object invokeEnumSelector(Method method, Object[] originalArgs, final Object originalProxy, final MethodInfo info, final ChainInfo[] chain) {
112+
final Method helperMethod = SpringMethodUtils.findMethod(helper.getClass(), method.getName(), method.getParameterTypes());
113+
final Class<? extends Enum> enumType = method.getAnnotation(EnumSelectorHint.class).value();
114+
115+
@SuppressWarnings("unchecked")
116+
final Consumer<Enum<?>> consumer = (Consumer<Enum<?>>) invokeMethod(helperMethod, Arrays.asList(originalArgs));
117+
118+
if (consumer == null) {
119+
throw new IllegalStateException("consumer instance cannot be null");
120+
}
121+
122+
return Proxy.newProxyInstance(Thread.currentThread().getContextClassLoader(), new Class[]{method.getReturnType()}, new InvocationHandler() {
123+
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
124+
125+
// lookup enum by method name
126+
Enum<?> enumValue = Enum.valueOf(enumType, method.getName());
127+
128+
// accept
129+
consumer.accept(enumValue);
130+
131+
// proceed
132+
return computeReturnValue(method, originalProxy, info, chain.length, helperMethod, consumer);
133+
}
134+
});
105135
}
106136

107137
private Object invokeAndReturn(Method method, Object[] originalArgs, Object proxy, MethodInfo info, ChainInfo[] chain) {
@@ -130,27 +160,8 @@ private Object invokeAndReturn(Method method, Object[] originalArgs, Object prox
130160
throw new IllegalStateException("unable to locate method '"+method.getName()+"' on helper");
131161
}
132162

133-
// make accessible if not (debatable as to whether this is a good idea)
134-
if (!helperMethod.isAccessible()) {
135-
helperMethod.setAccessible(true);
136-
}
137-
138-
final Object[] newArgsArray = newArgs.toArray(new Object[newArgs.size()]);
139-
140-
// invoke method
141-
Object result;
142-
try {
143-
result = helperMethod.invoke(helper, newArgsArray);
144-
} catch (IllegalAccessException ex) {
145-
throw new IllegalStateException(ex);
146-
} catch (InvocationTargetException ex) {
147-
if (ex.getTargetException() instanceof RuntimeException) {
148-
throw (RuntimeException) ex.getTargetException();
149-
} else {
150-
throw new RuntimeException(ex.getTargetException());
151-
}
152-
}
153-
163+
// invoke the method
164+
Object result = invokeMethod(helperMethod, newArgs);
154165
Object _returnValue = computeReturnValue(method, proxy, info, depth, helperMethod, result);
155166

156167
// unwrap helper results
@@ -171,6 +182,31 @@ private Object invokeAndReturn(Method method, Object[] originalArgs, Object prox
171182
return _returnValue;
172183
}
173184

185+
private Object invokeMethod(Method helperMethod, List<Object> args) {
186+
187+
// make accessible if not (debatable as to whether this is a good idea)
188+
if (!helperMethod.isAccessible()) {
189+
helperMethod.setAccessible(true);
190+
}
191+
192+
final Object[] argsArray = args.toArray(new Object[args.size()]);
193+
194+
// invoke method
195+
Object result;
196+
try {
197+
result = helperMethod.invoke(helper, argsArray);
198+
} catch (IllegalAccessException ex) {
199+
throw new IllegalStateException(ex);
200+
} catch (InvocationTargetException ex) {
201+
if (ex.getTargetException() instanceof RuntimeException) {
202+
throw (RuntimeException) ex.getTargetException();
203+
} else {
204+
throw new RuntimeException(ex.getTargetException());
205+
}
206+
}
207+
return result;
208+
}
209+
174210
private Object computeReturnValue(Method method, Object proxy, MethodInfo info, int depth, Method helperMethod, Object result) {
175211
switch (info.type()) {
176212
case Ascending: {
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/*********************************************************************
2+
Copyright 2015 the Flapi authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
********************************************************************/
16+
17+
package unquietcode.tools.flapi.runtime;
18+
19+
/**
20+
* Simple little ditty to consume things.
21+
*
22+
* This is meant to be replaced by the real type
23+
* contained within Java 8, whenever JDK 7 support
24+
* is discontinued.
25+
*
26+
* @author Ben Fagin
27+
* @version 2015-02-18
28+
*/
29+
public interface Consumer<T> {
30+
void accept(T element);
31+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*********************************************************************
2+
Copyright 2015 the Flapi authors
3+
4+
Licensed under the Apache License, Version 2.0 (the "License");
5+
you may not use this file except in compliance with the License.
6+
You may obtain a copy of the License at
7+
8+
http://www.apache.org/licenses/LICENSE-2.0
9+
10+
Unless required by applicable law or agreed to in writing, software
11+
distributed under the License is distributed on an "AS IS" BASIS,
12+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
See the License for the specific language governing permissions and
14+
limitations under the License.
15+
********************************************************************/
16+
17+
package unquietcode.tools.flapi.runtime;
18+
19+
import java.lang.annotation.ElementType;
20+
import java.lang.annotation.Retention;
21+
import java.lang.annotation.RetentionPolicy;
22+
import java.lang.annotation.Target;
23+
24+
/**
25+
* @author Ben Fagin
26+
* @version 2015-02-18
27+
*/
28+
@Target(ElementType.METHOD)
29+
@Retention(RetentionPolicy.RUNTIME)
30+
public @interface EnumSelectorHint {
31+
Class<? extends Enum> value();
32+
}

flapi-runtime/src/main/java/unquietcode/tools/flapi/runtime/Helpers.java

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.lang.reflect.Proxy;
2222
import java.util.HashMap;
2323
import java.util.Map;
24+
import java.util.concurrent.atomic.AtomicReference;
2425

2526
public final class Helpers {
2627

@@ -81,4 +82,20 @@ private Method getMethod(String methodName, Class[] parameterTypes) {
8182
}
8283
});
8384
}
85+
86+
public interface Invoker<In> {
87+
void call(AtomicReference<In> in);
88+
}
89+
90+
public static <T> T invoke(Invoker<T> invoker) {
91+
AtomicReference<T> reference = new AtomicReference<>();
92+
93+
try {
94+
invoker.call(reference);
95+
} catch (Exception ex) {
96+
throw new RuntimeException(ex);
97+
}
98+
99+
return reference.get();
100+
}
84101
}

src/main/java/unquietcode/tools/flapi/IntrospectorSupport.java

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -96,6 +96,7 @@ else if (parameterType.isArray()) {
9696
signature.append(makeTypeWithGenerics(typeName, generics));
9797

9898
// parameter name
99+
// TODO improve in JDK8
99100
signature.append(" p").append(i);
100101
}
101102

@@ -115,9 +116,13 @@ protected static String makeTypeWithGenerics(String base, Class<?>[] generics) {
115116

116117
for (int p = 0; p < generics.length; ++p) {
117118
if (p != 0) { sb.append(", "); }
118-
119119
Class<?> generic = generics[p];
120-
sb.append(generic.getName());
120+
121+
if (generic == null) {
122+
sb.append("?");
123+
} else {
124+
sb.append(generic.getName());
125+
}
121126
}
122127

123128
sb.append("> ");

0 commit comments

Comments
 (0)