diff options
| author | bculkin2442 <bjculkin@mix.wvu.edu> | 2016-04-03 19:22:48 -0400 |
|---|---|---|
| committer | bculkin2442 <bjculkin@mix.wvu.edu> | 2016-04-03 19:22:48 -0400 |
| commit | 1c8bc7132d980c1ff2dbd6b9af579c3b2fd8c63e (patch) | |
| tree | a29777f07ebd81fbef61b5ae02f13f1a9d8f65a2 /BJC-Utils2/src/main/java/bjc | |
| parent | a023de85aa08c8f2b8b2441c6b14064eabee2775 (diff) | |
General code refactoring and maintenance
Diffstat (limited to 'BJC-Utils2/src/main/java/bjc')
37 files changed, 1480 insertions, 329 deletions
diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java b/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java index 9d18390..5ab87bb 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java +++ b/BJC-Utils2/src/main/java/bjc/utils/components/ComponentDescriptionFileParser.java @@ -26,7 +26,7 @@ public class ComponentDescriptionFileParser { reader.addPragma("name", (tokenizer, state) -> { if (!tokenizer.hasMoreTokens()) { throw new PragmaFormatException( - "Pragma name requires at least one argument"); + "Pragma name requires one string argument"); } else { state.setName(ListUtils.collapseTokens( tokenizer.toList((strang) -> strang))); @@ -36,7 +36,7 @@ public class ComponentDescriptionFileParser { reader.addPragma("author", (tokenizer, state) -> { if (!tokenizer.hasMoreTokens()) { throw new PragmaFormatException( - "Pragma author requires at least one argument"); + "Pragma author requires one string argument"); } else { state.setAuthor(ListUtils.collapseTokens( tokenizer.toList((strang) -> strang))); @@ -46,7 +46,7 @@ public class ComponentDescriptionFileParser { reader.addPragma("description", (tokenizer, state) -> { if (!tokenizer.hasMoreTokens()) { throw new PragmaFormatException( - "Pragma description requires at least one argument"); + "Pragma description requires one string argument"); } else { state.setDescription(ListUtils.collapseTokens( tokenizer.toList((strang) -> strang))); @@ -56,7 +56,7 @@ public class ComponentDescriptionFileParser { reader.addPragma("version", (tokenizer, state) -> { if (!tokenizer.hasMoreTokens()) { throw new PragmaFormatException( - "Pragma name requires at least one argument"); + "Pragma name requires one integer argument"); } else { String token = tokenizer.nextToken(); diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java b/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java index d808b8e..4b8d87b 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java +++ b/BJC-Utils2/src/main/java/bjc/utils/components/FileComponentRepository.java @@ -123,4 +123,4 @@ public class FileComponentRepository<E extends IDescribedComponent> public E getComponentByName(String name) { return components.get(name); } -} +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/IComponentRepository.java b/BJC-Utils2/src/main/java/bjc/utils/components/IComponentRepository.java index 04ff51b..135e609 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/components/IComponentRepository.java +++ b/BJC-Utils2/src/main/java/bjc/utils/components/IComponentRepository.java @@ -44,4 +44,4 @@ public interface IComponentRepository<E extends IDescribedComponent> { * @return The source from which these components came */ public String getSource(); -} +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/IDescribedComponent.java b/BJC-Utils2/src/main/java/bjc/utils/components/IDescribedComponent.java index 15be70d..c3576a3 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/components/IDescribedComponent.java +++ b/BJC-Utils2/src/main/java/bjc/utils/components/IDescribedComponent.java @@ -50,4 +50,4 @@ public interface IDescribedComponent { public default int getVersion() { return 1; } -} +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/components/package-info.java b/BJC-Utils2/src/main/java/bjc/utils/components/package-info.java new file mode 100644 index 0000000..5ed7777 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/components/package-info.java @@ -0,0 +1,7 @@ +/** + * This package contains utilities for components + * + * @author ben + * + */ +package bjc.utils.components;
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/configuration/ConfigFile.java b/BJC-Utils2/src/main/java/bjc/utils/configuration/ConfigFile.java index 056f775..458aece 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/configuration/ConfigFile.java +++ b/BJC-Utils2/src/main/java/bjc/utils/configuration/ConfigFile.java @@ -19,6 +19,7 @@ public class ConfigFile { /** * A category in a configuration file + * * @author ben * */ @@ -32,6 +33,14 @@ public class ConfigFile { children = new HashMap<>(); } + /** + * Add a child category to this category + * + * @param childName + * The name of the child + * @param child + * The child category + */ public void addChild(String childName, ConfigCategory child) { children.put(childName, child); } @@ -64,14 +73,16 @@ public class ConfigFile { continue; } else if (SourceVersion.isName(currentTokens[0]) && currentTokens[1].equals("{")) { - topLevelCategories.put(currentTokens[0], parseCategory(currentTokens[0], scn)); + topLevelCategories.put(currentTokens[0], + parseCategory(currentTokens[0], scn)); } } return returnedFile; } - private static ConfigCategory parseCategory(String categoryName, + private static ConfigCategory parseCategory( + @SuppressWarnings("unused") String categoryName, Scanner inputSource) { ConfigCategory category = new ConfigCategory(); @@ -119,7 +130,8 @@ public class ConfigFile { return category; } - private static void parseEntry(ConfigCategory category, + private static void parseEntry( + @SuppressWarnings("unused") ConfigCategory category, String[] entryParts) { String entry = String.join("", entryParts); @@ -128,6 +140,7 @@ public class ConfigFile { String[] expSpecifiers = expParts[0].split(":"); String expType = expSpecifiers[0]; + @SuppressWarnings("unused") String expName = expSpecifiers[1]; switch (expType) { diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/GenHolder.java b/BJC-Utils2/src/main/java/bjc/utils/data/GenHolder.java index 6854440..e042554 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/data/GenHolder.java +++ b/BJC-Utils2/src/main/java/bjc/utils/data/GenHolder.java @@ -46,6 +46,10 @@ public class GenHolder<T> implements IHolder<T> { */ @Override public void doWith(Consumer<T> action) { + if (action == null) { + throw new NullPointerException("Action must be non-null"); + } + action.accept(heldValue); } @@ -56,6 +60,10 @@ public class GenHolder<T> implements IHolder<T> { */ @Override public <NewT> IHolder<NewT> map(Function<T, NewT> transformer) { + if (transformer == null) { + throw new NullPointerException("Transformer must be non-null"); + } + return new GenHolder<>(transformer.apply(heldValue)); } @@ -66,6 +74,10 @@ public class GenHolder<T> implements IHolder<T> { */ @Override public IHolder<T> transform(Function<T, T> transformer) { + if (transformer == null) { + throw new NullPointerException("Transformer must be non-null"); + } + heldValue = transformer.apply(heldValue); return this; @@ -78,11 +90,19 @@ public class GenHolder<T> implements IHolder<T> { */ @Override public <E> E unwrap(Function<T, E> unwrapper) { + if (unwrapper == null) { + throw new NullPointerException("Unwrapper must be null"); + } + return unwrapper.apply(heldValue); } @Override public String toString() { + if (heldValue == null) { + return "(null)"; + } + return heldValue.toString(); } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java b/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java index b5ad953..97cb195 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java +++ b/BJC-Utils2/src/main/java/bjc/utils/data/Pair.java @@ -56,6 +56,11 @@ public class Pair<L, R> implements IPair<L, R> { @Override public <L2, R2> IPair<L2, R2> apply(Function<L, L2> leftTransformer, Function<R, R2> rightTransformer) { + if (leftTransformer == null || rightTransformer == null) { + throw new NullPointerException( + "Transformers must be non-null"); + } + return new Pair<>(leftTransformer.apply(leftValue), rightTransformer.apply(rightValue)); } @@ -67,6 +72,10 @@ public class Pair<L, R> implements IPair<L, R> { */ @Override public void doWith(BiConsumer<L, R> action) { + if (action == null) { + throw new NullPointerException("Action must be non-null"); + } + action.accept(leftValue, rightValue); } @@ -77,6 +86,10 @@ public class Pair<L, R> implements IPair<L, R> { */ @Override public <E> E merge(BiFunction<L, R, E> merger) { + if (merger == null) { + throw new NullPointerException("Merger must be non-null"); + } + return merger.apply(leftValue, rightValue); } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/lazy/ILazy.java b/BJC-Utils2/src/main/java/bjc/utils/data/lazy/ILazy.java new file mode 100644 index 0000000..a4fab67 --- /dev/null +++ b/BJC-Utils2/src/main/java/bjc/utils/data/lazy/ILazy.java @@ -0,0 +1,35 @@ +package bjc.utils.data.lazy; + +/** + * Interface for some maintenance operations on lazy objects + * + * @author ben + * + */ +public interface ILazy { + /** + * Check if this object has been materialized + * + * @return Whether or not this object has been materialized + */ + public boolean isMaterialized(); + + /** + * Check if there are pending actions that need to be applied + * + * @return Whether or not there are pending actions + */ + public boolean hasPendingActions(); + + /** + * Make this object materialize itelf + */ + public void materialize(); + + /** + * Make this object apply any pending objects + * + * As a requirement, will materialize the object if it is not materialized + */ + public void applyPendingActions(); +} diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyHolder.java b/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyHolder.java index e74ce91..61a5956 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyHolder.java +++ b/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyHolder.java @@ -19,7 +19,7 @@ import bjc.utils.funcdata.FunctionalList; * @param <T> * The type of the data being held */ -public class LazyHolder<T> implements IHolder<T> { +public class LazyHolder<T> implements IHolder<T>, ILazy { private final class LazyHolderSupplier<NewT> implements Supplier<NewT> { private FunctionalList<Function<T, T>> pendingActions; @@ -70,8 +70,11 @@ public class LazyHolder<T> implements IHolder<T> { * The supplier for a value when it is neededs */ public LazyHolder(Supplier<T> source) { - heldSource = source; + if (source == null) { + throw new NullPointerException("Source must be non-null"); + } + heldSource = source; heldValue = null; } @@ -87,6 +90,10 @@ public class LazyHolder<T> implements IHolder<T> { @Override public void doWith(Consumer<T> action) { + if (action == null) { + throw new NullPointerException("Action must be non-null"); + } + transform((value) -> { // Do the action with the value action.accept(value); @@ -98,6 +105,10 @@ public class LazyHolder<T> implements IHolder<T> { @Override public <NewT> IHolder<NewT> map(Function<T, NewT> transform) { + if (transform == null) { + throw new NullPointerException("Transform must be non-null"); + } + // Don't actually map until we need to return new LazyHolder<>( new LazyHolderSupplier<>(actions, transform)); @@ -105,6 +116,10 @@ public class LazyHolder<T> implements IHolder<T> { @Override public IHolder<T> transform(Function<T, T> transform) { + if (transform == null) { + throw new NullPointerException("Transform must be non-null"); + } + // Queue the transform until we need to apply it actions.add(transform); @@ -113,6 +128,10 @@ public class LazyHolder<T> implements IHolder<T> { @Override public <E> E unwrap(Function<T, E> unwrapper) { + if (unwrapper == null) { + throw new NullPointerException("Unwrapper must be null"); + } + // Actualize ourselves if (heldValue == null) { heldValue = heldSource.get(); @@ -124,4 +143,36 @@ public class LazyHolder<T> implements IHolder<T> { return unwrapper.apply(heldValue); } -} + @Override + public boolean isMaterialized() { + if (heldSource != null) { + // We're materialized if a value exists + return heldValue == null; + } else { + // We're materialized by default + return true; + } + } + + @Override + public boolean hasPendingActions() { + return actions.isEmpty(); + } + + @Override + public void materialize() { + // Only materialize if we haven't already + if (!isMaterialized()) { + heldValue = heldSource.get(); + } + } + + @Override + public void applyPendingActions() { + materialize(); + + actions.forEach((action) -> { + heldValue = action.apply(heldValue); + }); + } +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyPair.java b/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyPair.java index 3c21b56..e08c8fb 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyPair.java +++ b/BJC-Utils2/src/main/java/bjc/utils/data/lazy/LazyPair.java @@ -21,11 +21,14 @@ import bjc.utils.data.Pair; * @param <R> * The type of value stored on the right side of the pair */ -public class LazyPair<L, R> implements IPair<L, R> { +public class LazyPair<L, R> implements IPair<L, R>, ILazy { /** * The backing store for this pair */ - protected IHolder<IPair<L, R>> delegatePair; + protected IHolder<IPair<L, R>> delegatePair; + + private boolean materialized = false; + private boolean pendingActions = false; /** * Create a new blank lazy pair @@ -43,6 +46,8 @@ public class LazyPair<L, R> implements IPair<L, R> { * The initial value for the right side of the pair */ public LazyPair(L leftValue, R rightValue) { + materialized = true; + delegatePair = new LazyHolder<>(new Pair<>(leftValue, rightValue)); } @@ -56,6 +61,10 @@ public class LazyPair<L, R> implements IPair<L, R> { */ public LazyPair(Supplier<L> leftValueSource, Supplier<R> rightValueSource) { + if (leftValueSource == null || rightValueSource == null) { + throw new NullPointerException("Sources must be non-null"); + } + delegatePair = new LazyHolder<>(() -> { return new Pair<>(leftValueSource.get(), rightValueSource.get()); @@ -68,7 +77,11 @@ public class LazyPair<L, R> implements IPair<L, R> { * @param delegate * The internal delegate for the pair */ - private LazyPair(IHolder<IPair<L, R>> delegate) { + private LazyPair(IHolder<IPair<L, R>> delegate, boolean mater, + boolean pend) { + materialized = mater; + pendingActions = pend; + delegatePair = delegate; } @@ -81,10 +94,15 @@ public class LazyPair<L, R> implements IPair<L, R> { @Override public <L2, R2> IPair<L2, R2> apply(Function<L, L2> leftTransform, Function<R, R2> rightTransform) { - IHolder<IPair<L2, R2>> newPair = delegatePair - .map((currentPair) -> currentPair.apply(leftTransform, rightTransform)); + if (leftTransform == null || rightTransform == null) { + throw new NullPointerException("Transforms must be non-null"); + } - return new LazyPair<>(newPair); + IHolder<IPair<L2, R2>> newPair = + delegatePair.map((currentPair) -> currentPair + .apply(leftTransform, rightTransform)); + + return new LazyPair<>(newPair, materialized, true); } /* @@ -94,6 +112,12 @@ public class LazyPair<L, R> implements IPair<L, R> { */ @Override public void doWith(BiConsumer<L, R> action) { + if (action == null) { + throw new NullPointerException("Action must be non-null"); + } + + pendingActions = true; + delegatePair.doWith((currentPair) -> { currentPair.doWith(action); }); @@ -106,6 +130,43 @@ public class LazyPair<L, R> implements IPair<L, R> { */ @Override public <E> E merge(BiFunction<L, R, E> merger) { - return delegatePair.unwrap((currentPair) -> currentPair.merge(merger)); + if (merger == null) { + throw new NullPointerException("Merger must be non-null"); + } + + materialized = true; + pendingActions = false; + + return delegatePair + .unwrap((currentPair) -> currentPair.merge(merger)); + } + + @Override + public boolean isMaterialized() { + return materialized; + } + + @Override + public boolean hasPendingActions() { + return pendingActions; + } + + /* + * Note: Materializing will also apply all currently pending actions + */ + @Override + public void materialize() { + merge((left, right) -> null); + + materialized = true; + pendingActions = false; + } + + @Override + public void applyPendingActions() { + merge((left, right) -> null); + + materialized = true; + pendingActions = false; } -} +}
\ No newline at end of file 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, diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java index 5215576..5eb488a 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcutils/ListUtils.java @@ -28,16 +28,24 @@ public class ListUtils { } @Override - public FunctionalList<String> apply(String opName, - String opRegex) { - if (StringUtils.containsOnly(token, opRegex)) { + public FunctionalList<String> apply(String operatorName, + String operatorRegex) { + if (operatorName == null) { + throw new NullPointerException( + "Operator name must not be null"); + } else if (operatorRegex == null) { + throw new NullPointerException( + "Operator regex must not be null"); + } + + if (StringUtils.containsOnly(token, operatorRegex)) { return new FunctionalList<>(token); - } else if (token.startsWith(opName)) { - return new FunctionalList<>(opName, - token.split(opRegex)[1]); - } else if (token.endsWith(opName)) { - return new FunctionalList<>(token.split(opRegex)[0], - opName); + } else if (token.startsWith(operatorName)) { + return new FunctionalList<>(operatorName, + token.split(operatorRegex)[1]); + } else if (token.endsWith(operatorName)) { + return new FunctionalList<>(token.split(operatorRegex)[0], + operatorName); } else { return new FunctionalList<>(token); } @@ -55,13 +63,22 @@ public class ListUtils { @Override public FunctionalList<String> apply(String operatorName, String operatorRegex) { + if (operatorName == null) { + throw new NullPointerException( + "Operator name must not be null"); + } else if (operatorRegex == null) { + throw new NullPointerException( + "Operator regex must not be null"); + } + if (tokenToSplit.contains(operatorName)) { if (StringUtils.containsOnly(tokenToSplit, operatorRegex)) { return new FunctionalList<>(tokenToSplit); } else { - FunctionalList<String> splitTokens = new FunctionalList<>( - tokenToSplit.split(operatorRegex)); + FunctionalList<String> splitTokens = + new FunctionalList<>( + tokenToSplit.split(operatorRegex)); FunctionalList<String> result = new FunctionalList<>(); @@ -161,16 +178,29 @@ public class ListUtils { public static <E> FunctionalList<FunctionalList<E>> groupPartition( FunctionalList<E> input, Function<E, Integer> elementCounter, int numberPerPartition) { + if (input == null) { + throw new NullPointerException("Input list must not be null"); + } else if (elementCounter == null) { + throw new NullPointerException("Counter must not be null"); + } else if (numberPerPartition < 1 + || numberPerPartition > input.getSize()) { + throw new IllegalArgumentException( + "" + numberPerPartition + " is not a valid" + + " partition size. Must be between 1 and " + + input.getSize()); + } + /* * List that holds our results */ - FunctionalList<FunctionalList<E>> returnedList = new FunctionalList<>(); + FunctionalList<FunctionalList<E>> returnedList = + new FunctionalList<>(); /* * List that holds current partition */ - GenHolder<FunctionalList<E>> currentPartition = new GenHolder<>( - new FunctionalList<>()); + GenHolder<FunctionalList<E>> currentPartition = + new GenHolder<>(new FunctionalList<>()); /* * List that holds elements rejected during current pass */ @@ -184,8 +214,10 @@ public class ListUtils { /* * Run up to a certain number of passes */ - for (int numberOfIterations = 0; numberOfIterations < MAX_NTRIESPART - && !rejectedElements.isEmpty(); numberOfIterations++) { + for (int numberOfIterations = + 0; numberOfIterations < MAX_NTRIESPART + && !rejectedElements + .isEmpty(); numberOfIterations++) { input.forEach(new GroupPartIteration<>(returnedList, currentPartition, rejectedElements, numberInCurrentPartition, numberPerPartition, @@ -226,14 +258,22 @@ public class ListUtils { public static FunctionalList<String> splitTokens( FunctionalList<String> input, Deque<Pair<String, String>> operators) { - GenHolder<FunctionalList<String>> ret = new GenHolder<>(input); + if (input == null) { + throw new NullPointerException("Input must not be null"); + } else if (operators == null) { + throw new NullPointerException( + "Set of operators must not be null"); + } + + GenHolder<FunctionalList<String>> returnedList = + new GenHolder<>(input); - operators.forEach( - (op) -> ret.transform((oldRet) -> oldRet.flatMap((tok) -> { - return op.merge(new TokenSplitter(tok)); + operators.forEach((operator) -> returnedList + .transform((oldReturn) -> oldReturn.flatMap((token) -> { + return operator.merge(new TokenSplitter(token)); }))); - return ret.unwrap((list) -> list); + return returnedList.unwrap((list) -> list); } /** @@ -241,20 +281,27 @@ public class ListUtils { * * @param input * The tokens to deaffix - * @param ops + * @param operators * The affixes to remove * @return The tokens that have been deaffixed * */ public static FunctionalList<String> deAffixTokens( FunctionalList<String> input, - Deque<Pair<String, String>> ops) { - GenHolder<FunctionalList<String>> returnedList = new GenHolder<>( - input); + Deque<Pair<String, String>> operators) { + if (input == null) { + throw new NullPointerException("Input must not be null"); + } else if (operators == null) { + throw new NullPointerException( + "Set of operators must not be null"); + } + + GenHolder<FunctionalList<String>> returnedList = + new GenHolder<>(input); - ops.forEach((op) -> returnedList - .transform((oldRet) -> oldRet.flatMap((tok) -> { - return op.merge(new TokenDeaffixer(tok)); + operators.forEach((operator) -> returnedList + .transform((oldReturn) -> oldReturn.flatMap((token) -> { + return operator.merge(new TokenDeaffixer(token)); }))); return returnedList.unwrap((list) -> list); @@ -269,6 +316,10 @@ public class ListUtils { * @return The collapsed string of tokens */ public static String collapseTokens(FunctionalList<String> input) { + if (input == null) { + throw new NullPointerException("Input must not be null"); + } + return collapseTokens(input, ""); } @@ -278,23 +329,29 @@ public class ListUtils { * * @param input * The list of tokens to collapse - * @param sep + * @param seperator * The seperator to use for seperating tokens * @return The collapsed string of tokens */ public static String collapseTokens(FunctionalList<String> input, - String sep) { + String seperator) { + if (input == null) { + throw new NullPointerException("Input must not be null"); + } else if (seperator == null) { + throw new NullPointerException("Seperator must not be null"); + } + if (input.getSize() < 1) { return ""; } else if (input.getSize() == 1) { return input.first(); } else { - return input.reduceAux("", - (currentString, state) -> state + currentString + sep, - (strang) -> { - return strang.substring(0, - strang.length() - sep.length()); - }); + return input.reduceAux("", (currentString, state) -> { + return state + currentString + seperator; + }, (strang) -> { + return strang.substring(0, + strang.length() - seperator.length()); + }); } } }
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java b/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java index b7d20aa..a73292c 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java +++ b/BJC-Utils2/src/main/java/bjc/utils/funcutils/StringUtils.java @@ -28,6 +28,12 @@ public class StringUtils { * group is then matched one or more times and the pattern matches * to the end of the string */ + if (input == null) { + throw new NullPointerException("Input must not be null"); + } else if (regex == null) { + throw new NullPointerException("Regex must not be null"); + } + return input.matches("\\A(?:" + regex + ")+\\Z"); } diff --git a/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java b/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java index ca0578d..4f194f3 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gen/RandomGrammar.java @@ -62,6 +62,10 @@ public class RandomGrammar<E> extends WeightedGrammar<E> { * The cases to add for this rule. */ public void makeRule(E rule, FunctionalList<FunctionalList<E>> cases) { + if (cases == null) { + throw new NullPointerException("Cases must not be null"); + } + super.addRule(rule); cases.forEach(currentCase -> super.addCase(rule, 1, currentCase)); diff --git a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedGrammar.java b/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedGrammar.java index 28b61c5..cdfe056 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedGrammar.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedGrammar.java @@ -57,21 +57,33 @@ public class WeightedGrammar<E> { public WeightedGrammar(Random source) { this(); + if (source == null) { + throw new NullPointerException( + "Source of randomness must be non-null"); + } + rng = source; } /** * Add a case to an already existing rule. * - * @param rule + * @param ruleName * The rule to add a case to. * @param probability * The probability for this rule to be chosen. * @param cse * The case being added. */ - public void addCase(E rule, int probability, FunctionalList<E> cse) { - rules.get(rule).addProbability(probability, cse); + public void addCase(E ruleName, int probability, + FunctionalList<E> cse) { + if (ruleName == null) { + throw new NullPointerException("Rule name must be not null"); + } else if (cse == null) { + throw new NullPointerException("Case body must not be null"); + } + + rules.get(ruleName).addProbability(probability, cse); } /** @@ -84,6 +96,14 @@ public class WeightedGrammar<E> { * @return Whether the alias was succesfully created */ public boolean addGrammarAlias(E name, E alias) { + if (name == null) { + throw new NullPointerException( + "Subgrammar name must not be null"); + } else if (alias == null) { + throw new NullPointerException( + "Subgrammar alias must not be null"); + } + if (subgrammars.containsKey(alias)) { return false; } else { @@ -107,6 +127,11 @@ public class WeightedGrammar<E> { if (rng == null) { rng = new Random(); } + + if (name == null) { + throw new NullPointerException("Rule name must not be null"); + } + return addRule(name, new WeightedRandom<>(rng)); } @@ -121,6 +146,12 @@ public class WeightedGrammar<E> { */ public boolean addRule(E name, WeightedRandom<FunctionalList<E>> cases) { + if (name == null) { + throw new NullPointerException("Name must not be null"); + } else if (cases == null) { + throw new NullPointerException("Cases must not be null"); + } + if (rules.containsKey(name)) { return false; } else { @@ -139,6 +170,13 @@ public class WeightedGrammar<E> { * @return Whether or not the subgrammar was succesfully added. */ public boolean addSubgrammar(E name, WeightedGrammar<E> subgrammar) { + if (name == null) { + throw new NullPointerException( + "Subgrammar name must not be null"); + } else if (subgrammar == null) { + throw new NullPointerException("Subgrammar must not be null"); + } + if (subgrammars.containsKey(name)) { return false; } else { @@ -157,6 +195,10 @@ public class WeightedGrammar<E> { */ public FunctionalList<FunctionalList<E>> generateDebugValues(E ruleName) { + if (ruleName == null) { + throw new NullPointerException("Rule name must not be null"); + } + FunctionalList<FunctionalList<E>> returnedList = new FunctionalList<>(); @@ -187,6 +229,15 @@ public class WeightedGrammar<E> { */ public <T> FunctionalList<T> generateGenericValues(E initRule, Function<E, T> tokenTransformer, T spacer) { + if (initRule == null) { + throw new NullPointerException( + "Initial rule must not be null"); + } else if (tokenTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } else if (spacer == null) { + throw new NullPointerException("Spacer must not be null"); + } + FunctionalList<T> returnedList = new FunctionalList<>(); if (subgrammars.containsKey(initRule)) { @@ -205,7 +256,14 @@ public class WeightedGrammar<E> { returnedList.add(spacer); })); } else { - returnedList.add(tokenTransformer.apply(initRule)); + T transformedToken = tokenTransformer.apply(initRule); + + if (transformedToken == null) { + throw new NullPointerException( + "Transformer created null token"); + } + + returnedList.add(transformedToken); returnedList.add(spacer); } @@ -244,6 +302,11 @@ public class WeightedGrammar<E> { * @return The subgrammar with the specified name. */ public WeightedGrammar<E> getSubgrammar(E name) { + if (name == null) { + throw new NullPointerException( + "Subgrammar name must not be null"); + } + return subgrammars.get(name); } @@ -270,6 +333,16 @@ public class WeightedGrammar<E> { */ public void multiPrefixRule(E ruleName, E prefixToken, int additionalProbability, int numberOfTimes) { + if (ruleName == null) { + throw new NullPointerException("Rule name must not be null"); + } else if (prefixToken == null) { + throw new NullPointerException( + "Prefix token must not be null"); + } else if (numberOfTimes < 1) { + throw new IllegalArgumentException( + "Number of times to prefix must be positive."); + } + WeightedRandom<FunctionalList<E>> rule = rules.get(ruleName); FunctionalList<Pair<Integer, FunctionalList<E>>> newResults = @@ -315,6 +388,13 @@ public class WeightedGrammar<E> { */ public void prefixRule(E ruleName, E prefixToken, int additionalProbability) { + if (ruleName == null) { + throw new NullPointerException("Rule name must not be null"); + } else if (prefixToken == null) { + throw new NullPointerException( + "Prefix token must not be null"); + } + WeightedRandom<FunctionalList<E>> rule = rules.get(ruleName); FunctionalList<Pair<Integer, FunctionalList<E>>> newResults = @@ -340,6 +420,10 @@ public class WeightedGrammar<E> { * The name of the rule to remove. */ public void deleteRule(E name) { + if (name == null) { + throw new NullPointerException("Rule name must not be null"); + } + rules.remove(name); } @@ -350,6 +434,10 @@ public class WeightedGrammar<E> { * The name of the subgrammar to remove. */ public void deleteSubgrammar(E name) { + if (name == null) { + throw new NullPointerException("Rule name must not be null"); + } + subgrammars.remove(name); } @@ -386,13 +474,20 @@ public class WeightedGrammar<E> { * * @param ruleName * The rule to suffix - * @param prefixToken + * @param suffixToken * The token to prefix to the rule * @param additionalProbability * Additional probability of the prefixed rule */ - public void suffixRule(E ruleName, E prefixToken, + public void suffixRule(E ruleName, E suffixToken, int additionalProbability) { + if (ruleName == null) { + throw new NullPointerException("Rule name must not be null"); + } else if (suffixToken == null) { + throw new NullPointerException( + "Prefix token must not be null"); + } + WeightedRandom<FunctionalList<E>> rule = rules.get(ruleName); FunctionalList<Pair<Integer, FunctionalList<E>>> newResults = @@ -401,7 +496,7 @@ public class WeightedGrammar<E> { rule.getValues().forEach((par) -> { FunctionalList<E> newCase = par.merge((left, right) -> right.clone()); - newCase.add(prefixToken); + newCase.add(suffixToken); newResults.add(new Pair<>(par.merge((left, right) -> left) + additionalProbability, newCase)); diff --git a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java b/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java index 5a8ef8f..5157ee2 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gen/WeightedRandom.java @@ -44,6 +44,11 @@ public class WeightedRandom<E> { probabilities = new FunctionalList<>(); results = new FunctionalList<>(); + if (src == null) { + throw new NullPointerException( + "Source of randomness must not be null"); + } + source = src; } @@ -109,4 +114,4 @@ public class WeightedRandom<E> { public FunctionalList<Pair<Integer, E>> getValues() { return probabilities.pairWith(results); } -} +}
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java b/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java index 513044e..e583210 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java +++ b/BJC-Utils2/src/main/java/bjc/utils/graph/AdjacencyMap.java @@ -5,6 +5,7 @@ import java.io.OutputStream; import java.io.PrintStream; import java.util.HashMap; import java.util.HashSet; +import java.util.InputMismatchException; import java.util.Map; import java.util.Map.Entry; import java.util.Scanner; @@ -30,36 +31,79 @@ public class AdjacencyMap<T> { * @return An adjacency map defined by the text */ public static AdjacencyMap<Integer> fromStream(InputStream stream) { - Scanner inputSource = new Scanner(stream); - inputSource.useDelimiter("\n"); - - // First, read in number of vertices - int numVertices = Integer.parseInt(inputSource.next()); - - Set<Integer> vertices = new HashSet<>(); - IntStream.range(0, numVertices) - .forEach(element -> vertices.add(element)); + if (stream == null) { + throw new NullPointerException( + "Input source must not be null"); + } // Create the adjacency map - AdjacencyMap<Integer> adjacencyMap = new AdjacencyMap<>(vertices); + AdjacencyMap<Integer> adjacencyMap; - GenHolder<Integer> row = new GenHolder<>(0); + try (Scanner inputSource = new Scanner(stream)) { + inputSource.useDelimiter("\n"); - inputSource.forEachRemaining((strang) -> { - String[] parts = strang.split(" "); - int column = 0; + int numVertices; - for (String part : parts) { - adjacencyMap.setWeight(row.unwrap(number -> number), - column, Integer.parseInt(part)); + String possibleVertices = inputSource.next(); - column++; + try { + // First, read in number of vertices + numVertices = Integer.parseInt(possibleVertices); + } catch (NumberFormatException nfex) { + throw new InputMismatchException( + "The first line must contain the number of vertices. " + + possibleVertices + + " is not a valid number"); } - row.transform((number) -> number + 1); - }); + if (numVertices <= 0) { + throw new InputMismatchException( + "The number of vertices must be greater than 0"); + } + + Set<Integer> vertices = new HashSet<>(); + + IntStream.range(0, numVertices) + .forEach(element -> vertices.add(element)); + + adjacencyMap = new AdjacencyMap<>(vertices); + + GenHolder<Integer> row = new GenHolder<>(0); + + inputSource.forEachRemaining((strang) -> { + String[] parts = strang.split(" "); + + if (parts.length != numVertices) { + throw new InputMismatchException( + "Must specify a weight for all " + numVertices + + " vertices"); + } + + int column = 0; - inputSource.close(); + for (String part : parts) { + int columnWeight; + + try { + columnWeight = Integer.parseInt(part); + } catch (NumberFormatException nfex) { + throw new InputMismatchException( + "" + part + " is not a valid weight."); + } + + adjacencyMap.setWeight(row.unwrap(number -> number), + column, columnWeight); + + column++; + } + + row.transform((number) -> { + int newNumber = number + 1; + + return newNumber; + }); + }); + } return adjacencyMap; } @@ -76,6 +120,10 @@ public class AdjacencyMap<T> { * The set of vertices to create a map from */ public AdjacencyMap(Set<T> vertices) { + if (vertices == null) { + throw new NullPointerException("Vertices must not be null"); + } + vertices.forEach(vertex -> { Map<T, Integer> vertexRow = new HashMap<>(); @@ -97,6 +145,7 @@ public class AdjacencyMap<T> { adjacencyMap.entrySet().forEach(mapEntry -> { Set<Entry<T, Integer>> entryVertices = mapEntry.getValue().entrySet(); + entryVertices.forEach(targetVertex -> { int leftValue = targetVertex.getValue(); int rightValue = adjacencyMap.get(targetVertex.getKey()) @@ -122,6 +171,22 @@ public class AdjacencyMap<T> { * The weight of the edge */ public void setWeight(T sourceVertex, T targetVertex, int edgeWeight) { + if (sourceVertex == null) { + throw new NullPointerException( + "Source vertex must not be null"); + } else if (targetVertex == null) { + throw new NullPointerException( + "Target vertex must not be null"); + } + + if (!adjacencyMap.containsKey(sourceVertex)) { + throw new IllegalArgumentException("Source vertex " + + sourceVertex + " isn't present in map"); + } else if (!adjacencyMap.containsKey(targetVertex)) { + throw new IllegalArgumentException("Target vertex " + + targetVertex + " isn't present in map"); + } + adjacencyMap.get(sourceVertex).put(targetVertex, edgeWeight); } @@ -145,11 +210,16 @@ public class AdjacencyMap<T> { /** * Convert an adjacency map back into a stream * - * @param outputSource + * @param outputSink * The stream to convert to */ - public void toStream(OutputStream outputSource) { - PrintStream outputPrinter = new PrintStream(outputSource); + public void toStream(OutputStream outputSink) { + if (outputSink == null) { + throw new NullPointerException( + "Output source must not be null"); + } + + PrintStream outputPrinter = new PrintStream(outputSink); adjacencyMap.entrySet().forEach(sourceVertex -> { sourceVertex.getValue().entrySet() diff --git a/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java b/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java index 44aa8e7..58a233a 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java +++ b/BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java @@ -30,6 +30,14 @@ public class Edge<T> { * The distance between initial and terminal edge */ public Edge(T initialNode, T terminalNode, int distance) { + if (initialNode == null) { + throw new NullPointerException( + "Initial node must not be null"); + } else if (terminalNode == null) { + throw new NullPointerException( + "Terminal node must not be null"); + } + this.source = initialNode; this.target = terminalNode; this.distance = distance; @@ -68,4 +76,58 @@ public class Edge<T> { return " first vertex " + source + " to vertex " + target + " with distance: " + distance; } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#hashCode() + */ + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + distance; + result = prime * result + + ((source == null) ? 0 : source.hashCode()); + result = prime * result + + ((target == null) ? 0 : target.hashCode()); + return result; + } + + /* + * (non-Javadoc) + * + * @see java.lang.Object#equals(java.lang.Object) + */ + @Override + public boolean equals(Object obj) { + if (this == obj) { + return true; + } else if (obj == null) { + return false; + } else if (getClass() != obj.getClass()) { + return false; + } else { + + Edge<?> other = (Edge<?>) obj; + + if (distance != other.distance) { + return false; + } else if (source == null) { + if (other.source != null) { + return false; + } + } else if (!source.equals(other.source)) { + return false; + } else if (target == null) { + if (other.target != null) { + return false; + } + } else if (!target.equals(other.target)) { + return false; + } + + return true; + } + } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java b/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java index 5172df5..f6bfc85 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java +++ b/BJC-Utils2/src/main/java/bjc/utils/graph/Graph.java @@ -53,9 +53,7 @@ public class Graph<T> { if (sourceVertex == null) { throw new NullPointerException( "The source vertex cannot be null"); - } - - if (targetVertex == null) { + } else if (targetVertex == null) { throw new NullPointerException( "The target vertex cannot be null"); } @@ -79,19 +77,25 @@ public class Graph<T> { * Execute an action for all edges of a specific vertex matching * conditions * - * @param source + * @param sourceVertex * The vertex to test edges for * @param edgeMatcher * The conditions an edge must match * @param edgeAction * The action to execute for matching edges */ - public void forAllEdgesMatchingAt(T source, + public void forAllEdgesMatchingAt(T sourceVertex, BiPredicate<T, Integer> edgeMatcher, BiConsumer<T, Integer> edgeAction) { - getEdges(source).forEach((tgt, weight) -> { - if (edgeMatcher.test(tgt, weight)) { - edgeAction.accept(tgt, weight); + if (edgeMatcher == null) { + throw new NullPointerException("Matcher must not be null"); + } else if (edgeAction == null) { + throw new NullPointerException("Action must not be null"); + } + + getEdges(sourceVertex).forEach((targetVertex, vertexWeight) -> { + if (edgeMatcher.test(targetVertex, vertexWeight)) { + edgeAction.accept(targetVertex, vertexWeight); } }); } @@ -107,6 +111,9 @@ public class Graph<T> { // Can't find edges for a null source if (source == null) { throw new NullPointerException("The source cannot be null."); + } else if (!backingGraph.containsKey(source)) { + throw new IllegalArgumentException( + "Vertex " + source + " is not in graph"); } return Collections.unmodifiableMap(backingGraph.get(source)); @@ -213,8 +220,7 @@ public class Graph<T> { if (sourceVertex == null) { throw new NullPointerException( "The source vertex cannot be null"); - } - if (targetVertex == null) { + } else if (targetVertex == null) { throw new NullPointerException( "The target vertex cannot be null"); } diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java b/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java index be1746f..e563530 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/ExtensionFileFilter.java @@ -49,6 +49,10 @@ public class ExtensionFileFilter extends FileFilter { @Override public boolean accept(File pathname) { + if (pathname == null) { + throw new NullPointerException("Pathname must not be null"); + } + return extensions.anyMatch(pathname.getName()::endsWith); } diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/ListParameterPanel.java b/BJC-Utils2/src/main/java/bjc/utils/gui/ListParameterPanel.java index 42c5761..4bbe6e7 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/ListParameterPanel.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/ListParameterPanel.java @@ -68,24 +68,60 @@ public class ListParameterPanel<E> extends JPanel { list.setSelectionMode(ListSelectionModel.SINGLE_SELECTION); JPanel buttonPanel = new JPanel(); - buttonPanel.setLayout(new HLayout(3)); - - JButton addParam = new JButton("Add..."); - JButton editParam = new JButton("Edit..."); - JButton removeParam = new JButton("Remove..."); - - addParam.addActionListener( - (event) -> ((DefaultListModel<E>) list.getModel()) - .addElement(addAction.get())); - editParam.addActionListener( - (event) -> editAction.accept(list.getSelectedValue())); - removeParam.addActionListener((event) -> removeAction - .accept(((DefaultListModel<E>) list.getModel()) - .remove(list.getSelectedIndex()))); - - buttonPanel.add(addParam); - buttonPanel.add(editParam); - buttonPanel.add(removeParam); + + int numButtons = 0; + + if (addAction != null) { + numButtons++; + } + + if (editAction != null) { + numButtons++; + } + + if (removeAction != null) { + numButtons++; + } + + buttonPanel.setLayout(new HLayout(numButtons)); + + JButton addParam = null; + + if (addAction != null) { + addParam = new JButton("Add..."); + addParam.addActionListener( + (event) -> ((DefaultListModel<E>) list.getModel()) + .addElement(addAction.get())); + } + + JButton editParam = null; + + if (editAction != null) { + editParam = new JButton("Edit..."); + editParam.addActionListener( + (event) -> editAction.accept(list.getSelectedValue())); + } + + JButton removeParam = null; + + if (removeAction != null) { + removeParam = new JButton("Remove..."); + removeParam.addActionListener((event) -> removeAction + .accept(((DefaultListModel<E>) list.getModel()) + .remove(list.getSelectedIndex()))); + } + + if (addAction != null) { + buttonPanel.add(addParam); + } + + if (editAction != null) { + buttonPanel.add(editParam); + } + + if (removeAction != null) { + buttonPanel.add(removeParam); + } add(list); add(buttonPanel); diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java index fd7d05a..b09fbd8 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleDialogs.java @@ -38,9 +38,10 @@ public class SimpleDialogs { */ public static int getBoundedInt(Component parent, String title, String prompt, int lowerBound, int upperBound) { - return getValue(parent, title, prompt, strang -> { + return getValue(parent, title, prompt, (strang) -> { try { int value = Integer.parseInt(strang); + return (value < upperBound) && (value > lowerBound); } catch (NumberFormatException nfe) { return false; @@ -67,6 +68,15 @@ public class SimpleDialogs { @SuppressWarnings("unchecked") public static <E> E getChoice(Frame parent, String title, String question, E... choices) { + + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (title == null) { + throw new NullPointerException("Title must not be null"); + } else if (question == null) { + throw new NullPointerException("Question must not be null"); + } + JDialog mainDialog = new JDialog(parent, title, true); mainDialog.setLayout(new VLayout(2)); @@ -134,6 +144,14 @@ public class SimpleDialogs { */ public static String getString(Component parent, String title, String prompt) { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (title == null) { + throw new NullPointerException("Title must not be null"); + } else if (prompt == null) { + throw new NullPointerException("Prompt must not be null"); + } + return JOptionPane.showInputDialog(parent, prompt, title, JOptionPane.QUESTION_MESSAGE); } @@ -159,6 +177,12 @@ public class SimpleDialogs { public static <E> E getValue(Component parent, String title, String prompt, Predicate<String> inputValidator, Function<String, E> inputTransformer) { + if (inputValidator == null) { + throw new NullPointerException("Validator must not be null"); + } else if (inputTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + String inputString = getString(parent, title, prompt); while (!inputValidator.test(inputString)) { @@ -199,6 +223,14 @@ public class SimpleDialogs { */ public static boolean getYesNo(Component parent, String title, String question) { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (title == null) { + throw new NullPointerException("Title must not be null"); + } else if (question == null) { + throw new NullPointerException("Question must not be null"); + } + int dialogResult = JOptionPane.showConfirmDialog(parent, question, title, JOptionPane.YES_NO_OPTION); @@ -217,6 +249,15 @@ public class SimpleDialogs { */ public static void showError(Component parent, String title, String errorMessage) { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (title == null) { + throw new NullPointerException("Title must not be null"); + } else if (errorMessage == null) { + throw new NullPointerException( + "Error message must not be null"); + } + JOptionPane.showMessageDialog(parent, errorMessage, title, JOptionPane.ERROR_MESSAGE); } diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleFileChooser.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleFileChooser.java index 9648762..f39ff7c 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleFileChooser.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleFileChooser.java @@ -18,6 +18,10 @@ import bjc.utils.exceptions.FileNotChosenException; public class SimpleFileChooser { private static File doOpenFile(Component parent, String title, JFileChooser files) { + if (title == null) { + throw new NullPointerException("Title must not be null"); + } + files.setDialogTitle(title); boolean success = false; @@ -38,6 +42,10 @@ public class SimpleFileChooser { private static File doSaveFile(Component parent, String title, JFileChooser files) { + if (title == null) { + throw new NullPointerException("Title must not be null"); + } + files.setDialogTitle(title); boolean success = false; @@ -87,6 +95,7 @@ public class SimpleFileChooser { public static File getOpenFile(Component parent, String title, String... extensions) { JFileChooser files = new JFileChooser(); + files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); return doOpenFile(parent, title, files); @@ -121,6 +130,7 @@ public class SimpleFileChooser { public static File getSaveFile(Component parent, String title, String... extensions) { JFileChooser files = new JFileChooser(); + files.addChoosableFileFilter(new ExtensionFileFilter(extensions)); return doSaveFile(parent, title, files); @@ -128,6 +138,13 @@ public class SimpleFileChooser { private static void maybeDoOpenFile(Component parent, JFileChooser files) throws FileNotChosenException { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (files == null) { + throw new NullPointerException( + "File chooser must not be null"); + } + int dialogResult = files.showSaveDialog(parent); if (dialogResult != JFileChooser.APPROVE_OPTION) { @@ -137,6 +154,13 @@ public class SimpleFileChooser { private static void maybeDoSaveFile(Component parent, JFileChooser files) throws FileNotChosenException { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (files == null) { + throw new NullPointerException( + "File chooser must not be null"); + } + int dialogResult = files.showSaveDialog(parent); if (dialogResult != JFileChooser.APPROVE_OPTION) { @@ -154,6 +178,10 @@ public class SimpleFileChooser { * @return The file if the user chose one or null if they didn't. */ public static File maybeOpenFile(Component parent, String title) { + if (title == null) { + throw new NullPointerException("Title must not be null"); + } + JFileChooser files = new JFileChooser(); files.setDialogTitle(title); @@ -175,6 +203,10 @@ public class SimpleFileChooser { * @return The file if the user chose one or null if they didn't. */ public static File maybeSaveFile(Component parent, String title) { + if (title == null) { + throw new NullPointerException("Title must not be null"); + } + JFileChooser files = new JFileChooser(); files.setDialogTitle(title); diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java index 4695318..4db5027 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleJList.java @@ -22,6 +22,10 @@ public class SimpleJList { * @return A JList populated with the elements from ls. */ public static <E> JList<E> buildFromList(Iterable<E> listSource) { + if (listSource == null) { + throw new NullPointerException("Source must not be null"); + } + return new JList<>(buildModel(listSource)); } @@ -36,6 +40,10 @@ public class SimpleJList { * @return A list model populated with the elements from ls. */ public static <E> ListModel<E> buildModel(Iterable<E> listSource) { + if (listSource == null) { + throw new NullPointerException("Source must not be null"); + } + DefaultListModel<E> defaultModel = new DefaultListModel<>(); listSource.forEach(defaultModel::addElement); diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java index ddf5492..e2fd390 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/SimpleTitledBorder.java @@ -22,5 +22,4 @@ public class SimpleTitledBorder extends TitledBorder { public SimpleTitledBorder(String title) { super(new EtchedBorder(), title); } - } diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java b/BJC-Utils2/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java index 368f8be..7e2c2d2 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/awt/ExtensionFileFilter.java @@ -29,6 +29,10 @@ public class ExtensionFileFilter implements FilenameFilter { * The extensions to show in this filter. */ public ExtensionFileFilter(List<String> exts) { + if (exts == null) { + throw new NullPointerException("Extensions must not be null"); + } + extensions = new FunctionalList<>(exts); } diff --git a/BJC-Utils2/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java b/BJC-Utils2/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java index a8df3b9..7617199 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java +++ b/BJC-Utils2/src/main/java/bjc/utils/gui/awt/SimpleFileDialog.java @@ -42,6 +42,12 @@ public class SimpleFileDialog { */ public static File getOpenFile(Frame parent, String title, String... extensions) { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (title == null) { + throw new NullPointerException("Title must not be null"); + } + FileDialog fileDialog = new FileDialog(parent, title, FileDialog.LOAD); @@ -87,6 +93,12 @@ public class SimpleFileDialog { */ public static File getSaveFile(Frame parent, String title, String... extensions) { + if (parent == null) { + throw new NullPointerException("Parent must not be null"); + } else if (title == null) { + throw new NullPointerException("Title must not be null"); + } + FileDialog fileDialog = new FileDialog(parent, title, FileDialog.SAVE); diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/AST.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/AST.java index 4bfb469..d5ae3f2 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/AST.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/AST.java @@ -17,11 +17,26 @@ import bjc.utils.funcdata.bst.ITreePart.TreeLinearizationMethod; * The type of token in this AST */ public class AST<T> { - private T token; + /** + * Indent a string n levels + * + * @param builder + * The string to indent + * @param levels + * The number of levels to indent + */ + protected static void indentNLevels(StringBuilder builder, + int levels) { + for (int i = 0; i <= levels; i++) { + builder.append("\t"); + } + } private AST<T> left; private AST<T> right; + private T token; + /** * Create a new leaf AST node * @@ -53,43 +68,6 @@ public class AST<T> { } /** - * Traverse an AST - * - * @param linearizationMethod - * The way to traverse the tree - * @param action - * The function to call on each traversed element - */ - public void traverse(TreeLinearizationMethod linearizationMethod, - Consumer<T> action) { - if (left != null && right != null) { - switch (linearizationMethod) { - case INORDER: - left.traverse(linearizationMethod, action); - action.accept(token); - right.traverse(linearizationMethod, action); - break; - case POSTORDER: - left.traverse(linearizationMethod, action); - right.traverse(linearizationMethod, action); - action.accept(token); - break; - case PREORDER: - action.accept(token); - left.traverse(linearizationMethod, action); - right.traverse(linearizationMethod, action); - break; - default: - throw new IllegalArgumentException( - "Got a invalid tree linearizer " - + linearizationMethod + ". WAT"); - } - } else { - action.accept(token); - } - } - - /** * Collapse this tree into a single node * * @param <E> @@ -104,11 +82,44 @@ public class AST<T> { * The function for transforming the result * @return The collapsed value of the tree */ + @SuppressWarnings("unchecked") public <E, T2> E collapse(Function<T, T2> tokenTransformer, Function<T, BinaryOperator<T2>> nodeTransformer, Function<T2, E> resultTransformer) { - return resultTransformer.apply( - internalCollapse(tokenTransformer, nodeTransformer)); + if (tokenTransformer == null) { + throw new NullPointerException( + "Token transformer must not be null"); + } else if (nodeTransformer == null) { + throw new NullPointerException( + "Node transformer must not be null"); + } + + if (resultTransformer != null) { + return resultTransformer.apply( + internalCollapse(tokenTransformer, nodeTransformer)); + } else { + // This is valid because if the user passes null as the last + // parameter, E will be inferred as Object, but will actually + // be T2 + return (E) internalCollapse(tokenTransformer, nodeTransformer); + } + } + + /** + * Expand the nodes in an AST + * + * @param expander + * The function to use for expanding nodes + * @return The expanded AST + */ + public AST<T> expand(Function<T, AST<T>> expander) { + if (expander == null) { + throw new NullPointerException("Expander must not be null"); + } + + return collapse(expander, (operator) -> (leftAST, rightAST) -> { + return new AST<>(operator, leftAST, rightAST); + }, null); } /* @@ -129,6 +140,7 @@ public class AST<T> { } T2 rightCollapsed; + if (right == null) { rightCollapsed = null; } else { @@ -141,15 +153,6 @@ public class AST<T> { } } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - internalToString(builder, -1); - - return builder.toString(); - } - /** * Internal version of toString for proper rendering * @@ -187,21 +190,6 @@ public class AST<T> { } /** - * Indent a string n levels - * - * @param builder - * The string to indent - * @param levels - * The number of levels to indent - */ - protected static void indentNLevels(StringBuilder builder, - int levels) { - for (int i = 0; i <= levels; i++) { - builder.append("\t"); - } - } - - /** * Execute a transform on selective nodes of the tree * * @param transformerPredicate @@ -211,6 +199,12 @@ public class AST<T> { */ public void selectiveTransform(Predicate<T> transformerPredicate, UnaryOperator<T> transformer) { + if (transformerPredicate == null) { + throw new NullPointerException("Predicate must not be null"); + } else if (transformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + if (transformerPredicate.test(token)) { token = transformer.apply(token); } @@ -224,6 +218,15 @@ public class AST<T> { } } + @Override + public String toString() { + StringBuilder builder = new StringBuilder(); + + internalToString(builder, -1); + + return builder.toString(); + } + /** * Transmute the tokens in an AST into a different sort of token * @@ -235,6 +238,10 @@ public class AST<T> { * @return The AST with transformed tokens */ public <E> AST<E> transmuteAST(Function<T, E> tokenTransformer) { + if (tokenTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + AST<E> leftBranch = null; AST<E> rightBranch = null; @@ -249,4 +256,48 @@ public class AST<T> { return new AST<>(tokenTransformer.apply(token), leftBranch, rightBranch); } + + /** + * Traverse an AST + * + * @param linearizationMethod + * The way to traverse the tree + * @param action + * The function to call on each traversed element + */ + public void traverse(TreeLinearizationMethod linearizationMethod, + Consumer<T> action) { + if (linearizationMethod == null) { + throw new NullPointerException( + "Linearization method must not be null"); + } else if (action == null) { + throw new NullPointerException("Action must not be null"); + } + + if (left != null && right != null) { + switch (linearizationMethod) { + case INORDER: + left.traverse(linearizationMethod, action); + action.accept(token); + right.traverse(linearizationMethod, action); + break; + case POSTORDER: + left.traverse(linearizationMethod, action); + right.traverse(linearizationMethod, action); + action.accept(token); + break; + case PREORDER: + action.accept(token); + left.traverse(linearizationMethod, action); + right.traverse(linearizationMethod, action); + break; + default: + throw new IllegalArgumentException( + "Got a invalid tree linearizer " + + linearizationMethod + ". WAT"); + } + } else { + action.accept(token); + } + } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/RuleBasedConfigReader.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/RuleBasedConfigReader.java index 46f5f1d..b6162e5 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/RuleBasedConfigReader.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/RuleBasedConfigReader.java @@ -2,6 +2,7 @@ package bjc.utils.parserutils; import java.io.InputStream; import java.util.HashMap; +import java.util.InputMismatchException; import java.util.Map; import java.util.Scanner; import java.util.function.BiConsumer; @@ -58,6 +59,13 @@ public class RuleBasedConfigReader<E> { */ public void addPragma(String pragmaName, BiConsumer<FunctionalStringTokenizer, E> pragmaAction) { + if (pragmaName == null) { + throw new NullPointerException("Pragma name must not be null"); + } else if (pragmaAction == null) { + throw new NullPointerException( + "Pragma action must not be null"); + } + pragmas.put(pragmaName, pragmaAction); } @@ -71,42 +79,77 @@ public class RuleBasedConfigReader<E> { * @return The final state of the reader */ public E fromStream(InputStream inputStream, E initialState) { - Scanner inputSource = new Scanner(inputStream); - - E state = initialState; - - while (inputSource.hasNextLine()) { - String line = inputSource.nextLine(); - - if (line.equals("")) { - endRule.accept(state); - continue; - } else if (line.startsWith("\t")) { - continueRule.accept(new FunctionalStringTokenizer( - line.substring(1), " "), state); - } else { - FunctionalStringTokenizer tokenizer = - new FunctionalStringTokenizer(line, " "); - - String nextToken = tokenizer.nextToken(); - if (nextToken.equals("#")) { - // Do nothing, this is a comment - } else if (nextToken.equals("pragma")) { - String token = tokenizer.nextToken(); - - pragmas.getOrDefault(token, (tokenzer, stat) -> { - throw new UnknownPragmaException( - "Unknown pragma " + token); - }).accept(tokenizer, state); + if (inputStream == null) { + throw new NullPointerException( + "Input stream must not be null"); + } + + E state; + + try (Scanner inputSource = new Scanner(inputStream)) { + + state = initialState; + boolean ruleOpen = false; + while (inputSource.hasNextLine()) { + String line = inputSource.nextLine(); + + if (line.equals("")) { + if (ruleOpen == false) { + // Ignore blank line without an open rule + } + + if (endRule == null) { + // Nothing happens on rule end + } else { + endRule.accept(state); + } + + continue; + } else if (line.startsWith("\t")) { + if (ruleOpen == false) { + throw new InputMismatchException( + "Can't continue rule with no rule currently open"); + } + + if (continueRule == null) { + throw new InputMismatchException( + "Attempted to continue rule with rule continuation disabled." + + " Check for extraneous tabs"); + } + + continueRule.accept(new FunctionalStringTokenizer( + line.substring(1), " "), state); } else { - startRule.accept(tokenizer, - new Pair<>(nextToken, state)); + FunctionalStringTokenizer tokenizer = + new FunctionalStringTokenizer(line, " "); + + String nextToken = tokenizer.nextToken(); + + if (nextToken.equals("#") || nextToken.equals("//")) { + // Do nothing, this is a comment + } else if (nextToken.equals("pragma")) { + String token = tokenizer.nextToken(); + + pragmas.getOrDefault(token, (tokenzer, stat) -> { + throw new UnknownPragmaException( + "Unknown pragma " + token); + }).accept(tokenizer, state); + } else { + if (ruleOpen == true) { + throw new InputMismatchException( + "Attempted to open a" + + " rule with a rule already open. Make sure rules are" + + " seperated by blank lines"); + } + + startRule.accept(tokenizer, + new Pair<>(nextToken, state)); + ruleOpen = true; + } } } } - inputSource.close(); - return state; } @@ -139,6 +182,11 @@ public class RuleBasedConfigReader<E> { */ public void setStartRule( BiConsumer<FunctionalStringTokenizer, Pair<String, E>> startRule) { + if (startRule == null) { + throw new NullPointerException( + "Action on rule start must be non-null"); + } + this.startRule = startRule; } } diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java index 0ca1879..1e5d487 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/ShuntingYard.java @@ -19,42 +19,6 @@ import bjc.utils.funcutils.StringUtils; * The type of tokens being shunted */ public class ShuntingYard<E> { - - private final class TokenShunter implements Consumer<String> { - private FunctionalList<E> output; - private Deque<String> stack; - private Function<String, E> transform; - - public TokenShunter(FunctionalList<E> outpt, Deque<String> stack, - Function<String, E> transform) { - this.output = outpt; - this.stack = stack; - this.transform = transform; - } - - @Override - public void accept(String token) { - if (operators.containsKey(token)) { - while (!stack.isEmpty() - && isHigherPrec(token, stack.peek())) { - output.add(transform.apply(stack.pop())); - } - - stack.push(token); - } else if (StringUtils.containsOnly(token, "\\(")) { - stack.push(token); - } else if (StringUtils.containsOnly(token, "\\)")) { - while (stack.peek().equals(token.replace(')', '('))) { - output.add(transform.apply(stack.pop())); - } - - stack.pop(); - } else { - output.add(transform.apply(token)); - } - } - } - /** * A enum representing the fundamental operator types * @@ -96,6 +60,58 @@ public class ShuntingYard<E> { } } + private final class TokenShunter implements Consumer<String> { + private FunctionalList<E> output; + private Deque<String> stack; + private Function<String, E> transform; + + public TokenShunter(FunctionalList<E> outpt, Deque<String> stack, + Function<String, E> transform) { + this.output = outpt; + this.stack = stack; + this.transform = transform; + } + + @Override + public void accept(String token) { + if (operators.containsKey(token)) { + while (!stack.isEmpty() + && isHigherPrec(token, stack.peek())) { + output.add(transform.apply(stack.pop())); + } + + stack.push(token); + } else if (StringUtils.containsOnly(token, "\\(")) { + // Handle groups of parenthesis for multiple nesting levels + stack.push(token); + } else if (StringUtils.containsOnly(token, "\\)")) { + // Handle groups of parenthesis for multiple nesting levels + while (stack.peek().equals(token.replace(')', '('))) { + output.add(transform.apply(stack.pop())); + } + + stack.pop(); + } else { + output.add(transform.apply(token)); + } + } + } + + private static boolean shouldConfigureBasicOperators = true; + + /** + * Set whether the shunter should configure the four basic math + * operators + * + * @param configureBasicOperators + * Whether or not the four basic math operators should be + * configured + */ + public static void setBasicOperatorConfiguration( + boolean configureBasicOperators) { + shouldConfigureBasicOperators = configureBasicOperators; + } + /** * Holds all the shuntable operations */ @@ -107,10 +123,12 @@ public class ShuntingYard<E> { public ShuntingYard() { operators = new HashMap<>(); - operators.put("+", Operator.ADD); - operators.put("-", Operator.SUBTRACT); - operators.put("*", Operator.MULTIPLY); - operators.put("/", Operator.DIVIDE); + if (shouldConfigureBasicOperators) { + operators.put("+", Operator.ADD); + operators.put("-", Operator.SUBTRACT); + operators.put("*", Operator.MULTIPLY); + operators.put("/", Operator.DIVIDE); + } } /** @@ -129,13 +147,17 @@ public class ShuntingYard<E> { /** * Add an operator to the list of shuntable operators * - * @param token + * @param operatorToken * The token representing the operator * @param precedence * The precedence of the operator */ - public void addOp(String token, IPrecedent precedence) { - operators.put(token, precedence); + public void addOp(String operatorToken, IPrecedent precedence) { + if (operatorToken == null) { + throw new NullPointerException("Operator must not be null"); + } + + operators.put(operatorToken, precedence); } private boolean isHigherPrec(String operator, String rightOperator) { @@ -155,7 +177,14 @@ public class ShuntingYard<E> { */ public FunctionalList<E> postfix(FunctionalList<String> input, Function<String, E> tokenTransformer) { + if (input == null) { + throw new NullPointerException("Input must not be null"); + } else if (tokenTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + FunctionalList<E> output = new FunctionalList<>(); + Deque<String> stack = new LinkedList<>(); input.forEach(new TokenShunter(output, stack, tokenTransformer)); @@ -174,6 +203,10 @@ public class ShuntingYard<E> { * The token representing the operator */ public void removeOp(String tok) { + if (tok == null) { + throw new NullPointerException("Token must not be null"); + } + operators.remove(tok); } }
\ No newline at end of file diff --git a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java index 42d5a9d..28ca1e3 100644 --- a/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java +++ b/BJC-Utils2/src/main/java/bjc/utils/parserutils/TreeConstructor.java @@ -2,10 +2,12 @@ package bjc.utils.parserutils; import java.util.Deque; import java.util.LinkedList; +import java.util.function.Consumer; import java.util.function.Function; import java.util.function.Predicate; import bjc.utils.data.GenHolder; +import bjc.utils.data.IPair; import bjc.utils.data.Pair; import bjc.utils.funcdata.FunctionalList; @@ -16,6 +18,89 @@ import bjc.utils.funcdata.FunctionalList; * */ public class TreeConstructor { + private static final class TokenTransformer<T> implements Consumer<T> { + private final class OperatorHandler implements + Function<IPair<Deque<AST<T>>, AST<T>>, IPair<Deque<AST<T>>, AST<T>>> { + private T element; + + public OperatorHandler(T element) { + this.element = element; + } + + @Override + public IPair<Deque<AST<T>>, AST<T>> + apply(IPair<Deque<AST<T>>, AST<T>> pair) { + Deque<AST<T>> queuedASTs = + pair.merge((queue, currentAST) -> queue); + + AST<T> mergedAST = pair.merge((queue, currentAST) -> { + AST<T> newAST; + + if (isSpecialOperator.test(element)) { + newAST = handleSpecialOperator.apply(queue); + } else { + if (queue.size() < 2) { + throw new IllegalStateException( + "Attempted to parse binary operator without enough operands"); + } + + AST<T> rightAST = queue.pop(); + AST<T> leftAST = queue.pop(); + + newAST = new AST<>(element, leftAST, rightAST); + } + + queue.push(newAST); + return newAST; + }); + + Pair<Deque<AST<T>>, AST<T>> newPair = + new Pair<>(queuedASTs, mergedAST); + + return newPair; + } + } + + private GenHolder<IPair<Deque<AST<T>>, AST<T>>> initialState; + private Predicate<T> operatorPredicate; + private Predicate<T> isSpecialOperator; + private Function<Deque<AST<T>>, AST<T>> handleSpecialOperator; + + public TokenTransformer( + GenHolder<IPair<Deque<AST<T>>, AST<T>>> initialState, + Predicate<T> operatorPredicate, + Predicate<T> isSpecialOperator, + Function<Deque<AST<T>>, AST<T>> handleSpecialOperator) { + this.initialState = initialState; + this.operatorPredicate = operatorPredicate; + this.isSpecialOperator = isSpecialOperator; + this.handleSpecialOperator = handleSpecialOperator; + } + + @Override + public void accept(T element) { + if (operatorPredicate.test(element)) { + initialState.transform(new OperatorHandler(element)); + } else { + AST<T> newAST = new AST<>(element); + + initialState.doWith((pair) -> { + pair.doWith((queue, currentAST) -> { + queue.push(newAST); + }); + }); + + initialState.transform((pair) -> { + return pair.apply((Deque<AST<T>> queue) -> { + return queue; + }, (AST<T> currentAST) -> { + return newAST; + }); + }); + } + } + } + /** * Construct a tree from a list of tokens in postfix notation * @@ -29,10 +114,6 @@ public class TreeConstructor { * The predicate to use to determine if something is a * operator * @return A AST from the expression - * - * @deprecated Use - * {@link TreeConstructor#constructTree(FunctionalList, Predicate, Predicate, Function)} - * instead */ public static <T> AST<T> constructTree(FunctionalList<T> tokens, Predicate<T> operatorPredicate) { @@ -43,7 +124,8 @@ public class TreeConstructor { /** * Construct a tree from a list of tokens in postfix notation * - * Only binary operators are accepted. + * Only binary operators are accepted by default. Use the last two + * parameters to handle non-binary operators * * @param <T> * The elements of the parse tree @@ -66,52 +148,25 @@ public class TreeConstructor { public static <T> AST<T> constructTree(FunctionalList<T> tokens, Predicate<T> operatorPredicate, Predicate<T> isSpecialOperator, Function<Deque<AST<T>>, AST<T>> handleSpecialOperator) { - GenHolder<Pair<Deque<AST<T>>, AST<T>>> initialState = - new GenHolder<>(new Pair<>(new LinkedList<>(), null)); - - tokens.forEach((element) -> { - if (operatorPredicate.test(element)) { - initialState.transform((pair) -> { - Deque<AST<T>> queuedASTs = - pair.merge((queue, currentAST) -> queue); - - AST<T> mergedAST = pair.merge((queue, currentAST) -> { - AST<T> newAST; - - if (isSpecialOperator.test(element)) { - newAST = handleSpecialOperator.apply(queue); - } else { - AST<T> rightAST = queue.pop(); - AST<T> leftAST = queue.pop(); - - newAST = new AST<>(element, leftAST, rightAST); - } + if (tokens == null) { + throw new NullPointerException("Tokens must not be null"); + } else if (operatorPredicate == null) { + throw new NullPointerException( + "Operator predicate must not be null"); + } else if (isSpecialOperator == null) { + throw new NullPointerException( + "Special operator determiner must not be null"); + } - queue.push(newAST); - return newAST; - }); - - Pair<Deque<AST<T>>, AST<T>> newPair = - new Pair<>(queuedASTs, mergedAST); - - return newPair; - }); - } else { - AST<T> newAST = new AST<>(element); + GenHolder<IPair<Deque<AST<T>>, AST<T>>> initialState = + new GenHolder<>(new Pair<>(new LinkedList<>(), null)); - initialState.doWith( - (pair) -> pair.doWith((queue, currentAST) -> { - queue.push(newAST); - })); + tokens.forEach( + new TokenTransformer<>(initialState, operatorPredicate, + isSpecialOperator, handleSpecialOperator)); - initialState.transform((pair) -> { - return (Pair<Deque<AST<T>>, AST<T>>) pair.apply( - (queue) -> queue, (currentAST) -> newAST); - }); - } + return initialState.unwrap((pair) -> { + return pair.merge((queue, currentAST) -> currentAST); }); - - return initialState.unwrap( - (pair) -> pair.merge((queue, currentAST) -> currentAST)); } } |
