From 1c8bc7132d980c1ff2dbd6b9af579c3b2fd8c63e Mon Sep 17 00:00:00 2001 From: bculkin2442 Date: Sun, 3 Apr 2016 19:22:48 -0400 Subject: General code refactoring and maintenance --- .../components/ComponentDescriptionFileParser.java | 8 +- .../utils/components/FileComponentRepository.java | 2 +- .../bjc/utils/components/IComponentRepository.java | 2 +- .../bjc/utils/components/IDescribedComponent.java | 2 +- .../java/bjc/utils/components/package-info.java | 7 + .../java/bjc/utils/configuration/ConfigFile.java | 19 ++- .../src/main/java/bjc/utils/data/GenHolder.java | 20 +++ BJC-Utils2/src/main/java/bjc/utils/data/Pair.java | 13 ++ .../src/main/java/bjc/utils/data/lazy/ILazy.java | 35 ++++ .../main/java/bjc/utils/data/lazy/LazyHolder.java | 57 ++++++- .../main/java/bjc/utils/data/lazy/LazyPair.java | 77 ++++++++- .../java/bjc/utils/funcdata/FunctionalList.java | 123 +++++++++++--- .../java/bjc/utils/funcdata/FunctionalMap.java | 151 +++++++++++++++++ .../utils/funcdata/FunctionalStringTokenizer.java | 47 +++++- .../bjc/utils/funcdata/bst/BinarySearchTree.java | 11 ++ .../utils/funcdata/bst/BinarySearchTreeLeaf.java | 12 ++ .../utils/funcdata/bst/BinarySearchTreeNode.java | 27 ++++ .../main/java/bjc/utils/funcutils/ListUtils.java | 129 ++++++++++----- .../main/java/bjc/utils/funcutils/StringUtils.java | 6 + .../src/main/java/bjc/utils/gen/RandomGrammar.java | 4 + .../main/java/bjc/utils/gen/WeightedGrammar.java | 109 ++++++++++++- .../main/java/bjc/utils/gen/WeightedRandom.java | 7 +- .../main/java/bjc/utils/graph/AdjacencyMap.java | 118 +++++++++++--- BJC-Utils2/src/main/java/bjc/utils/graph/Edge.java | 62 +++++++ .../src/main/java/bjc/utils/graph/Graph.java | 26 +-- .../java/bjc/utils/gui/ExtensionFileFilter.java | 4 + .../java/bjc/utils/gui/ListParameterPanel.java | 72 ++++++--- .../src/main/java/bjc/utils/gui/SimpleDialogs.java | 43 ++++- .../main/java/bjc/utils/gui/SimpleFileChooser.java | 32 ++++ .../src/main/java/bjc/utils/gui/SimpleJList.java | 8 + .../java/bjc/utils/gui/SimpleTitledBorder.java | 1 - .../bjc/utils/gui/awt/ExtensionFileFilter.java | 4 + .../java/bjc/utils/gui/awt/SimpleFileDialog.java | 12 ++ .../src/main/java/bjc/utils/parserutils/AST.java | 179 +++++++++++++-------- .../utils/parserutils/RuleBasedConfigReader.java | 110 +++++++++---- .../java/bjc/utils/parserutils/ShuntingYard.java | 119 +++++++++----- .../bjc/utils/parserutils/TreeConstructor.java | 151 +++++++++++------ 37 files changed, 1480 insertions(+), 329 deletions(-) create mode 100644 BJC-Utils2/src/main/java/bjc/utils/components/package-info.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/data/lazy/ILazy.java create mode 100644 BJC-Utils2/src/main/java/bjc/utils/funcdata/FunctionalMap.java (limited to 'BJC-Utils2/src') 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 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 { * @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 implements IHolder { */ @Override public void doWith(Consumer action) { + if (action == null) { + throw new NullPointerException("Action must be non-null"); + } + action.accept(heldValue); } @@ -56,6 +60,10 @@ public class GenHolder implements IHolder { */ @Override public IHolder map(Function 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 implements IHolder { */ @Override public IHolder transform(Function 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 implements IHolder { */ @Override public E unwrap(Function 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 implements IPair { @Override public IPair apply(Function leftTransformer, Function 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 implements IPair { */ @Override public void doWith(BiConsumer action) { + if (action == null) { + throw new NullPointerException("Action must be non-null"); + } + action.accept(leftValue, rightValue); } @@ -77,6 +86,10 @@ public class Pair implements IPair { */ @Override public E merge(BiFunction 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 * The type of the data being held */ -public class LazyHolder implements IHolder { +public class LazyHolder implements IHolder, ILazy { private final class LazyHolderSupplier implements Supplier { private FunctionalList> pendingActions; @@ -70,8 +70,11 @@ public class LazyHolder implements IHolder { * The supplier for a value when it is neededs */ public LazyHolder(Supplier 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 implements IHolder { @Override public void doWith(Consumer 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 implements IHolder { @Override public IHolder map(Function 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 implements IHolder { @Override public IHolder transform(Function 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 implements IHolder { @Override public E unwrap(Function 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 implements IHolder { 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 * The type of value stored on the right side of the pair */ -public class LazyPair implements IPair { +public class LazyPair implements IPair, ILazy { /** * The backing store for this pair */ - protected IHolder> delegatePair; + protected IHolder> delegatePair; + + private boolean materialized = false; + private boolean pendingActions = false; /** * Create a new blank lazy pair @@ -43,6 +46,8 @@ public class LazyPair implements IPair { * 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 implements IPair { */ public LazyPair(Supplier leftValueSource, Supplier 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 implements IPair { * @param delegate * The internal delegate for the pair */ - private LazyPair(IHolder> delegate) { + private LazyPair(IHolder> delegate, boolean mater, + boolean pend) { + materialized = mater; + pendingActions = pend; + delegatePair = delegate; } @@ -81,10 +94,15 @@ public class LazyPair implements IPair { @Override public IPair apply(Function leftTransform, Function rightTransform) { - IHolder> 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> newPair = + delegatePair.map((currentPair) -> currentPair + .apply(leftTransform, rightTransform)); + + return new LazyPair<>(newPair, materialized, true); } /* @@ -94,6 +112,12 @@ public class LazyPair implements IPair { */ @Override public void doWith(BiConsumer 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 implements IPair { */ @Override public E merge(BiFunction 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 implements Cloneable { * The source for a backing list */ public FunctionalList(FunctionalList 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 implements Cloneable { * The list to use as a backing list. */ public FunctionalList(List backingList) { + if (backingList == null) { + throw new NullPointerException( + "Backing list must be non-null"); + } + wrappedList = backingList; } @@ -112,6 +123,10 @@ public class FunctionalList implements Cloneable { * predicate. */ public boolean allMatch(Predicate 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 implements Cloneable { * predicate. */ public boolean anyMatch(Predicate 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 implements Cloneable { public FunctionalList combineWith( FunctionalList rightList, BiFunction 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 returnedList = new FunctionalList<>(); // Get the iterator for the other list Iterator rightIterator = rightList.toIterable().iterator(); - for (Iterator leftIterator = wrappedList - .iterator(); leftIterator.hasNext() + for (Iterator 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 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 implements Cloneable { * @return A new list containing the flattened results of applying the * provided function. */ - public FunctionalList flatMap( - Function> elementExpander) { - FunctionalList returnedList = new FunctionalList<>( - this.wrappedList.size()); + public FunctionalList + flatMap(Function> elementExpander) { + if (elementExpander == null) { + throw new NullPointerException("Expander must not be null"); + } + + FunctionalList returnedList = + new FunctionalList<>(this.wrappedList.size()); forEach(element -> { - FunctionalList expandedElement = elementExpander - .apply(element); + FunctionalList 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 implements Cloneable { * The action to apply to each member of the list. */ public void forEach(Consumer action) { + if (action == null) { + throw new NullPointerException("Action is null"); + } + wrappedList.forEach(action); } @@ -261,6 +305,10 @@ public class FunctionalList implements Cloneable { * index. */ public void forEachIndexed(BiConsumer 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 currentIndex = new GenHolder<>(0); @@ -302,6 +350,10 @@ public class FunctionalList implements Cloneable { * @return A list containing all elements that match the predicate */ public FunctionalList getMatching(Predicate matchPredicate) { + if (matchPredicate == null) { + throw new NullPointerException("Predicate must not be null"); + } + FunctionalList returnedList = new FunctionalList<>(); wrappedList.forEach((element) -> { @@ -344,8 +396,12 @@ public class FunctionalList implements Cloneable { * @return A new list containing the mapped elements of this list. */ public FunctionalList map(Function elementTransformer) { - FunctionalList returnedList = new FunctionalList<>( - this.wrappedList.size()); + if (elementTransformer == null) { + throw new NullPointerException("Transformer must be not null"); + } + + FunctionalList returnedList = + new FunctionalList<>(this.wrappedList.size()); forEach(element -> { // Add the transformed item to the result @@ -366,8 +422,8 @@ public class FunctionalList implements Cloneable { * @return A list containing pairs of this element and the specified * list */ - public FunctionalList> pairWith( - FunctionalList rightList) { + public FunctionalList> + pairWith(FunctionalList rightList) { return combineWith(rightList, Pair::new); } @@ -378,13 +434,21 @@ public class FunctionalList 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> partition( - int numberPerPartition) { - FunctionalList> returnedList = new FunctionalList<>(); + public FunctionalList> + 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> returnedList = + new FunctionalList<>(); // The current partition being filled - GenHolder> currentPartition = new GenHolder<>( - new FunctionalList<>()); + GenHolder> currentPartition = + new GenHolder<>(new FunctionalList<>()); this.forEach((element) -> { if (isPartitionFull(numberPerPartition, currentPartition)) { @@ -433,6 +497,11 @@ public class FunctionalList 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 implements Cloneable { public F reduceAux(T initialValue, BiFunction stateAccumulator, Function 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 currentState = new GenHolder<>(initialValue); @@ -481,6 +556,10 @@ public class FunctionalList implements Cloneable { * @return Whether there was anything that satisfied the predicate */ public boolean removeIf(Predicate removePredicate) { + if (removePredicate == null) { + throw new NullPointerException("Predicate must be non-null"); + } + return wrappedList.removeIf(removePredicate); } @@ -502,7 +581,8 @@ public class FunctionalList 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 comparator) { @@ -524,7 +604,8 @@ public class FunctionalList 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 comparator) { Collections.sort(wrappedList, comparator); @@ -554,7 +635,7 @@ public class FunctionalList 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 + * The type of this map's keys + * @param + * The type of this map's values + * + */ +public class FunctionalMap { + private final class TransformedMap extends FunctionalMap { + private FunctionalMap mapToTransform; + private Function transformer; + + public TransformedMap(FunctionalMap destMap, + Function transform) { + mapToTransform = destMap; + transformer = transform; + } + + @Override + public V2 get(K key) { + return transformer.apply(mapToTransform.get(key)); + } + } + + private Map 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 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... entries) { + this(); + + for (Pair 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 + * 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 FunctionalMap + mapValues(Function 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 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,19 +137,33 @@ public class FunctionalStringTokenizer { * The function to use to convert tokens. * @return A list containing all of the converted tokens. */ - public FunctionalList toList(Function tokenTransformer) { + public FunctionalList + toList(Function tokenTransformer) { + if (tokenTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + FunctionalList returnList = new FunctionalList<>(); // Add each token to the list after transforming it forEachToken(token -> { E transformedToken = tokenTransformer.apply(token); - + returnList.add(transformedToken); }); return returnList; } + /** + * Convert this tokenizer into a list of strings + * + * @return This tokenizer, converted into a list of strings + */ + public FunctionalList toList() { + return toList((String element) -> element); + } + /** * Check if 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 { * The thing to use for comparing elements */ public BinarySearchTree(Comparator cmp) { + if (cmp == null) { + throw new NullPointerException("Comparator must not be null"); + } + elementCount = 0; comparator = cmp; } @@ -169,6 +173,13 @@ public class BinarySearchTree { */ public void traverse(TreeLinearizationMethod linearizationMethod, Predicate 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 implements ITreePart { @Override public E collapse(Function leafTransformer, BiFunction branchCollapser) { + if (leafTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + return leafTransformer.apply(data); } @@ -101,6 +105,10 @@ public class BinarySearchTreeLeaf implements ITreePart { */ @Override public boolean directedWalk(DirectedWalkFunction 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 implements ITreePart { @Override public boolean forEach(TreeLinearizationMethod linearizationMethod, Predicate 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 extends BinarySearchTreeLeaf { */ @Override public void add(T element, Comparator 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 extends BinarySearchTreeLeaf { @Override public E collapse(Function nodeCollapser, BiFunction 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 extends BinarySearchTreeLeaf { @Override public boolean contains(T element, Comparator 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 extends BinarySearchTreeLeaf { @Override public void delete(T element, Comparator 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 extends BinarySearchTreeLeaf { @Override public boolean directedWalk(DirectedWalkFunction 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 extends BinarySearchTreeLeaf { @Override public boolean forEach(TreeLinearizationMethod linearizationMethod, Predicate 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 apply(String opName, - String opRegex) { - if (StringUtils.containsOnly(token, opRegex)) { + public FunctionalList 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 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 splitTokens = new FunctionalList<>( - tokenToSplit.split(operatorRegex)); + FunctionalList splitTokens = + new FunctionalList<>( + tokenToSplit.split(operatorRegex)); FunctionalList result = new FunctionalList<>(); @@ -161,16 +178,29 @@ public class ListUtils { public static FunctionalList> groupPartition( FunctionalList input, Function 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> returnedList = new FunctionalList<>(); + FunctionalList> returnedList = + new FunctionalList<>(); /* * List that holds current partition */ - GenHolder> currentPartition = new GenHolder<>( - new FunctionalList<>()); + GenHolder> 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 splitTokens( FunctionalList input, Deque> operators) { - GenHolder> 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> 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 deAffixTokens( FunctionalList input, - Deque> ops) { - GenHolder> returnedList = new GenHolder<>( - input); + Deque> 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> 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 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 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 extends WeightedGrammar { * The cases to add for this rule. */ public void makeRule(E rule, FunctionalList> 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 { 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 cse) { - rules.get(rule).addProbability(probability, cse); + public void addCase(E ruleName, int probability, + FunctionalList 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 { * @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 { 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 { */ public boolean addRule(E name, WeightedRandom> 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 { * @return Whether or not the subgrammar was succesfully added. */ public boolean addSubgrammar(E name, WeightedGrammar 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 { */ public FunctionalList> generateDebugValues(E ruleName) { + if (ruleName == null) { + throw new NullPointerException("Rule name must not be null"); + } + FunctionalList> returnedList = new FunctionalList<>(); @@ -187,6 +229,15 @@ public class WeightedGrammar { */ public FunctionalList generateGenericValues(E initRule, Function 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 returnedList = new FunctionalList<>(); if (subgrammars.containsKey(initRule)) { @@ -205,7 +256,14 @@ public class WeightedGrammar { 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 { * @return The subgrammar with the specified name. */ public WeightedGrammar 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 { */ 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> rule = rules.get(ruleName); FunctionalList>> newResults = @@ -315,6 +388,13 @@ public class WeightedGrammar { */ 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> rule = rules.get(ruleName); FunctionalList>> newResults = @@ -340,6 +420,10 @@ public class WeightedGrammar { * 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 { * 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 { * * @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> rule = rules.get(ruleName); FunctionalList>> newResults = @@ -401,7 +496,7 @@ public class WeightedGrammar { rule.getValues().forEach((par) -> { FunctionalList 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 { 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 { public FunctionalList> 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 { * @return An adjacency map defined by the text */ public static AdjacencyMap fromStream(InputStream stream) { - Scanner inputSource = new Scanner(stream); - inputSource.useDelimiter("\n"); - - // First, read in number of vertices - int numVertices = Integer.parseInt(inputSource.next()); - - Set 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 adjacencyMap = new AdjacencyMap<>(vertices); + AdjacencyMap adjacencyMap; - GenHolder 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 vertices = new HashSet<>(); + + IntStream.range(0, numVertices) + .forEach(element -> vertices.add(element)); + + adjacencyMap = new AdjacencyMap<>(vertices); + + GenHolder 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 { * The set of vertices to create a map from */ public AdjacencyMap(Set vertices) { + if (vertices == null) { + throw new NullPointerException("Vertices must not be null"); + } + vertices.forEach(vertex -> { Map vertexRow = new HashMap<>(); @@ -97,6 +145,7 @@ public class AdjacencyMap { adjacencyMap.entrySet().forEach(mapEntry -> { Set> entryVertices = mapEntry.getValue().entrySet(); + entryVertices.forEach(targetVertex -> { int leftValue = targetVertex.getValue(); int rightValue = adjacencyMap.get(targetVertex.getKey()) @@ -122,6 +171,22 @@ public class AdjacencyMap { * 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 { /** * 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 { * 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 { 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 { 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 { * 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 edgeMatcher, BiConsumer 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 { // 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 { 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 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) list.getModel()) - .addElement(addAction.get())); - editParam.addActionListener( - (event) -> editAction.accept(list.getSelectedValue())); - removeParam.addActionListener((event) -> removeAction - .accept(((DefaultListModel) 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) 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) 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 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 getValue(Component parent, String title, String prompt, Predicate inputValidator, Function 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 JList buildFromList(Iterable 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 ListModel buildModel(Iterable listSource) { + if (listSource == null) { + throw new NullPointerException("Source must not be null"); + } + DefaultListModel 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 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 { - 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 left; private AST right; + private T token; + /** * Create a new leaf AST node * @@ -52,43 +67,6 @@ public class AST { right = rght; } - /** - * 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 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 * @@ -104,11 +82,44 @@ public class AST { * The function for transforming the result * @return The collapsed value of the tree */ + @SuppressWarnings("unchecked") public E collapse(Function tokenTransformer, Function> nodeTransformer, Function 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 expand(Function> 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 { } T2 rightCollapsed; + if (right == null) { rightCollapsed = null; } else { @@ -141,15 +153,6 @@ public class AST { } } - @Override - public String toString() { - StringBuilder builder = new StringBuilder(); - - internalToString(builder, -1); - - return builder.toString(); - } - /** * Internal version of toString for proper rendering * @@ -186,21 +189,6 @@ public class AST { } } - /** - * 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 * @@ -211,6 +199,12 @@ public class AST { */ public void selectiveTransform(Predicate transformerPredicate, UnaryOperator 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 { } } + @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 { * @return The AST with transformed tokens */ public AST transmuteAST(Function tokenTransformer) { + if (tokenTransformer == null) { + throw new NullPointerException("Transformer must not be null"); + } + AST leftBranch = null; AST rightBranch = null; @@ -249,4 +256,48 @@ public class AST { 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 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 { */ public void addPragma(String pragmaName, BiConsumer 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 { * @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 { */ public void setStartRule( BiConsumer> 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 { - - private final class TokenShunter implements Consumer { - private FunctionalList output; - private Deque stack; - private Function transform; - - public TokenShunter(FunctionalList outpt, Deque stack, - Function 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 { } } + private final class TokenShunter implements Consumer { + private FunctionalList output; + private Deque stack; + private Function transform; + + public TokenShunter(FunctionalList outpt, Deque stack, + Function 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 { 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 { /** * 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 { */ public FunctionalList postfix(FunctionalList input, Function 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 output = new FunctionalList<>(); + Deque stack = new LinkedList<>(); input.forEach(new TokenShunter(output, stack, tokenTransformer)); @@ -174,6 +203,10 @@ public class ShuntingYard { * 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 implements Consumer { + private final class OperatorHandler implements + Function>, AST>, IPair>, AST>> { + private T element; + + public OperatorHandler(T element) { + this.element = element; + } + + @Override + public IPair>, AST> + apply(IPair>, AST> pair) { + Deque> queuedASTs = + pair.merge((queue, currentAST) -> queue); + + AST mergedAST = pair.merge((queue, currentAST) -> { + AST 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 rightAST = queue.pop(); + AST leftAST = queue.pop(); + + newAST = new AST<>(element, leftAST, rightAST); + } + + queue.push(newAST); + return newAST; + }); + + Pair>, AST> newPair = + new Pair<>(queuedASTs, mergedAST); + + return newPair; + } + } + + private GenHolder>, AST>> initialState; + private Predicate operatorPredicate; + private Predicate isSpecialOperator; + private Function>, AST> handleSpecialOperator; + + public TokenTransformer( + GenHolder>, AST>> initialState, + Predicate operatorPredicate, + Predicate isSpecialOperator, + Function>, AST> 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 newAST = new AST<>(element); + + initialState.doWith((pair) -> { + pair.doWith((queue, currentAST) -> { + queue.push(newAST); + }); + }); + + initialState.transform((pair) -> { + return pair.apply((Deque> queue) -> { + return queue; + }, (AST 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 AST constructTree(FunctionalList tokens, Predicate 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 * The elements of the parse tree @@ -66,52 +148,25 @@ public class TreeConstructor { public static AST constructTree(FunctionalList tokens, Predicate operatorPredicate, Predicate isSpecialOperator, Function>, AST> handleSpecialOperator) { - GenHolder>, AST>> initialState = - new GenHolder<>(new Pair<>(new LinkedList<>(), null)); - - tokens.forEach((element) -> { - if (operatorPredicate.test(element)) { - initialState.transform((pair) -> { - Deque> queuedASTs = - pair.merge((queue, currentAST) -> queue); - - AST mergedAST = pair.merge((queue, currentAST) -> { - AST newAST; - - if (isSpecialOperator.test(element)) { - newAST = handleSpecialOperator.apply(queue); - } else { - AST rightAST = queue.pop(); - AST 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>, AST> newPair = - new Pair<>(queuedASTs, mergedAST); - - return newPair; - }); - } else { - AST newAST = new AST<>(element); + GenHolder>, AST>> 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>, AST>) 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)); } } -- cgit v1.2.3