From: Stefan F. <ste...@us...> - 2012-07-03 04:37:20
|
.classpath | 2 junit/lib/fest-2.0M5/fest-assert-core-2.0M5.jar |binary junit/lib/fest-2.0M5/fest-util-1.2.0.jar |binary junit/rails/game/state/AbstractItemImpl.java | 7 + junit/rails/game/state/AbstractItemTest.java | 63 ++++++++++++++ junit/rails/game/state/ChangeStackTest.java | 7 - junit/rails/game/state/ManagerImpl.java | 7 + junit/rails/game/state/RootTest.java | 97 +++++++++++++++++++++ src/rails/common/parser/ComponentManager.java | 6 - src/rails/common/parser/GameFileParser.java | 4 src/rails/game/Game.java | 2 src/rails/game/GameManager.java | 2 src/rails/game/state/AbstractItem.java | 16 ++- src/rails/game/state/ArrayListChange.java | 4 src/rails/game/state/AutoChangeSet.java | 14 --- src/rails/game/state/BooleanChange.java | 2 src/rails/game/state/Change.java | 6 - src/rails/game/state/ChangeStack.java | 56 +++++------- src/rails/game/state/Context.java | 10 -- src/rails/game/state/GenericStateChange.java | 2 src/rails/game/state/HashMapChange.java | 4 src/rails/game/state/HashSetChange.java | 2 src/rails/game/state/IntegerChange.java | 2 src/rails/game/state/Item.java | 2 src/rails/game/state/Manager.java | 66 ++++---------- src/rails/game/state/MultimapChange.java | 2 src/rails/game/state/Observable.java | 106 ++++++++++++++++++------ src/rails/game/state/PortfolioChange.java | 2 src/rails/game/state/Root.java | 45 ++++------ src/rails/game/state/State.java | 19 +--- src/rails/game/state/StateManager.java | 39 +++----- src/rails/game/state/StringChange.java | 2 src/rails/game/state/WalletChange.java | 2 33 files changed, 390 insertions(+), 210 deletions(-) New commits: commit d776000fd05b1081b7ee4668fb91237553afe125 Author: Stefan Frey <ste...@we...> Date: Tue Jul 3 06:36:39 2012 +0200 further refactoring of item mechanisms, added tests for root and item, added FEST assertions libraries diff --git a/.classpath b/.classpath index 724802d..7893f0d 100644 --- a/.classpath +++ b/.classpath @@ -17,5 +17,7 @@ <classpathentry kind="lib" path="src/lib/logback-1.0.4/logback-core-1.0.4.jar"/> <classpathentry kind="lib" path="src/lib/sl4j-1.6.5/slf4j-api-1.6.5.jar"/> <classpathentry kind="lib" path="src/lib/mockito-1.9.0/mockito-all-1.9.0.jar"/> + <classpathentry kind="lib" path="junit/lib/fest-2.0M5/fest-assert-core-2.0M5.jar"/> + <classpathentry kind="lib" path="junit/lib/fest-2.0M5/fest-util-1.2.0.jar"/> <classpathentry kind="output" path="classes"/> </classpath> diff --git a/junit/lib/fest-2.0M5/fest-assert-core-2.0M5.jar b/junit/lib/fest-2.0M5/fest-assert-core-2.0M5.jar new file mode 100644 index 0000000..e7c4b78 Binary files /dev/null and b/junit/lib/fest-2.0M5/fest-assert-core-2.0M5.jar differ diff --git a/junit/lib/fest-2.0M5/fest-util-1.2.0.jar b/junit/lib/fest-2.0M5/fest-util-1.2.0.jar new file mode 100644 index 0000000..560c705 Binary files /dev/null and b/junit/lib/fest-2.0M5/fest-util-1.2.0.jar differ diff --git a/junit/rails/game/state/AbstractItemImpl.java b/junit/rails/game/state/AbstractItemImpl.java new file mode 100644 index 0000000..3aec737 --- /dev/null +++ b/junit/rails/game/state/AbstractItemImpl.java @@ -0,0 +1,7 @@ +package rails.game.state; + +class AbstractItemImpl extends AbstractItem { + AbstractItemImpl(Item parent, String id) { + super(parent, id); + } +} diff --git a/junit/rails/game/state/AbstractItemTest.java b/junit/rails/game/state/AbstractItemTest.java new file mode 100644 index 0000000..19c994e --- /dev/null +++ b/junit/rails/game/state/AbstractItemTest.java @@ -0,0 +1,63 @@ +package rails.game.state; + +import static org.junit.Assert.*; + +import org.junit.Before; +import org.junit.Test; + +public class AbstractItemTest { + + private static final String MANAGER_ID = "manager"; + private static final String ITEM_ID = "item"; + private static final String ANOTHER_ID = "anotherItem"; + + private Root root; + private Manager manager; + private Item item; + private Item anotherItem; + + @Before + public void setUp() { + root = Root.create(); + manager = new ManagerImpl(root, MANAGER_ID); + item = new AbstractItemImpl(manager, ITEM_ID); + anotherItem = new AbstractItemImpl(item, ANOTHER_ID); + } + + @Test + public void testGetId() { + assertEquals(item.getId(), ITEM_ID); + assertEquals(anotherItem.getId(), ANOTHER_ID); + } + + @Test + public void testGetParent() { + assertSame(item.getParent(), manager); + assertSame(anotherItem.getParent(), item); + } + + @Test + public void testGetContext() { + assertSame(item.getContext(), manager); + assertSame(anotherItem.getContext(), manager); + } + + @Test + public void testGetRoot() { + assertSame(item.getRoot(), root); + assertSame(anotherItem.getRoot(), root); + } + + @Test + public void testGetURI() { + assertEquals(item.getURI(), ITEM_ID); + assertEquals(anotherItem.getURI(), ITEM_ID + Item.SEP + ANOTHER_ID); + } + + @Test + public void testGetFullURI() { + assertEquals(item.getFullURI(), Item.SEP + MANAGER_ID + Item.SEP + ITEM_ID); + assertEquals(anotherItem.getFullURI(), Item.SEP + MANAGER_ID+ Item.SEP + ITEM_ID + Item.SEP + ANOTHER_ID); + } + +} diff --git a/junit/rails/game/state/ChangeStackTest.java b/junit/rails/game/state/ChangeStackTest.java index 04dbb87..67a8618 100644 --- a/junit/rails/game/state/ChangeStackTest.java +++ b/junit/rails/game/state/ChangeStackTest.java @@ -16,13 +16,6 @@ public class ChangeStackTest { assertNotNull(stack); } - @Test - public void testEnable() { - ChangeStack stack = ChangeStack.create(sm); - assertFalse(stack.isEnabled()); - stack.enable(); - assertTrue(stack.isEnabled()); - } @Test public void testIsEnabled() { diff --git a/junit/rails/game/state/ManagerImpl.java b/junit/rails/game/state/ManagerImpl.java new file mode 100644 index 0000000..a6fdf00 --- /dev/null +++ b/junit/rails/game/state/ManagerImpl.java @@ -0,0 +1,7 @@ +package rails.game.state; + +class ManagerImpl extends Manager { + ManagerImpl(Item parent, String id) { + super(parent, id); + } +} diff --git a/junit/rails/game/state/RootTest.java b/junit/rails/game/state/RootTest.java new file mode 100644 index 0000000..97b4482 --- /dev/null +++ b/junit/rails/game/state/RootTest.java @@ -0,0 +1,97 @@ +package rails.game.state; + +import static org.junit.Assert.*; +import static org.fest.assertions.api.Assertions.assertThat; + +import org.junit.Before; +import org.junit.Test; + +public class RootTest { + + private static final String MANAGER_ID = "manager"; + private static final String ITEM_ID = "item"; + private static final String ANOTHER_ID = "anotherItem"; + + private Root root; + private Manager manager; + private Item item; + private Item anotherItem; + + @Before + public void setUp() { + root = Root.create(); + manager = new ManagerImpl(root, MANAGER_ID); + item = new AbstractItemImpl(root, ITEM_ID); + anotherItem = new AbstractItemImpl(manager, ANOTHER_ID); + } + + @Test + public void testGetStateManager() { + assertThat(root.getStateManager()).isInstanceOf(StateManager.class); + } + + @Test(expected = UnsupportedOperationException.class) + public void testGetParent() { + root.getParent(); + } + + @Test + public void testGetId() { + assertEquals(root.getId(), Root.ID); + } + + @Test + public void testGetContext() { + assertSame(root.getContext(), root); + } + + @Test + public void testGetRoot() { + assertSame(root.getRoot(), root); + } + + @Test + public void testGetURI() { + assertSame(root.getURI(), Root.ID); + } + + @Test + public void testGetFullURI() { + assertSame(root.getFullURI(), Root.ID); + } + + @Test + public void testLocate() { + assertSame(root.locate(item.getFullURI()), item); + assertSame(root.locate(anotherItem.getFullURI()), anotherItem); + } + + @Test + public void testLocateFail() { + root.removeItem(item); + assertNull(root.locate(item.getFullURI())); + } + + @Test(expected=IllegalArgumentException.class) + public void testAddItemFail() { + root.addItem(item); + } + + public void testAddItemSuccess() { + root.removeItem(item); + root.addItem(item); + assertSame(root.locate(item.getFullURI()), item); + } + + public void testRemoveItemSuccess() { + root.removeItem(item); + root.locate(item.getFullURI()); + } + + @Test(expected=IllegalArgumentException.class) + public void testRemoveItemFail() { + root.removeItem(item); + root.removeItem(item); + } + +} diff --git a/src/rails/common/parser/ComponentManager.java b/src/rails/common/parser/ComponentManager.java index 3e0d57f..daa7f30 100644 --- a/src/rails/common/parser/ComponentManager.java +++ b/src/rails/common/parser/ComponentManager.java @@ -9,7 +9,7 @@ import org.slf4j.LoggerFactory; import rails.common.LocalText; import rails.common.parser.XMLTags; -import rails.game.state.Manager; +import rails.game.state.Context; /** * ComponentManage - an implementation of ComponentManagerI, which handles the @@ -28,7 +28,7 @@ public class ComponentManager { private Map<String, ConfigurableComponent> mComponentMap = new HashMap<String, ConfigurableComponent>(); - public ComponentManager(Manager context, String gameName, Tag tag, Map<String, String> gameOptions) + public ComponentManager(Context context, String gameName, Tag tag, Map<String, String> gameOptions) throws ConfigurationException { this.gameName = gameName; @@ -41,7 +41,7 @@ public class ComponentManager { } } - private void configureComponent(Manager context, Tag componentTag) + private void configureComponent(Context context, Tag componentTag) throws ConfigurationException { // Extract the attributes of the Component diff --git a/src/rails/common/parser/GameFileParser.java b/src/rails/common/parser/GameFileParser.java index f4ebe84..c1b6fd5 100644 --- a/src/rails/common/parser/GameFileParser.java +++ b/src/rails/common/parser/GameFileParser.java @@ -15,7 +15,7 @@ import rails.game.PlayerManager; import rails.game.StockMarket; import rails.game.TileManager; import rails.game.TrainManager; -import rails.game.state.Manager; +import rails.game.state.Root; public class GameFileParser extends XMLParser { private static String GAME_XML_FILE = "Game.xml"; @@ -34,7 +34,7 @@ public class GameFileParser extends XMLParser { private RevenueManager revenueManager; private Bank bank; - public GameFileParser(Manager context, String name, Map<String, String> gameOptions) { + public GameFileParser(Root context, String name, Map<String, String> gameOptions) { directories.add("data/" + name); diff --git a/src/rails/game/Game.java b/src/rails/game/Game.java index b71533c..3140cb0 100644 --- a/src/rails/game/Game.java +++ b/src/rails/game/Game.java @@ -77,7 +77,7 @@ public class Game { public boolean setup() { // first define root GameContext to be able to define states - Root root = Root.create("states"); + Root root = Root.create(); GameFileParser gfp = new GameFileParser(root, name, gameOptions); playerManager = gfp.getPlayerManager(); diff --git a/src/rails/game/GameManager.java b/src/rails/game/GameManager.java index 9fa593e..db9d978 100644 --- a/src/rails/game/GameManager.java +++ b/src/rails/game/GameManager.java @@ -567,7 +567,7 @@ public class GameManager extends AbstractItem implements ConfigurableComponent, } // Initialisation is complete. Undoability starts here. - changeStack.enable(); + // changeStack.enable(); } private void setGuiParameters () { diff --git a/src/rails/game/state/AbstractItem.java b/src/rails/game/state/AbstractItem.java index e76ec25..531724f 100644 --- a/src/rails/game/state/AbstractItem.java +++ b/src/rails/game/state/AbstractItem.java @@ -9,6 +9,7 @@ public abstract class AbstractItem implements Item { private final String id; private final Item parent; + private final Context context; protected AbstractItem(Item parent, String id){ checkNotNull(parent, "Parent cannot be null"); @@ -18,9 +19,10 @@ public abstract class AbstractItem implements Item { // defined standard fields this.parent = parent; this.id = id; + context = parent.getContext(); // add item to context - parent.getContext().addItem(this); + context.addItem(this); } public String getId() { @@ -32,12 +34,12 @@ public abstract class AbstractItem implements Item { } public Context getContext() { - if (parent instanceof Manager) { - return (Manager)parent; - } else { - // recursive definition - return parent.getContext(); - } + return context; + } + + public Root getRoot() { + // forward it to the context + return context.getRoot(); } public String getURI() { diff --git a/src/rails/game/state/ArrayListChange.java b/src/rails/game/state/ArrayListChange.java index 4e28257..f9016c1 100644 --- a/src/rails/game/state/ArrayListChange.java +++ b/src/rails/game/state/ArrayListChange.java @@ -21,22 +21,22 @@ final class ArrayListChange<E> extends Change { * Add object at the specified index */ ArrayListChange(ArrayListState<E> state, E object, int index) { - super(state); this.state = state; this.object = object; this.index = index; this.addToList = true; + super.init(state); } /** * Remove object at the specified index */ ArrayListChange(ArrayListState<E> state, int index) { - super(state); this.state = state; this.object = state.get(index); this.index = index; this.addToList = false; + super.init(state); } @Override diff --git a/src/rails/game/state/AutoChangeSet.java b/src/rails/game/state/AutoChangeSet.java index b1e8de8..11fe5a3 100644 --- a/src/rails/game/state/AutoChangeSet.java +++ b/src/rails/game/state/AutoChangeSet.java @@ -3,20 +3,12 @@ package rails.game.state; import rails.game.Player; /** - * AutoChangeSets are linked to previous ActionChangeSets - * @author freystef + * AutoChangeSets are ChangeSets that belong to no action directly */ final class AutoChangeSet extends ChangeSet { - private final ActionChangeSet previous; - AutoChangeSet(ActionChangeSet previous) { - this.previous = previous; - } - - ActionChangeSet getPrevious() { - return previous; - } + AutoChangeSet() {} @Override boolean isUndoableByPlayer(Player player) { @@ -25,7 +17,7 @@ final class AutoChangeSet extends ChangeSet { @Override public String toString() { - return "AutoChangeSet linked to action " + previous.getAction(); + return "AutoChangeSet"; } } diff --git a/src/rails/game/state/BooleanChange.java b/src/rails/game/state/BooleanChange.java index b899756..be51279 100644 --- a/src/rails/game/state/BooleanChange.java +++ b/src/rails/game/state/BooleanChange.java @@ -12,10 +12,10 @@ final class BooleanChange extends Change { private final boolean oldValue; BooleanChange(BooleanState state, boolean newValue) { - super(state); this.state = state; this.newValue = newValue; this.oldValue = state.booleanValue(); + super.init(state); } @Override diff --git a/src/rails/game/state/Change.java b/src/rails/game/state/Change.java index ec1d12a..8fd74c2 100644 --- a/src/rails/game/state/Change.java +++ b/src/rails/game/state/Change.java @@ -3,13 +3,11 @@ package rails.game.state; * Base Class for all Change Objects * * Replaces previous move interface - * - * @author freystef */ abstract class Change { - Change(State state){ - state.getStateManager().addChangeToStack(this); + protected void init(State state){ + state.getRoot().getStateManager().addChangeToStack(this); } abstract void execute(); diff --git a/src/rails/game/state/ChangeStack.java b/src/rails/game/state/ChangeStack.java index 3090ae0..7b7e52f 100644 --- a/src/rails/game/state/ChangeStack.java +++ b/src/rails/game/state/ChangeStack.java @@ -9,50 +9,32 @@ import rails.game.Player; import rails.game.ReportBuffer; import rails.game.action.PossibleAction; -public class ChangeStack extends AbstractItem { +public class ChangeStack { protected static Logger log = LoggerFactory.getLogger(ChangeStack.class.getPackage().getName()); private final LinkedList<ChangeSet> stack = new LinkedList<ChangeSet>(); - - private boolean enabled = false; + private final StateManager stateManager; - protected ChangeStack(Item parent, String id){ - super(parent, id); + private ChangeStack(StateManager parent) { + stateManager = parent; } /** * @param parent restricted to StateManager - * ID set to class name */ public static ChangeStack create(StateManager parent) { - return new ChangeStack(parent , ChangeStack.class.getSimpleName()); - } - - /** - * Start making moves undoable. Will be called once, after all - * initialisations are complete. - */ - public void enable() { - enabled = true; - } - - public boolean isEnabled() { - return enabled; + ChangeStack changeStack = new ChangeStack(parent); + changeStack.initAutoChangeSet(); + return changeStack; } - private void checkEnabled() { - if (!enabled) throw new IllegalStateException("ChangeStack is not enabled"); - } - /** * Returns a valid ChangeSet that is current for the ChangeStack * If the ChangeStack is not enabled or empty a IllegalStateExcpetion is thrown * @return the current changeSet */ public ChangeSet getAvailableChangeSet() { - // check preconditions - checkEnabled(); if (stack.isEmpty()) throw new IllegalStateException("No ChangeSet on ChangeStack"); // return the last on the @@ -77,6 +59,23 @@ public class ChangeStack extends AbstractItem { } return changeSet; } + + /** + * Creates new AutoChangeSet + */ + public AutoChangeSet initAutoChangeSet() { + if (stack.peekLast() != null && !stack.peekLast().isClosed()) + throw new IllegalStateException("Current ChangeSet not closed yet"); + + // create new ChangeSet + AutoChangeSet changeSet = new AutoChangeSet(); + + stack.offerLast(changeSet); + log.debug(">>> Start AutoChangeSet " + changeSet + " at index=" + stack.size() + " <<<"); + + return changeSet; + } + /** * Creates new ActionChangeSet @@ -85,9 +84,6 @@ public class ChangeStack extends AbstractItem { * @return the new current ChangeSet */ public ActionChangeSet start(Player player, PossibleAction action) { - - // check preconditions - checkEnabled(); if (stack.peekLast() != null && !stack.peekLast().isClosed()) throw new IllegalStateException("Current ChangeSet not closed yet"); @@ -159,11 +155,11 @@ public class ChangeStack extends AbstractItem { } public boolean isUndoableByManager() { - return enabled && stack.size() != 0; + return stack.size() != 0; } public boolean isOpen() { - return enabled && stack.size() != 0 && !getAvailableChangeSet().isClosed(); + return stack.size() != 0 && !getAvailableChangeSet().isClosed(); } // TODO: What is correct? diff --git a/src/rails/game/state/Context.java b/src/rails/game/state/Context.java index 487e1e3..9d5520c 100644 --- a/src/rails/game/state/Context.java +++ b/src/rails/game/state/Context.java @@ -2,14 +2,12 @@ package rails.game.state; /** * A context describe a service that allows to locate items */ -public interface Context extends Item { +public abstract class Context implements Item { - public Item localize(String uri); + public abstract Item locate(String uri); - public void addItem(Item item); + abstract void addItem(Item item); - public void removeItem(Item item); - - public Root getRoot(); + abstract void removeItem(Item item); } diff --git a/src/rails/game/state/GenericStateChange.java b/src/rails/game/state/GenericStateChange.java index 6922321..076f731 100644 --- a/src/rails/game/state/GenericStateChange.java +++ b/src/rails/game/state/GenericStateChange.java @@ -6,10 +6,10 @@ final class GenericStateChange<E> extends Change { final private E previous, next; public GenericStateChange(GenericState<E> state, E object) { - super(state); this.state = state; previous = state.get(); next = object; + super.init(state); } @Override diff --git a/src/rails/game/state/HashMapChange.java b/src/rails/game/state/HashMapChange.java index a3572bf..ac89844 100644 --- a/src/rails/game/state/HashMapChange.java +++ b/src/rails/game/state/HashMapChange.java @@ -16,26 +16,26 @@ final class HashMapChange<K,V> extends Change { * Put element into map */ HashMapChange(HashMapState<K,V> state, K key, V value) { - super(state); this.state = state; this.key = key; newValue = value; remove = false; oldValue = state.get(key); existed = state.containsKey(key); + super.init(state); } /** * Remove element from map */ HashMapChange(HashMapState<K,V> state, K key) { - super(state); this.state = state; this.key = key; newValue = null; remove = true; oldValue = state.get(key); existed = true; + super.init(state); } @Override diff --git a/src/rails/game/state/HashSetChange.java b/src/rails/game/state/HashSetChange.java index 31f260f..eaec2e4 100644 --- a/src/rails/game/state/HashSetChange.java +++ b/src/rails/game/state/HashSetChange.java @@ -12,10 +12,10 @@ final class HashSetChange<E> extends Change { * Add/Remove element to/from the set */ HashSetChange(HashSetState<E> state, E element, boolean addToSet){ - super(state); this.state = state; this.element = element; this.addToSet = addToSet; + super.init(state); } @Override diff --git a/src/rails/game/state/IntegerChange.java b/src/rails/game/state/IntegerChange.java index 55a56b3..0375d93 100644 --- a/src/rails/game/state/IntegerChange.java +++ b/src/rails/game/state/IntegerChange.java @@ -10,10 +10,10 @@ final class IntegerChange extends Change { private final int oldValue; IntegerChange(IntegerState state, int newValue) { - super(state); this.state = state; this.newValue = newValue; this.oldValue = state.value(); + super.init(state); } @Override diff --git a/src/rails/game/state/Item.java b/src/rails/game/state/Item.java index be16c84..d4a8877 100644 --- a/src/rails/game/state/Item.java +++ b/src/rails/game/state/Item.java @@ -21,6 +21,8 @@ public interface Item { Item getParent(); Context getContext(); + + Root getRoot(); /** * @return a string which allows to identify the item in the Context diff --git a/src/rails/game/state/Manager.java b/src/rails/game/state/Manager.java index 11d5a3b..2d995c5 100644 --- a/src/rails/game/state/Manager.java +++ b/src/rails/game/state/Manager.java @@ -4,36 +4,34 @@ import static com.google.common.base.Preconditions.checkArgument; import static com.google.common.base.Preconditions.checkNotNull; /** - * A Manager is an abstract implementation of a Context + * A Manager is a baseline implementation of a Context */ -public abstract class Manager implements Context { +public abstract class Manager extends Context { - // standard item fields + // item fields private final String id; private final Item parent; - - protected final Root root; - - // storage of items - protected final HashMapState<String, Item> items = HashMapState.create(this, "items"); + // context fields + private final Root root; + private final String fullURI; protected Manager(Item parent, String id) { checkNotNull(id, "Id cannot be null"); this.id = id; - if (this instanceof Root) { - this.parent = null; - this.root = null; - return; - } - // check arguments, parent can only be null for Root checkNotNull(parent, "Parent cannot be null"); this.parent = parent; + // URI defined recursively + fullURI = parent.getFullURI() + Item.SEP + id; + // find root and add context there root = parent.getContext().getRoot(); - root.addItem(this); + // add to root, except for StateManager, this is delayed + if (!(this instanceof StateManager)) { + root.addItem(this); + } } public String getId() { @@ -49,52 +47,30 @@ public abstract class Manager implements Context { } public String getURI() { - if (parent instanceof Manager) { - return id; - } else { - // recursive definition - return parent.getURI() + Item.SEP + id; - } + return ""; } public String getFullURI() { - // recursive definition - return parent.getFullURI() + Item.SEP + id; + return fullURI; } // Context methods - public Item localize(String uri) { - // either item can be found in the map - if (items.containsKey(uri)) { - return items.get(uri); - } else { - // otherwise search in root - return root.localize(uri); - } + public Item locate(String uri) { + return root.locate(fullURI + Item.SEP + uri); } - public void addItem(Item item) { + void addItem(Item item) { // check if this context is the containing one checkArgument(item.getContext() == this, "Context is not the container of the item to add"); - // check if it already exists - checkArgument(items.containsKey(item.getURI()), "Context already contains item with identical URI"); - - // all preconditions ok => add item - items.put(item.getURI(), item); // add item to root root.addItem(item); - } + } - public void removeItem(Item item) { + void removeItem(Item item) { // check if this context is the containing one checkArgument(item.getContext() == this, "Context is not the container of the item to add"); - // check if it is stored - checkArgument(!items.containsKey(item.getURI()), "Context does not contain item with that URI"); - - // all preconditions ok => remove item - items.remove(item.getURI()); - + // remove item from root root.removeItem(item); } diff --git a/src/rails/game/state/MultimapChange.java b/src/rails/game/state/MultimapChange.java index dedca99..115465e 100644 --- a/src/rails/game/state/MultimapChange.java +++ b/src/rails/game/state/MultimapChange.java @@ -7,11 +7,11 @@ final class MultimapChange<K,V> extends Change { final private boolean addToMap; MultimapChange(MultimapState<K,V> state, K key, V value, boolean addToMap) { - super(state); this.state = state; this.key = key; this.value = value; this.addToMap = addToMap; + super.init(state); } @Override diff --git a/src/rails/game/state/Observable.java b/src/rails/game/state/Observable.java index bc339cb..3272391 100644 --- a/src/rails/game/state/Observable.java +++ b/src/rails/game/state/Observable.java @@ -1,68 +1,128 @@ package rails.game.state; -import com.google.common.collect.ImmutableSet; - +import static com.google.common.base.Preconditions.checkNotNull; +import static com.google.common.base.Preconditions.checkState; +import com.google.common.collect.ImmutableSet; /** * Requirement: * The observable object has to call each observer per update() if the object has changed. - * - * @author freystef - * */ -public abstract class Observable extends AbstractItem { +public abstract class Observable implements Item { - // stores observers and models - private HashSetState<Observer> observers = null; // lazy initialization - private HashSetState<Model> models = null; // lazy initialization + // fields for Item implementation + private final String id; + private final Item parent; + private final Context context; + // stores observers and models if observable + // those are unobservable states themselves + private final HashSetState<Observer> observers; + private final HashSetState<Model> models; + + /** + * @param parent parent node in item hierarchy (cannot be null) + * @param id id of the observable + * If id is null it creates an "unobservable" observable + * This is required for the creation of states that are themselves stateless + */ + protected Observable(Item parent, String id) { - super(parent, id); + checkNotNull(parent, "Parent cannot be null"); + + // defined standard fields + this.parent = parent; + this.id = id; + context = parent.getContext(); + + // if id is null this is an "unobservable" observable + if (id == null) { + observers = null; + models = null; + } else { + observers = HashSetState.create(this, null); + models = HashSetState.create(this, null); + } + } public void addObserver(Observer o) { - if (observers == null) { - observers = HashSetState.create(this, "observers"); - } + checkState(id != null, "Cannot add observer to unobservable object"); observers.add(o); } public boolean removeObserver(Observer o) { - if (observers == null) return false; - + checkState(id != null, "Cannot remove observer from unobservable object"); return observers.remove(o); } public ImmutableSet<Observer> getObservers() { + checkState(id != null, "Cannot get observers of unobservable object"); return observers.view(); } public void addModel(Model m) { - if (models == null) { - models = HashSetState.create(this, "models"); - } - + checkState(id != null, "Cannot add model to unobservable object"); models.add(m); } public boolean removeModel(Model m) { - if (models == null) return false; - + checkState(id != null, "Cannot remove model from unobservable object"); return models.remove(m); } public ImmutableSet<Model> getModels() { + checkState(id != null, "Cannot get models of unobservable object"); return models.view(); } + /** + * Calls update() of registered Models + * Returns without error for unobservable models + */ public void updateModels() { - if (models == null) return; - + if (id == null) return; for (Model m:models) { m.update(); } } + // Item methods + + public String getId() { + return id; + } + + public Item getParent() { + return parent; + } + + public Context getContext() { + return context; + } + + public Root getRoot() { + // forward it to the context + return context.getRoot(); + } + + public String getURI() { + checkState(id != null, "Cannot get URI of unobservable object"); + if (parent instanceof Manager) { + return id; + } else { + // recursive definition + return parent.getURI() + Item.SEP + id; + } + } + + public String getFullURI() { + checkState(id != null, "Cannot get fullURI of unobservable object"); + // recursive definition + return parent.getFullURI() + Item.SEP + id; + } + + /** * @return text to be read by observers */ diff --git a/src/rails/game/state/PortfolioChange.java b/src/rails/game/state/PortfolioChange.java index f603789..eb1d3a8 100644 --- a/src/rails/game/state/PortfolioChange.java +++ b/src/rails/game/state/PortfolioChange.java @@ -7,10 +7,10 @@ final class PortfolioChange<T extends Ownable<T>> extends Change { private final T item; PortfolioChange(Portfolio<T> in, Portfolio<T> out, T item) { - super(in); this.in = in; this.out = out; this.item = item; + super.init(in); } @Override diff --git a/src/rails/game/state/Root.java b/src/rails/game/state/Root.java index 5f5a47d..8a177b8 100644 --- a/src/rails/game/state/Root.java +++ b/src/rails/game/state/Root.java @@ -4,48 +4,51 @@ import static com.google.common.base.Preconditions.checkArgument; /** * Root is the top node of the context/item hierachy */ -public final class Root extends Manager { +public final class Root extends Context { - public final static String ID = "/"; + public final static String ID = ""; private StateManager stateManager; + private final HashMapState<String, Item> items = HashMapState.create(this, null); - private Root() { - super(null, ID); - } + private Root() { } /** - * @param stateManagerId for the embedded StateManager * @return a Root object with initialized StateManager embedded */ - public static Root create(String stateManagerId) { + public static Root create() { Root root = new Root(); - StateManager stateManager = StateManager.create(root, stateManagerId); + StateManager stateManager = StateManager.create(root, "states"); root.addStateManager(stateManager); return root; } private void addStateManager(StateManager stateManager) { this.stateManager = stateManager; + this.addItem(stateManager); } public StateManager getStateManager() { return stateManager; } + + // Item methods /** * @throws UnsupportedOperationsException * Not supported for Root */ - @Override public Item getParent() { throw new UnsupportedOperationException(); } + + public String getId() { + return ""; + } /** * @return this */ - @Override public Context getContext() { return this; } @@ -53,41 +56,34 @@ public final class Root extends Manager { /** * @return this */ - @Override public Root getRoot() { return this; } - - @Override public String getURI() { - return ID; + return ""; } - @Override public String getFullURI() { - return ID; + return ""; } // Context methods - @Override - public Item localize(String uri) { + public Item locate(String uri) { return items.get(uri); } - @Override - public void addItem(Item item) { + void addItem(Item item) { // check if it already exists - checkArgument(items.containsKey(item.getFullURI()), "Root already contains item with identical fullURI"); + checkArgument(!items.containsKey(item.getFullURI()), "Root already contains item with identical fullURI"); // all preconditions ok => add items.put(item.getFullURI(), item); } - @Override - public void removeItem(Item item) { + void removeItem(Item item) { // check if it already exists - checkArgument(!items.containsKey(item.getFullURI()), "Root does not contain item with that fullURI"); + checkArgument(items.containsKey(item.getFullURI()), "Root does not contain item with that fullURI"); // all preconditions ok => remove items.remove(item.getFullURI()); @@ -97,5 +93,6 @@ public final class Root extends Manager { public String toString() { return ID; } + } diff --git a/src/rails/game/state/State.java b/src/rails/game/state/State.java index 0428a7e..fc2ef79 100644 --- a/src/rails/game/state/State.java +++ b/src/rails/game/state/State.java @@ -15,9 +15,6 @@ import static com.google.common.base.Preconditions.*; */ public abstract class State extends Observable { - // reference to StateManager - private StateManager stateManager; - // optional formatter private Formatter<State> formatter = null; @@ -28,11 +25,13 @@ public abstract class State extends Observable { addModel((Model)parent); } - // check if there is a StateManager available - checkState(getContext().getRoot().getStateManager() == null, "Root of state has no StateManager attached"); - // if so => register state there - stateManager = getContext().getRoot().getStateManager(); - stateManager.registerState(this); + // register if observable state + if (id != null) { + // check if there is a StateManager available + checkState(getContext().getRoot().getStateManager() != null, "Root of state has no StateManager attached"); + // if so => register state there + getRoot().getStateManager().registerState(this); + } } // Observable methods @@ -56,8 +55,4 @@ public abstract class State extends Observable { public void setFormatter(Formatter<State> formatter) { this.formatter = formatter; } - - StateManager getStateManager() { - return stateManager; - } } \ No newline at end of file diff --git a/src/rails/game/state/StateManager.java b/src/rails/game/state/StateManager.java index 4526712..e48f17d 100644 --- a/src/rails/game/state/StateManager.java +++ b/src/rails/game/state/StateManager.java @@ -1,6 +1,5 @@ package rails.game.state; -import java.util.HashSet; import java.util.List; import java.util.Set; @@ -19,31 +18,29 @@ public final class StateManager extends Manager { LoggerFactory.getLogger(StateManager.class.getPackage().getName()); private final ChangeStack changeStack = ChangeStack.create(this); - private final PortfolioManager portfolioManager = PortfolioManager.create(this, "portfolioManager"); - private final WalletManager walletManager = WalletManager.create(this, "walletManager"); - - private final Set<State> allStates = new HashSet<State>(); + private final HashSetState<State> allStates = HashSetState.create(this, null); +// private final PortfolioManager portfolioManager = PortfolioManager.create(this, "portfolioManager"); +// private final WalletManager walletManager = WalletManager.create(this, "walletManager"); + private StateManager(Root parent, String id) { super(parent, id); } - public static StateManager create(Root parent, String id){ + static StateManager create(Root parent, String id){ return new StateManager(parent, id); } - /** * Register states * Remark: Portfolios and Wallets get added from their respective managers automatically */ - boolean registerState(State state) { - if (!allStates.add(state)) return false; - if (state instanceof Portfolio) { - return portfolioManager.addPortfolio((Portfolio<?>) state); - } else if (state instanceof Wallet) { - return walletManager.addWallet((Wallet<?>) state); - } - return true; + void registerState(State state) { + allStates.add(state); +// if (state instanceof Portfolio) { +// return portfolioManager.addPortfolio((Portfolio<?>) state); +// } else if (state instanceof Wallet) { +// return walletManager.addWallet((Wallet<?>) state); +// } } /** @@ -52,11 +49,11 @@ public final class StateManager extends Manager { */ boolean deRegisterState(State state) { if (!allStates.remove(state)) return false; - if (state instanceof PortfolioMap) { - return portfolioManager.removePortfolio((PortfolioMap<?>) state); - } else if (state instanceof Wallet) { - return walletManager.removeWallet((Wallet<?>) state); - } +// if (state instanceof PortfolioMap) { +// return portfolioManager.removePortfolio((PortfolioMap<?>) state); +// } else if (state instanceof Wallet) { +// return walletManager.removeWallet((Wallet<?>) state); +// } return true; } @@ -187,6 +184,4 @@ public final class StateManager extends Manager { return changeStack; } - - } diff --git a/src/rails/game/state/StringChange.java b/src/rails/game/state/StringChange.java index 1ca9680..ae551f6 100644 --- a/src/rails/game/state/StringChange.java +++ b/src/rails/game/state/StringChange.java @@ -12,10 +12,10 @@ final class StringChange extends Change { private final String oldValue; StringChange(StringState state, String newValue) { - super(state); this.state = state; this.newValue = newValue; this.oldValue = state.stringValue(); + super.init(state); } @Override diff --git a/src/rails/game/state/WalletChange.java b/src/rails/game/state/WalletChange.java index 59afb72..147fdaa 100644 --- a/src/rails/game/state/WalletChange.java +++ b/src/rails/game/state/WalletChange.java @@ -8,11 +8,11 @@ final class WalletChange<T extends CountableItem> extends Change { private final int amount; WalletChange(Wallet<T> in, Wallet<T> out, T item, int amount) { - super(in); this.in = in; this.out = out; this.item = item; this.amount = amount; + super.init(in); } @Override |