Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP

Comparing changes

Choose two branches to see what's changed or to start a new pull request. If you need to, you can also compare across forks.

Open a pull request

Create a new pull request by comparing changes across two branches. If you need to, you can also compare across forks.
  • 3 commits
  • 4 files changed
  • 0 commit comments
  • 1 contributor
Commits on Mar 16, 2012
@Max-Hailperin Max-Hailperin First version of making the spreadsheet stuff more generic. 9296ca4
@Max-Hailperin Max-Hailperin Switched the Loader implementation over to use the relatively generic
SpreadsheetDrivenInstantiator. (Note that this means the spreadsheets
are now accessed primarily by column number rather than the headings in
the first row.)
c9fd1e2
@Max-Hailperin Max-Hailperin Slightly cleaner way of toggling the style of menu items. 1aac7cd
View
6 Gack/src/edu/gac/mcs270/gack/client/ui/GraphicalUserInterface.java
@@ -179,11 +179,7 @@ public void execute() {
private void selectPace(int newPace) {
for(MenuItem i : paceMenuItems){
- if(i.getText().equals(""+newPace)){
- i.addStyleDependentName("chosen");
- } else {
- i.removeStyleDependentName("chosen");
- }
+ i.setStyleDependentName("chosen", i.getText().equals(""+newPace));
}
pace = newPace;
}
View
158 Gack/src/edu/gac/mcs270/gack/server/LoaderImpl.java
@@ -1,168 +1,24 @@
package edu.gac.mcs270.gack.server;
-import java.net.URL;
-import java.util.HashMap;
-import java.util.Map;
-
-import com.google.gdata.client.spreadsheet.SpreadsheetService;
-import com.google.gdata.data.spreadsheet.CustomElementCollection;
-import com.google.gdata.data.spreadsheet.ListEntry;
-import com.google.gdata.data.spreadsheet.ListFeed;
-import com.google.gdata.data.spreadsheet.WorksheetEntry;
-import com.google.gdata.data.spreadsheet.WorksheetFeed;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import edu.gac.mcs270.gack.shared.Loader;
-import edu.gac.mcs270.gack.shared.domain.AutoPerson;
import edu.gac.mcs270.gack.shared.domain.Person;
-import edu.gac.mcs270.gack.shared.domain.Place;
-import edu.gac.mcs270.gack.shared.domain.Scroll;
-import edu.gac.mcs270.gack.shared.domain.Thing;
-import edu.gac.mcs270.gack.shared.domain.Witch;
-import edu.gac.mcs270.gack.shared.domain.Wizard;
+
+import edu.gac.mcs270.utilities.SpreadsheetDrivenInstantiator;
public class LoaderImpl extends RemoteServiceServlet implements Loader {
+ private static final long serialVersionUID = -282279873378400604L;
- private static final long serialVersionUID = 3141859568997802325L;
- private Map<String, Place> places;
- private Map<String, Thing> things;
-
public Person getPlayer(){
- SpreadsheetService service = new SpreadsheetService("max.mcs.gac.edu-Gack-0.0");
- WorksheetFeed feed;
- try {
- URL feedURL = new URL("https://spreadsheets.google.com/feeds/worksheets/0Av6ZjRxtPYJqdHI3aEtxR3lUeEtMeDNQcnpKZDZvbGc/public/values");
- feed = service.getFeed(feedURL, WorksheetFeed.class);
- } catch (Exception e) {
- throw new Error("Trouble getting worksheet feed", e);
- }
- ListFeed placeFeed=null, neighborsFeed=null, autoPersonFeed=null, witchFeed=null, wizardFeed=null,
- thingFeed=null, scrollFeed=null, gainFeed=null, personFeed=null;
- for (WorksheetEntry worksheet : feed.getEntries()) {
- String title = worksheet.getTitle().getPlainText();
- if("Place".equals(title)){
- placeFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Place.addNewNeighbor".equals(title)){
- neighborsFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("AutoPerson".equals(title)){
- autoPersonFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Witch".equals(title)){
- witchFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Wizard".equals(title)){
- wizardFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Thing".equals(title)){
- thingFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Scroll".equals(title)){
- scrollFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Place.gain".equals(title)){
- gainFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else if("Person".equals(title)){
- personFeed = getListFeed(service, worksheet.getListFeedUrl());
- } else {
- throw new Error("Unexpected worksheet " + title);
- }
- }
-
- places = new HashMap<String, Place>();
- things = new HashMap<String, Thing>();
- for (ListEntry entry : placeFeed.getEntries()) {
- String name = entry.getCustomElements().getValue("name");
- places.put(name, new Place(name));
- }
-
- for (ListEntry entry : neighborsFeed.getEntries()) {
- CustomElementCollection customElements = entry.getCustomElements();
- Place source = getPlace(customElements.getValue("source"));
- String direction = customElements.getValue("direction");
- Place destination = getPlace(customElements.getValue("destination"));
- source.addNewNeighbor(direction, destination);
- }
-
- for (ListEntry entry : autoPersonFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- String name = customElements.getValue("name");
- Place place = getPlace(customElements.getValue("place"));
- int threshold = Integer.parseInt(customElements.getValue("threshold"));
- new AutoPerson(name, place, threshold);
- }
-
- for (ListEntry entry : witchFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- String name = customElements.getValue("name");
- Place place = getPlace(customElements.getValue("place"));
- int threshold = Integer.parseInt(customElements.getValue("threshold"));
- Place pond = getPlace(customElements.getValue("pond"));
- new Witch(name, place, threshold, pond);
- }
-
- for (ListEntry entry : wizardFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- String name = customElements.getValue("name");
- Place place = getPlace(customElements.getValue("place"));
- int threshold = Integer.parseInt(customElements.getValue("threshold"));
- Place chamber = getPlace(customElements.getValue("chamber"));
- new Wizard(name, place, threshold, chamber);
- }
-
- for (ListEntry entry : thingFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- String name = customElements.getValue("name");
- things.put(name, new Thing(name));
- }
-
- for (ListEntry entry : scrollFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- String name = customElements.getValue("name");
- things.put(name, new Scroll(name));
- }
-
- for (ListEntry entry : gainFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- Place place = getPlace(customElements.getValue("place"));
- Thing thing = getThing(customElements.getValue("thing"));
- place.gain(thing);
- }
-
- Person player = null;
- for (ListEntry entry : personFeed.getEntries()){
- CustomElementCollection customElements = entry.getCustomElements();
- String name = customElements.getValue("name");
- Place place = getPlace(customElements.getValue("place"));
- Person p = new Person(name, place);
- if(name.equals("player")){
- player = p;
- }
- }
- return player;
+ SpreadsheetDrivenInstantiator sdi = new SpreadsheetDrivenInstantiator(
+ "0Av6ZjRxtPYJqdHI3aEtxR3lUeEtMeDNQcnpKZDZvbGc",
+ "edu.gac.mcs270.gack.shared.domain");
+ return sdi.getInstance(Person.class, "player");
}
public LoaderImpl() {
super();
}
-
- private ListFeed getListFeed(SpreadsheetService service, URL url) throws Error {
- ListFeed feed;
- try {
- feed = service.getFeed(url, ListFeed.class);
- } catch (Exception e) {
- throw new Error("Trouble getting list feed", e);
- }
- return feed;
- }
-
- private Place getPlace(String placeName) throws Error {
- Place place = places.get(placeName);
- if(place==null)
- throw new Error("Unkown place " + placeName);
- return place;
- }
-
- private Thing getThing(String thingName) throws Error {
- Thing thing = things.get(thingName);
- if(thing==null)
- throw new Error("Unkown thing " + thingName);
- return thing;
- }
-
}
View
288 Gack/src/edu/gac/mcs270/utilities/SpreadsheetDrivenInstantiator.java
@@ -0,0 +1,288 @@
+package edu.gac.mcs270.utilities;
+import java.lang.reflect.Constructor;
+import java.lang.reflect.Method;
+import java.net.URL;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gdata.client.spreadsheet.SpreadsheetService;
+import com.google.gdata.data.spreadsheet.Cell;
+import com.google.gdata.data.spreadsheet.CellEntry;
+import com.google.gdata.data.spreadsheet.CellFeed;
+import com.google.gdata.data.spreadsheet.WorksheetEntry;
+import com.google.gdata.data.spreadsheet.WorksheetFeed;
+
+public class SpreadsheetDrivenInstantiator {
+
+ private Map<Class<?>, Map<String, Object>> instances = new HashMap<Class<?>, Map<String, Object>>();
+
+ public SpreadsheetDrivenInstantiator(String spreadsheetID,
+ String packageName) {
+ super();
+ SpreadsheetService service = new SpreadsheetService("max.mcs.gac.edu-SpreadsheetDrivenInstantiator-0.0");
+ WorksheetFeed feed;
+ try {
+ URL feedURL = new URL("https://spreadsheets.google.com/feeds/worksheets/" +
+ spreadsheetID + "/public/values");
+ feed = service.getFeed(feedURL, WorksheetFeed.class);
+ } catch (Exception e) {
+ throw new Error("Trouble getting worksheet feed", e);
+ }
+ for (WorksheetEntry worksheet : feed.getEntries()) {
+ String title = worksheet.getTitle().getPlainText();
+ String[] names = title.split("\\.");
+ if(names.length < 1 || names.length > 2){
+ throw new Error("Invalid worksheet name " + title);
+ }
+ Class<?> type;
+ try {
+ type = Class.forName(packageName + "." + names[0]);
+ } catch (ClassNotFoundException e1) {
+ throw new Error("Spreadsheet contained unknown class name", e1);
+ }
+ CellFeed cellFeed;
+ try {
+ cellFeed = service.getFeed(worksheet.getCellFeedUrl(), CellFeed.class);
+ } catch (Exception e) {
+ throw new Error("Trouble getting cell feed", e);
+ }
+ if(names.length == 2){
+ invokeMethod(type, names[1], cellFeed);
+ } else {
+ instantiate(type, cellFeed);
+ }
+ }
+ }
+
+ private interface Converter {
+ public Object convert(String s);
+ }
+
+ // TODO refactor commonality from invokeMethod, instantiate
+ private <T> void invokeMethod(Class<?> type, String methodName, CellFeed feed) {
+ Method method = null;
+ int arity = 0, prevRow = 1, prevCol = 0;
+ Object[] args = null;
+ Converter[] converters = null;
+ String key = null;
+ ArrayList<String> columnHeadings = new ArrayList<String>();
+ for(CellEntry entry : feed.getEntries()){
+ Cell cell = entry.getCell();
+ if(cell.getRow() == 1){
+ int n = cell.getCol();
+ arity = n - 1;
+ if(n > 1){
+ while(columnHeadings.size() < n-2)
+ columnHeadings.add("");
+ columnHeadings.add(cell.getValue());
+ }
+ }else{
+ if(method == null){
+ ArrayList<Method> candidateMethods = new ArrayList<Method>();
+ for(Method m : type.getMethods()){
+ if(m.getName().equals(methodName)){
+ int mArity = m.getParameterTypes().length;
+ if(mArity <= arity && m.isVarArgs())
+ throw new Error("varargs constructors not yet supported");
+ if(mArity == arity)
+ candidateMethods.add(m);
+ }
+ }
+ if(candidateMethods.size() == 0)
+ throw new Error("No method of arity " + arity + " for " + type.getName() + "." + methodName);
+ else if(candidateMethods.size() > 1){
+ ArrayList<Method> survivors = new ArrayList<Method>();
+ for(Method m : candidateMethods){
+ survivors.add(m); // tentatively
+ Class<?>[] types = m.getParameterTypes();
+ for(int i = 0; i < types.length; i++){
+ String name = types[i].getName();
+ name = name.substring(name.lastIndexOf('.')+1);
+ if(!name.equals(columnHeadings.get(i))){
+ survivors.remove(survivors.size()-1);
+ break;
+ }
+ }
+ }
+ candidateMethods = survivors;
+ if(candidateMethods.size() == 0)
+ throw new Error("Overloaded " + type.getName() + "." + methodName +
+ " methods of arity " + arity + " but none matches column headings");
+ else if(candidateMethods.size() > 1)
+ throw new Error("Overloaded " + type.getName() + "." + methodName +
+ " methods of arity " + arity + " not disambiguated by column headings");
+ }
+ method = candidateMethods.get(0);
+ args = new Object[arity];
+ converters = new Converter[arity];
+ for(int i = 0; i < arity; i++){
+ converters[i] = converterForType(method.getParameterTypes()[i]);
+ }
+ }
+ if(prevRow != cell.getRow()){
+ if(prevRow != 1){
+ invokeFromRow(key, type, method, arity, prevCol, args, converters);
+ }
+ prevRow = cell.getRow();
+ prevCol = 0;
+ key = cell.getCol()==1 ? cell.getValue() : ""; // TODO consider more rigid checking
+ }
+ interpolateBlankColumns(cell.getCol()-1, prevCol, args, converters);
+ prevCol = cell.getCol();
+ if(prevCol != 1)
+ args[prevCol-2] = converters[prevCol-2].convert(cell.getValue());
+ }
+ }
+ if(method != null){
+ invokeFromRow(key, type, method, arity, prevCol, args, converters);
+ }
+ }
+
+ private void invokeFromRow(String key, Class<?> type, Method method,
+ int arity, int prevCol, Object[] args, Converter[] converters) {
+ interpolateBlankColumns(arity, prevCol, args, converters);
+ Object o = converterForType(type).convert(key);
+ try{
+ method.invoke(o, args);
+ } catch(Exception e){
+ throw new Error("Trouble invoking " + type.getName() + "." + method.getName() + " on " + key,
+ e);
+ }
+ }
+
+ @SuppressWarnings("unchecked") // TODO is there a better option?
+ private <T> void instantiate(Class<T> type, CellFeed feed) {
+ ArrayList<Map<String, Object>> instanceMaps = new ArrayList<Map<String, Object>>();
+ for(Class<?> c = type; c != null; c = c.getSuperclass()){
+ Map<String, Object> m = instances.get(c);
+ if(m == null){
+ m = new HashMap<String, Object>();
+ instances.put(c, m);
+ }
+ instanceMaps.add(m);
+ }
+ for(Class<?> c : type.getInterfaces()){
+ Map<String, Object> m = instances.get(c);
+ if(m == null){
+ m = new HashMap<String, Object>();
+ instances.put(c, m);
+ }
+ instanceMaps.add(m);
+ }
+ Constructor<T> constructor = null;
+ int arity = 0, prevRow = 1, prevCol = 0;
+ Object[] initArgs = null;
+ Converter[] converters = null;
+ String key = null;
+ for(CellEntry entry : feed.getEntries()){
+ Cell cell = entry.getCell();
+ if(cell.getRow() == 1)
+ arity = cell.getCol();
+ else{
+ if(constructor == null){
+ ArrayList<Constructor<T>> candidateConstructors = new ArrayList<Constructor<T>>();
+ for(Constructor<?> c : type.getConstructors()){
+ int cArity = c.getParameterTypes().length;
+ if(cArity <= arity && c.isVarArgs())
+ throw new Error("varargs constructors not yet supported");
+ if(cArity == arity)
+ candidateConstructors.add((Constructor<T>) c);
+ }
+ if(candidateConstructors.size() == 0)
+ throw new Error("No constructor of arity " + arity + " for " + type.getName());
+ else if(candidateConstructors.size() > 1)
+ throw new Error("Multiple constructors of arity " + arity + " for " + type.getName());
+ constructor = candidateConstructors.get(0);
+ initArgs = new Object[arity];
+ converters = new Converter[arity];
+ for(int i = 0; i < arity; i++){
+ converters[i] = converterForType(constructor.getParameterTypes()[i]);
+ }
+ }
+ if(prevRow != cell.getRow()){
+ if(prevRow != 1){
+// System.err.println("Instantiating " + type.getName() + " " + key);
+ instantiateRow(instanceMaps, key, constructor, arity, prevCol, initArgs, converters);
+ }
+ prevRow = cell.getRow();
+ prevCol = 0;
+ key = cell.getCol()==1 ? cell.getValue() : ""; // TODO consider more rigid checking
+ }
+ interpolateBlankColumns(cell.getCol()-1, prevCol, initArgs, converters);
+ prevCol = cell.getCol();
+// System.err.println("Converting " + cell.getValue() + "(" + prevCol + "/" + arity + ")");
+ initArgs[prevCol-1] = converters[prevCol-1].convert(cell.getValue());
+ }
+ }
+ if(constructor != null){
+// System.err.println("Instantiating " + type.getName() + " " + key);
+ instantiateRow(instanceMaps, key, constructor, arity, prevCol, initArgs, converters);
+ }
+ }
+
+ private Converter converterForType(Class<?> type) {
+ if(type == Integer.class || type == int.class){
+ return new Converter(){
+ public Object convert(String s){
+ return Integer.parseInt(s);
+ }
+ };
+ } else if(type == String.class){
+ return new Converter(){
+ public Object convert(String s){
+ return s;
+ }
+ };
+ } else if(instances.containsKey(type)){
+ final Map<String, ?> map = instances.get(type);
+ final String typeName = type.getName();
+ return new Converter(){
+ public Object convert(String s){
+ Object o = map.get(s);
+ if(o == null){
+ throw new Error("No object named " + s + " was created of type " + typeName);
+ }
+ return o;
+ }
+ };
+ } else {
+ throw new Error("No string conversion yet implemented for type " + type.getName());
+ }
+ }
+
+ private <T> void instantiateRow(ArrayList<Map<String, Object>> instanceMaps, String key,
+ Constructor<T> constructor, int arity,
+ int prevCol, Object[] initArgs, Converter[] converters)
+ throws Error {
+ interpolateBlankColumns(arity, prevCol, initArgs, converters);
+ try {
+ Object instance = constructor.newInstance(initArgs);
+ for(Map<String,Object> m : instanceMaps)
+ m.put(key, instance);
+ } catch (Exception e) {
+ throw new Error("Trouble invoking constructor", e);
+ }
+ }
+
+ private void interpolateBlankColumns(int lastColumn, int prevCol, Object[] initArgs, Converter[] converters) {
+ for(; prevCol < lastColumn; prevCol++){
+ initArgs[prevCol] = converters[prevCol].convert("");
+ }
+ }
+
+ public <T> T getInstance(Class<T> type, String key){
+ return type.cast(getInstances(type).get(key));
+ }
+
+ @SuppressWarnings("unchecked") // TODO is there a better option?
+ private <T> Map<String, T> getInstances(Class<T> type){
+ Map<String, Object> instances = this.instances.get(type);
+ if(instances == null){
+ instances = new HashMap<String, Object>();
+ this.instances.put(type, instances);
+ }
+ return (Map<String, T>) instances;
+ }
+
+}
View
29 Gack/tests/edu/gac/mcs270/gack/server/TestSDI.java
@@ -0,0 +1,29 @@
+package edu.gac.mcs270.gack.server;
+import static org.junit.Assert.*;
+
+import org.junit.Test;
+
+import edu.gac.mcs270.gack.shared.domain.Person;
+import edu.gac.mcs270.gack.shared.domain.Place;
+import edu.gac.mcs270.gack.shared.domain.Thing;
+import edu.gac.mcs270.utilities.SpreadsheetDrivenInstantiator;
+
+
+public class TestSDI {
+
+ @Test
+ public void test() {
+ SpreadsheetDrivenInstantiator sdi = new SpreadsheetDrivenInstantiator("0Av6ZjRxtPYJqdHI3aEtxR3lUeEtMeDNQcnpKZDZvbGc",
+ "edu.gac.mcs270.gack.shared.domain");
+ Person player = sdi.getInstance(Person.class, "player");
+ Place dormitory = sdi.getInstance(Place.class, "Dormitory");
+ assertEquals(dormitory, player.getPlace());
+ assertTrue(dormitory.getOccupants().contains(player));
+ Place lounge = sdi.getInstance(Place.class, "Lounge");
+ Thing glasses = sdi.getInstance(Thing.class, "Karl's Glasses");
+ Place gso = sdi.getInstance(Place.class, "Good Ship Olin");
+ assertEquals(lounge, gso.neighborTowards("up"));
+ assertTrue(lounge.getContents().contains(glasses));
+ }
+
+}

No commit comments for this range

Something went wrong with that request. Please try again.