Skip to content

Commit

Permalink
use reflections library for class search
Browse files Browse the repository at this point in the history
  • Loading branch information
Nitay Joffe committed Oct 26, 2013
1 parent c5d9a4e commit d67fe14
Show file tree
Hide file tree
Showing 10 changed files with 71 additions and 104 deletions.
1 change: 1 addition & 0 deletions build.xml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
<pathelement location="src/external/Guava/guava-14.0.1.jar"/>
<pathelement location="src/external/JUnit/junit-4.11.jar"/>
<pathelement location="src/external/JUnit/hamcrest-core-1.3.jar"/>
<pathelement location="src/external/reflections/reflections-0.9.9-RC1.jar"/>
</path>
<target name="init">
<mkdir dir="bin"/>
Expand Down
6 changes: 6 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@
<dep.jtidy.version>r938</dep.jtidy.version>
<dep.junit.version>4.11</dep.junit.version>
<dep.jython.version>2.7-b1</dep.jython.version>
<dep.reflections.version>0.9.9-RC1</dep.reflections.version>
<dep.xml-apis.version>1.3.04</dep.xml-apis.version>
</properties>

Expand Down Expand Up @@ -93,6 +94,11 @@
<artifactId>jython</artifactId>
<version>${dep.jython.version}</version>
</dependency>
<dependency>
<groupId>org.reflections</groupId>
<artifactId>reflections</artifactId>
<version>${dep.reflections.version}</version>
</dependency>

<!-- TODO: junit should really only be used for testing... -->
<dependency>
Expand Down
Binary file not shown.
Binary file not shown.
20 changes: 11 additions & 9 deletions src/org/ggp/base/apps/kiosk/Kiosk.java
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@
import java.awt.event.ItemListener;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

import javax.swing.JButton;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
Expand All @@ -26,6 +26,7 @@
import javax.swing.ListSelectionModel;
import javax.swing.border.TitledBorder;

import com.google.common.collect.Lists;
import org.ggp.base.player.GamePlayer;
import org.ggp.base.player.gamer.Gamer;
import org.ggp.base.player.gamer.exception.AbortingException;
Expand All @@ -43,7 +44,8 @@
import org.ggp.base.util.observer.Observer;
import org.ggp.base.util.reflection.ProjectSearcher;
import org.ggp.base.util.symbol.grammar.SymbolPool;
import org.ggp.base.util.ui.*;
import org.ggp.base.util.ui.NativeUI;
import org.ggp.base.util.ui.PublishButton;

/**
* Kiosk is a program for running two-player human-vs-computer matches
Expand Down Expand Up @@ -92,24 +94,24 @@ public void run() {
private final JPanel theGUIPanel;

private final JComboBox<String> playerComboBox;
private List<Class<?>> gamers = null;
private List<Class<? extends Gamer>> gamers = null;
private final JTextField computerAddress;

private final GameRepository theRepository;

public Kiosk()
{
super(new GridBagLayout());
super(new GridBagLayout());
setPreferredSize(new Dimension(1050, 900));

NativeUI.setNativeUI();
GamerLogger.setFileToDisplay("GamePlayer");

SortedSet<AvailableGame> theAvailableGames = new TreeSet<AvailableGame>();
List<Class<?>> theAvailableCanvasList = ProjectSearcher.getAllClassesThatAre(GameCanvas.class);
for(Class<?> availableCanvas : theAvailableCanvasList) {
Set<Class<? extends GameCanvas>> theAvailableCanvasList = ProjectSearcher.GAME_CANVASES.getConcreteClasses();
for(Class<? extends GameCanvas> availableCanvas : theAvailableCanvasList) {
try {
GameCanvas theCanvas = (GameCanvas) availableCanvas.newInstance();
GameCanvas theCanvas = availableCanvas.newInstance();
theAvailableGames.add(new AvailableGame(theCanvas.getGameName(), theCanvas.getGameKey(), availableCanvas));
} catch(Exception e) {
;
Expand All @@ -127,8 +129,8 @@ public Kiosk()
playerComboBox = new JComboBox<String>();
playerComboBox.addItemListener(this);

gamers = ProjectSearcher.getAllClassesThatAre(Gamer.class);
List<Class<?>> gamersCopy = new ArrayList<Class<?>>(gamers);
gamers = Lists.newArrayList(ProjectSearcher.GAMERS.getConcreteClasses());
List<Class<?>> gamersCopy = new ArrayList<Class<?>>(gamers);
for(Class<?> gamer : gamersCopy)
{
try {
Expand Down
9 changes: 5 additions & 4 deletions src/org/ggp/base/apps/player/Player.java
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
import javax.swing.JTextField;
import javax.swing.border.TitledBorder;

import com.google.common.collect.Lists;
import org.ggp.base.apps.player.config.ConfigPanel;
import org.ggp.base.apps.player.detail.DetailPanel;
import org.ggp.base.apps.player.match.MatchPanel;
Expand Down Expand Up @@ -68,7 +69,7 @@ public void run()

private Integer defaultPort = 9147;

private List<Class<?>> gamers = ProjectSearcher.getAllClassesThatAre(Gamer.class);
private List<Class<? extends Gamer>> gamers = Lists.newArrayList(ProjectSearcher.GAMERS.getConcreteClasses());

public Player()
{
Expand All @@ -81,12 +82,12 @@ public Player()

portTextField.setColumns(15);

List<Class<?>> gamersCopy = new ArrayList<Class<?>>(gamers);
for(Class<?> gamer : gamersCopy)
List<Class<? extends Gamer>> gamersCopy = new ArrayList<Class<? extends Gamer>>(gamers);
for(Class<? extends Gamer> gamer : gamersCopy)
{
Gamer g;
try {
g = (Gamer) gamer.newInstance();
g = gamer.newInstance();
typeComboBox.addItem(g.getName());
} catch(Exception ex) {
gamers.remove(gamer);
Expand Down
3 changes: 1 addition & 2 deletions src/org/ggp/base/apps/player/PlayerRunner.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ public static void main(String[] args) throws IOException, InstantiationExceptio
System.out.println("Starting up preconfigured player on port " + port + " using player class named " + name);
Class<?> chosenGamerClass = null;
List<String> availableGamers = new ArrayList<String>();
for (Class<?> gamerClass : ProjectSearcher.getAllClassesThatAre(Gamer.class)) {
for (Class<?> gamerClass : ProjectSearcher.GAMERS.getConcreteClasses()) {
availableGamers.add(gamerClass.getSimpleName());
if (gamerClass.getSimpleName().equals(name)) {
chosenGamerClass = gamerClass;
Expand All @@ -39,6 +39,5 @@ public static void main(String[] args) throws IOException, InstantiationExceptio
}
Gamer gamer = (Gamer) chosenGamerClass.newInstance();
new GamePlayer(port, gamer).start();
return;
}
}
4 changes: 3 additions & 1 deletion src/org/ggp/base/player/proxy/ProxyGamePlayerClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,9 @@
import java.net.Socket;
import java.util.ArrayList;
import java.util.List;
import java.util.Set;

import com.google.common.collect.Lists;
import org.ggp.base.player.event.PlayerDroppedPacketEvent;
import org.ggp.base.player.event.PlayerReceivedMessageEvent;
import org.ggp.base.player.event.PlayerSentMessageEvent;
Expand Down Expand Up @@ -56,7 +58,7 @@ public static void main(String[] args) {
return;
}

List<Class<?>> gamers = ProjectSearcher.getAllClassesThatAre(Gamer.class);
List<Class<? extends Gamer>> gamers = Lists.newArrayList(ProjectSearcher.GAMERS.getConcreteClasses());
List<String> gamerNames = new ArrayList<String>();
if(gamerNames.size()!=gamers.size())
{
Expand Down
15 changes: 0 additions & 15 deletions src/org/ggp/base/util/configuration/ProjectConfiguration.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,4 @@
/**
* ProjectConfiguration handles the project-specific directory settings.
* This class stores the paths of the game directory and the class binary
* directories, so they can quickly be changed and overridden.
*
* For example, if you create a second project in Eclipse which includes
* this "GGP Base" project, you can override "gamesRootDirectoryPath" to
* be "..\\GGP Base\\games" in the new project, and add "..\\GGP Base\\bin"
* to the classRoots array, and then your new project will automagically be
* able to find all of the games, and all of the player objects stored both
* in your new project, and in the GGP Base project.
*
* @author Sam Schreiber
*/

Expand All @@ -22,8 +11,4 @@ public class ProjectConfiguration {
private static final String gamesRootDirectoryPath = "games";

public static final File gameImagesDirectory = new File(new File(gamesRootDirectoryPath, "resources"), "images");
public static final File gameCacheDirectory = new File(gamesRootDirectoryPath, "cache");

/* Class object file information */
public static final String[] classRoots = new String[] {"bin"};
}
117 changes: 44 additions & 73 deletions src/org/ggp/base/util/reflection/ProjectSearcher.java
Original file line number Diff line number Diff line change
@@ -1,93 +1,64 @@
package org.ggp.base.util.reflection;

import java.io.File;
import java.io.FilenameFilter;
import java.lang.reflect.Modifier;
import java.util.*;

import com.google.common.base.Objects;
import com.google.common.base.Predicate;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.Sets;
import org.ggp.base.apps.kiosk.GameCanvas;
import org.ggp.base.player.gamer.Gamer;
import org.ggp.base.util.configuration.ProjectConfiguration;
import org.reflections.Reflections;

import java.lang.reflect.Modifier;

public class ProjectSearcher {
public static void main(String[] args)
{
System.out.println(getAllClassesThatAre(Gamer.class));
}

public static List<Class<?>> getAllClassesThatAre(Class<?> ofThisType)
{
return getAllClassesThatAre(ofThisType, true);
System.out.println(GAMERS);
System.out.println(GAME_CANVASES);
}

public static List<Class<?>> getAllClassesThatAre(Class<?> ofThisType, boolean mustBeConcrete)
{
List<Class<?>> rval = new ArrayList<Class<?>>();
findClassesInList(allClasses, ofThisType, mustBeConcrete, rval);
findClassesInList(injectedClasses, ofThisType, mustBeConcrete, rval);
return rval;
}
private static final Reflections REFLECTIONS = new Reflections();

private static void findClassesInList(Set<String> classesToSearch, Class<?> ofThisType,
boolean mustBeConcrete, List<Class<?>> rval) {
for(String name : classesToSearch) {
if(name.contains("Test_"))
continue;
public static final LoadedClasses<Gamer> GAMERS = new LoadedClasses(Gamer.class);
public static final LoadedClasses<GameCanvas> GAME_CANVASES = new LoadedClasses(GameCanvas.class);

Class<?> c = null;
try {
c = Class.forName(name);
} catch (ClassNotFoundException ex) {
throw new RuntimeException(ex);
public static class LoadedClasses<T> {
private static Predicate<Class> IS_CONCRETE_CLASS = new Predicate<Class>() {
@Override
public boolean apply(Class klass) {
return !Modifier.isAbstract(klass.getModifiers());
}
};

if(ofThisType.isAssignableFrom(c) && (!mustBeConcrete || !Modifier.isAbstract(c.getModifiers())) )
rval.add(c);
}
}
private final Class<T> interfaceClass;
private final ImmutableSet<Class<? extends T>> allClasses;
private final ImmutableSet<Class<? extends T>> concreteClasses;

private static Set<String> allClasses = findAllClasses();
private static Set<String> injectedClasses = Sets.newHashSet();
private LoadedClasses(Class<T> interfaceClass) {
this.interfaceClass = interfaceClass;
this.allClasses = ImmutableSet.copyOf(REFLECTIONS.getSubTypesOf(interfaceClass));
this.concreteClasses = ImmutableSet.copyOf(Sets.filter(allClasses, IS_CONCRETE_CLASS));
}

public static <T> void injectClass(Class<T> klass) {
injectedClasses.add(klass.getCanonicalName());
}
public Class<T> getInterfaceClass() {
return interfaceClass;
}

private static Set<String> findAllClasses()
{
FilenameFilter filter = new FilenameFilter() {
public boolean accept(File dir, String name) {
return !name.startsWith(".");
}
};
public ImmutableSet<Class<? extends T>> getConcreteClasses() {
return concreteClasses;
}

Set<String> rval = Sets.newHashSet();
Stack<File> toProcess = new Stack<File>();
for(String classDirName : ProjectConfiguration.classRoots)
toProcess.add(new File(classDirName));
while(!toProcess.empty())
{
File f = toProcess.pop();
if(!f.exists())
System.out.println("Could not find expected file: [" + f + "] when running ProjectSearcher.");
if(f.isDirectory())
toProcess.addAll(Arrays.asList(f.listFiles(filter)));
else
{
if(f.getName().endsWith(".class"))
{
String fullyQualifiedName = f.getPath();
for(String classDirName : ProjectConfiguration.classRoots) {
fullyQualifiedName = fullyQualifiedName.replaceAll("^" + classDirName.replace(File.separatorChar, '.'), "");
}
fullyQualifiedName = fullyQualifiedName.replaceAll("\\.class$","");
fullyQualifiedName = fullyQualifiedName.replaceAll("^[\\\\/]", "");
fullyQualifiedName = fullyQualifiedName.replaceAll("[\\\\/]", ".");
rval.add(fullyQualifiedName);
}
}
}
public ImmutableSet<Class<? extends T>> getAllClasses() {
return allClasses;
}

return rval;
}
@Override
public String toString() {
return Objects.toStringHelper(this)
.add("allClasses", allClasses)
.add("interfaceClass", interfaceClass)
.add("concreteClasses", concreteClasses)
.toString();
}
}
}

0 comments on commit d67fe14

Please sign in to comment.