diff options
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc/utils/funcdata')
6 files changed, 347 insertions, 24 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java index a260d78..ff02515 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalList.java @@ -1,10 +1,10 @@ package bjc.utils.funcdata; -import java.util.ArrayList; import java.util.Collections; import java.util.Comparator; import java.util.Iterator; import java.util.List; +import java.util.NoSuchElementException; import java.util.Random; import java.util.function.BiConsumer; import java.util.function.BiFunction; @@ -16,6 +16,8 @@ import bjc.utils.data.GenHolder; import bjc.utils.data.IHolder; import bjc.utils.data.Pair; +import java.util.ArrayList; + /** * A wrapper over another list that provides eager functional operations * over it. Differs from a stream in every way except for the fact that @@ -62,6 +64,10 @@ public class FunctionalList<E> implements Cloneable { * The source for a backing list */ public FunctionalList(FunctionalList<E> sourceList) { + if (sourceList == null) { + throw new NullPointerException("Source list must be non-null"); + } + // Find out if this should make a copy of the source's wrapped list // instead. // @@ -88,6 +94,11 @@ public class FunctionalList<E> implements Cloneable { * The list to use as a backing list. */ public FunctionalList(List<E> backingList) { + if (backingList == null) { + throw new NullPointerException( + "Backing list must be non-null"); + } + wrappedList = backingList; } @@ -112,6 +123,10 @@ public class FunctionalList<E> implements Cloneable { * predicate. */ public boolean allMatch(Predicate<E> matchPredicate) { + if (matchPredicate == null) { + throw new NullPointerException("Predicate must be non-null"); + } + for (E item : wrappedList) { if (!matchPredicate.test(item)) { // We've found a non-matching item @@ -132,6 +147,10 @@ public class FunctionalList<E> implements Cloneable { * predicate. */ public boolean anyMatch(Predicate<E> matchPredicate) { + if (matchPredicate == null) { + throw new NullPointerException("Predicate must be not null"); + } + for (E item : wrappedList) { if (matchPredicate.test(item)) { // We've found a matching item @@ -176,13 +195,20 @@ public class FunctionalList<E> implements Cloneable { public <T, F> FunctionalList<F> combineWith( FunctionalList<T> rightList, BiFunction<E, T, F> itemCombiner) { + if (rightList == null) { + throw new NullPointerException( + "Target combine list must not be null"); + } else if (itemCombiner == null) { + throw new NullPointerException("Combiner must not be null"); + } + FunctionalList<F> returnedList = new FunctionalList<>(); // Get the iterator for the other list Iterator<T> rightIterator = rightList.toIterable().iterator(); - for (Iterator<E> leftIterator = wrappedList - .iterator(); leftIterator.hasNext() + for (Iterator<E> leftIterator = + wrappedList.iterator(); leftIterator.hasNext() && rightIterator.hasNext();) { // Add the transformed items to the result list E leftVal = leftIterator.next(); @@ -212,6 +238,11 @@ public class FunctionalList<E> implements Cloneable { * @return The first element in this list. */ public E first() { + if (wrappedList.size() < 1) { + throw new NoSuchElementException( + "Attempted to get first element of empty list"); + } + return wrappedList.get(0); } @@ -227,14 +258,23 @@ public class FunctionalList<E> implements Cloneable { * @return A new list containing the flattened results of applying the * provided function. */ - public <T> FunctionalList<T> flatMap( - Function<E, FunctionalList<T>> elementExpander) { - FunctionalList<T> returnedList = new FunctionalList<>( - this.wrappedList.size()); + public <T> FunctionalList<T> + flatMap(Function<E, FunctionalList<T>> elementExpander) { + if (elementExpander == null) { + throw new NullPointerException("Expander must not be null"); + } + + FunctionalList<T> returnedList = + new FunctionalList<>(this.wrappedList.size()); forEach(element -> { - FunctionalList<T> expandedElement = elementExpander - .apply(element); + FunctionalList<T> expandedElement = + elementExpander.apply(element); + + if (expandedElement == null) { + throw new NullPointerException( + "Expander returned null list"); + } // Add each element to the returned list expandedElement.forEach(returnedList::add); @@ -250,6 +290,10 @@ public class FunctionalList<E> implements Cloneable { * The action to apply to each member of the list. */ public void forEach(Consumer<E> action) { + if (action == null) { + throw new NullPointerException("Action is null"); + } + wrappedList.forEach(action); } @@ -261,6 +305,10 @@ public class FunctionalList<E> implements Cloneable { * index. */ public void forEachIndexed(BiConsumer<Integer, E> indexedAction) { + if (indexedAction == null) { + throw new NullPointerException("Action must not be null"); + } + // This is held b/c ref'd variables must be final/effectively final GenHolder<Integer> currentIndex = new GenHolder<>(0); @@ -302,6 +350,10 @@ public class FunctionalList<E> implements Cloneable { * @return A list containing all elements that match the predicate */ public FunctionalList<E> getMatching(Predicate<E> matchPredicate) { + if (matchPredicate == null) { + throw new NullPointerException("Predicate must not be null"); + } + FunctionalList<E> returnedList = new FunctionalList<>(); wrappedList.forEach((element) -> { @@ -344,8 +396,12 @@ public class FunctionalList<E> implements Cloneable { * @return A new list containing the mapped elements of this list. */ public <T> FunctionalList<T> map(Function<E, T> elementTransformer) { - FunctionalList<T> returnedList = new FunctionalList<>( - this.wrappedList.size()); + if (elementTransformer == null) { + throw new NullPointerException("Transformer must be not null"); + } + + FunctionalList<T> returnedList = + new FunctionalList<>(this.wrappedList.size()); forEach(element -> { // Add the transformed item to the result @@ -366,8 +422,8 @@ public class FunctionalList<E> implements Cloneable { * @return A list containing pairs of this element and the specified * list */ - public <T> FunctionalList<Pair<E, T>> pairWith( - FunctionalList<T> rightList) { + public <T> FunctionalList<Pair<E, T>> + pairWith(FunctionalList<T> rightList) { return combineWith(rightList, Pair<E, T>::new); } @@ -378,13 +434,21 @@ public class FunctionalList<E> implements Cloneable { * The size of elements to put into each one of the sublists * @return A list partitioned into partitions of size nPerPart */ - public FunctionalList<FunctionalList<E>> partition( - int numberPerPartition) { - FunctionalList<FunctionalList<E>> returnedList = new FunctionalList<>(); + public FunctionalList<FunctionalList<E>> + partition(int numberPerPartition) { + if (numberPerPartition < 1 + || numberPerPartition > wrappedList.size()) { + throw new IllegalArgumentException("" + numberPerPartition + + " is an invalid partition size. Must be between 1 and " + + wrappedList.size()); + } + + FunctionalList<FunctionalList<E>> returnedList = + new FunctionalList<>(); // The current partition being filled - GenHolder<FunctionalList<E>> currentPartition = new GenHolder<>( - new FunctionalList<>()); + GenHolder<FunctionalList<E>> currentPartition = + new GenHolder<>(new FunctionalList<>()); this.forEach((element) -> { if (isPartitionFull(numberPerPartition, currentPartition)) { @@ -433,6 +497,11 @@ public class FunctionalList<E> implements Cloneable { * @return A random element from this list. */ public E randItem(Random rnd) { + if (rnd == null) { + throw new NullPointerException( + "Random source must not be null"); + } + int randomIndex = rnd.nextInt(wrappedList.size()); return wrappedList.get(randomIndex); @@ -460,6 +529,12 @@ public class FunctionalList<E> implements Cloneable { public <T, F> F reduceAux(T initialValue, BiFunction<E, T, T> stateAccumulator, Function<T, F> resultTransformer) { + if (stateAccumulator == null) { + throw new NullPointerException("Accumulator must not be null"); + } else if (resultTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + // The current collapsed list IHolder<T> currentState = new GenHolder<>(initialValue); @@ -481,6 +556,10 @@ public class FunctionalList<E> implements Cloneable { * @return Whether there was anything that satisfied the predicate */ public boolean removeIf(Predicate<E> removePredicate) { + if (removePredicate == null) { + throw new NullPointerException("Predicate must be non-null"); + } + return wrappedList.removeIf(removePredicate); } @@ -502,7 +581,8 @@ public class FunctionalList<E> implements Cloneable { * @param searchKey * The key to search for. * @param comparator - * The way to compare elements for searching + * The way to compare elements for searching. Pass null to + * use the natural ordering for E * @return The element if it is in this list, or null if it is not. */ public E search(E searchKey, Comparator<E> comparator) { @@ -524,7 +604,8 @@ public class FunctionalList<E> implements Cloneable { * elements. Does change the underlying list. * * @param comparator - * The way to compare elements for sorting. + * The way to compare elements for sorting. Pass null to use + * E's natural ordering */ public void sort(Comparator<E> comparator) { Collections.sort(wrappedList, comparator); @@ -554,7 +635,7 @@ public class FunctionalList<E> implements Cloneable { // Remove trailing space and comma sb.deleteCharAt(sb.length() - 1); - //sb.deleteCharAt(sb.length() - 2); + // sb.deleteCharAt(sb.length() - 2); sb.append(")"); diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java new file mode 100644 index 0000000..0eb2e94 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java @@ -0,0 +1,151 @@ +package bjc.utils.funcdata; + +import java.util.HashMap; +import java.util.Map; +import java.util.function.Function; + +import bjc.utils.data.Pair; + +/** + * Functional wrapper over map providing some useful things + * + * @author ben + * + * @param <K> + * The type of this map's keys + * @param <V> + * The type of this map's values + * + */ +public class FunctionalMap<K, V> { + private final class TransformedMap<V2> extends FunctionalMap<K, V2> { + private FunctionalMap<K, V> mapToTransform; + private Function<V, V2> transformer; + + public TransformedMap(FunctionalMap<K, V> destMap, + Function<V, V2> transform) { + mapToTransform = destMap; + transformer = transform; + } + + @Override + public V2 get(K key) { + return transformer.apply(mapToTransform.get(key)); + } + } + + private Map<K, V> wrappedMap; + + /** + * Create a new blank functional map + */ + public FunctionalMap() { + wrappedMap = new HashMap<>(); + } + + /** + * Create a new functional map wrapping the specified map + * + * @param wrap + * The map to wrap + */ + public FunctionalMap(Map<K, V> wrap) { + if (wrap == null) { + throw new NullPointerException("Map to wrap must not be null"); + } + + wrappedMap = wrap; + } + + /** + * Create a new functional map with the specified entries + * + * @param entries + * The entries to put into the map + */ + @SafeVarargs + public FunctionalMap(Pair<K, V>... entries) { + this(); + + for (Pair<K, V> entry : entries) { + entry.doWith((key, val) -> { + wrappedMap.put(key, val); + }); + } + } + + /** + * Add an entry to the map + * + * @param key + * The key to put the value under + * @param val + * The value to add + * @return The previous value of the key in the map, or null if the key + * wasn't in the map. However, note that it may also return + * null if the key was set to null. + * + */ + public V put(K key, V val) { + if (key == null) { + throw new NullPointerException("Key must not be null"); + } + + return wrappedMap.put(key, val); + } + + /** + * Get the value assigned to the given key + * + * @param key + * The key to look for a value under + * @return The value of the key + * + * + */ + public V get(K key) { + if (key == null) { + throw new NullPointerException("Key must not be null"); + } + + if (wrappedMap.containsKey(wrappedMap)) { + return wrappedMap.get(key); + } else { + throw new IllegalArgumentException( + "Key " + key + " is not present in the map"); + } + } + + /** + * Transform the values returned by this map. + * + * NOTE: This transform is applied once for each lookup of a value, so + * the transform passed should be a proper function, or things will + * likely not work as expected. + * + * @param <V2> + * The new type of returned values + * @param transformer + * The function to use to transform values + * @return The map where each value will be transformed after lookup + */ + public <V2> FunctionalMap<K, V2> + mapValues(Function<V, V2> transformer) { + if (transformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + + return new TransformedMap<>(this, transformer); + } + + /** + * Check if this map contains the specified key + * + * @param key + * The key to check + * @return Whether or not the map contains the key + */ + public boolean containsKey(K key) { + return wrappedMap.containsKey(key); + } +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java index 3c819cd..386b732 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalStringTokenizer.java @@ -19,6 +19,11 @@ public class FunctionalStringTokenizer { * @return A new tokenizer that splits the provided string on spaces. */ public static FunctionalStringTokenizer fromString(String strang) { + if (strang == null) { + throw new NullPointerException( + "String to tokenize must be non-null"); + } + return new FunctionalStringTokenizer( new StringTokenizer(strang, " ")); } @@ -35,6 +40,11 @@ public class FunctionalStringTokenizer { * The string to tokenize */ public FunctionalStringTokenizer(String inp) { + if (inp == null) { + throw new NullPointerException( + "String to tokenize must be non-null"); + } + this.input = new StringTokenizer(inp); } @@ -49,6 +59,14 @@ public class FunctionalStringTokenizer { */ public FunctionalStringTokenizer(String inputString, String seperators) { + if (inputString == null) { + throw new NullPointerException( + "String to tokenize must not be null"); + } else if (seperators == null) { + throw new NullPointerException( + "Tokens to split on must not be null"); + } + this.input = new StringTokenizer(inputString, seperators); } @@ -59,6 +77,11 @@ public class FunctionalStringTokenizer { * The non-functional string tokenizer to wrap */ public FunctionalStringTokenizer(StringTokenizer toWrap) { + if (toWrap == null) { + throw new NullPointerException( + "Wrapped tokenizer must not be null"); + } + this.input = toWrap; } @@ -69,6 +92,10 @@ public class FunctionalStringTokenizer { * The action to execute for each token */ public void forEachToken(Consumer<String> action) { + if (action == null) { + throw new NullPointerException("Action must not be null"); + } + while (input.hasMoreTokens()) { action.accept(input.nextToken()); } @@ -84,7 +111,7 @@ public class FunctionalStringTokenizer { } /** - * Return the next token from the tokenizer Returns null if no more + * Return the next token from the tokenizer. Returns null if no more * tokens are available * * @return The next token from the tokenizer @@ -110,13 +137,18 @@ public class FunctionalStringTokenizer { * The function to use to convert tokens. * @return A list containing all of the converted tokens. */ - public <E> FunctionalList<E> toList(Function<String, E> tokenTransformer) { + public <E> FunctionalList<E> + toList(Function<String, E> tokenTransformer) { + if (tokenTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + FunctionalList<E> returnList = new FunctionalList<>(); // Add each token to the list after transforming it forEachToken(token -> { E transformedToken = tokenTransformer.apply(token); - + returnList.add(transformedToken); }); @@ -124,6 +156,15 @@ public class FunctionalStringTokenizer { } /** + * Convert this tokenizer into a list of strings + * + * @return This tokenizer, converted into a list of strings + */ + public FunctionalList<String> toList() { + return toList((String element) -> element); + } + + /** * Check if this tokenizer has more tokens * * @return Whether or not this tokenizer has more tokens diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java index ec0e4df..fa92f85 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTree.java @@ -39,6 +39,10 @@ public class BinarySearchTree<T> { * The thing to use for comparing elements */ public BinarySearchTree(Comparator<T> cmp) { + if (cmp == null) { + throw new NullPointerException("Comparator must not be null"); + } + elementCount = 0; comparator = cmp; } @@ -169,6 +173,13 @@ public class BinarySearchTree<T> { */ public void traverse(TreeLinearizationMethod linearizationMethod, Predicate<T> traversalPredicate) { + if (linearizationMethod == null) { + throw new NullPointerException( + "Linearization method must not be null"); + } else if (traversalPredicate == null) { + throw new NullPointerException("Predicate must not be nulls"); + } + rootElement.forEach(linearizationMethod, traversalPredicate); } diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java index 7e31328..d2f9013 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeLeaf.java @@ -55,6 +55,10 @@ public class BinarySearchTreeLeaf<T> implements ITreePart<T> { @Override public <E> E collapse(Function<T, E> leafTransformer, BiFunction<E, E, E> branchCollapser) { + if (leafTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + return leafTransformer.apply(data); } @@ -101,6 +105,10 @@ public class BinarySearchTreeLeaf<T> implements ITreePart<T> { */ @Override public boolean directedWalk(DirectedWalkFunction<T> treeWalker) { + if (treeWalker == null) { + throw new NullPointerException("Tree walker must not be null"); + } + switch (treeWalker.walk(data)) { case SUCCESS: return true; @@ -119,6 +127,10 @@ public class BinarySearchTreeLeaf<T> implements ITreePart<T> { @Override public boolean forEach(TreeLinearizationMethod linearizationMethod, Predicate<T> traversalPredicate) { + if (traversalPredicate == null) { + throw new NullPointerException("Predicate must not be null"); + } + return traversalPredicate.test(data); } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java index 77bb196..09a4912 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcdata/bst/BinarySearchTreeNode.java @@ -51,6 +51,10 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { */ @Override public void add(T element, Comparator<T> comparator) { + if (comparator == null) { + throw new NullPointerException("Comparator must not be null"); + } + switch (comparator.compare(data, element)) { case -1: if (leftBranch == null) { @@ -79,6 +83,10 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { @Override public <E> E collapse(Function<T, E> nodeCollapser, BiFunction<E, E, E> branchCollapser) { + if (nodeCollapser == null || branchCollapser == null) { + throw new NullPointerException("Collapser must not be null"); + } + E collapsedNode = nodeCollapser.apply(data); if (leftBranch != null) { @@ -112,6 +120,10 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { @Override public boolean contains(T element, Comparator<T> comparator) { + if (comparator == null) { + throw new NullPointerException("Comparator must not be null"); + } + return directedWalk(currentElement -> { switch (comparator.compare(element, currentElement)) { case -1: @@ -128,6 +140,10 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { @Override public void delete(T element, Comparator<T> comparator) { + if (comparator == null) { + throw new NullPointerException("Comparator must not be null"); + } + directedWalk(currentElement -> { switch (comparator.compare(data, element)) { case -1: @@ -145,6 +161,10 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { @Override public boolean directedWalk(DirectedWalkFunction<T> treeWalker) { + if (treeWalker == null) { + throw new NullPointerException("Walker must not be null"); + } + switch (treeWalker.walk(data)) { case SUCCESS: return true; @@ -162,6 +182,13 @@ public class BinarySearchTreeNode<T> extends BinarySearchTreeLeaf<T> { @Override public boolean forEach(TreeLinearizationMethod linearizationMethod, Predicate<T> traversalPredicate) { + if (linearizationMethod == null) { + throw new NullPointerException( + "Linearization method must not be null"); + } else if (traversalPredicate == null) { + throw new NullPointerException("Predicate must not be null"); + } + switch (linearizationMethod) { case PREORDER: return preorderTraverse(linearizationMethod, |
