Skip to content

Commit

Permalink
Remove duplication in Reflections implementations.
Browse files Browse the repository at this point in the history
Renamed Reflections to ClassFinder and removed generic methods.
Created a new Reflections class that delegates to a ClassFinder.
  • Loading branch information
aslakhellesoy committed Sep 8, 2013
1 parent 022f12f commit cb3a4cd
Show file tree
Hide file tree
Showing 17 changed files with 180 additions and 175 deletions.
12 changes: 6 additions & 6 deletions android/src/cucumber/api/android/CucumberInstrumentation.java
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,8 @@
import cucumber.runtime.android.AndroidFormatter;
import cucumber.runtime.android.AndroidObjectFactory;
import cucumber.runtime.android.AndroidResourceLoader;
import cucumber.runtime.android.DexReflections;
import cucumber.runtime.io.Reflections;
import cucumber.runtime.android.DexClassFinder;
import cucumber.runtime.ClassFinder;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.java.JavaBackend;
import cucumber.runtime.java.ObjectFactory;
Expand Down Expand Up @@ -54,10 +54,10 @@ public void onCreate(Bundle arguments) {
classLoader = context.getClassLoader();

String apkPath = context.getPackageCodePath();
Reflections reflections = new DexReflections(newDexFile(apkPath));
ClassFinder classFinder = new DexClassFinder(newDexFile(apkPath));

Class<?> optionsAnnotatedClass = null;
for (Class<?> clazz : reflections.getDescendants(Object.class, context.getPackageName())) {
for (Class<?> clazz : classFinder.getDescendants(Object.class, context.getPackageName())) {
if (clazz.isAnnotationPresent(CucumberOptions.class)) {
Log.d(TAG, "Found CucumberOptions in class " + clazz.getName());
optionsAnnotatedClass = clazz;
Expand All @@ -74,9 +74,9 @@ public void onCreate(Bundle arguments) {
resourceLoader = new AndroidResourceLoader(context);

List<Backend> backends = new ArrayList<Backend>();
ObjectFactory delegateObjectFactory = JavaBackend.loadObjectFactory(reflections);
ObjectFactory delegateObjectFactory = JavaBackend.loadObjectFactory(classFinder);
AndroidObjectFactory objectFactory = new AndroidObjectFactory(delegateObjectFactory, this);
backends.add(new JavaBackend(objectFactory, reflections));
backends.add(new JavaBackend(objectFactory, classFinder));
runtime = new Runtime(resourceLoader, classLoader, backends, runtimeOptions);

start();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,28 +1,22 @@
package cucumber.runtime.android;

import cucumber.runtime.CucumberException;
import cucumber.runtime.io.Reflections;
import cucumber.runtime.ClassFinder;
import dalvik.system.DexFile;

import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Enumeration;
import java.util.List;

public class DexReflections implements Reflections {
private static final ClassLoader CLASS_LOADER = DexReflections.class.getClassLoader();
public class DexClassFinder implements ClassFinder {
private static final ClassLoader CLASS_LOADER = DexClassFinder.class.getClassLoader();
private final DexFile dexFile;

public DexReflections(DexFile dexFile) {
public DexClassFinder(DexFile dexFile) {
this.dexFile = dexFile;
}

@Override
public Collection<Class<? extends Annotation>> getAnnotations(String packageName) {
return getDescendants(Annotation.class, packageName);
}

@Override
public <T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packageName) {
List<Class<? extends T>> result = new ArrayList<Class<? extends T>>();
Expand Down Expand Up @@ -60,15 +54,4 @@ private boolean isGenerated(String className) {
String shortName = lastDotIndex == -1 ? className : className.substring(lastDotIndex + 1);
return shortName.equals("Manifest") || shortName.equals("R") || shortName.startsWith("R$");
}


@Override
public <T> T instantiateExactlyOneSubclass(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
throw new UnsupportedOperationException();
}

@Override
public <T> Collection<? extends T> instantiateSubclasses(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
throw new UnsupportedOperationException();
}
}
7 changes: 6 additions & 1 deletion core/src/main/java/cucumber/api/cli/Main.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package cucumber.api.cli;

import cucumber.runtime.ClassFinder;
import cucumber.runtime.Runtime;
import cucumber.runtime.RuntimeOptions;
import cucumber.runtime.io.MultiLoader;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.ResourceLoaderClassFinder;

import java.io.IOException;

Expand All @@ -15,7 +18,9 @@ public static void main(String[] argv) throws Throwable {
public static void run(String[] argv, ClassLoader classLoader) throws IOException {
RuntimeOptions runtimeOptions = new RuntimeOptions(System.getProperties(), argv);

Runtime runtime = new Runtime(new MultiLoader(classLoader), classLoader, runtimeOptions);
ResourceLoader resourceLoader = new MultiLoader(classLoader);
ClassFinder classFinder = new ResourceLoaderClassFinder(resourceLoader, classLoader);
Runtime runtime = new Runtime(resourceLoader, classFinder, classLoader, runtimeOptions);
runtime.writeStepdefsJson();
runtime.run();
System.exit(runtime.exitStatus());
Expand Down
7 changes: 7 additions & 0 deletions core/src/main/java/cucumber/runtime/ClassFinder.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package cucumber.runtime;

import java.util.Collection;

public interface ClassFinder {
<T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packageName);
}
59 changes: 59 additions & 0 deletions core/src/main/java/cucumber/runtime/Reflections.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package cucumber.runtime;

import java.lang.reflect.InvocationTargetException;
import java.util.Collection;
import java.util.HashSet;

public class Reflections {
private final ClassFinder classFinder;

public Reflections(ClassFinder classFinder) {
this.classFinder = classFinder;
}

public <T> T instantiateExactlyOneSubclass(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
Collection<? extends T> instances = instantiateSubclasses(parentType, packageName, constructorParams, constructorArgs);
if (instances.size() == 1) {
return instances.iterator().next();
} else if (instances.size() == 0) {
throw new CucumberException("Couldn't find a single implementation of " + parentType);
} else {
throw new CucumberException("Expected only one instance, but found too many: " + instances);
}
}

public <T> Collection<? extends T> instantiateSubclasses(Class<T> parentType, String packageName, Class[] constructorParams, Object[] constructorArgs) {
Collection<T> result = new HashSet<T>();
for (Class<? extends T> clazz : classFinder.getDescendants(parentType, packageName)) {
if (Utils.isInstantiable(clazz) && hasConstructor(clazz, constructorParams)) {
result.add(newInstance(constructorParams, constructorArgs, clazz));
}
}
return result;
}

public <T> T newInstance(Class[] constructorParams, Object[] constructorArgs, Class<? extends T> clazz) {
try {
return clazz.getConstructor(constructorParams).newInstance(constructorArgs);
} catch (InstantiationException e) {
throw new CucumberException(e);
} catch (IllegalAccessException e) {
throw new CucumberException(e);
} catch (InvocationTargetException e) {
throw new CucumberException(e);
} catch (NoSuchMethodException e) {
throw new CucumberException(e);
}
}

private boolean hasConstructor(Class<?> clazz, Class[] paramTypes) {
try {
clazz.getConstructor(paramTypes);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}


}
10 changes: 5 additions & 5 deletions core/src/main/java/cucumber/runtime/Runtime.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import cucumber.api.Pending;
import cucumber.runtime.io.ResourceLoader;
import cucumber.runtime.io.ResourceLoaderReflections;
import cucumber.runtime.model.CucumberFeature;
import cucumber.runtime.snippets.SummaryPrinter;
import cucumber.runtime.xstream.LocalizedXStreams;
Expand Down Expand Up @@ -60,8 +59,8 @@ public class Runtime implements UnreportedStepExecutor {
private boolean skipNextStep = false;
private ScenarioImpl scenarioResult = null;

public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, RuntimeOptions runtimeOptions) {
this(resourceLoader, classLoader, loadBackends(resourceLoader, classLoader), runtimeOptions);
public Runtime(ResourceLoader resourceLoader, ClassFinder classFinder, ClassLoader classLoader, RuntimeOptions runtimeOptions) {
this(resourceLoader, classLoader, loadBackends(resourceLoader, classFinder), runtimeOptions);
}

public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collection<? extends Backend> backends, RuntimeOptions runtimeOptions) {
Expand Down Expand Up @@ -92,8 +91,9 @@ public Runtime(ResourceLoader resourceLoader, ClassLoader classLoader, Collectio
}
}

private static Collection<? extends Backend> loadBackends(ResourceLoader resourceLoader, ClassLoader classLoader) {
return new ResourceLoaderReflections(resourceLoader, classLoader).instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader});
private static Collection<? extends Backend> loadBackends(ResourceLoader resourceLoader, ClassFinder classFinder) {
Reflections reflections = new Reflections(classFinder);
return reflections.instantiateSubclasses(Backend.class, "cucumber.runtime", new Class[]{ResourceLoader.class}, new Object[]{resourceLoader});
}

public void addError(Throwable error) {
Expand Down
11 changes: 2 additions & 9 deletions core/src/main/java/cucumber/runtime/Utils.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.List;
import java.util.Map;

Expand All @@ -25,15 +27,6 @@ public static boolean isInstantiable(Class<?> clazz) {
return Modifier.isPublic(clazz.getModifiers()) && !Modifier.isAbstract(clazz.getModifiers()) && !isNonStaticInnerClass;
}

public static boolean hasConstructor(Class<?> clazz, Class[] paramTypes) {
try {
clazz.getConstructor(paramTypes);
return true;
} catch (NoSuchMethodException e) {
return false;
}
}

public static Object invoke(final Object target, final Method method, int timeoutMillis, final Object... args) throws Throwable {
return Timeout.timeout(new Timeout.Callback<Object>() {
@Override
Expand Down
14 changes: 0 additions & 14 deletions core/src/main/java/cucumber/runtime/io/Reflections.java

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package cucumber.runtime.io;

import cucumber.runtime.ClassFinder;

import java.io.File;
import java.util.Collection;
import java.util.HashSet;

public class ResourceLoaderClassFinder implements ClassFinder {
private final ResourceLoader resourceLoader;
private final ClassLoader classLoader;

public ResourceLoaderClassFinder(ResourceLoader resourceLoader, ClassLoader classLoader) {
this.resourceLoader = resourceLoader;
this.classLoader = classLoader;
}

@Override
public <T> Collection<Class<? extends T>> getDescendants(Class<T> parentType, String packageName) {
Collection<Class<? extends T>> result = new HashSet<Class<? extends T>>();
String packagePath = "classpath:" + packageName.replace('.', '/').replace(File.separatorChar, '/');
for (Resource classResource : resourceLoader.resources(packagePath, ".class")) {
String className = classResource.getClassName();
Class<?> clazz = loadClass(className, classLoader);
if (clazz != null && !parentType.equals(clazz) && parentType.isAssignableFrom(clazz)) {
result.add(clazz.asSubclass(parentType));
}
}
return result;
}

private Class<?> loadClass(String className, ClassLoader classLoader) {
try {
return classLoader.loadClass(className);
} catch (ClassNotFoundException ignore) {
return null;
} catch (NoClassDefFoundError ignore) {
return null;
}
}
}

This file was deleted.

Loading

0 comments on commit cb3a4cd

Please sign in to comment.