diff options
| author | Ben Culkin <scorpress@gmail.com> | 2020-11-17 17:44:05 -0500 |
|---|---|---|
| committer | Ben Culkin <scorpress@gmail.com> | 2020-11-17 17:44:05 -0500 |
| commit | 213171f95845ebc5efcfa73d1cab4b723ff2abf2 (patch) | |
| tree | e0c167842d8923e5a0235a9e64fc0d7fe7201cf8 | |
| parent | 38e97f991ee69afd53f36fd7296b4afd5a621311 (diff) | |
Implement IFreezable for IMap
| -rw-r--r-- | src/main/java/bjc/esodata/PushdownMap.java | 77 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/ExtendedMap.java | 125 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/FunctionalMap.java | 108 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/IMap.java | 24 | ||||
| -rw-r--r-- | src/main/java/bjc/funcdata/TransformedValueMap.java | 63 |
5 files changed, 253 insertions, 144 deletions
diff --git a/src/main/java/bjc/esodata/PushdownMap.java b/src/main/java/bjc/esodata/PushdownMap.java index a10c149..456d33b 100644 --- a/src/main/java/bjc/esodata/PushdownMap.java +++ b/src/main/java/bjc/esodata/PushdownMap.java @@ -1,12 +1,8 @@ package bjc.esodata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.function.*; -import bjc.funcdata.FunctionalMap; -import bjc.funcdata.IList; -import bjc.funcdata.IMap; +import bjc.funcdata.*; /** * A variant of a map where inserting a duplicate key shadows the existing value @@ -26,18 +22,18 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> /* Our backing storage. */ private final IMap<KeyType, Stack<ValueType>> backing; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** Create a new empty stack-based map. */ public PushdownMap() { backing = new FunctionalMap<>(); } - /** Create a new empty stack-based map using the specified backing. */ - private PushdownMap(final IMap<KeyType, Stack<ValueType>> back) { - backing = back; - } - @Override public void clear() { + if (isFrozen) throw new ObjectFrozen("Can't clear frozen map"); + backing.clear(); } @@ -45,12 +41,7 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> public boolean containsKey(final KeyType key) { return backing.containsKey(key); } - - @Override - public IMap<KeyType, ValueType> extend() { - return new PushdownMap<>(backing.extend()); - } - + @Override public void forEach(final BiConsumer<KeyType, ValueType> action) { backing.forEach((key, stk) -> action.accept(key, stk.top())); @@ -82,16 +73,9 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> } @Override - public <V2> IMap<KeyType, V2> transform(final Function<ValueType, V2> transformer) { - /* - * @NOTE Can and should we support this? More to the point, maybe this should be - * a map sub-type that does what it needs to? - */ - throw new UnsupportedOperationException("Cannot transform pushdown maps."); - } - - @Override public ValueType put(final KeyType key, final ValueType val) { + if (isFrozen) throw new ObjectFrozen("Can't insert key " + key + " into frozen map"); + if (backing.containsKey(key)) { final Stack<ValueType> stk = backing.get(key); @@ -111,6 +95,8 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> @Override public ValueType remove(final KeyType key) { + if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + final Stack<ValueType> stk = backing.get(key); if (stk.size() > 1) return stk.pop(); @@ -154,4 +140,43 @@ public class PushdownMap<KeyType, ValueType> implements IMap<KeyType, ValueType> public String toString() { return String.format("PushdownMap [backing=%s]", backing); } + + @Override + public boolean freeze() { + isFrozen = true; + + return true; + } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { + return false; + } + } + + @Override + public boolean deepFreeze() { + thawEnabled = true; + + return freeze(); + } + + @Override + public boolean canFreeze() { + return true; + } + + @Override + public boolean canThaw() { + return thawEnabled; + } + + @Override + public boolean isFrozen() { + return isFrozen; + } } diff --git a/src/main/java/bjc/funcdata/ExtendedMap.java b/src/main/java/bjc/funcdata/ExtendedMap.java index bd500f4..19e3b4e 100644 --- a/src/main/java/bjc/funcdata/ExtendedMap.java +++ b/src/main/java/bjc/funcdata/ExtendedMap.java @@ -1,8 +1,7 @@ package bjc.funcdata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.*; +import java.util.function.*; /** * An extended version of a map, that stores values into a map, but can look @@ -22,6 +21,9 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { /* The map we store things in. */ private final IMap<KeyType, ValueType> store; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** * Create a new extended map. * @@ -39,20 +41,15 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { @Override public void clear() { + if (isFrozen) return; + store.clear(); } @Override public boolean containsKey(final KeyType key) { - if (store.containsKey(key)) - return true; - - return delegate.containsKey(key); - } - - @Override - public IMap<KeyType, ValueType> extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); + if (store.containsKey(key)) return true; + else return delegate.containsKey(key); } @Override @@ -78,10 +75,8 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { @Override public ValueType get(final KeyType key) { - if (store.containsKey(key)) - return store.get(key); - - return delegate.get(key); + if (store.containsKey(key)) return store.get(key); + else return delegate.get(key); } @Override @@ -100,22 +95,20 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { } @Override - public <MappedValue> IMap<KeyType, MappedValue> - transform(final Function<ValueType, MappedValue> transformer) { - return new TransformedValueMap<>(this, transformer); - } - - @Override public ValueType put(final KeyType key, final ValueType val) { + if (isFrozen) + throw new ObjectFrozen("Can't insert key " + key + " into frozen map"); + return store.put(key, val); } @Override public ValueType remove(final KeyType key) { - if (!store.containsKey(key)) - return delegate.remove(key); - - return store.remove(key); + if (isFrozen) + throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + + if (!store.containsKey(key)) return delegate.remove(key); + else return store.remove(key); } @Override @@ -127,43 +120,69 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> { return ilst; } - + @Override - public int hashCode() { - final int prime = 31; - int result = 1; - result = prime * result + (delegate == null ? 0 : delegate.hashCode()); - result = prime * result + (store == null ? 0 : store.hashCode()); - return result; + public int hashCode() { + // isFrozen isn't counted + return Objects.hash(delegate, store); + } + + /* Misc. object support. */ + @Override + public boolean equals(Object obj) { + if (this == obj) return true; + if (obj == null) return false; + if (getClass() != obj.getClass()) return false; + + ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj; + + // isFrozen isn't counted + return Objects.equals(delegate, other.delegate) && Objects.equals(store, other.store); + } + + @Override + public String toString() { + return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store); } + /* IFreezable support */ + @Override - public boolean equals(final Object obj) { - if (this == obj) + public boolean freeze() { + isFrozen = true; + return true; - if (obj == null) - return false; - if (!(obj instanceof ExtendedMap)) - return false; + } - final ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj; + @Override + public boolean deepFreeze() { + thawEnabled = false; - if (delegate == null) { - if (other.delegate != null) - return false; - } else if (!delegate.equals(other.delegate)) - return false; - if (store == null) { - if (other.store != null) - return false; - } else if (!store.equals(other.store)) + return freeze(); + } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { return false; + } + } + @Override + public boolean isFrozen() { + return isFrozen; + } + + @Override + public boolean canFreeze() { return true; } - + @Override - public String toString() { - return String.format("ExtendedMap [delegate=%s, store=%s]", delegate, store); + public boolean canThaw() { + return thawEnabled; } -} +}
\ No newline at end of file diff --git a/src/main/java/bjc/funcdata/FunctionalMap.java b/src/main/java/bjc/funcdata/FunctionalMap.java index aba3dd1..c088ff3 100644 --- a/src/main/java/bjc/funcdata/FunctionalMap.java +++ b/src/main/java/bjc/funcdata/FunctionalMap.java @@ -1,12 +1,9 @@ package bjc.funcdata; -import java.util.HashMap; -import java.util.Map; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.*; +import java.util.function.*; -import bjc.data.IPair; +import bjc.data.*; /** * Basic implementation of {@link IMap} @@ -23,6 +20,9 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp /* Our backing store. */ private Map<KeyType, ValueType> wrappedMap; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** Create a new blank functional map */ public FunctionalMap() { wrappedMap = new HashMap<>(); @@ -39,9 +39,7 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp this(); for (final IPair<KeyType, ValueType> entry : entries) { - entry.doWith((key, val) -> { - wrappedMap.put(key, val); - }); + entry.doWith(wrappedMap::put); } } @@ -52,14 +50,15 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp * The map to wrap. */ public FunctionalMap(final Map<KeyType, ValueType> wrap) { - if (wrap == null) - throw new NullPointerException("Map to wrap must not be null"); + if (wrap == null) throw new NullPointerException("Map to wrap must not be null"); wrappedMap = wrap; } @Override public void clear() { + if (isFrozen) throw new ObjectFrozen("Can't clear frozen map"); + wrappedMap.clear(); } @@ -69,11 +68,6 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp } @Override - public IMap<KeyType, ValueType> extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override public void forEach(final BiConsumer<KeyType, ValueType> action) { wrappedMap.forEach(action); } @@ -90,8 +84,7 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp @Override public ValueType get(final KeyType key) { - if (key == null) - throw new NullPointerException("Key must not be null"); + if (key == null) throw new NullPointerException("Key must not be null"); if (!wrappedMap.containsKey(key)) { final String msg = String.format("Key %s is not present in the map", key); @@ -111,32 +104,23 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp public IList<KeyType> keyList() { final FunctionalList<KeyType> keys = new FunctionalList<>(); - wrappedMap.keySet().forEach(key -> { - keys.add(key); - }); + wrappedMap.keySet().forEach(keys::add); return keys; } @Override - public <MappedValue> IMap<KeyType, MappedValue> - transform(final Function<ValueType, MappedValue> transformer) { - if (transformer == null) - throw new NullPointerException("Transformer must not be null"); - - return new TransformedValueMap<>(this, transformer); - } - - @Override public ValueType put(final KeyType key, final ValueType val) { - if (key == null) - throw new NullPointerException("Key must not be null"); + if (isFrozen) throw new ObjectFrozen("Can't put key " + key + " into frozen map"); + if (key == null) throw new NullPointerException("Key must not be null"); return wrappedMap.put(key, val); } @Override public ValueType remove(final KeyType key) { + if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + return wrappedMap.remove(key); } @@ -149,9 +133,7 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp public IList<ValueType> valueList() { final FunctionalList<ValueType> values = new FunctionalList<>(); - wrappedMap.values().forEach(value -> { - values.add(value); - }); + wrappedMap.values().forEach(values::add); return values; } @@ -166,20 +148,58 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp @Override public boolean equals(final Object obj) { - if (this == obj) - return true; - if (obj == null) - return false; - if (!(obj instanceof FunctionalMap)) - return false; + if (this == obj) return true; + if (obj == null) return false; + if (!(obj instanceof FunctionalMap)) return false; final FunctionalMap<?, ?> other = (FunctionalMap<?, ?>) obj; if (wrappedMap == null) { - if (other.wrappedMap != null) - return false; - } else if (!wrappedMap.equals(other.wrappedMap)) + if (other.wrappedMap != null) return false; + } else if (!wrappedMap.equals(other.wrappedMap)) { + return false; + } + + return true; + } + + // IFreezable support + @Override + public boolean freeze() { + isFrozen = true; + + return true; + } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { return false; + } + } + + @Override + public boolean deepFreeze() { + thawEnabled = false; + + return freeze(); + } + + @Override + public boolean canFreeze() { return true; } + + @Override + public boolean canThaw() { + return thawEnabled; + } + + @Override + public boolean isFrozen() { + return isFrozen; + } } diff --git a/src/main/java/bjc/funcdata/IMap.java b/src/main/java/bjc/funcdata/IMap.java index dc5ee00..e723507 100644 --- a/src/main/java/bjc/funcdata/IMap.java +++ b/src/main/java/bjc/funcdata/IMap.java @@ -1,8 +1,6 @@ package bjc.funcdata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.function.*; /** * Functional wrapper over map providing some useful things. @@ -15,7 +13,7 @@ import java.util.function.Function; * @param <ValueType> * The type of this map's values. */ -public interface IMap<KeyType, ValueType> { +public interface IMap<KeyType, ValueType> extends IFreezable { /** * Execute an action for each entry in the map. * @@ -109,7 +107,7 @@ public interface IMap<KeyType, ValueType> { /** Delete all the values in the map. */ default void clear() { - keyList().forEach(key -> remove(key)); + keyList().forEach(IMap.this::remove); } /** @@ -151,7 +149,21 @@ public interface IMap<KeyType, ValueType> { * * @return An extended map. */ - IMap<KeyType, ValueType> extend(); + default IMap<KeyType, ValueType> extend() { + return extend(new FunctionalMap<>()); + }; + + /** + * Extends this map, creating a new map that will delegate queries to this map, + * but store any added values in the provided map. + * + * @param backer The map to store added values in. + * + * @return An extended map. + */ + default IMap<KeyType, ValueType> extend(IMap<KeyType, ValueType> backer) { + return new ExtendedMap<>(this, backer); + }; /** * Remove the value bound to the key. diff --git a/src/main/java/bjc/funcdata/TransformedValueMap.java b/src/main/java/bjc/funcdata/TransformedValueMap.java index 5de6fc3..e51c9eb 100644 --- a/src/main/java/bjc/funcdata/TransformedValueMap.java +++ b/src/main/java/bjc/funcdata/TransformedValueMap.java @@ -1,8 +1,6 @@ package bjc.funcdata; -import java.util.function.BiConsumer; -import java.util.function.Consumer; -import java.util.function.Function; +import java.util.function.*; /** * A map that transforms values from one type to another @@ -26,6 +24,9 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> /* Our transforming function. */ private final Function<OldValue, NewValue> transformer; + private boolean isFrozen = false; + private boolean thawEnabled = true; + /** * Create a new transformed-value loop. * @@ -43,6 +44,8 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> @Override public void clear() { + if (isFrozen) throw new ObjectFrozen("Can't clear frozen map"); + backing.clear(); } @@ -52,11 +55,6 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> } @Override - public IMap<OldKey, NewValue> extend() { - return new ExtendedMap<>(this, new FunctionalMap<>()); - } - - @Override public void forEach(final BiConsumer<OldKey, NewValue> action) { backing.forEach((key, value) -> { action.accept(key, transformer.apply(value)); @@ -91,18 +89,14 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> } @Override - public <MappedValue> IMap<OldKey, MappedValue> - transform(final Function<NewValue, MappedValue> transform) { - return new TransformedValueMap<>(this, transform); - } - - @Override public NewValue put(final OldKey key, final NewValue value) { throw new UnsupportedOperationException("Can't add items to transformed map"); } @Override public NewValue remove(final OldKey key) { + if (isFrozen) throw new ObjectFrozen("Can't remove key " + key + " from frozen map"); + return transformer.apply(backing.remove(key)); } @@ -115,4 +109,43 @@ final class TransformedValueMap<OldKey, OldValue, NewValue> public IList<NewValue> valueList() { return backing.valueList().map(transformer); } -} + + @Override + public boolean freeze() { + isFrozen = true; + + return true; + } + + @Override + public boolean thaw() { + if (thawEnabled) { + isFrozen = false; + return true; + } else { + return false; + } + } + + @Override + public boolean deepFreeze() { + thawEnabled = false; + + return freeze(); + } + + @Override + public boolean canFreeze() { + return true; + } + + @Override + public boolean canThaw() { + return thawEnabled; + } + + @Override + public boolean isFrozen() { + return isFrozen; + } +}
\ No newline at end of file |
