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); + } + +} |