summaryrefslogtreecommitdiff
path: root/src/main/java/bjc/funcdata
diff options
context:
space:
mode:
Diffstat (limited to 'src/main/java/bjc/funcdata')
-rw-r--r--src/main/java/bjc/funcdata/ExtendedMap.java157
-rw-r--r--src/main/java/bjc/funcdata/Freezable.java73
-rw-r--r--src/main/java/bjc/funcdata/FunctionalList.java62
-rw-r--r--src/main/java/bjc/funcdata/FunctionalMap.java149
-rw-r--r--src/main/java/bjc/funcdata/FunctionalStringTokenizer.java6
-rw-r--r--src/main/java/bjc/funcdata/ListEx.java (renamed from src/main/java/bjc/funcdata/IList.java)22
-rw-r--r--src/main/java/bjc/funcdata/MapEx.java (renamed from src/main/java/bjc/funcdata/IMap.java)146
-rw-r--r--src/main/java/bjc/funcdata/ObjectFrozen.java47
-rw-r--r--src/main/java/bjc/funcdata/TransformedValueMap.java87
-rw-r--r--src/main/java/bjc/funcdata/bst/BinarySearchTree.java21
-rw-r--r--src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java2
-rw-r--r--src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java8
-rw-r--r--src/main/java/bjc/funcdata/bst/TreePart.java (renamed from src/main/java/bjc/funcdata/bst/ITreePart.java)2
13 files changed, 468 insertions, 314 deletions
diff --git a/src/main/java/bjc/funcdata/ExtendedMap.java b/src/main/java/bjc/funcdata/ExtendedMap.java
index bd500f4..379ff65 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
@@ -16,12 +15,15 @@ import java.util.function.Function;
* @param <ValueType>
* The type of the values of the map.
*/
-class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
+class ExtendedMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> {
/* The map we delegate lookups to. */
- private final IMap<KeyType, ValueType> delegate;
+ private final MapEx<KeyType, ValueType> delegate;
/* The map we store things in. */
- private final IMap<KeyType, ValueType> store;
+ private final MapEx<KeyType, ValueType> store;
+ private boolean isFrozen = false;
+ private boolean thawEnabled = true;
+
/**
* Create a new extended map.
*
@@ -31,28 +33,23 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
* @param store
* The map to store things in.
*/
- public ExtendedMap(final IMap<KeyType, ValueType> delegate,
- final IMap<KeyType, ValueType> store) {
+ public ExtendedMap(final MapEx<KeyType, ValueType> delegate,
+ final MapEx<KeyType, ValueType> store) {
this.delegate = delegate;
this.store = store;
}
@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
@@ -63,25 +60,9 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
}
@Override
- public void forEachKey(final Consumer<KeyType> action) {
- store.forEachKey(action);
-
- delegate.forEachKey(action);
- }
-
- @Override
- public void forEachValue(final Consumer<ValueType> action) {
- store.forEachValue(action);
-
- delegate.forEachValue(action);
- }
-
- @Override
- public ValueType get(final KeyType key) {
- if (store.containsKey(key))
- return store.get(key);
-
- return delegate.get(key);
+ public Optional<ValueType> get(final KeyType key) {
+ if (store.containsKey(key)) return store.get(key);
+ else return delegate.get(key);
}
@Override
@@ -90,8 +71,8 @@ class ExtendedMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
}
@Override
- public IList<KeyType> keyList() {
- IList<KeyType> ilst = new FunctionalList<>();
+ public ListEx<KeyType> keyList() {
+ ListEx<KeyType> ilst = new FunctionalList<>();
ilst.addAll(store.keyList());
ilst.addAll(delegate.keyList());
@@ -100,70 +81,84 @@ 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
- public IList<ValueType> valueList() {
- IList<ValueType> ilst = new FunctionalList<>();
+ public int hashCode() {
+ // isFrozen isn't counted
+ return Objects.hash(delegate, store);
+ }
- ilst.addAll(store.valueList());
- ilst.addAll(delegate.valueList());
+ /* 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;
- return ilst;
- }
+ ExtendedMap<?, ?> other = (ExtendedMap<?, ?>) obj;
- @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;
+ // 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/Freezable.java b/src/main/java/bjc/funcdata/Freezable.java
new file mode 100644
index 0000000..e83accb
--- /dev/null
+++ b/src/main/java/bjc/funcdata/Freezable.java
@@ -0,0 +1,73 @@
+package bjc.funcdata;
+
+/**
+ * Indicates that an object can switch between immutable and mutable modes.
+ *
+ * Note that this only implements 'shallow' immutability. Namely, any sub-objects
+ * are not made immutable, and if the type is a collection, the elements are still
+ * as mutable as they were before.
+ *
+ * Implementations of this interface may choose to throw {@link ObjectFrozen} if
+ * you attempt to modify a frozen object, but they may also choose not to.
+ *
+ * @author Ben Culkin
+ */
+public interface Freezable {
+ /**
+ * Freezes the internal state of this object, making it immutable.
+ *
+ * @return True if the object is frozen, false if it couldn't be frozen.
+ */
+ public boolean freeze();
+ /**
+ * Thaws the internal state of this object, making it mutable.
+ *
+ * @return True if the object is thawed, false if it couldn't be thawed.
+ */
+ public boolean thaw();
+
+ /**
+ * 'Deep-freeze' this object, making it immutable and disabling the ability to
+ * thaw it.
+ *
+ * @return True if the object was deep-frozen, false if that couldn't happen.
+ */
+ default boolean deepFreeze() {
+ return false;
+ }
+
+ /**
+ * Check if this object can be frozen.
+ *
+ * @return Whether or not the object can be frozen.
+ */
+ default boolean canFreeze() {
+ return false;
+ }
+
+ /**
+ * Checks if this object can be thawed.
+ *
+ * @return Whether or not the object can be thawed.
+ */
+ default boolean canThaw() {
+ return false;
+ }
+
+ /**
+ * Determines if this object is frozen.
+ *
+ * @return True if the object is frozen, false if the object is thawed.
+ */
+ public boolean isFrozen();
+
+
+ /**
+ * Determines if this object is thawed.
+ *
+ * @return True if the object is thawed, false if the object is thawed.
+ */
+ default boolean isThawed() {
+ return !isFrozen();
+ }
+}
diff --git a/src/main/java/bjc/funcdata/FunctionalList.java b/src/main/java/bjc/funcdata/FunctionalList.java
index 2cdfa27..88f49c4 100644
--- a/src/main/java/bjc/funcdata/FunctionalList.java
+++ b/src/main/java/bjc/funcdata/FunctionalList.java
@@ -12,10 +12,10 @@ import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Predicate;
-import bjc.data.IHolder;
-import bjc.data.IPair;
-import bjc.data.Identity;
+import bjc.data.Holder;
import bjc.data.Pair;
+import bjc.data.Identity;
+import bjc.data.SimplePair;
/**
* A wrapper over another list that provides eager functional operations over
@@ -29,7 +29,7 @@ import bjc.data.Pair;
* @param <E>
* The type in this list
*/
-public class FunctionalList<E> implements Cloneable, IList<E> {
+public class FunctionalList<E> implements Cloneable, ListEx<E> {
/* The list used as a backing store */
private final List<E> wrapped;
@@ -60,20 +60,21 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
*
* Takes O(n) time, where n is the number of items specified
*
- * @param items
- * The items to put into this functional list.
+ * @param <T> The type of items to put into the list.
+ *
+ * @param items The items to put into this functional list.
+ *
* @return The returned list.
*/
@SafeVarargs
- public static <T> IList<T> listOf(final T... items) {
+ public static <T> ListEx<T> listOf(final T... items) {
return new FunctionalList<>(items);
}
/**
* Create a new functional list with the specified size.
*
- * @param size
- * The size of the backing list .
+ * @param size The size of the backing list .
*/
private FunctionalList(final int size) {
wrapped = new ArrayList<>(size);
@@ -84,8 +85,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
*
* Takes O(1) time, since it doesn't copy the list.
*
- * @param backing
- * The list to use as a backing list.
+ * @param backing The list to use as a backing list.
*/
public FunctionalList(final List<E> backing) {
if (backing == null)
@@ -137,8 +137,8 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
* @return A copy of the list.
*/
@Override
- public IList<E> clone() {
- final IList<E> cloned = new FunctionalList<>();
+ public ListEx<E> clone() {
+ final ListEx<E> cloned = new FunctionalList<>();
for (final E element : wrapped) {
cloned.add(element);
@@ -148,7 +148,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public <T, F> IList<F> combineWith(final IList<T> rightList,
+ public <T, F> ListEx<F> combineWith(final ListEx<T> rightList,
final BiFunction<E, T, F> itemCombiner) {
if (rightList == null) {
throw new NullPointerException("Target combine list must not be null");
@@ -156,7 +156,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
throw new NullPointerException("Combiner must not be null");
}
- final IList<F> returned = new FunctionalList<>();
+ final ListEx<F> returned = new FunctionalList<>();
/* Get the iterator for the other list. */
final Iterator<T> rightIterator = rightList.toIterable().iterator();
@@ -222,14 +222,14 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public <T> IList<T> flatMap(final Function<E, IList<T>> expander) {
+ public <T> ListEx<T> flatMap(final Function<E, ListEx<T>> expander) {
if (expander == null)
throw new NullPointerException("Expander must not be null");
- final IList<T> returned = new FunctionalList<>(this.wrapped.size());
+ final ListEx<T> returned = new FunctionalList<>(this.wrapped.size());
forEach(element -> {
- final IList<T> expandedElement = expander.apply(element);
+ final ListEx<T> expandedElement = expander.apply(element);
if (expandedElement == null)
throw new NullPointerException("Expander returned null list");
@@ -257,7 +257,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
/*
* This is held b/c ref'd variables must be final/effectively final.
*/
- final IHolder<Integer> currentIndex = new Identity<>(0);
+ final Holder<Integer> currentIndex = new Identity<>(0);
wrapped.forEach(element -> {
/* Call the action with the index and the value. */
@@ -283,11 +283,11 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public IList<E> getMatching(final Predicate<E> predicate) {
+ public ListEx<E> getMatching(final Predicate<E> predicate) {
if (predicate == null)
throw new NullPointerException("Predicate must not be null");
- final IList<E> returned = new FunctionalList<>();
+ final ListEx<E> returned = new FunctionalList<>();
wrapped.forEach(element -> {
if (predicate.test(element)) {
@@ -313,17 +313,17 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
/* Check if a partition has room for another item. */
private Boolean isPartitionFull(final int numberPerPartition,
- final IHolder<IList<E>> currentPartition) {
+ final Holder<ListEx<E>> currentPartition) {
return currentPartition
.unwrap(partition -> partition.getSize() >= numberPerPartition);
}
@Override
- public <T> IList<T> map(final Function<E, T> elementTransformer) {
+ public <T> ListEx<T> map(final Function<E, T> elementTransformer) {
if (elementTransformer == null)
throw new NullPointerException("Transformer must be not null");
- final IList<T> returned = new FunctionalList<>(this.wrapped.size());
+ final ListEx<T> returned = new FunctionalList<>(this.wrapped.size());
forEach(element -> {
// Add the transformed item to the result
@@ -334,12 +334,12 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public <T> IList<IPair<E, T>> pairWith(final IList<T> rightList) {
- return combineWith(rightList, Pair<E, T>::new);
+ public <T> ListEx<Pair<E, T>> pairWith(final ListEx<T> rightList) {
+ return combineWith(rightList, SimplePair<E, T>::new);
}
@Override
- public IList<IList<E>> partition(final int numberPerPartition) {
+ public ListEx<ListEx<E>> partition(final int numberPerPartition) {
if (numberPerPartition < 1 || numberPerPartition > wrapped.size()) {
final String fmt
= "%s is an invalid partition size. Must be between 1 and %d";
@@ -348,10 +348,10 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
throw new IllegalArgumentException(msg);
}
- final IList<IList<E>> returned = new FunctionalList<>();
+ final ListEx<ListEx<E>> returned = new FunctionalList<>();
/* The current partition being filled. */
- final IHolder<IList<E>> currentPartition = new Identity<>(new FunctionalList<>());
+ final Holder<ListEx<E>> currentPartition = new Identity<>(new FunctionalList<>());
this.forEach(element -> {
if (isPartitionFull(numberPerPartition, currentPartition)) {
@@ -395,7 +395,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
/* The current collapsed list. */
- final IHolder<T> currentState = new Identity<>(initialValue);
+ final Holder<T> currentState = new Identity<>(initialValue);
wrapped.forEach(element -> {
/* Accumulate a new value into the state. */
@@ -444,7 +444,7 @@ public class FunctionalList<E> implements Cloneable, IList<E> {
}
@Override
- public IList<E> tail() {
+ public ListEx<E> tail() {
return new FunctionalList<>(wrapped.subList(1, getSize()));
}
diff --git a/src/main/java/bjc/funcdata/FunctionalMap.java b/src/main/java/bjc/funcdata/FunctionalMap.java
index aba3dd1..3427a91 100644
--- a/src/main/java/bjc/funcdata/FunctionalMap.java
+++ b/src/main/java/bjc/funcdata/FunctionalMap.java
@@ -1,15 +1,12 @@
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}
+ * Basic implementation of {@link MapEx}
*
* @author ben
*
@@ -19,10 +16,13 @@ import bjc.data.IPair;
* @param <ValueType>
* The type of the map's values.
*/
-public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueType> {
+public class FunctionalMap<KeyType, ValueType> implements MapEx<KeyType, ValueType> {
/* 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<>();
@@ -35,13 +35,11 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
* The entries to put into the map.
*/
@SafeVarargs
- public FunctionalMap(final IPair<KeyType, ValueType>... entries) {
+ public FunctionalMap(final Pair<KeyType, ValueType>... entries) {
this();
- for (final IPair<KeyType, ValueType> entry : entries) {
- entry.doWith((key, val) -> {
- wrappedMap.put(key, val);
- });
+ for (final Pair<KeyType, ValueType> entry : entries) {
+ 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,37 +68,19 @@ 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);
}
@Override
- public void forEachKey(final Consumer<KeyType> action) {
- wrappedMap.keySet().forEach(action);
- }
+ public Optional<ValueType> get(final KeyType key) {
+ if (key == null) throw new NullPointerException("Key must not be null");
- @Override
- public void forEachValue(final Consumer<ValueType> action) {
- wrappedMap.values().forEach(action);
- }
-
- @Override
- public ValueType get(final KeyType key) {
- 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);
-
- throw new IllegalArgumentException(msg);
+ if (wrappedMap.containsKey(key)) {
+ return Optional.of(wrappedMap.get(key));
+ } else {
+ return Optional.empty();
}
-
- return wrappedMap.get(key);
}
@Override
@@ -108,35 +89,26 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
}
@Override
- public IList<KeyType> keyList() {
+ public ListEx<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);
}
@@ -144,18 +116,7 @@ public class FunctionalMap<KeyType, ValueType> implements IMap<KeyType, ValueTyp
public String toString() {
return wrappedMap.toString();
}
-
- @Override
- public IList<ValueType> valueList() {
- final FunctionalList<ValueType> values = new FunctionalList<>();
-
- wrappedMap.values().forEach(value -> {
- values.add(value);
- });
-
- return values;
- }
-
+
@Override
public int hashCode() {
final int prime = 31;
@@ -166,20 +127,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/FunctionalStringTokenizer.java b/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java
index 856c153..3344e05 100644
--- a/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java
+++ b/src/main/java/bjc/funcdata/FunctionalStringTokenizer.java
@@ -128,7 +128,7 @@ public class FunctionalStringTokenizer {
*
* @return This tokenizer, converted into a list of strings.
*/
- public IList<String> toList() {
+ public ListEx<String> toList() {
return toList((final String element) -> element);
}
@@ -144,11 +144,11 @@ public class FunctionalStringTokenizer {
*
* @return A list containing all of the converted tokens.
*/
- public <E> IList<E> toList(final Function<String, E> transformer) {
+ public <E> ListEx<E> toList(final Function<String, E> transformer) {
if (transformer == null)
throw new NullPointerException("Transformer must not be null");
- final IList<E> returned = new FunctionalList<>();
+ final ListEx<E> returned = new FunctionalList<>();
/* Add each token to the list after transforming it. */
forEachToken(token -> {
diff --git a/src/main/java/bjc/funcdata/IList.java b/src/main/java/bjc/funcdata/ListEx.java
index 0ff8e30..164a71d 100644
--- a/src/main/java/bjc/funcdata/IList.java
+++ b/src/main/java/bjc/funcdata/ListEx.java
@@ -9,7 +9,7 @@ import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collector;
-import bjc.data.IPair;
+import bjc.data.Pair;
import bjc.functypes.ID;
/**
@@ -20,7 +20,7 @@ import bjc.functypes.ID;
* @param <ContainedType>
* The type in this list
*/
-public interface IList<ContainedType> extends Iterable<ContainedType> {
+public interface ListEx<ContainedType> extends Iterable<ContainedType> {
/**
* Add an item to this list.
*
@@ -40,7 +40,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
* @return True if every item was successfully added to the list, false
* otherwise.
*/
- default boolean addAll(final IList<ContainedType> items) {
+ default boolean addAll(final ListEx<ContainedType> items) {
return items.map(this::add).anyMatch(bl -> bl == false);
}
@@ -136,7 +136,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A new list containing the merged pairs of lists.
*/
- <OtherType, CombinedType> IList<CombinedType> combineWith(IList<OtherType> list,
+ <OtherType, CombinedType> ListEx<CombinedType> combineWith(ListEx<OtherType> list,
BiFunction<ContainedType, OtherType, CombinedType> combiner);
/**
@@ -191,8 +191,8 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
* @return A new list containing the flattened results of applying the provided
* function.
*/
- <MappedType> IList<MappedType>
- flatMap(Function<ContainedType, IList<MappedType>> expander);
+ <MappedType> ListEx<MappedType>
+ flatMap(Function<ContainedType, ListEx<MappedType>> expander);
/**
* Apply a given action for each member of the list.
@@ -230,7 +230,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A list containing all elements that match the predicate.
*/
- IList<ContainedType> getMatching(Predicate<ContainedType> predicate);
+ ListEx<ContainedType> getMatching(Predicate<ContainedType> predicate);
/**
* Retrieve the size of the wrapped list.
@@ -259,7 +259,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A new list containing the mapped elements of this list.
*/
- <MappedType> IList<MappedType> map(Function<ContainedType, MappedType> transformer);
+ <MappedType> ListEx<MappedType> map(Function<ContainedType, MappedType> transformer);
/**
* Zip two lists into a list of pairs.
@@ -272,7 +272,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return A list containing pairs of this element and the specified list.
*/
- <OtherType> IList<IPair<ContainedType, OtherType>> pairWith(IList<OtherType> list);
+ <OtherType> ListEx<Pair<ContainedType, OtherType>> pairWith(ListEx<OtherType> list);
/**
* Partition this list into a list of sublists.
@@ -285,7 +285,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
* partition may not be completely full if the size of the list is not a
* multiple of partitionSize.
*/
- IList<IList<ContainedType>> partition(int partitionSize);
+ ListEx<ListEx<ContainedType>> partition(int partitionSize);
/**
* Prepend an item to the list.
@@ -429,7 +429,7 @@ public interface IList<ContainedType> extends Iterable<ContainedType> {
*
* @return The list without the first element.
*/
- IList<ContainedType> tail();
+ ListEx<ContainedType> tail();
/**
* Convert this list into an array.
diff --git a/src/main/java/bjc/funcdata/IMap.java b/src/main/java/bjc/funcdata/MapEx.java
index dc5ee00..a7af4c5 100644
--- a/src/main/java/bjc/funcdata/IMap.java
+++ b/src/main/java/bjc/funcdata/MapEx.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.*;
/**
* Functional wrapper over map providing some useful things.
@@ -15,7 +14,7 @@ import java.util.function.Function;
* @param <ValueType>
* The type of this map's values.
*/
-public interface IMap<KeyType, ValueType> {
+public interface MapEx<KeyType, ValueType> extends Freezable {
/**
* Execute an action for each entry in the map.
*
@@ -62,31 +61,7 @@ public interface IMap<KeyType, ValueType> {
*
* @return The value of the key.
*/
- ValueType get(KeyType key);
-
- /**
- * Get a value from the map, and return a default value if the key doesn't
- * exist.
- *
- * @param key
- * The key to attempt to retrieve.
- *
- * @param defaultValue
- * The value to return if the key doesn't exist.
- *
- * @return The value associated with the key, or the default value if the key
- * doesn't exist.
- */
- default ValueType getOrDefault(final KeyType key, final ValueType defaultValue) {
- try {
- return get(key);
- } catch (final IllegalArgumentException iaex) {
- /*
- * We don't care about this, because it indicates a key is missing.
- */
- return defaultValue;
- }
- }
+ Optional<ValueType> get(KeyType key);
/**
* Add an entry to the map.
@@ -109,7 +84,9 @@ public interface IMap<KeyType, ValueType> {
/** Delete all the values in the map. */
default void clear() {
- keyList().forEach(key -> remove(key));
+ if (isFrozen()) throw new ObjectFrozen("Can't clear a frozen map");
+
+ keyList().forEach(MapEx.this::remove);
}
/**
@@ -121,11 +98,47 @@ public interface IMap<KeyType, ValueType> {
return keyList().getSize();
}
+ /**
+ * Remove the value bound to the key.
+ *
+ * @param key
+ * The key to remove from the map.
+ *
+ * @return The previous value for the key in the map, or null if the key wasn't
+ * in the class. NOTE: Just because you received null, doesn't mean the
+ * map wasn't changed. It may mean that someone put a null value for
+ * that key into the map.
+ */
+ ValueType remove(KeyType key);
+
+ /**
+ * Get a list of all the keys in this map.
+ *
+ * @return A list of all the keys in this map.
+ */
+ ListEx<KeyType> keyList();
+
+ /**
+ * Get a list of the values in this map.
+ *
+ * @return A list of values in this map.
+ */
+ default ListEx<ValueType> valueList() {
+ final ListEx<ValueType> returns = new FunctionalList<>();
+
+ for (final KeyType key : keyList()) {
+ returns.add(get(key).orElse(null));
+ }
+
+ return returns;
+ }
+
/*
* @NOTE Do we want this to be the semantics for transform, or do we want to go
* to semantics using something like Isomorphism, or doing a one-time bulk
* conversion of the values?
*/
+
/**
* Transform the values returned by this map.
*
@@ -141,7 +154,7 @@ public interface IMap<KeyType, ValueType> {
*
* @return The map where each value will be transformed after lookup.
*/
- default <V2> IMap<KeyType, V2> transform(final Function<ValueType, V2> transformer) {
+ default <V2> MapEx<KeyType, V2> transform(final Function<ValueType, V2> transformer) {
return new TransformedValueMap<>(this, transformer);
}
@@ -151,40 +164,53 @@ public interface IMap<KeyType, ValueType> {
*
* @return An extended map.
*/
- IMap<KeyType, ValueType> extend();
+ default MapEx<KeyType, ValueType> extend() {
+ return extend(new FunctionalMap<>());
+ };
+
/**
- * Remove the value bound to the key.
- *
- * @param key
- * The key to remove from the map.
- *
- * @return The previous value for the key in the map, or null if the key wasn't
- * in the class. NOTE: Just because you received null, doesn't mean the
- * map wasn't changed. It may mean that someone put a null value for
- * that key into the map.
+ * Extend this map, creating a new map that will delegate queries to
+ * the current map but store any added values in the provided map.
+ *
+ * @param backer The map to store values in.
+ *
+ * @return An extended map, with the specified backing map.
*/
- ValueType remove(KeyType key);
-
+ default MapEx<KeyType, ValueType> extend(MapEx<KeyType, ValueType> backer) {
+ return new ExtendedMap<>(this, backer);
+ };
+
/**
- * Get a list of all the keys in this map.
- *
- * @return A list of all the keys in this map.
+ * Static method to create a basic instance of IMap.
+ *
+ * @param <KeyType2> The key type of the map.
+ * @param <ValueType2> The value type of the map.
+ *
+ * @param args A series of key-value pairs. You will get an error if you don't
+ * provide the correct number of arguments (a number divisible by 2);
+ * however, if you pass arguments of the wrong type, you will not
+ * get a compile error, and usually won't get a runtime error in
+ * construction. However, you will get a ClassCastException as soon
+ * as you do something that attempts to iterate over the keys or values
+ * of the map.
+ *
+ * @return A map, constructed from the provided values.
+ *
+ * @throws IllegalArgumentException If you provide an incomplete pair of arguments.
*/
- IList<KeyType> keyList();
-
- /**
- * Get a list of the values in this map.
- *
- * @return A list of values in this map.
- */
- default IList<ValueType> valueList() {
- final IList<ValueType> returns = new FunctionalList<>();
-
- for (final KeyType key : keyList()) {
- returns.add(get(key));
+ @SuppressWarnings("unchecked")
+ static <KeyType2, ValueType2> MapEx<KeyType2, ValueType2> of(Object... args) {
+ if (args.length % 2 != 0) throw new IllegalArgumentException("Args must be in the form of key-value pairs");
+
+ MapEx<KeyType2, ValueType2> map = new FunctionalMap<>();
+ for (int index = 0; index < args.length; index += 2) {
+ KeyType2 key = (KeyType2) args[index];
+ ValueType2 value = (ValueType2) args[index + 1];
+
+ map.put(key, value);
}
-
- return returns;
+
+ return map;
}
}
diff --git a/src/main/java/bjc/funcdata/ObjectFrozen.java b/src/main/java/bjc/funcdata/ObjectFrozen.java
new file mode 100644
index 0000000..a9f58b8
--- /dev/null
+++ b/src/main/java/bjc/funcdata/ObjectFrozen.java
@@ -0,0 +1,47 @@
+package bjc.funcdata;
+
+/**
+ * Exception that implementations of {@link Freezable} can throw if you attempt
+ * to modify a frozen object.
+ *
+ * @author Ben Culkin
+ *
+ */
+public class ObjectFrozen extends RuntimeException {
+ private static final long serialVersionUID = -1567447627139090728L;
+
+ /**
+ * Create a new ObjectFrozen exception.
+ */
+ public ObjectFrozen() {
+ super();
+ }
+
+ /**
+ * Create a new ObjectFrozen exception.
+ *
+ * @param message The message of the exception.
+ */
+ public ObjectFrozen(String message) {
+ super(message);
+ }
+
+ /**
+ * Create a new ObjectFrozen exception.
+ *
+ * @param cause The root cause of this exception.
+ */
+ public ObjectFrozen(Throwable cause) {
+ super(cause);
+ }
+
+ /**
+ * Create a new ObjectFrozen exception.
+ *
+ * @param message The message of the exception.
+ * @param cause The root cause of this exception.
+ */
+ public ObjectFrozen(String message, Throwable cause) {
+ super(message, cause);
+ }
+}
diff --git a/src/main/java/bjc/funcdata/TransformedValueMap.java b/src/main/java/bjc/funcdata/TransformedValueMap.java
index 5de6fc3..643f610 100644
--- a/src/main/java/bjc/funcdata/TransformedValueMap.java
+++ b/src/main/java/bjc/funcdata/TransformedValueMap.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.*;
/**
* A map that transforms values from one type to another
@@ -20,12 +19,15 @@ import java.util.function.Function;
*
*/
final class TransformedValueMap<OldKey, OldValue, NewValue>
- implements IMap<OldKey, NewValue> {
+ implements MapEx<OldKey, NewValue> {
/* Our backing map. */
- private final IMap<OldKey, OldValue> backing;
+ private final MapEx<OldKey, OldValue> backing;
/* Our transforming function. */
private final Function<OldValue, NewValue> transformer;
+ private boolean isFrozen = false;
+ private boolean thawEnabled = true;
+
/**
* Create a new transformed-value loop.
*
@@ -35,7 +37,7 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
* @param transform
* The function to use for the transform.
*/
- public TransformedValueMap(final IMap<OldKey, OldValue> backingMap,
+ public TransformedValueMap(final MapEx<OldKey, OldValue> backingMap,
final Function<OldValue, NewValue> transform) {
backing = backingMap;
transformer = transform;
@@ -43,6 +45,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 +56,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));
@@ -64,20 +63,8 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
}
@Override
- public void forEachKey(final Consumer<OldKey> action) {
- backing.forEachKey(action);
- }
-
- @Override
- public void forEachValue(final Consumer<NewValue> action) {
- backing.forEachValue(value -> {
- action.accept(transformer.apply(value));
- });
- }
-
- @Override
- public NewValue get(final OldKey key) {
- return transformer.apply(backing.get(key));
+ public Optional<NewValue> get(final OldKey key) {
+ return backing.get(key).map(transformer);
}
@Override
@@ -86,23 +73,19 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
}
@Override
- public IList<OldKey> keyList() {
+ public ListEx<OldKey> keyList() {
return backing.keyList();
}
@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));
}
@@ -110,9 +93,43 @@ final class TransformedValueMap<OldKey, OldValue, NewValue>
public String toString() {
return backing.toString();
}
+
+ @Override
+ public boolean freeze() {
+ isFrozen = true;
+
+ return true;
+ }
+
+ @Override
+ public boolean thaw() {
+ if (thawEnabled) {
+ isFrozen = false;
+ return true;
+ } else {
+ return false;
+ }
+ }
@Override
- public IList<NewValue> valueList() {
- return backing.valueList().map(transformer);
+ 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
diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTree.java b/src/main/java/bjc/funcdata/bst/BinarySearchTree.java
index e22a8da..99b67cd 100644
--- a/src/main/java/bjc/funcdata/bst/BinarySearchTree.java
+++ b/src/main/java/bjc/funcdata/bst/BinarySearchTree.java
@@ -6,7 +6,7 @@ import java.util.List;
import java.util.function.Predicate;
import bjc.funcdata.FunctionalList;
-import bjc.funcdata.IList;
+import bjc.funcdata.ListEx;
/**
* A binary search tree, with some mild support for functional traversal.
@@ -24,7 +24,7 @@ public class BinarySearchTree<T> {
private int elementCount;
/* The root element of the tree */
- private ITreePart<T> root;
+ private TreePart<T> root;
/**
* Create a new tree using the specified way to compare elements.
@@ -33,8 +33,7 @@ public class BinarySearchTree<T> {
* The thing to use for comparing elements
*/
public BinarySearchTree(final Comparator<T> cmp) {
- if (cmp == null)
- throw new NullPointerException("Comparator must not be null");
+ if (cmp == null) throw new NullPointerException("Comparator must not be null");
elementCount = 0;
comparator = cmp;
@@ -49,11 +48,8 @@ public class BinarySearchTree<T> {
public void addNode(final T element) {
elementCount++;
- if (root == null) {
- root = new BinarySearchTreeNode<>(element, null, null);
- } else {
- root.add(element, comparator);
- }
+ if (root == null) root = new BinarySearchTreeNode<>(element, null, null);
+ else root.add(element, comparator);
}
/**
@@ -70,7 +66,7 @@ public class BinarySearchTree<T> {
*
* @return Whether the adjusted pivot is with the list.
*/
- private boolean adjustedPivotInBounds(final IList<T> elements, final int pivot,
+ private boolean adjustedPivotInBounds(final ListEx<T> elements, final int pivot,
final int pivotAdjustment) {
return ((pivot - pivotAdjustment) >= 0)
&& ((pivot + pivotAdjustment) < elements.getSize());
@@ -82,7 +78,7 @@ public class BinarySearchTree<T> {
* Takes O(N) time, but also O(N) space.
*/
public void balance() {
- final IList<T> elements = new FunctionalList<>();
+ final ListEx<T> elements = new FunctionalList<>();
/* Add each element to the list in sorted order. */
root.forEach(TreeLinearizationMethod.INORDER, element -> elements.add(element));
@@ -139,7 +135,7 @@ public class BinarySearchTree<T> {
*
* @return The root of the tree.
*/
- public ITreePart<T> getRoot() {
+ public TreePart<T> getRoot() {
return root;
}
@@ -184,6 +180,7 @@ public class BinarySearchTree<T> {
*/
traverse(TreeLinearizationMethod.PREORDER, node -> {
nodes.add(node);
+
return true;
});
diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java b/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java
index 0b99cad..9532555 100644
--- a/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java
+++ b/src/main/java/bjc/funcdata/bst/BinarySearchTreeLeaf.java
@@ -13,7 +13,7 @@ import java.util.function.Predicate;
* @param <T>
* The data stored in the tree.
*/
-public class BinarySearchTreeLeaf<T> implements ITreePart<T> {
+public class BinarySearchTreeLeaf<T> implements TreePart<T> {
/** The data held in this tree leaf */
protected T data;
diff --git a/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java b/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java
index a73f81a..0eef92a 100644
--- a/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java
+++ b/src/main/java/bjc/funcdata/bst/BinarySearchTreeNode.java
@@ -20,10 +20,10 @@ import java.util.function.Predicate;
*/
public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> {
/* The left child of this node */
- private ITreePart<T> left;
+ private TreePart<T> left;
/* The right child of this node */
- private ITreePart<T> right;
+ private TreePart<T> right;
/**
* Create a new node with the specified data and children.
@@ -37,8 +37,8 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> {
* @param rght
* The right child of this node.
*/
- public BinarySearchTreeNode(final T element, final ITreePart<T> lft,
- final ITreePart<T> rght) {
+ public BinarySearchTreeNode(final T element, final TreePart<T> lft,
+ final TreePart<T> rght) {
super(element);
this.left = lft;
this.right = rght;
diff --git a/src/main/java/bjc/funcdata/bst/ITreePart.java b/src/main/java/bjc/funcdata/bst/TreePart.java
index bac640d..b451463 100644
--- a/src/main/java/bjc/funcdata/bst/ITreePart.java
+++ b/src/main/java/bjc/funcdata/bst/TreePart.java
@@ -13,7 +13,7 @@ import java.util.function.Predicate;
* @param <T>
* The data contained in this part of the tree.
*/
-public interface ITreePart<T> {
+public interface TreePart<T> {
/**
* Add a element below this tree part somewhere.
*