|
From: Stefan F. <ste...@us...> - 2012-02-07 18:05:12
|
rails/game/state/ArrayListMultimapState.java | 4
rails/game/state/Context.java | 2
rails/game/state/CountableItem.java | 11 ++
rails/game/state/HashMultimapState.java | 2
rails/game/state/ItemType.java | 5 +
rails/game/state/MultimapState.java | 2
rails/game/state/OwnableItem.java | 16 +++
rails/game/state/PortfolioChange.java | 44 ++++++++++
rails/game/state/PortfolioManager.java | 38 ++++++++
rails/game/state/PortfolioNG.java | 117 +++++++++++++++++++++++++++
rails/game/state/StateManager.java | 61 +++++++++++---
rails/game/state/Wallet.java | 111 +++++++++++++++++++++++++
rails/game/state/WalletChange.java | 50 +++++++++++
rails/game/state/WalletManager.java | 38 ++++++++
14 files changed, 485 insertions(+), 16 deletions(-)
New commits:
commit 6d9cd8a9cc6a0e0f1f638d7b518fd75d9b363f7f
Author: Stefan Frey <ste...@we...>
Date: Mon Feb 6 20:27:46 2012 +0100
Creation of next generation portfolio mechanism based on a portfolio class that extends State class
Creation of a wallet class that allows countable items
diff --git a/rails/game/state/ArrayListMultimapState.java b/rails/game/state/ArrayListMultimapState.java
index 406a90e..d23a0eb 100644
--- a/rails/game/state/ArrayListMultimapState.java
+++ b/rails/game/state/ArrayListMultimapState.java
@@ -42,7 +42,7 @@ public class ArrayListMultimapState<K,V> extends MultimapState<K,V> {
*/
public boolean put(K key, V value) {
- new MultimapChange<K,V>(this, key, value, true );
+ new MultimapChange<K,V>(this, key, value, true);
return true;
}
@@ -65,7 +65,7 @@ public class ArrayListMultimapState<K,V> extends MultimapState<K,V> {
return map.toString();
}
- public void change(K key, V value, boolean addToMap) {
+ void change(K key, V value, boolean addToMap) {
if (addToMap) {
map.put(key, value);
} else {
diff --git a/rails/game/state/Context.java b/rails/game/state/Context.java
index 65b69ba..2036777 100644
--- a/rails/game/state/Context.java
+++ b/rails/game/state/Context.java
@@ -68,7 +68,7 @@ public class Context extends GameItem {
private Context initRoot() {
super.init(this); // sets parent identical to ROOT
- stateManager = new StateManager(this);
+ stateManager = StateManager.create();
return this;
}
diff --git a/rails/game/state/CountableItem.java b/rails/game/state/CountableItem.java
new file mode 100644
index 0000000..122b22c
--- /dev/null
+++ b/rails/game/state/CountableItem.java
@@ -0,0 +1,11 @@
+package rails.game.state;
+
+/**
+ * Identifies items which are countable
+ * They are stored inside wallets
+ * @author freystef
+ */
+
+public interface CountableItem extends Item {
+
+}
diff --git a/rails/game/state/HashMultimapState.java b/rails/game/state/HashMultimapState.java
index 0c2faa4..50a3640 100644
--- a/rails/game/state/HashMultimapState.java
+++ b/rails/game/state/HashMultimapState.java
@@ -106,7 +106,7 @@ public final class HashMultimapState<K,V> extends MultimapState<K,V> implements
return map.toString();
}
- public void change(K key, V value, boolean addToMap) {
+ void change(K key, V value, boolean addToMap) {
if (addToMap) {
map.put(key, value);
} else {
diff --git a/rails/game/state/ItemType.java b/rails/game/state/ItemType.java
new file mode 100644
index 0000000..efd0639
--- /dev/null
+++ b/rails/game/state/ItemType.java
@@ -0,0 +1,5 @@
+package rails.game.state;
+
+public interface ItemType extends Item {
+
+}
diff --git a/rails/game/state/MultimapState.java b/rails/game/state/MultimapState.java
index 3750ffb..f9f3550 100644
--- a/rails/game/state/MultimapState.java
+++ b/rails/game/state/MultimapState.java
@@ -13,6 +13,6 @@ abstract class MultimapState<K,V> extends State {
public abstract boolean remove(K key, V value);
- public abstract void change(K key, V value, boolean addToMap);
+ abstract void change(K key, V value, boolean addToMap);
}
diff --git a/rails/game/state/OwnableItem.java b/rails/game/state/OwnableItem.java
new file mode 100644
index 0000000..aad0cad
--- /dev/null
+++ b/rails/game/state/OwnableItem.java
@@ -0,0 +1,16 @@
+package rails.game.state;
+
+
+public interface OwnableItem<T extends OwnableItem<T>> extends Item {
+
+ /**
+ * @return the parent of an ownableItem has to be an ItemType
+ */
+ public ItemType getParent();
+
+ /**
+ * @return the current portfolio
+ */
+ public PortfolioNG<T> getPortfolio();
+
+}
diff --git a/rails/game/state/PortfolioChange.java b/rails/game/state/PortfolioChange.java
new file mode 100644
index 0000000..acdf5d3
--- /dev/null
+++ b/rails/game/state/PortfolioChange.java
@@ -0,0 +1,44 @@
+package rails.game.state;
+
+final class PortfolioChange<T extends OwnableItem<T>> implements Change {
+
+ private final PortfolioNG<T> in;
+ private final PortfolioNG<T> out; // can be null
+ private final T item;
+
+ PortfolioChange(PortfolioNG<T> in, PortfolioNG<T> out, T item) {
+ this.in = in;
+ this.out = out;
+ this.item = item;
+
+ ChangeStack.add(this);
+ }
+
+ public void execute() {
+ in.change(item, true);
+ if (out != null) {
+ out.change(item, false);
+ }
+ }
+
+ public void undo() {
+ in.change(item, false);
+ if (out != null) {
+ out.change(item, true);
+ }
+ }
+
+ public PortfolioNG<T> getState() {
+ return in;
+ }
+
+ @Override
+ public String toString() {
+ if (out == null) {
+ return "PortfolioChange: Adds item " + item + " to portfolio " + in.getId();
+ } else {
+ return "PortfolioChange: Moves item " + item + " from portfolio " + out.getId() + " to portfolio " + in.getId();
+ }
+ }
+
+}
diff --git a/rails/game/state/PortfolioManager.java b/rails/game/state/PortfolioManager.java
new file mode 100644
index 0000000..b7dab6c
--- /dev/null
+++ b/rails/game/state/PortfolioManager.java
@@ -0,0 +1,38 @@
+package rails.game.state;
+
+import com.google.common.collect.HashMultimap;
+
+/**
+ * PortfolioManager stores links to all existing portfolios
+ * @author freystef
+ */
+
+public class PortfolioManager extends Context {
+
+ public static final String ID = "Portfolios";
+
+ private final HashMultimap<Item, PortfolioNG<?>> portfolios = HashMultimap.create();
+
+ private PortfolioManager() {
+ super(ID);
+ }
+
+ static PortfolioManager create() {
+ return new PortfolioManager();
+ }
+
+ @Override
+ public PortfolioManager init(Item parent) {
+ super.init(parent);
+ return this;
+ }
+
+ boolean addPortfolio(PortfolioNG<?> p){
+ return portfolios.put(p.getParent(), p);
+ }
+
+ boolean removePortfolio(PortfolioNG<?> p){
+ return portfolios.remove(p.getParent(), p);
+ }
+
+}
diff --git a/rails/game/state/PortfolioNG.java b/rails/game/state/PortfolioNG.java
new file mode 100644
index 0000000..e6eb2b3
--- /dev/null
+++ b/rails/game/state/PortfolioNG.java
@@ -0,0 +1,117 @@
+package rails.game.state;
+
+import java.util.Collection;
+
+import com.google.common.collect.ArrayListMultimap;
+import com.google.common.collect.ImmutableList;
+import com.google.common.collect.ImmutableListMultimap;
+import com.google.common.collect.ImmutableMap;
+
+public final class PortfolioNG<T extends OwnableItem<T>> extends State {
+
+ private final ArrayListMultimap<ItemType, T> portfolio;
+
+ private PortfolioNG(String id) {
+ super(id);
+ portfolio = ArrayListMultimap.create();
+ }
+
+ /**
+ * Creates an owned and empty Portfolio
+ */
+ public static <T extends OwnableItem<T>> PortfolioNG<T> create(Item parent, String id){
+ return new PortfolioNG<T>(id).init(parent);
+ }
+
+ /**
+ * Creates an unowned and empty Portfolio
+ * Remark: Still requires a call to the init-method
+ */
+ public static <T extends OwnableItem<T>> PortfolioNG<T> create( String id){
+ return new PortfolioNG<T>(id);
+ }
+
+ @Override
+ public PortfolioNG<T> init(Item parent){
+ super.init(parent);
+ return this;
+ }
+
+ /**
+ * Adds an item to the portfolio
+ * @param item to add
+ * @return false if portfolio already contains the item, otherwise true
+ */
+ public boolean initialAdd(T item) {
+ if (portfolio.containsValue(item)) return false;
+ new PortfolioChange<T>(this, null, item);
+ return true;
+ }
+
+ /**
+ * Move a new item to the portfolio
+ * and removes the item from the previous portfolio
+ * @param item to move
+ * @return false if the portfolio already contains the item, otherwise true
+ */
+ public boolean moveInto(T item) {
+ if (portfolio.containsValue(item)) return false;
+ new PortfolioChange<T>(this, item.getPortfolio(), item);
+ return true;
+ }
+
+ /**
+ * @param item that is checked if it is in the portfolio
+ * @return true if contained, false otherwise
+ */
+ public boolean containsItem(T item) {
+ return portfolio.containsValue(item);
+ }
+
+ /**
+ * @param type that is checked if there are items stored for
+ * @return true if there a items stored under that type, false otherwise
+ */
+ public boolean containsKey(ItemType type) {
+ return portfolio.containsKey(type);
+ }
+
+ /**
+ * @return all items contained in the portfolio
+ */
+ public ImmutableList<T> items() {
+ return ImmutableList.copyOf(portfolio.values());
+ }
+
+
+ /**
+ * @param key that defines the specific itemtype for which the portfolio members get returned
+ * @return all items of type key contained in the portfolio
+ */
+ public ImmutableList<T> getItems(ItemType key) {
+ return ImmutableList.copyOf(portfolio.get(key));
+ }
+
+ /**
+ * @return a ListMultimap view of the Portfolio
+ */
+ public ImmutableListMultimap<ItemType, T> view() {
+ return ImmutableListMultimap.copyOf(portfolio);
+ }
+
+ /**
+ * @return a Map view of the Portfolio
+ */
+ public ImmutableMap<ItemType, Collection<T>> viewAsMap() {
+ return ImmutableMap.copyOf(portfolio.asMap());
+ }
+
+ void change (T item, boolean intoPortfolio){
+ if (intoPortfolio) {
+ portfolio.put(item.getParent(), item);
+ } else {
+ portfolio.remove(item.getParent(), item);
+ }
+ }
+
+}
diff --git a/rails/game/state/StateManager.java b/rails/game/state/StateManager.java
index 7ef27c5..3812e44 100644
--- a/rails/game/state/StateManager.java
+++ b/rails/game/state/StateManager.java
@@ -15,33 +15,72 @@ import com.google.common.collect.Sets;
import rails.game.GameManager;
import rails.game.model.Model;
+// TODO: Change it to a Context subclass
+
public final class StateManager extends GameItem {
+ public static final String ID = "States";
+
protected static Logger log =
Logger.getLogger(StateManager.class.getPackage().getName());
private final Set<State> allStates = new HashSet<State>();
+
+ private final PortfolioManager portfolioManager = PortfolioManager.create();
+ private final WalletManager walletManager = WalletManager.create();
+
+ private StateManager() {
+ super(ID);
+ }
/**
- * Creates a StateManager (only possible for a root GameContext)
- * @param parent a root GameContext
+ * Creates an (unowned) StateManager
+ * Remark: Still requires a call to the init-method
*/
- StateManager(Context parent) {
- super("StateManager");
-
- if (parent.getId() != Context.ROOT) {
+ public static StateManager create(){
+ return new StateManager();
+ }
+
+ /**
+ * @exception IllegalArgumentException if parent is not the Context with an id equal to Context.ROOT
+ */
+ @Override
+ public StateManager init(Item parent) {
+ if (!(parent instanceof Context && parent.getId() != Context.ROOT)) {
throw new IllegalArgumentException("StateManager can only be created for a root GameContext");
}
- // immediate initialization
- init(parent);
+ super.init(parent);
+ portfolioManager.init(parent);
+ walletManager.init(parent);
+ return this;
}
- void registerState(State state) {
- allStates.add(state);
+ /**
+ * 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 PortfolioNG) {
+ return portfolioManager.addPortfolio((PortfolioNG<?>) state);
+ } else if (state instanceof Wallet) {
+ return walletManager.addWallet((Wallet<?>) state);
+ }
+ return true;
}
+ /**
+ * De-Register states
+ * Remark: Portfolios and Wallets are removed from their respective managers automatically
+ */
boolean deRegisterState(State state) {
- return allStates.remove(state);
+ if (!allStates.remove(state)) return false;
+ if (state instanceof PortfolioNG) {
+ return portfolioManager.removePortfolio((PortfolioNG<?>) state);
+ } else if (state instanceof Wallet) {
+ return walletManager.removeWallet((Wallet<?>) state);
+ }
+ return true;
}
/**
diff --git a/rails/game/state/Wallet.java b/rails/game/state/Wallet.java
new file mode 100644
index 0000000..da119bd
--- /dev/null
+++ b/rails/game/state/Wallet.java
@@ -0,0 +1,111 @@
+package rails.game.state;
+
+import com.google.common.collect.HashMultiset;
+import com.google.common.collect.ImmutableMultiset;
+
+/**
+ * A wallet allows the storage of CountableItem(s)
+ *
+ * Remark: It is a wrapper around a Multiset, thus it does not support negative
+ * numbers.
+ *
+ * @author freystef
+ */
+
+public final class Wallet<T extends CountableItem> extends State {
+
+
+ private final HashMultiset<T> wallet;
+
+
+ private Wallet(String id) {
+ super(id);
+ wallet = HashMultiset.create();
+ }
+
+ /**
+ * Creates an owned and empty Wallet
+ */
+ public static <T extends CountableItem> Wallet<T> create(Item parent, String id){
+ return new Wallet<T>(id).init(parent);
+ }
+
+ /**
+ * Creates an unowned and empty Wallet
+ * Remark: Still requires a call to the init-method
+ */
+ public static <T extends CountableItem> Wallet<T> create( String id){
+ return new Wallet<T>(id);
+ }
+
+ @Override
+ public Wallet<T> init(Item parent){
+ super.init(parent);
+ return this;
+ }
+
+ /**
+ * Sets an item to the wallet to the amount given (only if there is no amount defined yet)
+ * @param item to set
+ * @param amount initial (has to be positive)
+ * @return false if portfolio already contains the item (=> no change), otherwise true
+
+ * @exception IllegalArgumentException if amount is negative
+ */
+ public boolean initialSet(T item, int amount) {
+ if (amount < 0) throw new IllegalArgumentException("Wallet amounts have to be positive");
+ if (wallet.contains(item)) return false;
+
+ new WalletChange<T>(this, null, item, amount);
+ return true;
+ }
+
+ /**
+ * Adds a specific amount to the specified item
+ * @param item to change
+ * @param amount initial (has to be positive)
+ * @param source the wallet from which the amount is moved
+
+ * @exception IllegalArgumentException if amount is negative
+ * @exception ArithmeticException if wallet which is used as source does not contain at least the amount
+ */
+
+ public void moveInto(T item, int amount, Wallet<T> source) {
+ if (amount < 0) throw new IllegalArgumentException("Wallet amounts have to be positive");
+
+ if (amount > source.count(item)) throw new ArithmeticException("Source wallet does not contain required amount");
+
+ new WalletChange<T>(this, source, item, amount);
+ }
+
+ /**
+ * Adds one unit of the specified item
+ * @param item to change
+ * @param from the wallet from which the unit is taken
+ */
+ public void moveInto(T item, Wallet<T> from) {
+ moveInto(item, 1, from);
+ }
+
+
+ /**
+ * @param item to count
+ * @return the current number of the specified amount in the wallet
+ */
+ public int count(T item) {
+ return wallet.count(item);
+ }
+
+ /**
+ * @return a Multiset view of the Wallet
+ */
+ public ImmutableMultiset<T> view() {
+ return ImmutableMultiset.copyOf(wallet);
+ }
+
+
+ void change (T item, int value) {
+ wallet.add(item, value);
+ }
+
+}
diff --git a/rails/game/state/WalletChange.java b/rails/game/state/WalletChange.java
new file mode 100644
index 0000000..da646c3
--- /dev/null
+++ b/rails/game/state/WalletChange.java
@@ -0,0 +1,50 @@
+package rails.game.state;
+
+final class WalletChange<T extends CountableItem> implements Change {
+
+ private final Wallet<T> in;
+ private final Wallet<T> out;
+ private final T item;
+ private final int amount;
+
+
+ WalletChange(Wallet<T> in, Wallet<T> out, T item, int amount) {
+ this.in = in;
+ this.out = out;
+ this.item = item;
+ this.amount = amount;
+
+ ChangeStack.add(this);
+ }
+
+
+ public void execute() {
+ in.change(item, amount);
+ if (out != null) {
+ out.change(item, - amount);
+ }
+ }
+
+ public void undo() {
+ in.change(item, - amount);
+ if (out != null) {
+ out.change(item, amount);
+ }
+ }
+
+ public Wallet<T> getState() {
+ return in;
+ }
+
+ @Override
+ public String toString() {
+ if (out == null) {
+ return "WalletChange: Sets " + amount + " of " + item + " in wallet " + in.getId();
+ } else {
+ return "WalletChange: Moves " + amount + " of " + item + " from wallet " + out.getId() + " to wallet " + in.getId();
+ }
+ }
+
+
+
+}
diff --git a/rails/game/state/WalletManager.java b/rails/game/state/WalletManager.java
new file mode 100644
index 0000000..2d00847
--- /dev/null
+++ b/rails/game/state/WalletManager.java
@@ -0,0 +1,38 @@
+package rails.game.state;
+
+import com.google.common.collect.HashMultimap;
+
+/**
+ * WalletManager stores links to all existing wallets
+ * @author freystef
+ */
+
+public class WalletManager extends Context {
+
+ public static final String ID = "Wallets";
+
+ private final HashMultimap<Item, Wallet<?>> wallets = HashMultimap.create();
+
+ private WalletManager() {
+ super(ID);
+ }
+
+ static WalletManager create() {
+ return new WalletManager();
+ }
+
+ @Override
+ public WalletManager init(Item parent) {
+ super.init(parent);
+ return this;
+ }
+
+ boolean addWallet(Wallet<?> w){
+ return wallets.put(w.getParent(), w);
+ }
+
+ boolean removeWallet(Wallet<?> w){
+ return wallets.remove(w.getParent(), w);
+ }
+
+}
|