Skip to content

Commit

Permalink
add gather operations
Browse files Browse the repository at this point in the history
  • Loading branch information
brianm committed Apr 28, 2010
1 parent 52fde9d commit c711c43
Show file tree
Hide file tree
Showing 10 changed files with 424 additions and 121 deletions.
4 changes: 4 additions & 0 deletions README
@@ -0,0 +1,4 @@
# TODO Items
* [ ] Convert Authority to a Guard
* [ ] No-Match behavior?
* [ ] Some serious code-smell refactorings :-)
7 changes: 7 additions & 0 deletions pom.xml
Expand Up @@ -7,6 +7,13 @@
<version>0.0.1-SNAPSHOT</version>
<name>timebox</name>
<url>http://maven.apache.org</url>
<licenses>
<license>
<name>Apache License 2.0</name>
<url>http://www.apache.org/licenses/LICENSE-2.0.html</url>
<distribution>repo</distribution>
</license>
</licenses>
<dependencies>

<dependency>
Expand Down
9 changes: 9 additions & 0 deletions src/main/java/com/ning/timebox/Gather.java
@@ -0,0 +1,9 @@
package com.ning.timebox;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

@Retention(RetentionPolicy.RUNTIME)
public @interface Gather
{
}
1 change: 1 addition & 0 deletions src/main/java/com/ning/timebox/GuardHouse.java
Expand Up @@ -7,4 +7,5 @@ public interface GuardHouse
{
Predicate<Object[]> buildMethodPredicate(Annotation a, Object handler, Method m);
Predicate<Object> buildArgumentPredicate(Annotation a, Object handler, Method m, int argumentIndex);
Predicate<Object> buildGatherPredicate(Annotation a, Object handler, Method m, Class expectedType, int argumentIndex);
}
42 changes: 41 additions & 1 deletion src/main/java/com/ning/timebox/GuardMethodGuardHouse.java
Expand Up @@ -44,7 +44,7 @@ public Predicate<Object> buildArgumentPredicate(Annotation a, final Object targe
String method_name = ((GuardMethod) a).value();
final Method guard_method;
try {
guard_method = target.getClass().getMethod(method_name, m.getParameterTypes());
guard_method = target.getClass().getMethod(method_name, m.getParameterTypes()[argumentIndex]);
}
catch (NoSuchMethodException e) {
throw new IllegalStateException("no method with correct signature matches " + method_name, e);
Expand Down Expand Up @@ -73,4 +73,44 @@ public boolean test(Object arg)
}
};
}

public Predicate<Object> buildGatherPredicate(Annotation a,
final Object target,
Method m,
Class expectedType,
int argumentIndex)
{
String method_name = ((GuardMethod) a).value();
final Method guard_method;
try {
guard_method = target.getClass().getMethod(method_name, expectedType);
}
catch (NoSuchMethodException e) {
throw new IllegalStateException("no method with correct signature matches " + method_name, e);
}

if (!(Boolean.class.equals(guard_method.getReturnType())
|| boolean.class.equals(guard_method.getReturnType())))
{
throw new IllegalStateException("guard method, " + method_name + " must return boolean");
}

return new Predicate<Object>()
{

public boolean test(Object arg)
{
try {
return (Boolean) guard_method.invoke(target, arg);
}
catch (IllegalAccessException e) {
throw new IllegalStateException(e);
}
catch (InvocationTargetException e) {
throw new IllegalStateException(e);
}
}
};

}
}
176 changes: 176 additions & 0 deletions src/main/java/com/ning/timebox/Handler.java
@@ -0,0 +1,176 @@
package com.ning.timebox;

import java.lang.annotation.Annotation;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.ParameterizedType;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

class Handler
{
private final Object[] values;
private final long[] authorities;
private final Object target;
private final Method method;
private final Class[] types;
private final List<Predicate<Object[]>> methodTests = new ArrayList<Predicate<Object[]>>();
private final List<Collection<Predicate>> parameterTests;

// null means do not gather
private final Class[] gatheredTypes;

public Handler(Factory factory, Object target, Method method)
{
this.types = method.getParameterTypes();
this.target = target;
this.method = method;
this.values = new Object[types.length];
this.authorities = new long[types.length];
this.gatheredTypes = new Class[types.length];
this.parameterTests = new ArrayList<Collection<Predicate>>(method.getParameterTypes().length);

for (Annotation annotation : method.getAnnotations()) {
for (Class<?> iface : annotation.getClass().getInterfaces()) {
if (iface.isAnnotationPresent(GuardAnnotation.class)) {
GuardAnnotation sp = iface.getAnnotation(GuardAnnotation.class);
final GuardHouse p;
try {
p = factory.instantiate(sp.value());
}
catch (Exception e) {
throw new IllegalStateException(e);
}
methodTests.add(p.buildMethodPredicate(annotation, target, method));
}
}
}

for (int i = 0; i < method.getParameterTypes().length; i++) {
parameterTests.add(new ArrayList<Predicate>());
}

// now prefill authorities to required authority - 1,
// so that when needed authoity comes in, it is at higher
Annotation[][] param_annos = method.getParameterAnnotations();
for (int i = 0; i < param_annos.length; i++) {
// loop through each parameter
authorities[i] = Long.MIN_VALUE;

GatherData gd = isGather(param_annos[i], i);
if (gd.isGather) {
this.values[gd.gatherIndex] = new ArrayList();
this.gatheredTypes[i] = gd.gatherType;
}

for (Annotation annotation : param_annos[i]) {

if (annotation instanceof Authority) {
authorities[i] = ((Authority) annotation).value();
}

for (Class<?> iface : annotation.getClass().getInterfaces()) {
if (iface.isAnnotationPresent(GuardAnnotation.class)) {
GuardAnnotation sp = iface.getAnnotation(GuardAnnotation.class);
final GuardHouse p;
try {
p = factory.instantiate(sp.value());
}
catch (Exception e) {
throw new IllegalStateException(e);
}
final Predicate<Object> pred;
if (gd.isGather) {
pred = p.buildGatherPredicate(annotation, target, method, gd.gatherType, i);
}
else {
pred = p.buildArgumentPredicate(annotation, target, method, i);
}
parameterTests.get(i).add(pred);
}
}
}
}
}


private class GatherData
{
boolean isGather = false;
Class gatherType;
int gatherIndex;
}

private GatherData isGather(Annotation[] annos, int parameterIndex)
{
GatherData gd = new GatherData();
gd.gatherIndex = parameterIndex;
for (Annotation annotation : annos) {
if (annotation instanceof Gather) {
Class param_type = method.getParameterTypes()[parameterIndex];
if (!Collection.class.isAssignableFrom(param_type)) {
throw new IllegalArgumentException("Can only @Gather against Collection");
}

// I hate always having to do this
ParameterizedType gen_type = (ParameterizedType) method.getGenericParameterTypes()[parameterIndex];
gd.gatherType = (Class) gen_type.getActualTypeArguments()[0];
gd.isGather = true;
break;
}
}
return gd;
}

public void provide(Class type, Object value, long authority)
{
for (int i = 0; i < types.length; i++) {
if (types[i].isAssignableFrom(type)
&& authorities[i] <= authority
&& testParameterPredicates(value, parameterTests.get(i)))
{
values[i] = value;
authorities[i] = authority;
return;
}

if (gatheredTypes[i] != null
&& gatheredTypes[i].isAssignableFrom(type)
&& testParameterPredicates(value, parameterTests.get(i)))
{
((Collection) values[i]).add(value);
}
}
}

private boolean testParameterPredicates(Object value, Collection<Predicate> predicates)
{
for (Predicate predicate : predicates) {
if (!predicate.test(value)) {
return false;
}
}
return true;
}

public boolean isSatisfied()
{
for (Object value : values) {
if (value == null) {
return false;
}
}
for (Predicate<Object[]> test : methodTests) {
if (!test.test(values)) {
return false;
}
}
return true;
}

public void handle() throws InvocationTargetException, IllegalAccessException
{
method.invoke(target, values);
}
}

0 comments on commit c711c43

Please sign in to comment.